twl4030.c revision 1.4
1/* $NetBSD: twl4030.c,v 1.4 2021/01/17 21:42:35 thorpej Exp $ */
2
3/*-
4 * Copyright (c) 2019 Jared McNeill <jmcneill@invisible.ca>
5 * All rights reserved.
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 NETBSD FOUNDATION, INC. AND CONTRIBUTORS
17 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
18 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
19 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
20 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
21 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
22 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
23 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
24 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
25 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
26 * POSSIBILITY OF SUCH DAMAGE.
27 */
28
29#include "opt_fdt.h"
30
31#include <sys/cdefs.h>
32__KERNEL_RCSID(0, "$NetBSD: twl4030.c,v 1.4 2021/01/17 21:42:35 thorpej Exp $");
33
34#include <sys/param.h>
35#include <sys/systm.h>
36#include <sys/kernel.h>
37#include <sys/device.h>
38#include <sys/conf.h>
39#include <sys/bus.h>
40#include <sys/kmem.h>
41#include <sys/gpio.h>
42
43#include <dev/i2c/i2cvar.h>
44
45#include <dev/fdt/fdtvar.h>
46
47#define	TWL_PIN_COUNT	16
48
49/* TWL4030 is a multi-function IC. Each module is at a separate I2C address */
50#define	ADDR_USB	0x00
51#define	ADDR_INT	0x01
52#define	ADDR_AUX	0x02
53#define	ADDR_POWER	0x03
54
55/* INTBR registers */
56#define	IDCODE_7_0	0x85
57#define	IDCODE_15_8	0x86
58#define	IDCODE_23_16	0x87
59#define	IDCODE_31_24	0x88
60
61/* GPIO registers */
62#define	GPIOBASE		0x98
63#define	GPIODATAIN(pin)		(GPIOBASE + 0x00 + (pin) / 8)
64#define	GPIODATADIR(pin)	(GPIOBASE + 0x03 + (pin) / 8)
65#define	CLEARGPIODATAOUT(pin)	(GPIOBASE + 0x09 + (pin) / 8)
66#define	SETGPIODATAOUT(pin)	(GPIOBASE + 0x0c + (pin) / 8)
67#define	 PIN_BIT(pin)		__BIT((pin) % 8)
68#define	GPIOPUPDCTR(pin)	(GPIOBASE + 0x13 + (n) / 4)
69#define	 PUPD_BITS(pin)		__BITS((pin) % 4 + 1, (pin) % 4)
70
71/* POWER registers */
72#define	SECONDS_REG		0x1c
73#define	MINUTES_REG		0x1d
74#define	HOURS_REG		0x1e
75#define	DAYS_REG		0x1f
76#define	MONTHS_REG		0x20
77#define	YEARS_REG		0x21
78#define	WEEKS_REG		0x22
79#define	RTC_CTRL_REG		0x29
80#define	 GET_TIME		__BIT(6)
81#define	 STOP_RTC		__BIT(0)
82
83struct twl_softc {
84	device_t	sc_dev;
85	i2c_tag_t	sc_i2c;
86	i2c_addr_t	sc_addr;
87	int		sc_phandle;
88
89	int		sc_npins;
90
91	struct todr_chip_handle sc_todr;
92};
93
94struct twl_pin {
95	struct twl_softc	*pin_sc;
96	int			pin_num;
97	int			pin_flags;
98	bool			pin_actlo;
99};
100
101static const struct device_compatible_entry compat_data[] = {
102	{ .compat = "ti,twl4030" },
103
104	{ 0 }
105};
106
107#ifdef FDT
108static const char * const rtc_compatible[] = { "ti,twl4030-rtc", NULL };
109static const char * const gpio_compatible[] = { "ti,twl4030-gpio", NULL };
110#endif
111
112static uint8_t
113twl_read(struct twl_softc *sc, uint8_t mod, uint8_t reg, int flags)
114{
115	uint8_t val = 0;
116	int error;
117
118	error = iic_exec(sc->sc_i2c, I2C_OP_READ_WITH_STOP, sc->sc_addr + mod,
119	    &reg, 1, &val, 1, flags);
120	if (error != 0)
121		aprint_error_dev(sc->sc_dev, "error reading reg %#x: %d\n", reg, error);
122
123	return val;
124}
125
126static void
127twl_write(struct twl_softc *sc, uint8_t mod, uint8_t reg, uint8_t val, int flags)
128{
129	uint8_t buf[2];
130	int error;
131
132	buf[0] = reg;
133	buf[1] = val;
134
135	error = iic_exec(sc->sc_i2c, I2C_OP_WRITE_WITH_STOP, sc->sc_addr + mod,
136	    NULL, 0, buf, 2, flags);
137	if (error != 0)
138		aprint_error_dev(sc->sc_dev, "error writing reg %#x: %d\n", reg, error);
139}
140
141#define	I2C_LOCK(sc)		iic_acquire_bus((sc)->sc_i2c, I2C_F_POLL)
142#define	I2C_UNLOCK(sc)		iic_release_bus((sc)->sc_i2c, I2C_F_POLL)
143
144#define	INT_READ(sc, reg)	twl_read((sc), ADDR_INT, (reg), I2C_F_POLL)
145#define	INT_WRITE(sc, reg, val)	twl_write((sc), ADDR_INT, (reg), (val), I2C_F_POLL)
146
147#define	POWER_READ(sc, reg)	twl_read((sc), ADDR_POWER, (reg), I2C_F_POLL)
148#define	POWER_WRITE(sc, reg, val) twl_write((sc), ADDR_POWER, (reg), (val), I2C_F_POLL)
149
150static void
151twl_rtc_enable(struct twl_softc *sc, bool onoff)
152{
153	uint8_t rtc_ctrl;
154
155	rtc_ctrl = POWER_READ(sc, RTC_CTRL_REG);
156	if (onoff)
157		rtc_ctrl |= STOP_RTC;	/* 1: RTC is running */
158	else
159		rtc_ctrl &= ~STOP_RTC;	/* 0: RTC is frozen */
160	POWER_WRITE(sc, RTC_CTRL_REG, rtc_ctrl);
161}
162
163static int
164twl_rtc_gettime(todr_chip_handle_t tch, struct clock_ymdhms *dt)
165{
166	struct twl_softc *sc = tch->cookie;
167	uint8_t seconds_reg, minutes_reg, hours_reg,
168	    days_reg, months_reg, years_reg, weeks_reg;
169
170	iic_acquire_bus(sc->sc_i2c, I2C_F_POLL);
171	seconds_reg = POWER_READ(sc, SECONDS_REG);
172	minutes_reg = POWER_READ(sc, MINUTES_REG);
173	hours_reg = POWER_READ(sc, HOURS_REG);
174	days_reg = POWER_READ(sc, DAYS_REG);
175	months_reg = POWER_READ(sc, MONTHS_REG);
176	years_reg = POWER_READ(sc, YEARS_REG);
177	weeks_reg = POWER_READ(sc, WEEKS_REG);
178	iic_release_bus(sc->sc_i2c, I2C_F_POLL);
179
180	dt->dt_sec = bcdtobin(seconds_reg);
181	dt->dt_min = bcdtobin(minutes_reg);
182	dt->dt_hour = bcdtobin(hours_reg);
183	dt->dt_day = bcdtobin(days_reg);
184	dt->dt_mon = bcdtobin(months_reg);
185	dt->dt_year = bcdtobin(years_reg) + 2000;
186	dt->dt_wday = bcdtobin(weeks_reg);
187
188	return 0;
189}
190
191static int
192twl_rtc_settime(todr_chip_handle_t tch, struct clock_ymdhms *dt)
193{
194	struct twl_softc *sc = tch->cookie;
195
196	iic_acquire_bus(sc->sc_i2c, I2C_F_POLL);
197	twl_rtc_enable(sc, false);
198        POWER_WRITE(sc, SECONDS_REG, bintobcd(dt->dt_sec));
199        POWER_WRITE(sc, MINUTES_REG, bintobcd(dt->dt_min));
200        POWER_WRITE(sc, HOURS_REG, bintobcd(dt->dt_hour));
201        POWER_WRITE(sc, DAYS_REG, bintobcd(dt->dt_day));
202        POWER_WRITE(sc, MONTHS_REG, bintobcd(dt->dt_mon));
203        POWER_WRITE(sc, YEARS_REG, bintobcd(dt->dt_year % 100));
204        POWER_WRITE(sc, WEEKS_REG, bintobcd(dt->dt_wday));
205	twl_rtc_enable(sc, true);
206	iic_release_bus(sc->sc_i2c, I2C_F_POLL);
207
208	return 0;
209}
210
211#ifdef FDT
212static int
213twl_gpio_config(struct twl_softc *sc, int pin, int flags)
214{
215	uint8_t dir;
216
217	KASSERT(pin >= 0 && pin < sc->sc_npins);
218
219	dir = INT_READ(sc, GPIODATADIR(pin));
220
221	switch (flags & (GPIO_PIN_INPUT|GPIO_PIN_OUTPUT)) {
222	case GPIO_PIN_INPUT:
223		dir &= ~PIN_BIT(pin);
224		break;
225	case GPIO_PIN_OUTPUT:
226		dir |= PIN_BIT(pin);
227		break;
228	default:
229		return EINVAL;
230	}
231
232	INT_WRITE(sc, GPIODATADIR(pin), dir);
233
234	return 0;
235}
236
237static void *
238twl_gpio_acquire(device_t dev, const void *data, size_t len, int flags)
239{
240	struct twl_softc * const sc = device_private(dev);
241	struct twl_pin *gpin;
242	const u_int *gpio = data;
243	int error;
244
245	if (len != 12)
246		return NULL;
247
248	const uint8_t pin = be32toh(gpio[1]) & 0xff;
249	const bool actlo = (be32toh(gpio[2]) & __BIT(0)) != 0;
250
251	if (pin >= sc->sc_npins)
252		return NULL;
253
254	I2C_LOCK(sc);
255	error = twl_gpio_config(sc, pin, flags);
256	I2C_UNLOCK(sc);
257
258	if (error != 0) {
259		device_printf(dev, "bad pin %d config %#x\n", pin, flags);
260		return NULL;
261	}
262
263	gpin = kmem_zalloc(sizeof(*gpin), KM_SLEEP);
264	gpin->pin_sc = sc;
265	gpin->pin_num = pin;
266	gpin->pin_flags = flags;
267	gpin->pin_actlo = actlo;
268
269	return gpin;
270}
271
272static void
273twl_gpio_release(device_t dev, void *priv)
274{
275	struct twl_softc * const sc = device_private(dev);
276	struct twl_pin *gpin = priv;
277
278	I2C_LOCK(sc);
279	twl_gpio_config(sc, gpin->pin_num, GPIO_PIN_INPUT);
280	I2C_UNLOCK(sc);
281
282	kmem_free(gpin, sizeof(*gpin));
283}
284
285static int
286twl_gpio_read(device_t dev, void *priv, bool raw)
287{
288	struct twl_softc * const sc = device_private(dev);
289	struct twl_pin *gpin = priv;
290	uint8_t gpio;
291	int val;
292
293	I2C_LOCK(sc);
294	gpio = INT_READ(sc, GPIODATAIN(gpin->pin_num));
295	I2C_UNLOCK(sc);
296
297	val = __SHIFTOUT(gpio, PIN_BIT(gpin->pin_num));
298	if (!raw && gpin->pin_actlo)
299		val = !val;
300
301	return val;
302}
303
304static void
305twl_gpio_write(device_t dev, void *priv, int val, bool raw)
306{
307	struct twl_softc * const sc = device_private(dev);
308	struct twl_pin *gpin = priv;
309
310	if (!raw && gpin->pin_actlo)
311		val = !val;
312
313	I2C_LOCK(sc);
314	if (val)
315		INT_WRITE(sc, SETGPIODATAOUT(gpin->pin_num), PIN_BIT(gpin->pin_num));
316	else
317		INT_WRITE(sc, CLEARGPIODATAOUT(gpin->pin_num), PIN_BIT(gpin->pin_num));
318	I2C_UNLOCK(sc);
319}
320
321static struct fdtbus_gpio_controller_func twl_gpio_funcs = {
322	.acquire = twl_gpio_acquire,
323	.release = twl_gpio_release,
324	.read = twl_gpio_read,
325	.write = twl_gpio_write,
326};
327#endif /* !FDT */
328
329static void
330twl_rtc_attach(struct twl_softc *sc, const int phandle)
331{
332	iic_acquire_bus(sc->sc_i2c, I2C_F_POLL);
333	twl_rtc_enable(sc, true);
334	iic_release_bus(sc->sc_i2c, I2C_F_POLL);
335
336	sc->sc_todr.todr_gettime_ymdhms = twl_rtc_gettime;
337	sc->sc_todr.todr_settime_ymdhms = twl_rtc_settime;
338	sc->sc_todr.cookie = sc;
339#ifdef FDT
340	fdtbus_todr_attach(sc->sc_dev, phandle, &sc->sc_todr);
341#else
342	todr_attach(&sc->sc_todr);
343#endif
344}
345
346static void
347twl_gpio_attach(struct twl_softc *sc, const int phandle)
348{
349#ifdef FDT
350	fdtbus_register_gpio_controller(sc->sc_dev, phandle, &twl_gpio_funcs);
351#endif
352}
353
354static int
355twl_match(device_t parent, cfdata_t match, void *aux)
356{
357	struct i2c_attach_args *ia = aux;
358	int match_result;
359
360	if (iic_use_direct_match(ia, match, compat_data, &match_result))
361		return match_result;
362
363	if (ia->ia_addr == 0x48)
364		return I2C_MATCH_ADDRESS_ONLY;
365
366	return 0;
367}
368
369static void
370twl_attach(device_t parent, device_t self, void *aux)
371{
372	struct twl_softc * const sc = device_private(self);
373	struct i2c_attach_args *ia = aux;
374	uint32_t idcode;
375
376	sc->sc_dev = self;
377	sc->sc_i2c = ia->ia_tag;
378	sc->sc_addr = ia->ia_addr;
379	sc->sc_phandle = ia->ia_cookie;
380	sc->sc_npins = TWL_PIN_COUNT;
381
382	aprint_naive("\n");
383	aprint_normal(": TWL4030");
384
385#ifdef FDT
386	for (int child = OF_child(sc->sc_phandle); child; child = OF_peer(child)) {
387		if (of_match_compatible(child, gpio_compatible)) {
388			aprint_normal(", GPIO");
389			twl_gpio_attach(sc, child);
390		} else if (of_match_compatible(child, rtc_compatible)) {
391			aprint_normal(", RTC");
392			twl_rtc_attach(sc, child);
393		}
394	}
395#else
396	aprint_normal("\n");
397	twl_gpio_attach(sc, -1);
398	twl_rtc_attach(sc, -1);
399#endif
400
401	I2C_LOCK(sc);
402	idcode = INT_READ(sc, IDCODE_7_0);
403	idcode |= (uint32_t)INT_READ(sc, IDCODE_15_8) << 8;
404	idcode |= (uint32_t)INT_READ(sc, IDCODE_23_16) << 16;
405	idcode |= (uint32_t)INT_READ(sc, IDCODE_31_24) << 24;
406	I2C_UNLOCK(sc);
407
408	aprint_normal(", IDCODE 0x%08x\n", idcode);
409}
410
411CFATTACH_DECL_NEW(twl, sizeof(struct twl_softc),
412    twl_match, twl_attach, NULL, NULL);
413