1*4d2890e1Sthorpej /* $NetBSD: axp809.c,v 1.3 2019/07/27 16:02:27 thorpej Exp $ */
288804502Sjmcneill
388804502Sjmcneill /*-
488804502Sjmcneill * Copyright (c) 2014 Jared D. McNeill <jmcneill@invisible.ca>
588804502Sjmcneill * All rights reserved.
688804502Sjmcneill *
788804502Sjmcneill * Redistribution and use in source and binary forms, with or without
888804502Sjmcneill * modification, are permitted provided that the following conditions
988804502Sjmcneill * are met:
1088804502Sjmcneill * 1. Redistributions of source code must retain the above copyright
1188804502Sjmcneill * notice, this list of conditions and the following disclaimer.
1288804502Sjmcneill * 2. Redistributions in binary form must reproduce the above copyright
1388804502Sjmcneill * notice, this list of conditions and the following disclaimer in the
1488804502Sjmcneill * documentation and/or other materials provided with the distribution.
1588804502Sjmcneill *
1688804502Sjmcneill * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
1788804502Sjmcneill * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
1888804502Sjmcneill * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
1988804502Sjmcneill * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
2088804502Sjmcneill * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
2188804502Sjmcneill * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
2288804502Sjmcneill * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
2388804502Sjmcneill * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
2488804502Sjmcneill * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
2588804502Sjmcneill * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
2688804502Sjmcneill * POSSIBILITY OF SUCH DAMAGE.
2788804502Sjmcneill */
2888804502Sjmcneill
2988804502Sjmcneill #define AXP_DEBUG
3088804502Sjmcneill
3188804502Sjmcneill #include <sys/cdefs.h>
32*4d2890e1Sthorpej __KERNEL_RCSID(0, "$NetBSD: axp809.c,v 1.3 2019/07/27 16:02:27 thorpej Exp $");
3388804502Sjmcneill
3488804502Sjmcneill #include <sys/param.h>
3588804502Sjmcneill #include <sys/systm.h>
3688804502Sjmcneill #include <sys/kernel.h>
3788804502Sjmcneill #include <sys/device.h>
3888804502Sjmcneill #include <sys/conf.h>
3988804502Sjmcneill #include <sys/bus.h>
4088804502Sjmcneill #include <sys/kmem.h>
4188804502Sjmcneill
4288804502Sjmcneill #include <dev/i2c/i2cvar.h>
4388804502Sjmcneill #include <dev/i2c/axp809.h>
4488804502Sjmcneill
4588804502Sjmcneill #define AXP_GPIO1_CTRL_REG 0x92
4688804502Sjmcneill #define AXP_GPIO1LDO_CTRL_REG 0x93
4788804502Sjmcneill
4888804502Sjmcneill struct axp809_ctrl {
4988804502Sjmcneill device_t c_dev;
5088804502Sjmcneill
5188804502Sjmcneill const char * c_name;
5288804502Sjmcneill u_int c_min;
5388804502Sjmcneill u_int c_max;
5488804502Sjmcneill u_int c_step1;
5588804502Sjmcneill u_int c_step1cnt;
5688804502Sjmcneill u_int c_step2;
5788804502Sjmcneill u_int c_step2cnt;
5888804502Sjmcneill
5988804502Sjmcneill uint8_t c_enable_reg;
6088804502Sjmcneill uint8_t c_enable_mask;
6188804502Sjmcneill
6288804502Sjmcneill uint8_t c_voltage_reg;
6388804502Sjmcneill uint8_t c_voltage_mask;
6488804502Sjmcneill };
6588804502Sjmcneill
6688804502Sjmcneill #define AXP_CTRL(name, min, max, step, ereg, emask, vreg, vmask) \
6788804502Sjmcneill { .c_name = (name), .c_min = (min), .c_max = (max), \
6888804502Sjmcneill .c_step1 = (step), .c_step1cnt = (((max) - (min)) / (step)) + 1, \
6988804502Sjmcneill .c_step2 = 0, .c_step2cnt = 0, \
7088804502Sjmcneill .c_enable_reg = AXP_##ereg##_REG, .c_enable_mask = (emask), \
7188804502Sjmcneill .c_voltage_reg = AXP_##vreg##_REG, .c_voltage_mask = (vmask) }
7288804502Sjmcneill
7388804502Sjmcneill #define AXP_CTRL2(name, min, max, step1, step1cnt, step2, step2cnt, ereg, emask, vreg, vmask) \
7488804502Sjmcneill { .c_name = (name), .c_min = (min), .c_max = (max), \
7588804502Sjmcneill .c_step1 = (step1), .c_step1cnt = (step1cnt), \
7688804502Sjmcneill .c_step2 = (step2), .c_step2cnt = (step2cnt), \
7788804502Sjmcneill .c_enable_reg = AXP_##ereg##_REG, .c_enable_mask = (emask), \
7888804502Sjmcneill .c_voltage_reg = AXP_##vreg##_REG, .c_voltage_mask = (vmask) }
7988804502Sjmcneill
8088804502Sjmcneill static const struct axp809_ctrl axp809_ctrls[] = {
8188804502Sjmcneill AXP_CTRL("GPIO1", 700, 3300, 100,
8288804502Sjmcneill GPIO1_CTRL, __BIT(0), GPIO1LDO_CTRL, __BITS(4,0)),
8388804502Sjmcneill };
8488804502Sjmcneill
8588804502Sjmcneill struct axp809_softc {
8688804502Sjmcneill device_t sc_dev;
8788804502Sjmcneill i2c_tag_t sc_i2c;
8888804502Sjmcneill i2c_addr_t sc_addr;
8988804502Sjmcneill
9088804502Sjmcneill u_int sc_nctrl;
9188804502Sjmcneill struct axp809_ctrl *sc_ctrl;
9288804502Sjmcneill };
9388804502Sjmcneill
9488804502Sjmcneill static int axp809_match(device_t, cfdata_t, void *);
9588804502Sjmcneill static void axp809_attach(device_t, device_t, void *);
9688804502Sjmcneill
9788804502Sjmcneill static int axp809_read(struct axp809_softc *, uint8_t, uint8_t *);
9888804502Sjmcneill static int axp809_write(struct axp809_softc *, uint8_t, uint8_t);
9988804502Sjmcneill
10088804502Sjmcneill static void axp809_print(struct axp809_ctrl *c);
10188804502Sjmcneill
10288804502Sjmcneill CFATTACH_DECL_NEW(axp809pm, sizeof(struct axp809_softc),
10388804502Sjmcneill axp809_match, axp809_attach, NULL, NULL);
10488804502Sjmcneill
10588804502Sjmcneill static int
axp809_match(device_t parent,cfdata_t match,void * aux)10688804502Sjmcneill axp809_match(device_t parent, cfdata_t match, void *aux)
10788804502Sjmcneill {
108aa41e992Sthorpej struct i2c_attach_args *ia = aux;
109aa41e992Sthorpej int match_result;
110aa41e992Sthorpej
111aa41e992Sthorpej if (iic_use_direct_match(ia, match, NULL, &match_result))
112aa41e992Sthorpej return match_result;
113aa41e992Sthorpej
114aa41e992Sthorpej /* This device is direct-config only. */
115aa41e992Sthorpej
116aa41e992Sthorpej return 0;
11788804502Sjmcneill }
11888804502Sjmcneill
11988804502Sjmcneill static void
axp809_attach(device_t parent,device_t self,void * aux)12088804502Sjmcneill axp809_attach(device_t parent, device_t self, void *aux)
12188804502Sjmcneill {
12288804502Sjmcneill struct axp809_softc *sc = device_private(self);
12388804502Sjmcneill struct i2c_attach_args *ia = aux;
12488804502Sjmcneill u_int n;
12588804502Sjmcneill
12688804502Sjmcneill sc->sc_dev = self;
12788804502Sjmcneill sc->sc_i2c = ia->ia_tag;
12888804502Sjmcneill sc->sc_addr = ia->ia_addr;
12988804502Sjmcneill
13088804502Sjmcneill aprint_naive("\n");
13188804502Sjmcneill aprint_normal("\n");
13288804502Sjmcneill
13388804502Sjmcneill sc->sc_nctrl = __arraycount(axp809_ctrls);
13488804502Sjmcneill sc->sc_ctrl = kmem_alloc(sizeof(axp809_ctrls), KM_SLEEP);
13588804502Sjmcneill memcpy(sc->sc_ctrl, axp809_ctrls, sizeof(axp809_ctrls));
13688804502Sjmcneill for (n = 0; n < sc->sc_nctrl; n++) {
13788804502Sjmcneill sc->sc_ctrl[n].c_dev = self;
13888804502Sjmcneill }
13988804502Sjmcneill
14088804502Sjmcneill #ifdef AXP_DEBUG
14188804502Sjmcneill for (n = 0; n < sc->sc_nctrl; n++) {
14288804502Sjmcneill axp809_print(&sc->sc_ctrl[n]);
14388804502Sjmcneill }
14488804502Sjmcneill #endif
14588804502Sjmcneill }
14688804502Sjmcneill
14788804502Sjmcneill static int
axp809_read(struct axp809_softc * sc,uint8_t reg,uint8_t * val)14888804502Sjmcneill axp809_read(struct axp809_softc *sc, uint8_t reg, uint8_t *val)
14988804502Sjmcneill {
150*4d2890e1Sthorpej return iic_smbus_read_byte(sc->sc_i2c, sc->sc_addr, reg, val, 0);
15188804502Sjmcneill }
15288804502Sjmcneill
15388804502Sjmcneill static int
axp809_write(struct axp809_softc * sc,uint8_t reg,uint8_t val)15488804502Sjmcneill axp809_write(struct axp809_softc *sc, uint8_t reg, uint8_t val)
15588804502Sjmcneill {
156*4d2890e1Sthorpej return iic_smbus_write_byte(sc->sc_i2c, sc->sc_addr, reg, val, 0);
15788804502Sjmcneill }
15888804502Sjmcneill
15988804502Sjmcneill static void
axp809_print(struct axp809_ctrl * c)16088804502Sjmcneill axp809_print(struct axp809_ctrl *c)
16188804502Sjmcneill {
16288804502Sjmcneill struct axp809_softc *sc = device_private(c->c_dev);
16388804502Sjmcneill u_int voltage;
16488804502Sjmcneill bool enabled;
16588804502Sjmcneill
16688804502Sjmcneill device_printf(sc->sc_dev, "%s:", c->c_name);
16788804502Sjmcneill if (c->c_voltage_reg) {
16888804502Sjmcneill if (axp809_get_voltage(c, &voltage)) {
16988804502Sjmcneill printf(" [??? V]");
17088804502Sjmcneill } else {
17188804502Sjmcneill printf(" [%d.%03dV]", voltage / 1000,
17288804502Sjmcneill voltage % 1000);
17388804502Sjmcneill }
17488804502Sjmcneill }
17588804502Sjmcneill if (c->c_enable_reg) {
17688804502Sjmcneill if (axp809_is_enabled(c, &enabled)) {
17788804502Sjmcneill printf(" [unknown state]");
17888804502Sjmcneill } else {
17988804502Sjmcneill printf(" [%s]", enabled ? "ON" : "OFF");
18088804502Sjmcneill }
18188804502Sjmcneill }
18288804502Sjmcneill printf("\n");
18388804502Sjmcneill }
18488804502Sjmcneill
18588804502Sjmcneill struct axp809_ctrl *
axp809_lookup(device_t dev,const char * name)18688804502Sjmcneill axp809_lookup(device_t dev, const char *name)
18788804502Sjmcneill {
18888804502Sjmcneill struct axp809_softc *sc = device_private(dev);
18988804502Sjmcneill struct axp809_ctrl *c;
19088804502Sjmcneill u_int n;
19188804502Sjmcneill
19288804502Sjmcneill for (n = 0; n < sc->sc_nctrl; n++) {
19388804502Sjmcneill c = &sc->sc_ctrl[n];
19488804502Sjmcneill if (strcmp(c->c_name, name) == 0) {
19588804502Sjmcneill return c;
19688804502Sjmcneill }
19788804502Sjmcneill }
19888804502Sjmcneill
19988804502Sjmcneill return NULL;
20088804502Sjmcneill }
20188804502Sjmcneill
20288804502Sjmcneill int
axp809_set_voltage(struct axp809_ctrl * c,u_int min,u_int max)20388804502Sjmcneill axp809_set_voltage(struct axp809_ctrl *c, u_int min, u_int max)
20488804502Sjmcneill {
20588804502Sjmcneill struct axp809_softc *sc = device_private(c->c_dev);
20688804502Sjmcneill u_int vol, reg_val;
20788804502Sjmcneill int nstep, error;
20888804502Sjmcneill uint8_t val;
20988804502Sjmcneill
21088804502Sjmcneill if (!c->c_voltage_mask)
21188804502Sjmcneill return EINVAL;
21288804502Sjmcneill
21388804502Sjmcneill if (min < c->c_min || min > c->c_max)
21488804502Sjmcneill return EINVAL;
21588804502Sjmcneill
21688804502Sjmcneill reg_val = 0;
21788804502Sjmcneill nstep = 1;
21888804502Sjmcneill vol = c->c_min;
21988804502Sjmcneill
22088804502Sjmcneill for (nstep = 0; nstep < c->c_step1cnt && vol < min; nstep++) {
22188804502Sjmcneill ++reg_val;
22288804502Sjmcneill vol += c->c_step1;
22388804502Sjmcneill }
22488804502Sjmcneill for (nstep = 0; nstep < c->c_step2cnt && vol < min; nstep++) {
22588804502Sjmcneill ++reg_val;
22688804502Sjmcneill vol += c->c_step2;
22788804502Sjmcneill }
22888804502Sjmcneill
22988804502Sjmcneill if (vol > max)
23088804502Sjmcneill return EINVAL;
23188804502Sjmcneill
23288804502Sjmcneill iic_acquire_bus(sc->sc_i2c, 0);
23388804502Sjmcneill if ((error = axp809_read(sc, c->c_voltage_reg, &val)) != 0)
23488804502Sjmcneill goto done;
23588804502Sjmcneill val &= ~c->c_voltage_mask;
23688804502Sjmcneill val |= __SHIFTIN(reg_val, c->c_voltage_mask);
23788804502Sjmcneill error = axp809_write(sc, c->c_voltage_reg, val);
23888804502Sjmcneill done:
23988804502Sjmcneill iic_release_bus(sc->sc_i2c, 0);
24088804502Sjmcneill #ifdef AXP_DEBUG
24188804502Sjmcneill if (error == 0)
24288804502Sjmcneill axp809_print(c);
24388804502Sjmcneill #endif
24488804502Sjmcneill
24588804502Sjmcneill return error;
24688804502Sjmcneill }
24788804502Sjmcneill
24888804502Sjmcneill int
axp809_get_voltage(struct axp809_ctrl * c,u_int * pvol)24988804502Sjmcneill axp809_get_voltage(struct axp809_ctrl *c, u_int *pvol)
25088804502Sjmcneill {
25188804502Sjmcneill struct axp809_softc *sc = device_private(c->c_dev);
25288804502Sjmcneill int reg_val, error;
25388804502Sjmcneill uint8_t val;
25488804502Sjmcneill
25588804502Sjmcneill if (!c->c_voltage_mask)
25688804502Sjmcneill return EINVAL;
25788804502Sjmcneill
25888804502Sjmcneill iic_acquire_bus(sc->sc_i2c, 0);
25988804502Sjmcneill error = axp809_read(sc, c->c_voltage_reg, &val);
26088804502Sjmcneill iic_release_bus(sc->sc_i2c, 0);
26188804502Sjmcneill if (error)
26288804502Sjmcneill return error;
26388804502Sjmcneill
26488804502Sjmcneill reg_val = __SHIFTOUT(val, c->c_voltage_mask);
26588804502Sjmcneill if (reg_val < c->c_step1cnt) {
26688804502Sjmcneill *pvol = c->c_min + reg_val * c->c_step1;
26788804502Sjmcneill } else {
26888804502Sjmcneill *pvol = c->c_min + (c->c_step1cnt * c->c_step1) +
26988804502Sjmcneill ((reg_val - c->c_step1cnt) * c->c_step2);
27088804502Sjmcneill }
27188804502Sjmcneill
27288804502Sjmcneill return 0;
27388804502Sjmcneill }
27488804502Sjmcneill
27588804502Sjmcneill int
axp809_is_enabled(struct axp809_ctrl * c,bool * penabled)27688804502Sjmcneill axp809_is_enabled(struct axp809_ctrl *c, bool *penabled)
27788804502Sjmcneill {
27888804502Sjmcneill struct axp809_softc *sc = device_private(c->c_dev);
27988804502Sjmcneill uint8_t val;
28088804502Sjmcneill int error;
28188804502Sjmcneill
28288804502Sjmcneill if (!c->c_enable_mask)
28388804502Sjmcneill return EINVAL;
28488804502Sjmcneill
28588804502Sjmcneill iic_acquire_bus(sc->sc_i2c, 0);
28688804502Sjmcneill error = axp809_read(sc, c->c_enable_reg, &val);
28788804502Sjmcneill iic_release_bus(sc->sc_i2c, 0);
28888804502Sjmcneill if (error)
28988804502Sjmcneill return error;
29088804502Sjmcneill
29188804502Sjmcneill *penabled = !!(val & c->c_enable_mask);
29288804502Sjmcneill return 0;
29388804502Sjmcneill }
29488804502Sjmcneill
29588804502Sjmcneill int
axp809_enable(struct axp809_ctrl * c)29688804502Sjmcneill axp809_enable(struct axp809_ctrl *c)
29788804502Sjmcneill {
29888804502Sjmcneill struct axp809_softc *sc = device_private(c->c_dev);
29988804502Sjmcneill uint8_t val;
30088804502Sjmcneill int error;
30188804502Sjmcneill
30288804502Sjmcneill if (!c->c_enable_mask)
30388804502Sjmcneill return EINVAL;
30488804502Sjmcneill
30588804502Sjmcneill iic_acquire_bus(sc->sc_i2c, 0);
30688804502Sjmcneill if ((error = axp809_read(sc, c->c_enable_reg, &val)) != 0)
30788804502Sjmcneill goto done;
30888804502Sjmcneill val |= c->c_enable_mask;
30988804502Sjmcneill error = axp809_write(sc, c->c_enable_reg, val);
31088804502Sjmcneill done:
31188804502Sjmcneill iic_release_bus(sc->sc_i2c, 0);
31288804502Sjmcneill #ifdef AXP_DEBUG
31388804502Sjmcneill if (error == 0)
31488804502Sjmcneill axp809_print(c);
31588804502Sjmcneill #endif
31688804502Sjmcneill
31788804502Sjmcneill return error;
31888804502Sjmcneill }
31988804502Sjmcneill
32088804502Sjmcneill int
axp809_disable(struct axp809_ctrl * c)32188804502Sjmcneill axp809_disable(struct axp809_ctrl *c)
32288804502Sjmcneill {
32388804502Sjmcneill struct axp809_softc *sc = device_private(c->c_dev);
32488804502Sjmcneill uint8_t val;
32588804502Sjmcneill int error;
32688804502Sjmcneill
32788804502Sjmcneill if (!c->c_enable_mask)
32888804502Sjmcneill return EINVAL;
32988804502Sjmcneill
33088804502Sjmcneill iic_acquire_bus(sc->sc_i2c, 0);
33188804502Sjmcneill if ((error = axp809_read(sc, c->c_enable_reg, &val)) != 0)
33288804502Sjmcneill goto done;
33388804502Sjmcneill val &= ~c->c_enable_mask;
33488804502Sjmcneill error = axp809_write(sc, c->c_enable_reg, val);
33588804502Sjmcneill done:
33688804502Sjmcneill iic_release_bus(sc->sc_i2c, 0);
33788804502Sjmcneill #ifdef AXP_DEBUG
33888804502Sjmcneill if (error == 0)
33988804502Sjmcneill axp809_print(c);
34088804502Sjmcneill #endif
34188804502Sjmcneill
34288804502Sjmcneill return error;
34388804502Sjmcneill }
344