xref: /freebsd/sys/dev/iicbus/pmic/silergy/syr827.c (revision 2008043f386721d58158e37e0d7e50df8095942d)
1 /*-
2  * SPDX-License-Identifier: BSD-2-Clause
3  *
4  * Copyright (c) 2016 Jared McNeill <jmcneill@invisible.ca>
5  * Copyright (c) 2018 Emmanuel Vadot <manu@FreeBSD.org>
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer.
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in the
14  *    documentation and/or other materials provided with the distribution.
15  *
16  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
17  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
20  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26  * SUCH DAMAGE.
27  */
28 
29 #include <sys/cdefs.h>
30 #include <sys/param.h>
31 #include <sys/systm.h>
32 #include <sys/bus.h>
33 #include <sys/rman.h>
34 #include <sys/kernel.h>
35 #include <sys/reboot.h>
36 #include <sys/module.h>
37 
38 #include <dev/iicbus/iicbus.h>
39 #include <dev/iicbus/iiconf.h>
40 
41 #include <dev/ofw/ofw_bus.h>
42 #include <dev/ofw/ofw_bus_subr.h>
43 
44 #include <dev/extres/regulator/regulator.h>
45 
46 #include "iicbus_if.h"
47 #include "regdev_if.h"
48 
49 #define	VSEL0	0x00
50 #define	VSEL1	0x01
51 
52 #define	VSEL_BUCK_EN	(1 << 7)
53 #define	VSEL_NSEL_MASK	0x3F
54 #define	VSEL_VOLTAGE_BASE	712500 /* uV */
55 #define	VSEL_VOLTAGE_STEP	12500  /* uV */
56 
57 #define	ID1	0x03
58 #define	 ID1_VENDOR_MASK	0xE0
59 #define	 ID1_VENDOR_SHIFT	5
60 #define	 ID1_DIE_MASK		0xF
61 
62 #define	ID2	0x4
63 #define	 ID2_DIE_REV_MASK	0xF
64 
65 static struct ofw_compat_data compat_data[] = {
66 	{ "silergy,syr827",			1 },
67 	{ NULL,					0 }
68 };
69 
70 struct syr827_reg_sc {
71 	struct regnode		*regnode;
72 	device_t		base_dev;
73 	phandle_t		xref;
74 	struct regnode_std_param *param;
75 
76 	int			volt_reg;
77 	int			suspend_reg;
78 };
79 
80 struct syr827_softc {
81 	uint16_t		addr;
82 	struct intr_config_hook	intr_hook;
83 
84 	/* Regulator */
85 	struct syr827_reg_sc	*reg;
86 };
87 
88 static int
89 syr827_read(device_t dev, uint8_t reg, uint8_t *data, uint8_t size)
90 {
91 	return (iicdev_readfrom(dev, reg, data, size, IIC_INTRWAIT));
92 }
93 
94 static int
95 syr827_write(device_t dev, uint8_t reg, uint8_t val)
96 {
97 	return (iicdev_writeto(dev, reg, &val, 1, IIC_INTRWAIT));
98 }
99 
100 static int
101 syr827_regnode_init(struct regnode *regnode)
102 {
103 	return (0);
104 }
105 
106 static int
107 syr827_regnode_enable(struct regnode *regnode, bool enable, int *udelay)
108 {
109 	struct syr827_reg_sc *sc;
110 	uint8_t val;
111 
112 	sc = regnode_get_softc(regnode);
113 
114 	syr827_read(sc->base_dev, sc->volt_reg, &val, 1);
115 	if (enable)
116 		val &= ~VSEL_BUCK_EN;
117 	else
118 		val |= VSEL_BUCK_EN;
119 	syr827_write(sc->base_dev, sc->volt_reg, val);
120 
121 	*udelay = sc->param->ramp_delay;
122 
123 	return (0);
124 }
125 
126 static int
127 syr827_regnode_set_voltage(struct regnode *regnode, int min_uvolt,
128     int max_uvolt, int *udelay)
129 {
130 	struct syr827_reg_sc *sc;
131 	int cur_uvolt;
132 	uint8_t val;
133 
134 	sc = regnode_get_softc(regnode);
135 
136 	/* Get current voltage */
137 	syr827_read(sc->base_dev, sc->volt_reg, &val, 1);
138 	cur_uvolt = (val & VSEL_NSEL_MASK) * VSEL_VOLTAGE_STEP +
139 	    VSEL_VOLTAGE_BASE;
140 
141 	/* Set new voltage */
142 	val &= ~VSEL_NSEL_MASK;
143 	val |= ((min_uvolt - VSEL_VOLTAGE_BASE) / VSEL_VOLTAGE_STEP);
144 	syr827_write(sc->base_dev, sc->volt_reg, val);
145 
146 	/* Time to delay is based on the number of voltage steps */
147 	*udelay = sc->param->ramp_delay *
148 	    (abs(cur_uvolt - min_uvolt) / VSEL_VOLTAGE_STEP);
149 
150 	return (0);
151 }
152 
153 static int
154 syr827_regnode_get_voltage(struct regnode *regnode, int *uvolt)
155 {
156 	struct syr827_reg_sc *sc;
157 	uint8_t val;
158 
159 	sc = regnode_get_softc(regnode);
160 
161 	syr827_read(sc->base_dev, sc->volt_reg, &val, 1);
162 	*uvolt = (val & VSEL_NSEL_MASK) * VSEL_VOLTAGE_STEP +
163 	    VSEL_VOLTAGE_BASE;
164 
165 	return (0);
166 }
167 
168 static regnode_method_t syr827_regnode_methods[] = {
169 	/* Regulator interface */
170 	REGNODEMETHOD(regnode_init,		syr827_regnode_init),
171 	REGNODEMETHOD(regnode_enable,		syr827_regnode_enable),
172 	REGNODEMETHOD(regnode_set_voltage,	syr827_regnode_set_voltage),
173 	REGNODEMETHOD(regnode_get_voltage,	syr827_regnode_get_voltage),
174 	REGNODEMETHOD_END
175 };
176 DEFINE_CLASS_1(syr827_regnode, syr827_regnode_class, syr827_regnode_methods,
177     sizeof(struct syr827_reg_sc), regnode_class);
178 
179 static struct syr827_reg_sc *
180 syr827_reg_attach(device_t dev, phandle_t node)
181 {
182 	struct syr827_reg_sc *reg_sc;
183 	struct regnode_init_def initdef;
184 	struct regnode *regnode;
185 	int suspend_reg;
186 
187 	memset(&initdef, 0, sizeof(initdef));
188 	regulator_parse_ofw_stdparam(dev, node, &initdef);
189 	initdef.id = 0;
190 	initdef.ofw_node = node;
191 	regnode = regnode_create(dev, &syr827_regnode_class, &initdef);
192 	if (regnode == NULL) {
193 		device_printf(dev, "cannot create regulator\n");
194 		return (NULL);
195 	}
196 
197 	reg_sc = regnode_get_softc(regnode);
198 	reg_sc->regnode = regnode;
199 	reg_sc->base_dev = dev;
200 	reg_sc->xref = OF_xref_from_node(node);
201 	reg_sc->param = regnode_get_stdparam(regnode);
202 
203 	if (OF_getencprop(node, "fcs,suspend-voltage-selector", &suspend_reg,
204 	    sizeof(uint32_t)) <= 0)
205 		suspend_reg = 0;
206 
207 	switch (suspend_reg) {
208 	case 0:
209 		reg_sc->suspend_reg = VSEL0;
210 		reg_sc->volt_reg = VSEL1;
211 		break;
212 	case 1:
213 		reg_sc->suspend_reg = VSEL1;
214 		reg_sc->volt_reg = VSEL0;
215 		break;
216 	}
217 
218 	regnode_register(regnode);
219 
220 	return (reg_sc);
221 }
222 
223 static int
224 syr827_regdev_map(device_t dev, phandle_t xref, int ncells, pcell_t *cells,
225     intptr_t *num)
226 {
227 	struct syr827_softc *sc;
228 
229 	sc = device_get_softc(dev);
230 
231 	if (sc->reg->xref != xref)
232 		return (ENXIO);
233 
234 	*num = 0;
235 
236 	return (0);
237 }
238 
239 static int
240 syr827_probe(device_t dev)
241 {
242 	if (!ofw_bus_status_okay(dev))
243 		return (ENXIO);
244 
245 	if (ofw_bus_search_compatible(dev, compat_data)->ocd_data == 0)
246 		return (ENXIO);
247 
248 	device_set_desc(dev, "Silergy SYR827 regulator");
249 
250 	return (BUS_PROBE_DEFAULT);
251 }
252 
253 static void
254 syr827_start(void *pdev)
255 {
256 	struct syr827_softc *sc;
257 	device_t dev;
258 	uint8_t val;
259 
260 	dev = pdev;
261 	sc = device_get_softc(dev);
262 
263 	if (bootverbose) {
264 		syr827_read(dev, ID1, &val, 1);
265 		device_printf(dev, "Vendor ID: %x, DIE ID: %x\n",
266 		    (val & ID1_VENDOR_MASK) >> ID1_VENDOR_SHIFT,
267 		    val & ID1_DIE_MASK);
268 		syr827_read(dev, ID2, &val, 1);
269 		device_printf(dev, "DIE Rev: %x\n", val & ID2_DIE_REV_MASK);
270 	}
271 
272 	config_intrhook_disestablish(&sc->intr_hook);
273 }
274 
275 static int
276 syr827_attach(device_t dev)
277 {
278 	struct syr827_softc *sc;
279 	phandle_t node;
280 
281 	sc = device_get_softc(dev);
282 	node = ofw_bus_get_node(dev);
283 
284 	sc->addr = iicbus_get_addr(dev);
285 
286 	sc->intr_hook.ich_func = syr827_start;
287 	sc->intr_hook.ich_arg = dev;
288 
289 	if (config_intrhook_establish(&sc->intr_hook) != 0)
290 		return (ENOMEM);
291 
292 	sc->reg = syr827_reg_attach(dev, node);
293 	if (sc->reg == NULL) {
294 		device_printf(dev, "cannot attach regulator\n");
295 		return (ENXIO);
296 	}
297 
298 	return (0);
299 }
300 
301 static device_method_t syr827_methods[] = {
302 	/* Device interface */
303 	DEVMETHOD(device_probe,		syr827_probe),
304 	DEVMETHOD(device_attach,	syr827_attach),
305 
306 	/* Regdev interface */
307 	DEVMETHOD(regdev_map,		syr827_regdev_map),
308 
309 	DEVMETHOD_END
310 };
311 
312 static driver_t syr827_driver = {
313 	"syr827",
314 	syr827_methods,
315 	sizeof(struct syr827_softc),
316 };
317 
318 EARLY_DRIVER_MODULE(syr827, iicbus, syr827_driver, 0, 0, BUS_PASS_RESOURCE);
319 MODULE_VERSION(syr827, 1);
320 MODULE_DEPEND(syr827, iicbus, 1, 1, 1);
321 IICBUS_FDT_PNP_INFO(compat_data);
322