xref: /netbsd-src/sys/dev/i2c/axp809.c (revision 4d2890e121d913234847fd56b03b93e02f722549)
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