axppmic.c revision 1.1
1/*	$OpenBSD: axppmic.c,v 1.1 2017/12/17 18:25:25 kettenis Exp $	*/
2/*
3 * Copyright (c) 2017 Mark Kettenis <kettenis@openbsd.org>
4 *
5 * Permission to use, copy, modify, and distribute this software for any
6 * purpose with or without fee is hereby granted, provided that the above
7 * copyright notice and this permission notice appear in all copies.
8 *
9 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16 */
17
18#include <sys/param.h>
19#include <sys/systm.h>
20#include <sys/device.h>
21#include <sys/malloc.h>
22
23#include <dev/fdt/rsbvar.h>
24
25#include <dev/ofw/openfirm.h>
26#include <dev/ofw/ofw_regulator.h>
27#include <dev/ofw/fdt.h>
28
29#define AXP806_REG_ADDR_EXT			0xff
30#define  AXP806_REG_ADDR_EXT_MASTER_MODE	(0 << 4)
31#define  AXP806_REG_ADDR_EXT_SLAVE_MODE		(1 << 4)
32
33struct axppmic_regdata {
34	const char *name;
35	uint8_t ereg, emask, eval, dval;
36	uint8_t vreg, vmask;
37	uint32_t base, delta;
38	uint32_t base2, delta2;
39};
40
41struct axppmic_regdata axp806_regdata[] = {
42	{ "dcdca", 0x10, (1 << 0), (1 << 0), (0 << 0),
43	  0x12, 0x7f, 600000, 10000, 1120000, 20000 },
44	{ "dcdcb", 0x10, (1 << 1), (1 << 1), (0 << 1),
45	  0x13, 0x1f, 1000000, 50000 },
46	{ "dcdcc", 0x10, (1 << 2), (1 << 2), (0 << 2),
47	  0x14, 0x7f, 600000, 10000, 1120000, 20000 },
48	{ "dcdcd", 0x10, (1 << 3), (1 << 3), (0 << 3),
49	  0x15, 0x3f, 600000, 20000, 1600000, 100000 },
50	{ "dcdce", 0x10, (1 << 4), (1 << 4), (0 << 4),
51	  0x16, 0x1f, 1100000, 100000 },
52	{ "aldo1", 0x10, (1 << 5), (1 << 5), (0 << 5),
53	  0x17, 0x1f, 700000, 100000 },
54	{ "aldo2", 0x10, (1 << 6), (1 << 6), (0 << 6),
55	  0x18, 0x1f, 700000, 100000 },
56	{ "aldo3", 0x10, (1 << 7), (1 << 7), (0 << 7),
57	  0x19, 0x1f, 700000, 100000 },
58	{ "bldo1", 0x11, (1 << 0), (1 << 0), (0 << 0),
59	  0x20, 0x0f, 700000, 100000 },
60	{ "bldo2", 0x11, (1 << 1), (1 << 1), (0 << 1),
61	  0x21, 0x0f, 700000, 100000 },
62	{ "bldo3", 0x11, (1 << 2), (1 << 2), (0 << 2),
63	  0x22, 0x0f, 700000, 100000 },
64	{ "bldo4", 0x11, (1 << 3), (1 << 3), (0 << 3),
65	  0x23, 0x0f, 700000, 100000 },
66	{ "cldo1", 0x11, (1 << 4), (1 << 4), (0 << 4),
67	  0x24, 0x1f, 700000, 100000 },
68	{ "cldo2", 0x11, (1 << 5), (1 << 5), (0 << 5),
69	  0x25, 0x1f, 700000, 100000, 3600000, 200000 },
70	{ "cldo3", 0x11, (1 << 6), (1 << 6), (0 << 6),
71	  0x26, 0x1f, 700000, 100000 },
72	{ "sw", 0x11, (1 << 7), (1 << 7), (0 << 7) },
73	{ NULL }
74};
75
76struct axppmic_regdata axp809_regdata[] = {
77	{ "dcdc1", 0x10, (1 << 1), (1 << 1), (0 << 1),
78	  0x21, 0x1f, 1600000, 100000 },
79	{ "dcdc2", 0x10, (1 << 2), (1 << 2), (0 << 2),
80	  0x22, 0x3f, 600000, 20000 },
81	{ "dcdc3", 0x10, (1 << 3), (1 << 3), (0 << 3),
82	  0x23, 0x3f, 600000, 20000 },
83	{ "dcdc4", 0x10, (1 << 4), (1 << 4), (0 << 4),
84	  0x24, 0x3f, 600000, 20000, 1800000, 100000 },
85	{ "dcdc5", 0x10, (1 << 5), (1 << 5), (0 << 5),
86	  0x25, 0x1f, 1000000, 50000 },
87	{ "dc5ldo", 0x10, (1 << 0), (1 << 0), (0 << 0),
88	  0x1c, 0x07, 700000, 100000 },
89	{ "aldo1", 0x10, (1 << 6), (1 << 6), (0 << 6),
90	  0x28, 0x1f, 700000, 100000 },
91	{ "aldo2", 0x10, (1 << 7), (1 << 7), (0 << 7),
92	  0x28, 0x1f, 700000, 100000 },
93	{ "aldo3", 0x12, (1 << 5), (1 << 5), (0 << 5),
94	  0x28, 0x1f, 700000, 100000 },
95	{ "dldo1", 0x12, (1 << 3), (1 << 3), (0 << 3),
96	  0x15, 0x1f, 700000, 100000 },
97	{ "dldo2", 0x12, (1 << 4), (1 << 4), (0 << 4),
98	  0x16, 0x1f, 700000, 100000 },
99	{ "eldo1", 0x12, (1 << 0), (1 << 0), (0 << 0),
100	  0x19, 0x1f, 700000, 100000 },
101	{ "eldo2", 0x12, (1 << 1), (1 << 1), (0 << 1),
102	  0x1a, 0x1f, 700000, 100000 },
103	{ "eldo3", 0x12, (1 << 2), (1 << 2), (0 << 2),
104	  0x19, 0x1f, 700000, 100000 },
105	{ "ldo_io0", 0x90, 0x07, 0x03, 0x04,
106	  0x91, 0x1f, 700000, 100000 },
107	{ "ldo_io1", 0x92, 0x07, 0x03, 0x04,
108	  0x93, 0x1f, 700000, 100000 },
109	{ NULL }
110};
111
112struct axppmic_device {
113	const char *name;
114	const char *chip;
115	struct axppmic_regdata *regdata;
116};
117
118struct axppmic_device axppmic_devices[] = {
119	{ "x-powers,axp806", "AXP806", axp806_regdata },
120	{ "x-powers,axp809", "AXP809", axp809_regdata }
121};
122
123const struct axppmic_device *
124axppmic_lookup(const char *name)
125{
126	int i;
127
128	for (i = 0; i < nitems(axppmic_devices); i++) {
129		if (strcmp(name, axppmic_devices[i].name) == 0)
130			return &axppmic_devices[i];
131	}
132
133	return NULL;
134}
135
136struct axppmic_softc {
137	struct device	sc_dev;
138	void		*sc_cookie;
139	uint16_t 	sc_rta;
140
141	struct axppmic_regdata *sc_regdata;
142};
143
144inline uint8_t
145axppmic_read_reg(struct axppmic_softc *sc, uint8_t reg)
146{
147	return rsb_read_1(sc->sc_cookie, sc->sc_rta, reg);
148}
149
150inline void
151axppmic_write_reg(struct axppmic_softc *sc, uint8_t reg, uint8_t value)
152{
153	rsb_write_1(sc->sc_cookie, sc->sc_rta, reg, value);
154}
155
156int	axppmic_match(struct device *, void *, void *);
157void	axppmic_attach(struct device *, struct device *, void *);
158
159struct cfattach axppmic_rsb_ca = {
160	sizeof(struct axppmic_softc), axppmic_match, axppmic_attach
161};
162
163struct cfdriver axppmic_rsb_cd = {
164	NULL, "axppmic", DV_DULL
165};
166
167void	axppmic_attach_regulator(struct axppmic_softc *, int);
168
169int
170axppmic_match(struct device *parent, void *match, void *aux)
171{
172	struct rsb_attach_args *ra = aux;
173
174	if (axppmic_lookup(ra->ra_name))
175		return 1;
176	return 0;
177}
178
179void
180axppmic_attach(struct device *parent, struct device *self, void *aux)
181{
182	struct axppmic_softc *sc = (struct axppmic_softc *)self;
183	const struct axppmic_device *device;
184	struct rsb_attach_args *ra = aux;
185	int node;
186
187	sc->sc_cookie = ra->ra_cookie;
188	sc->sc_rta = ra->ra_rta;
189
190	device = axppmic_lookup(ra->ra_name);
191	printf(": %s\n", device->chip);
192
193	sc->sc_regdata = device->regdata;
194
195	/* Switch AXP806 into master or slave mode. */
196	if (strcmp(ra->ra_name, "x-powers,axp806") == 0) {
197	    if (OF_getproplen(ra->ra_node, "x-powers,master-mode") == 0) {
198			axppmic_write_reg(sc, AXP806_REG_ADDR_EXT,
199			    AXP806_REG_ADDR_EXT_MASTER_MODE);
200		} else {
201			axppmic_write_reg(sc, AXP806_REG_ADDR_EXT,
202			    AXP806_REG_ADDR_EXT_SLAVE_MODE);
203		}
204	}
205
206	node = OF_getnodebyname(ra->ra_node, "regulators");
207	if (node == 0)
208		return;
209	for (node = OF_child(node); node; node = OF_peer(node))
210		axppmic_attach_regulator(sc, node);
211}
212
213struct axppmic_regulator {
214	struct axppmic_softc *ar_sc;
215
216	uint8_t ar_ereg, ar_emask;
217	uint8_t ar_eval, ar_dval;
218
219	uint8_t ar_vreg, ar_vmask;
220	uint32_t ar_base, ar_delta;
221	uint32_t ar_base2, ar_delta2;
222
223	struct regulator_device ar_rd;
224};
225
226uint32_t axppmic_get_voltage(void *);
227int	axppmic_set_voltage(void *, uint32_t);
228int	axppmic_enable(void *, int);
229
230void
231axppmic_attach_regulator(struct axppmic_softc *sc, int node)
232{
233	struct axppmic_regulator *ar;
234	char name[32];
235	int i;
236
237	name[0] = 0;
238	OF_getprop(node, "name", name, sizeof(name));
239	name[sizeof(name) - 1] = 0;
240	for (i = 0; sc->sc_regdata[i].name; i++) {
241		if (strcmp(sc->sc_regdata[i].name, name) == 0)
242			break;
243	}
244	if (sc->sc_regdata[i].name == NULL)
245		return;
246
247	ar = malloc(sizeof(*ar), M_DEVBUF, M_WAITOK | M_ZERO);
248	ar->ar_sc = sc;
249
250	ar->ar_ereg = sc->sc_regdata[i].ereg;
251	ar->ar_emask = sc->sc_regdata[i].emask;
252	ar->ar_eval = sc->sc_regdata[i].eval;
253	ar->ar_dval = sc->sc_regdata[i].dval;
254	ar->ar_vreg = sc->sc_regdata[i].vreg;
255	ar->ar_vmask = sc->sc_regdata[i].vmask;
256	ar->ar_base = sc->sc_regdata[i].base;
257	ar->ar_delta = sc->sc_regdata[i].delta;
258
259	ar->ar_rd.rd_node = node;
260	ar->ar_rd.rd_cookie = ar;
261	ar->ar_rd.rd_get_voltage = axppmic_get_voltage;
262	ar->ar_rd.rd_set_voltage = axppmic_set_voltage;
263	ar->ar_rd.rd_enable = axppmic_enable;
264	regulator_register(&ar->ar_rd);
265}
266
267uint32_t
268axppmic_get_voltage(void *cookie)
269{
270	struct axppmic_regulator *ar = cookie;
271	uint32_t voltage;
272	uint8_t value;
273
274	value = axppmic_read_reg(ar->ar_sc, ar->ar_vreg);
275	value &= ar->ar_vmask;
276	voltage = ar->ar_base + value * ar->ar_delta;
277	if (ar->ar_base2 > 0 && voltage > ar->ar_base2) {
278		value -= (ar->ar_base2 - ar->ar_base) / ar->ar_delta;
279		voltage = ar->ar_base2 + value * ar->ar_delta2;
280	}
281	return voltage;
282}
283
284int
285axppmic_set_voltage(void *cookie, uint32_t voltage)
286{
287	struct axppmic_regulator *ar = cookie;
288	uint32_t value, reg;
289
290	if (voltage < ar->ar_base)
291		return EINVAL;
292	value = (voltage - ar->ar_base) / ar->ar_delta;
293	if (ar->ar_base2 > 0 && voltage > ar->ar_base2) {
294		value = (ar->ar_base2 - ar->ar_base) / ar->ar_delta;
295		value += (voltage - ar->ar_base2) / ar->ar_delta2;
296	}
297	if (value > ar->ar_vmask)
298		return EINVAL;
299
300	reg = axppmic_read_reg(ar->ar_sc, ar->ar_vreg);
301	reg &= ar->ar_vmask;
302	axppmic_write_reg(ar->ar_sc, ar->ar_vreg, reg | value);
303	return 0;
304}
305
306int
307axppmic_enable(void *cookie, int on)
308{
309	struct axppmic_regulator *ar = cookie;
310	uint8_t reg;
311
312	reg = axppmic_read_reg(ar->ar_sc, ar->ar_ereg);
313	reg &= ~ar->ar_emask;
314	if (on)
315		reg |= ar->ar_eval;
316	else
317		reg |= ar->ar_dval;
318	axppmic_write_reg(ar->ar_sc, ar->ar_ereg, reg);
319	return 0;
320}
321