xref: /openbsd-src/sys/dev/fdt/fanpwr.c (revision f057eb46eec31c2ec2ac0ab0bb7833545e1bcb0e)
1*f057eb46Skettenis /*	$OpenBSD: fanpwr.c,v 1.10 2024/05/26 22:04:52 kettenis Exp $	*/
28bda9086Skettenis /*
38bda9086Skettenis  * Copyright (c) 2018 Mark Kettenis <kettenis@openbsd.org>
48bda9086Skettenis  *
58bda9086Skettenis  * Permission to use, copy, modify, and distribute this software for any
68bda9086Skettenis  * purpose with or without fee is hereby granted, provided that the above
78bda9086Skettenis  * copyright notice and this permission notice appear in all copies.
88bda9086Skettenis  *
98bda9086Skettenis  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
108bda9086Skettenis  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
118bda9086Skettenis  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
128bda9086Skettenis  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
138bda9086Skettenis  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
148bda9086Skettenis  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
158bda9086Skettenis  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
168bda9086Skettenis  */
178bda9086Skettenis 
188bda9086Skettenis #include <sys/param.h>
198bda9086Skettenis #include <sys/systm.h>
208bda9086Skettenis #include <sys/device.h>
218bda9086Skettenis #include <sys/malloc.h>
228bda9086Skettenis 
238bda9086Skettenis #include <dev/ofw/openfirm.h>
24edd07788Skettenis #include <dev/ofw/ofw_pinctrl.h>
258bda9086Skettenis #include <dev/ofw/ofw_regulator.h>
268bda9086Skettenis #include <dev/ofw/fdt.h>
278bda9086Skettenis 
288bda9086Skettenis #include <dev/i2c/i2cvar.h>
298bda9086Skettenis 
308bda9086Skettenis /* Registers */
318bda9086Skettenis #define FAN53555_VSEL0			0x00
328bda9086Skettenis #define FAN53555_VSEL1			0x01
338bda9086Skettenis #define  FAN53555_VSEL_NSEL_MASK	0x3f
348bda9086Skettenis #define FAN53555_CONTROL		0x02
358bda9086Skettenis #define  FAN53555_CONTROL_SLEW_MASK	(0x7 << 4)
368bda9086Skettenis #define  FAN53555_CONTROL_SLEW_SHIFT	4
378bda9086Skettenis #define FAN53555_ID1			0x03
388bda9086Skettenis #define FAN53555_ID2			0x04
398bda9086Skettenis 
40f478b554Skettenis #define TCS4525_VSEL1			0x10
41f478b554Skettenis #define TCS4525_VSEL0			0x11
42f478b554Skettenis #define  TCS4525_VSEL_NSEL_MASK		0x7f
43f478b554Skettenis #define TCS4525_TIME			0x13
44f478b554Skettenis #define  TCS4525_TIME_SLEW_MASK		(0x3 << 3)
45f478b554Skettenis #define  TCS4525_TIME_SLEW_SHIFT	3
46f478b554Skettenis 
47a90343f6Spatrick #define RK8602_VSEL0			0x06
48a90343f6Spatrick #define RK8602_VSEL1			0x07
49a90343f6Spatrick #define  RK8602_VSEL_NSEL_MASK		0xff
50a90343f6Spatrick 
519bec9e43Sjsg /* Distinguish between Fairchild original and Silergy clones. */
528bda9086Skettenis enum fanpwr_id {
538bda9086Skettenis 	FANPWR_FAN53555,	/* Fairchild FAN53555 */
54a90343f6Spatrick 	FANPWR_RK8602,		/* Rockchip RK8602 */
558bda9086Skettenis 	FANPWR_SYR827,		/* Silergy SYR827 */
56f478b554Skettenis 	FANPWR_SYR828,		/* Silergy SYR828 */
57f478b554Skettenis 	FANPWR_TCS4525,		/* TCS TCS4525 */
588bda9086Skettenis };
598bda9086Skettenis 
608bda9086Skettenis struct fanpwr_softc {
618bda9086Skettenis 	struct device	sc_dev;
628bda9086Skettenis 	i2c_tag_t	sc_tag;
638bda9086Skettenis 	i2c_addr_t	sc_addr;
648bda9086Skettenis 
658bda9086Skettenis 	enum fanpwr_id	sc_id;
668bda9086Skettenis 	uint8_t		sc_vsel;
67f478b554Skettenis 	uint8_t		sc_vsel_nsel_mask;
688bda9086Skettenis 
698bda9086Skettenis 	struct regulator_device sc_rd;
708bda9086Skettenis 	uint32_t	sc_vbase;
718bda9086Skettenis 	uint32_t	sc_vstep;
728bda9086Skettenis };
738bda9086Skettenis 
748bda9086Skettenis int	fanpwr_match(struct device *, void *, void *);
758bda9086Skettenis void	fanpwr_attach(struct device *, struct device *, void *);
768bda9086Skettenis 
779fdf0c62Smpi const struct cfattach fanpwr_ca = {
788bda9086Skettenis 	sizeof(struct fanpwr_softc), fanpwr_match, fanpwr_attach
798bda9086Skettenis };
808bda9086Skettenis 
818bda9086Skettenis struct cfdriver fanpwr_cd = {
828bda9086Skettenis 	NULL, "fanpwr", DV_DULL
838bda9086Skettenis };
848bda9086Skettenis 
858bda9086Skettenis uint8_t	fanpwr_read(struct fanpwr_softc *, int);
868bda9086Skettenis void	fanpwr_write(struct fanpwr_softc *, int, uint8_t);
878bda9086Skettenis uint32_t fanpwr_get_voltage(void *);
88ff8aec25Skettenis int	fanpwr_set_voltage(void *, uint32_t);
898bda9086Skettenis 
908bda9086Skettenis int
fanpwr_match(struct device * parent,void * match,void * aux)918bda9086Skettenis fanpwr_match(struct device *parent, void *match, void *aux)
928bda9086Skettenis {
938bda9086Skettenis 	struct i2c_attach_args *ia = aux;
948bda9086Skettenis 
951ac62f4eSpatrick 	return (strcmp(ia->ia_name, "fcs,fan53555") == 0 ||
96a90343f6Spatrick 	    strcmp(ia->ia_name, "rockchip,rk8602") == 0 ||
97a90343f6Spatrick 	    strcmp(ia->ia_name, "rockchip,rk8603") == 0 ||
981ac62f4eSpatrick 	    strcmp(ia->ia_name, "silergy,syr827") == 0 ||
99f478b554Skettenis 	    strcmp(ia->ia_name, "silergy,syr828") == 0 ||
100f478b554Skettenis 	    strcmp(ia->ia_name, "tcs,tcs4525") == 0);
1018bda9086Skettenis }
1028bda9086Skettenis 
1038bda9086Skettenis void
fanpwr_attach(struct device * parent,struct device * self,void * aux)1048bda9086Skettenis fanpwr_attach(struct device *parent, struct device *self, void *aux)
1058bda9086Skettenis {
1068bda9086Skettenis 	struct fanpwr_softc *sc = (struct fanpwr_softc *)self;
1078bda9086Skettenis 	struct i2c_attach_args *ia = aux;
1088bda9086Skettenis 	int node = *(int *)ia->ia_cookie;
1098bda9086Skettenis 	uint32_t voltage, ramp_delay;
1108bda9086Skettenis 	uint8_t id1, id2;
1118bda9086Skettenis 
112edd07788Skettenis 	pinctrl_byname(node, "default");
113edd07788Skettenis 
1148bda9086Skettenis 	sc->sc_tag = ia->ia_tag;
1158bda9086Skettenis 	sc->sc_addr = ia->ia_addr;
1168bda9086Skettenis 
117a90343f6Spatrick 	if (OF_is_compatible(node, "rockchip,rk8602") ||
118a90343f6Spatrick 	    OF_is_compatible(node, "rockchip,rk8603")) {
119a90343f6Spatrick 		printf(": RK8602");
120a90343f6Spatrick 		sc->sc_id = FANPWR_RK8602;
121a90343f6Spatrick 	} else if (OF_is_compatible(node, "silergy,syr827")) {
1228bda9086Skettenis 		printf(": SYR827");
1238bda9086Skettenis 		sc->sc_id = FANPWR_SYR827;
1248bda9086Skettenis 	} else if (OF_is_compatible(node, "silergy,syr828")) {
1258bda9086Skettenis 		printf(": SYR828");
1268bda9086Skettenis 		sc->sc_id = FANPWR_SYR828;
127f478b554Skettenis 	} else if (OF_is_compatible(node, "tcs,tcs4525")) {
128f478b554Skettenis 		printf(": TCS4525");
129f478b554Skettenis 		sc->sc_id = FANPWR_TCS4525;
1308bda9086Skettenis 	} else {
1318bda9086Skettenis 		printf(": FAN53555");
1328bda9086Skettenis 		sc->sc_id = FANPWR_FAN53555;
1338bda9086Skettenis 	}
1348bda9086Skettenis 
135f478b554Skettenis 	if (sc->sc_id == FANPWR_TCS4525) {
136*f057eb46Skettenis 		if (OF_getpropint(node, "fcs,suspend-voltage-selector", 0))
137f478b554Skettenis 			sc->sc_vsel = TCS4525_VSEL0;
138*f057eb46Skettenis 		else
139f478b554Skettenis 			sc->sc_vsel = TCS4525_VSEL1;
140f478b554Skettenis 		sc->sc_vsel_nsel_mask = TCS4525_VSEL_NSEL_MASK;
141a90343f6Spatrick 	} else if (sc->sc_id == FANPWR_RK8602) {
142*f057eb46Skettenis 		if (OF_getpropint(node, "fcs,suspend-voltage-selector", 0))
143a90343f6Spatrick 			sc->sc_vsel = RK8602_VSEL0;
144*f057eb46Skettenis 		else
145a90343f6Spatrick 			sc->sc_vsel = RK8602_VSEL1;
146a90343f6Spatrick 		sc->sc_vsel_nsel_mask = RK8602_VSEL_NSEL_MASK;
147f478b554Skettenis 	} else {
148*f057eb46Skettenis 		if (OF_getpropint(node, "fcs,suspend-voltage-selector", 0))
149f478b554Skettenis 			sc->sc_vsel = FAN53555_VSEL0;
150*f057eb46Skettenis 		else
151f478b554Skettenis 			sc->sc_vsel = FAN53555_VSEL1;
152f478b554Skettenis 		sc->sc_vsel_nsel_mask = FAN53555_VSEL_NSEL_MASK;
153f478b554Skettenis 	}
154f478b554Skettenis 
1558bda9086Skettenis 	id1 = fanpwr_read(sc, FAN53555_ID1);
1568bda9086Skettenis 	id2 = fanpwr_read(sc, FAN53555_ID2);
1578bda9086Skettenis 
1588bda9086Skettenis 	switch (sc->sc_id) {
1598bda9086Skettenis 	case FANPWR_FAN53555:
1608bda9086Skettenis 		switch (id1 << 8 | id2) {
1618bda9086Skettenis 		case 0x8003:	/* 00 Option */
1628bda9086Skettenis 		case 0x8103:	/* 01 Option */
1638bda9086Skettenis 		case 0x8303:	/* 03 Option */
1648bda9086Skettenis 		case 0x8503:	/* 05 Option */
1658bda9086Skettenis 		case 0x8801:	/* 08, 18 Options */
1668bda9086Skettenis 		case 0x880f:	/* BUC08, BUC18 Options */
1678bda9086Skettenis 		case 0x8108:	/* 79 Option */
1688bda9086Skettenis 			sc->sc_vbase = 600000;
1698bda9086Skettenis 			sc->sc_vstep = 10000;
1708bda9086Skettenis 			break;
1718bda9086Skettenis 		case 0x840f:	/* 04 Option */
1728bda9086Skettenis 		case 0x8c0f:	/* 09 Option */
1738bda9086Skettenis 			sc->sc_vbase = 603000;
1748bda9086Skettenis 			sc->sc_vstep = 12826;
1758bda9086Skettenis 			break;
1768bda9086Skettenis 		case 0x800f:	/* 13 Option */
1778bda9086Skettenis 			sc->sc_vbase = 800000;
1788bda9086Skettenis 			sc->sc_vstep = 10000;
1798bda9086Skettenis 			break;
1808bda9086Skettenis 		case 0x800c:	/* 23 Option */
1818bda9086Skettenis 			sc->sc_vbase = 600000;
1828bda9086Skettenis 			sc->sc_vstep = 12500;
1838bda9086Skettenis 			break;
1848bda9086Skettenis 		case 0x8004:	/* 24 Option */
1858bda9086Skettenis 			sc->sc_vbase = 603000;
1868bda9086Skettenis 			sc->sc_vstep = 12967;
1878bda9086Skettenis 			break;
1888bda9086Skettenis 		default:
1898bda9086Skettenis 			printf(", unknown ID1 0x%02x ID2 0x%02x\n", id1, id2);
1908bda9086Skettenis 			return;
1918bda9086Skettenis 		}
1928bda9086Skettenis 		break;
193a90343f6Spatrick 	case FANPWR_RK8602:
194a90343f6Spatrick 		sc->sc_vbase = 500000;
195a90343f6Spatrick 		sc->sc_vstep = 6250;
196a90343f6Spatrick 		break;
1978bda9086Skettenis 	case FANPWR_SYR827:
1988bda9086Skettenis 	case FANPWR_SYR828:
1998bda9086Skettenis 		sc->sc_vbase = 712500;
2008bda9086Skettenis 		sc->sc_vstep = 12500;
2018bda9086Skettenis 		break;
202f478b554Skettenis 	case FANPWR_TCS4525:
203f478b554Skettenis 		sc->sc_vbase = 600000;
204f478b554Skettenis 		sc->sc_vstep = 6250;
205f478b554Skettenis 		break;
2068bda9086Skettenis 	}
2078bda9086Skettenis 
2088bda9086Skettenis 	voltage = fanpwr_get_voltage(sc);
2098bda9086Skettenis 	printf(", %d.%02d VDC", voltage / 1000000,
2108bda9086Skettenis 		    (voltage % 1000000) / 10000);
2118bda9086Skettenis 
2128bda9086Skettenis 	ramp_delay = OF_getpropint(node, "regulator-ramp-delay", 0);
2138bda9086Skettenis 	if (ramp_delay > 0) {
214f478b554Skettenis 		if (sc->sc_id == FANPWR_TCS4525) {
215f478b554Skettenis 			uint8_t ctrl, slew;
216f478b554Skettenis 
217f478b554Skettenis 			if (ramp_delay >= 18700)
218f478b554Skettenis 				slew = 0;
219f478b554Skettenis 			else if (ramp_delay >= 9300)
220f478b554Skettenis 				slew = 1;
221f478b554Skettenis 			else if (ramp_delay >= 4600)
222f478b554Skettenis 				slew = 2;
223f478b554Skettenis 			else
224f478b554Skettenis 				slew = 3;
225f478b554Skettenis 			ctrl = fanpwr_read(sc, TCS4525_TIME);
226f478b554Skettenis 			ctrl &= ~TCS4525_TIME_SLEW_MASK;
227f478b554Skettenis 			ctrl |= slew << TCS4525_TIME_SLEW_SHIFT;
228f478b554Skettenis 			fanpwr_write(sc, TCS4525_TIME, ctrl);
229f478b554Skettenis 		} else {
2308bda9086Skettenis 			uint8_t ctrl, slew;
2318bda9086Skettenis 
2328bda9086Skettenis 			for (slew = 7; slew > 0; slew--)
2338bda9086Skettenis 				if ((64000 >> slew) >= ramp_delay)
2348bda9086Skettenis 					break;
2358bda9086Skettenis 			ctrl = fanpwr_read(sc, FAN53555_CONTROL);
2368bda9086Skettenis 			ctrl &= ~FAN53555_CONTROL_SLEW_MASK;
2378bda9086Skettenis 			ctrl |= slew << FAN53555_CONTROL_SLEW_SHIFT;
2388bda9086Skettenis 			fanpwr_write(sc, FAN53555_CONTROL, ctrl);
2398bda9086Skettenis 		}
240f478b554Skettenis 	}
2418bda9086Skettenis 
2428bda9086Skettenis 	sc->sc_rd.rd_node = node;
2438bda9086Skettenis 	sc->sc_rd.rd_cookie = sc;
2448bda9086Skettenis 	sc->sc_rd.rd_get_voltage = fanpwr_get_voltage;
2458bda9086Skettenis 	sc->sc_rd.rd_set_voltage = fanpwr_set_voltage;
2468bda9086Skettenis 	regulator_register(&sc->sc_rd);
2478bda9086Skettenis 
2488bda9086Skettenis 	printf("\n");
2498bda9086Skettenis }
2508bda9086Skettenis 
2518bda9086Skettenis uint8_t
fanpwr_read(struct fanpwr_softc * sc,int reg)2528bda9086Skettenis fanpwr_read(struct fanpwr_softc *sc, int reg)
2538bda9086Skettenis {
2548bda9086Skettenis 	uint8_t cmd = reg;
2558bda9086Skettenis 	uint8_t val;
2568bda9086Skettenis 	int error;
2578bda9086Skettenis 
2588bda9086Skettenis 	iic_acquire_bus(sc->sc_tag, I2C_F_POLL);
2598bda9086Skettenis 	error = iic_exec(sc->sc_tag, I2C_OP_READ_WITH_STOP, sc->sc_addr,
2608bda9086Skettenis 	    &cmd, sizeof cmd, &val, sizeof val, I2C_F_POLL);
2618bda9086Skettenis 	iic_release_bus(sc->sc_tag, I2C_F_POLL);
2628bda9086Skettenis 
2638bda9086Skettenis 	if (error) {
2648bda9086Skettenis 		printf("error %d\n", error);
2658bda9086Skettenis 		printf("%s: can't read register 0x%02x\n",
2668bda9086Skettenis 		    sc->sc_dev.dv_xname, reg);
2678bda9086Skettenis 		val = 0xff;
2688bda9086Skettenis 	}
2698bda9086Skettenis 
2708bda9086Skettenis 	return val;
2718bda9086Skettenis }
2728bda9086Skettenis 
2738bda9086Skettenis void
fanpwr_write(struct fanpwr_softc * sc,int reg,uint8_t val)2748bda9086Skettenis fanpwr_write(struct fanpwr_softc *sc, int reg, uint8_t val)
2758bda9086Skettenis {
2768bda9086Skettenis 	uint8_t cmd = reg;
2778bda9086Skettenis 	int error;
2788bda9086Skettenis 
2798bda9086Skettenis 	iic_acquire_bus(sc->sc_tag, I2C_F_POLL);
2808bda9086Skettenis 	error = iic_exec(sc->sc_tag, I2C_OP_WRITE_WITH_STOP, sc->sc_addr,
2818bda9086Skettenis 	    &cmd, sizeof cmd, &val, sizeof val, I2C_F_POLL);
2828bda9086Skettenis 	iic_release_bus(sc->sc_tag, I2C_F_POLL);
2838bda9086Skettenis 
2848bda9086Skettenis 	if (error) {
2858bda9086Skettenis 		printf("%s: can't write register 0x%02x\n",
2868bda9086Skettenis 		    sc->sc_dev.dv_xname, reg);
2878bda9086Skettenis 	}
2888bda9086Skettenis }
2898bda9086Skettenis 
2908bda9086Skettenis uint32_t
fanpwr_get_voltage(void * cookie)2918bda9086Skettenis fanpwr_get_voltage(void *cookie)
2928bda9086Skettenis {
2938bda9086Skettenis 	struct fanpwr_softc *sc = cookie;
2948bda9086Skettenis 	uint8_t vsel;
2958bda9086Skettenis 
2968bda9086Skettenis 	vsel = fanpwr_read(sc, sc->sc_vsel);
297f478b554Skettenis 	return sc->sc_vbase + (vsel & sc->sc_vsel_nsel_mask) * sc->sc_vstep;
2988bda9086Skettenis }
2998bda9086Skettenis 
3008bda9086Skettenis int
fanpwr_set_voltage(void * cookie,uint32_t voltage)3018bda9086Skettenis fanpwr_set_voltage(void *cookie, uint32_t voltage)
3028bda9086Skettenis {
3038bda9086Skettenis 	struct fanpwr_softc *sc = cookie;
3048bda9086Skettenis 	uint32_t vmin = sc->sc_vbase;
305f478b554Skettenis 	uint32_t vmax = vmin + sc->sc_vsel_nsel_mask * sc->sc_vstep;
3068bda9086Skettenis 	uint8_t vsel;
3078bda9086Skettenis 
3088bda9086Skettenis 	if (voltage < vmin || voltage > vmax)
3098bda9086Skettenis 		return EINVAL;
3108bda9086Skettenis 
3118bda9086Skettenis 	vsel = fanpwr_read(sc, sc->sc_vsel);
312f478b554Skettenis 	vsel &= ~sc->sc_vsel_nsel_mask;
313ff8aec25Skettenis 	vsel |= (voltage - sc->sc_vbase) / sc->sc_vstep;
3148bda9086Skettenis 	fanpwr_write(sc, sc->sc_vsel, vsel);
3158bda9086Skettenis 
3168bda9086Skettenis 	return 0;
3178bda9086Skettenis }
318