xref: /netbsd-src/sys/dev/fdt/fdt_regulator.c (revision b554808555a60080315a050a14e6c0ddb0c1b08b)
1*b5548085Sjmcneill /* $NetBSD: fdt_regulator.c,v 1.9 2021/08/08 15:23:42 jmcneill Exp $ */
27d3ef0b6Sjmcneill 
37d3ef0b6Sjmcneill /*-
47d3ef0b6Sjmcneill  * Copyright (c) 2015 Jared D. McNeill <jmcneill@invisible.ca>
57d3ef0b6Sjmcneill  * All rights reserved.
67d3ef0b6Sjmcneill  *
77d3ef0b6Sjmcneill  * Redistribution and use in source and binary forms, with or without
87d3ef0b6Sjmcneill  * modification, are permitted provided that the following conditions
97d3ef0b6Sjmcneill  * are met:
107d3ef0b6Sjmcneill  * 1. Redistributions of source code must retain the above copyright
117d3ef0b6Sjmcneill  *    notice, this list of conditions and the following disclaimer.
127d3ef0b6Sjmcneill  * 2. Redistributions in binary form must reproduce the above copyright
137d3ef0b6Sjmcneill  *    notice, this list of conditions and the following disclaimer in the
147d3ef0b6Sjmcneill  *    documentation and/or other materials provided with the distribution.
157d3ef0b6Sjmcneill  *
167d3ef0b6Sjmcneill  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
177d3ef0b6Sjmcneill  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
187d3ef0b6Sjmcneill  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
197d3ef0b6Sjmcneill  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
207d3ef0b6Sjmcneill  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
217d3ef0b6Sjmcneill  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
227d3ef0b6Sjmcneill  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
237d3ef0b6Sjmcneill  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
247d3ef0b6Sjmcneill  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
257d3ef0b6Sjmcneill  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
267d3ef0b6Sjmcneill  * SUCH DAMAGE.
277d3ef0b6Sjmcneill  */
287d3ef0b6Sjmcneill 
297d3ef0b6Sjmcneill #include <sys/cdefs.h>
30*b5548085Sjmcneill __KERNEL_RCSID(0, "$NetBSD: fdt_regulator.c,v 1.9 2021/08/08 15:23:42 jmcneill Exp $");
317d3ef0b6Sjmcneill 
327d3ef0b6Sjmcneill #include <sys/param.h>
337d3ef0b6Sjmcneill #include <sys/bus.h>
347d3ef0b6Sjmcneill #include <sys/kmem.h>
35233c5d85Sjmcneill #include <sys/queue.h>
367d3ef0b6Sjmcneill 
377d3ef0b6Sjmcneill #include <libfdt.h>
387d3ef0b6Sjmcneill #include <dev/fdt/fdtvar.h>
397d3ef0b6Sjmcneill 
40*b5548085Sjmcneill #define	REGULATOR_TO_RC(_reg)	\
41*b5548085Sjmcneill 	container_of((_reg), struct fdtbus_regulator_controller, rc_reg)
42*b5548085Sjmcneill 
437d3ef0b6Sjmcneill struct fdtbus_regulator_controller {
447d3ef0b6Sjmcneill 	device_t rc_dev;
457d3ef0b6Sjmcneill 	int rc_phandle;
467d3ef0b6Sjmcneill 	const struct fdtbus_regulator_controller_func *rc_funcs;
477d3ef0b6Sjmcneill 
48d509ac51Sjmcneill 	u_int rc_enable_ramp_delay;
49d509ac51Sjmcneill 
50*b5548085Sjmcneill 	struct fdtbus_regulator rc_reg;	/* handle returned by acquire() */
51*b5548085Sjmcneill 
52233c5d85Sjmcneill 	LIST_ENTRY(fdtbus_regulator_controller) rc_next;
537d3ef0b6Sjmcneill };
547d3ef0b6Sjmcneill 
55233c5d85Sjmcneill static LIST_HEAD(, fdtbus_regulator_controller) fdtbus_regulator_controllers =
56233c5d85Sjmcneill     LIST_HEAD_INITIALIZER(fdtbus_regulator_controllers);
577d3ef0b6Sjmcneill 
587d3ef0b6Sjmcneill int
fdtbus_register_regulator_controller(device_t dev,int phandle,const struct fdtbus_regulator_controller_func * funcs)597d3ef0b6Sjmcneill fdtbus_register_regulator_controller(device_t dev, int phandle,
607d3ef0b6Sjmcneill     const struct fdtbus_regulator_controller_func *funcs)
617d3ef0b6Sjmcneill {
627d3ef0b6Sjmcneill 	struct fdtbus_regulator_controller *rc;
637d3ef0b6Sjmcneill 
64d509ac51Sjmcneill 	rc = kmem_zalloc(sizeof(*rc), KM_SLEEP);
657d3ef0b6Sjmcneill 	rc->rc_dev = dev;
667d3ef0b6Sjmcneill 	rc->rc_phandle = phandle;
677d3ef0b6Sjmcneill 	rc->rc_funcs = funcs;
68*b5548085Sjmcneill 	rc->rc_reg.reg_rc = rc;
697d3ef0b6Sjmcneill 
70d509ac51Sjmcneill 	of_getprop_uint32(phandle, "regulator-enable-ramp-delay", &rc->rc_enable_ramp_delay);
71d509ac51Sjmcneill 
72233c5d85Sjmcneill 	LIST_INSERT_HEAD(&fdtbus_regulator_controllers, rc, rc_next);
737d3ef0b6Sjmcneill 
747d3ef0b6Sjmcneill 	return 0;
757d3ef0b6Sjmcneill }
767d3ef0b6Sjmcneill 
777d3ef0b6Sjmcneill static struct fdtbus_regulator_controller *
fdtbus_get_regulator_controller(int phandle)787d3ef0b6Sjmcneill fdtbus_get_regulator_controller(int phandle)
797d3ef0b6Sjmcneill {
807d3ef0b6Sjmcneill 	struct fdtbus_regulator_controller *rc;
817d3ef0b6Sjmcneill 
82233c5d85Sjmcneill 	LIST_FOREACH(rc, &fdtbus_regulator_controllers, rc_next) {
83233c5d85Sjmcneill 		if (rc->rc_phandle == phandle)
847d3ef0b6Sjmcneill 			return rc;
857d3ef0b6Sjmcneill 	}
867d3ef0b6Sjmcneill 
877d3ef0b6Sjmcneill 	return NULL;
887d3ef0b6Sjmcneill }
897d3ef0b6Sjmcneill 
907d3ef0b6Sjmcneill struct fdtbus_regulator *
fdtbus_regulator_acquire(int phandle,const char * prop)917d3ef0b6Sjmcneill fdtbus_regulator_acquire(int phandle, const char *prop)
927d3ef0b6Sjmcneill {
937d3ef0b6Sjmcneill 	struct fdtbus_regulator_controller *rc;
947d3ef0b6Sjmcneill 	int regulator_phandle;
957d3ef0b6Sjmcneill 	int error;
967d3ef0b6Sjmcneill 
977d3ef0b6Sjmcneill 	regulator_phandle = fdtbus_get_phandle(phandle, prop);
987d3ef0b6Sjmcneill 	if (regulator_phandle == -1) {
997d3ef0b6Sjmcneill 		return NULL;
1007d3ef0b6Sjmcneill 	}
1017d3ef0b6Sjmcneill 
1027d3ef0b6Sjmcneill 	rc = fdtbus_get_regulator_controller(regulator_phandle);
1037d3ef0b6Sjmcneill 	if (rc == NULL) {
1047d3ef0b6Sjmcneill 		return NULL;
1057d3ef0b6Sjmcneill 	}
1067d3ef0b6Sjmcneill 
1077d3ef0b6Sjmcneill 	error = rc->rc_funcs->acquire(rc->rc_dev);
1087d3ef0b6Sjmcneill 	if (error) {
10950506f20Sjmcneill 		aprint_error_dev(rc->rc_dev, "failed to acquire regulator: %d\n", error);
1107d3ef0b6Sjmcneill 		return NULL;
1117d3ef0b6Sjmcneill 	}
1127d3ef0b6Sjmcneill 
113*b5548085Sjmcneill 	return &rc->rc_reg;
1147d3ef0b6Sjmcneill }
1157d3ef0b6Sjmcneill 
1167d3ef0b6Sjmcneill void
fdtbus_regulator_release(struct fdtbus_regulator * reg)1177d3ef0b6Sjmcneill fdtbus_regulator_release(struct fdtbus_regulator *reg)
1187d3ef0b6Sjmcneill {
119*b5548085Sjmcneill 	struct fdtbus_regulator_controller *rc = REGULATOR_TO_RC(reg);
1207d3ef0b6Sjmcneill 
1217d3ef0b6Sjmcneill 	rc->rc_funcs->release(rc->rc_dev);
1227d3ef0b6Sjmcneill }
1237d3ef0b6Sjmcneill 
1247d3ef0b6Sjmcneill int
fdtbus_regulator_enable(struct fdtbus_regulator * reg)1257d3ef0b6Sjmcneill fdtbus_regulator_enable(struct fdtbus_regulator *reg)
1267d3ef0b6Sjmcneill {
127*b5548085Sjmcneill 	struct fdtbus_regulator_controller *rc = REGULATOR_TO_RC(reg);
128d509ac51Sjmcneill 	int error;
1297d3ef0b6Sjmcneill 
130d509ac51Sjmcneill 	error = rc->rc_funcs->enable(rc->rc_dev, true);
131d509ac51Sjmcneill 	if (error != 0)
132d509ac51Sjmcneill 		return error;
133d509ac51Sjmcneill 
134d509ac51Sjmcneill 	if (rc->rc_enable_ramp_delay != 0)
135d509ac51Sjmcneill 		delay(rc->rc_enable_ramp_delay);
136d509ac51Sjmcneill 
137d509ac51Sjmcneill 	return 0;
1387d3ef0b6Sjmcneill }
1397d3ef0b6Sjmcneill 
1407d3ef0b6Sjmcneill int
fdtbus_regulator_disable(struct fdtbus_regulator * reg)1417d3ef0b6Sjmcneill fdtbus_regulator_disable(struct fdtbus_regulator *reg)
1427d3ef0b6Sjmcneill {
143*b5548085Sjmcneill 	struct fdtbus_regulator_controller *rc = REGULATOR_TO_RC(reg);
1447d3ef0b6Sjmcneill 
145cbe9be62Sjmcneill 	if (of_hasprop(rc->rc_phandle, "regulator-always-on"))
146cbe9be62Sjmcneill 		return EIO;
147cbe9be62Sjmcneill 
1487d3ef0b6Sjmcneill 	return rc->rc_funcs->enable(rc->rc_dev, false);
1497d3ef0b6Sjmcneill }
150d1727b27Sjmcneill 
151d1727b27Sjmcneill int
fdtbus_regulator_set_voltage(struct fdtbus_regulator * reg,u_int min_uvol,u_int max_uvol)152d1727b27Sjmcneill fdtbus_regulator_set_voltage(struct fdtbus_regulator *reg, u_int min_uvol,
153d1727b27Sjmcneill     u_int max_uvol)
154d1727b27Sjmcneill {
155*b5548085Sjmcneill 	struct fdtbus_regulator_controller *rc = REGULATOR_TO_RC(reg);
156d1727b27Sjmcneill 
157d1727b27Sjmcneill 	if (rc->rc_funcs->set_voltage == NULL)
158d1727b27Sjmcneill 		return EINVAL;
159d1727b27Sjmcneill 
160d1727b27Sjmcneill 	return rc->rc_funcs->set_voltage(rc->rc_dev, min_uvol, max_uvol);
161d1727b27Sjmcneill }
162d1727b27Sjmcneill 
163d1727b27Sjmcneill int
fdtbus_regulator_get_voltage(struct fdtbus_regulator * reg,u_int * puvol)164d1727b27Sjmcneill fdtbus_regulator_get_voltage(struct fdtbus_regulator *reg, u_int *puvol)
165d1727b27Sjmcneill {
166*b5548085Sjmcneill 	struct fdtbus_regulator_controller *rc = REGULATOR_TO_RC(reg);
167d1727b27Sjmcneill 
16845076dbfSbouyer 	if (rc->rc_funcs->get_voltage == NULL)
169d1727b27Sjmcneill 		return EINVAL;
170d1727b27Sjmcneill 
171d1727b27Sjmcneill 	return rc->rc_funcs->get_voltage(rc->rc_dev, puvol);
172d1727b27Sjmcneill }
173cbe9be62Sjmcneill 
174cbe9be62Sjmcneill int
fdtbus_regulator_supports_voltage(struct fdtbus_regulator * reg,u_int min_uvol,u_int max_uvol)175cbe9be62Sjmcneill fdtbus_regulator_supports_voltage(struct fdtbus_regulator *reg, u_int min_uvol,
176cbe9be62Sjmcneill     u_int max_uvol)
177cbe9be62Sjmcneill {
178*b5548085Sjmcneill 	struct fdtbus_regulator_controller *rc = REGULATOR_TO_RC(reg);
179cbe9be62Sjmcneill 	u_int uvol;
180cbe9be62Sjmcneill 
181cbe9be62Sjmcneill 	if (rc->rc_funcs->set_voltage == NULL)
182cbe9be62Sjmcneill 		return EINVAL;
183cbe9be62Sjmcneill 
184cbe9be62Sjmcneill 	if (of_getprop_uint32(rc->rc_phandle, "regulator-min-microvolt", &uvol) == 0) {
185cbe9be62Sjmcneill 		if (uvol < min_uvol)
186cbe9be62Sjmcneill 			return ERANGE;
187cbe9be62Sjmcneill 	}
188cbe9be62Sjmcneill 	if (of_getprop_uint32(rc->rc_phandle, "regulator-max-microvolt", &uvol) == 0) {
189cbe9be62Sjmcneill 		if (uvol > max_uvol)
190cbe9be62Sjmcneill 			return ERANGE;
191cbe9be62Sjmcneill 	}
192cbe9be62Sjmcneill 
193cbe9be62Sjmcneill 	return 0;
194cbe9be62Sjmcneill }
195