1296936Smmel/*-
2296936Smmel * Copyright (c) 2016 Michal Meloun <mmel@FreeBSD.org>
3296936Smmel * All rights reserved.
4296936Smmel *
5296936Smmel * Redistribution and use in source and binary forms, with or without
6296936Smmel * modification, are permitted provided that the following conditions
7296936Smmel * are met:
8296936Smmel * 1. Redistributions of source code must retain the above copyright
9296936Smmel *    notice, this list of conditions and the following disclaimer.
10296936Smmel * 2. Redistributions in binary form must reproduce the above copyright
11296936Smmel *    notice, this list of conditions and the following disclaimer in the
12296936Smmel *    documentation and/or other materials provided with the distribution.
13296936Smmel *
14296936Smmel * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15296936Smmel * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16296936Smmel * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17296936Smmel * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18296936Smmel * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19296936Smmel * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20296936Smmel * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21296936Smmel * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22296936Smmel * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23296936Smmel * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24296936Smmel * SUCH DAMAGE.
25296936Smmel */
26296936Smmel
27296936Smmel#include <sys/cdefs.h>
28296936Smmel__FBSDID("$FreeBSD: stable/11/sys/arm/nvidia/as3722.c 308335 2016-11-05 10:56:32Z mmel $");
29296936Smmel
30296936Smmel/*
31296936Smmel * AS3722 PMIC driver
32296936Smmel */
33296936Smmel
34296936Smmel#include <sys/param.h>
35296936Smmel#include <sys/systm.h>
36296936Smmel#include <sys/bus.h>
37296936Smmel#include <sys/gpio.h>
38296936Smmel#include <sys/kernel.h>
39296936Smmel#include <sys/module.h>
40296936Smmel#include <sys/malloc.h>
41296936Smmel#include <sys/rman.h>
42296936Smmel#include <sys/sx.h>
43296936Smmel
44296936Smmel#include <machine/bus.h>
45296936Smmel
46296936Smmel#include <dev/extres/regulator/regulator.h>
47296936Smmel#include <dev/fdt/fdt_pinctrl.h>
48296936Smmel#include <dev/gpio/gpiobusvar.h>
49296936Smmel#include <dev/iicbus/iiconf.h>
50296936Smmel#include <dev/iicbus/iicbus.h>
51296936Smmel#include <dev/ofw/ofw_bus.h>
52296936Smmel#include <dev/ofw/ofw_bus_subr.h>
53296936Smmel
54296936Smmel#include <gnu/dts/include/dt-bindings/mfd/as3722.h>
55296936Smmel
56296936Smmel#include "clock_if.h"
57296936Smmel#include "regdev_if.h"
58296936Smmel
59296936Smmel#include "as3722.h"
60296936Smmel
61296936Smmelstatic struct ofw_compat_data compat_data[] = {
62296936Smmel	{"ams,as3722",		1},
63296936Smmel	{NULL,			0},
64296936Smmel};
65296936Smmel
66296936Smmel#define	LOCK(_sc)		sx_xlock(&(_sc)->lock)
67296936Smmel#define	UNLOCK(_sc)		sx_xunlock(&(_sc)->lock)
68296936Smmel#define	LOCK_INIT(_sc)		sx_init(&(_sc)->lock, "as3722")
69296936Smmel#define	LOCK_DESTROY(_sc)	sx_destroy(&(_sc)->lock);
70296936Smmel#define	ASSERT_LOCKED(_sc)	sx_assert(&(_sc)->lock, SA_XLOCKED);
71296936Smmel#define	ASSERT_UNLOCKED(_sc)	sx_assert(&(_sc)->lock, SA_UNLOCKED);
72296936Smmel
73296936Smmel#define	AS3722_DEVICE_ID	0x0C
74296936Smmel
75296936Smmel/*
76296936Smmel * Raw register access function.
77296936Smmel */
78296936Smmelint
79296936Smmelas3722_read(struct as3722_softc *sc, uint8_t reg, uint8_t *val)
80296936Smmel{
81296936Smmel	uint8_t addr;
82296936Smmel	int rv;
83296936Smmel	struct iic_msg msgs[2] = {
84296936Smmel		{0, IIC_M_WR, 1, &addr},
85296936Smmel		{0, IIC_M_RD, 1, val},
86296936Smmel	};
87296936Smmel
88296936Smmel	msgs[0].slave = sc->bus_addr;
89296936Smmel	msgs[1].slave = sc->bus_addr;
90296936Smmel	addr = reg;
91296936Smmel
92296936Smmel	rv = iicbus_transfer(sc->dev, msgs, 2);
93296936Smmel	if (rv != 0) {
94296936Smmel		device_printf(sc->dev,
95296936Smmel		    "Error when reading reg 0x%02X, rv: %d\n", reg,  rv);
96296936Smmel		return (EIO);
97296936Smmel	}
98296936Smmel
99296936Smmel	return (0);
100296936Smmel}
101296936Smmel
102296936Smmelint as3722_read_buf(struct as3722_softc *sc, uint8_t reg, uint8_t *buf,
103296936Smmel    size_t size)
104296936Smmel{
105296936Smmel	uint8_t addr;
106296936Smmel	int rv;
107296936Smmel	struct iic_msg msgs[2] = {
108296936Smmel		{0, IIC_M_WR, 1, &addr},
109296936Smmel		{0, IIC_M_RD, size, buf},
110296936Smmel	};
111296936Smmel
112296936Smmel	msgs[0].slave = sc->bus_addr;
113296936Smmel	msgs[1].slave = sc->bus_addr;
114296936Smmel	addr = reg;
115296936Smmel
116296936Smmel	rv = iicbus_transfer(sc->dev, msgs, 2);
117296936Smmel	if (rv != 0) {
118296936Smmel		device_printf(sc->dev,
119296936Smmel		    "Error when reading reg 0x%02X, rv: %d\n", reg,  rv);
120296936Smmel		return (EIO);
121296936Smmel	}
122296936Smmel
123296936Smmel	return (0);
124296936Smmel}
125296936Smmel
126296936Smmelint
127296936Smmelas3722_write(struct as3722_softc *sc, uint8_t reg, uint8_t val)
128296936Smmel{
129296936Smmel	uint8_t data[2];
130296936Smmel	int rv;
131296936Smmel
132296936Smmel	struct iic_msg msgs[1] = {
133296936Smmel		{0, IIC_M_WR, 2, data},
134296936Smmel	};
135296936Smmel
136296936Smmel	msgs[0].slave = sc->bus_addr;
137296936Smmel	data[0] = reg;
138296936Smmel	data[1] = val;
139296936Smmel
140296936Smmel	rv = iicbus_transfer(sc->dev, msgs, 1);
141296936Smmel	if (rv != 0) {
142296936Smmel		device_printf(sc->dev,
143296936Smmel		    "Error when writing reg 0x%02X, rv: %d\n", reg, rv);
144296936Smmel		return (EIO);
145296936Smmel	}
146296936Smmel	return (0);
147296936Smmel}
148296936Smmel
149296936Smmelint as3722_write_buf(struct as3722_softc *sc, uint8_t reg, uint8_t *buf,
150296936Smmel    size_t size)
151296936Smmel{
152296936Smmel	uint8_t data[1];
153296936Smmel	int rv;
154296936Smmel	struct iic_msg msgs[2] = {
155296936Smmel		{0, IIC_M_WR, 1, data},
156296936Smmel		{0, IIC_M_WR | IIC_M_NOSTART, size, buf},
157296936Smmel	};
158296936Smmel
159296936Smmel	msgs[0].slave = sc->bus_addr;
160296936Smmel	msgs[1].slave = sc->bus_addr;
161296936Smmel	data[0] = reg;
162296936Smmel
163296936Smmel	rv = iicbus_transfer(sc->dev, msgs, 2);
164296936Smmel	if (rv != 0) {
165296936Smmel		device_printf(sc->dev,
166296936Smmel		    "Error when writing reg 0x%02X, rv: %d\n", reg, rv);
167296936Smmel		return (EIO);
168296936Smmel	}
169296936Smmel	return (0);
170296936Smmel}
171296936Smmel
172296936Smmelint
173296936Smmelas3722_modify(struct as3722_softc *sc, uint8_t reg, uint8_t clear, uint8_t set)
174296936Smmel{
175296936Smmel	uint8_t val;
176296936Smmel	int rv;
177296936Smmel
178296936Smmel	rv = as3722_read(sc, reg, &val);
179296936Smmel	if (rv != 0)
180296936Smmel		return (rv);
181296936Smmel
182296936Smmel	val &= ~clear;
183296936Smmel	val |= set;
184296936Smmel
185296936Smmel	rv = as3722_write(sc, reg, val);
186296936Smmel	if (rv != 0)
187296936Smmel		return (rv);
188296936Smmel
189296936Smmel	return (0);
190296936Smmel}
191296936Smmel
192296936Smmelstatic int
193296936Smmelas3722_get_version(struct as3722_softc *sc)
194296936Smmel{
195296936Smmel	uint8_t reg;
196296936Smmel	int rv;
197296936Smmel
198296936Smmel	/* Verify AS3722 ID and version. */
199296936Smmel	rv = RD1(sc, AS3722_ASIC_ID1, &reg);
200296936Smmel	if (rv != 0)
201296936Smmel		return (ENXIO);
202296936Smmel
203296936Smmel	if (reg != AS3722_DEVICE_ID) {
204296936Smmel		device_printf(sc->dev, "Invalid chip ID is 0x%x\n", reg);
205296936Smmel		return (ENXIO);
206296936Smmel	}
207296936Smmel
208296936Smmel	rv = RD1(sc, AS3722_ASIC_ID2, &sc->chip_rev);
209296936Smmel	if (rv != 0)
210296936Smmel		return (ENXIO);
211296936Smmel
212296936Smmel	if (bootverbose)
213296936Smmel		device_printf(sc->dev, "AS3722 rev: 0x%x\n", sc->chip_rev);
214296936Smmel	return (0);
215296936Smmel}
216296936Smmel
217296936Smmelstatic int
218296936Smmelas3722_init(struct as3722_softc *sc)
219296936Smmel{
220296936Smmel	uint32_t reg;
221296936Smmel	int rv;
222296936Smmel
223296936Smmel	reg = 0;
224296936Smmel	if (sc->int_pullup)
225296936Smmel		reg |= AS3722_INT_PULL_UP;
226296936Smmel	if (sc->i2c_pullup)
227296936Smmel		reg |= AS3722_I2C_PULL_UP;
228296936Smmel
229296936Smmel	rv = RM1(sc, AS3722_IO_VOLTAGE,
230296936Smmel	    AS3722_INT_PULL_UP | AS3722_I2C_PULL_UP, reg);
231296936Smmel	if (rv != 0)
232296936Smmel		return (ENXIO);
233296936Smmel
234296936Smmel	/* mask interrupts */
235296936Smmel	rv = WR1(sc, AS3722_INTERRUPT_MASK1, 0);
236296936Smmel	if (rv != 0)
237296936Smmel		return (ENXIO);
238296936Smmel	rv = WR1(sc, AS3722_INTERRUPT_MASK2, 0);
239296936Smmel	if (rv != 0)
240296936Smmel		return (ENXIO);
241296936Smmel	rv = WR1(sc, AS3722_INTERRUPT_MASK3, 0);
242296936Smmel	if (rv != 0)
243296936Smmel		return (ENXIO);
244296936Smmel	rv = WR1(sc, AS3722_INTERRUPT_MASK4, 0);
245296936Smmel	if (rv != 0)
246296936Smmel		return (ENXIO);
247296936Smmel	return (0);
248296936Smmel}
249296936Smmel
250296936Smmelstatic int
251296936Smmelas3722_parse_fdt(struct as3722_softc *sc, phandle_t node)
252296936Smmel{
253296936Smmel
254296936Smmel	sc->int_pullup =
255296936Smmel	    OF_hasprop(node, "ams,enable-internal-int-pullup") ? 1 : 0;
256296936Smmel	sc->i2c_pullup =
257296936Smmel	    OF_hasprop(node, "ams,enable-internal-i2c-pullup") ? 1 : 0;
258296936Smmel	return 0;
259296936Smmel}
260296936Smmel
261296936Smmelstatic void
262296936Smmelas3722_intr(void *arg)
263296936Smmel{
264296936Smmel	struct as3722_softc *sc;
265296936Smmel
266296936Smmel	sc = (struct as3722_softc *)arg;
267296936Smmel	/* XXX Finish temperature alarms. */
268296936Smmel}
269296936Smmel
270296936Smmelstatic int
271296936Smmelas3722_probe(device_t dev)
272296936Smmel{
273296936Smmel
274296936Smmel	if (!ofw_bus_status_okay(dev))
275296936Smmel		return (ENXIO);
276296936Smmel
277296936Smmel	if (!ofw_bus_search_compatible(dev, compat_data)->ocd_data)
278296936Smmel		return (ENXIO);
279296936Smmel
280296936Smmel	device_set_desc(dev, "AS3722 PMIC");
281296936Smmel	return (BUS_PROBE_DEFAULT);
282296936Smmel}
283296936Smmel
284296936Smmelstatic int
285296936Smmelas3722_attach(device_t dev)
286296936Smmel{
287296936Smmel	struct as3722_softc *sc;
288296936Smmel	const char *dname;
289296936Smmel	int dunit, rv, rid;
290296936Smmel	phandle_t node;
291296936Smmel
292296936Smmel	sc = device_get_softc(dev);
293296936Smmel	sc->dev = dev;
294296936Smmel	sc->bus_addr = iicbus_get_addr(dev);
295296936Smmel	node = ofw_bus_get_node(sc->dev);
296296936Smmel	dname = device_get_name(dev);
297296936Smmel	dunit = device_get_unit(dev);
298296936Smmel	rv = 0;
299296936Smmel	LOCK_INIT(sc);
300296936Smmel
301296936Smmel	rid = 0;
302296936Smmel	sc->irq_res = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid,
303296936Smmel	    RF_ACTIVE);
304296936Smmel	if (sc->irq_res == NULL) {
305296936Smmel		device_printf(dev, "Cannot allocate interrupt.\n");
306296936Smmel		rv = ENXIO;
307296936Smmel		goto fail;
308296936Smmel	}
309296936Smmel
310296936Smmel	rv = as3722_parse_fdt(sc, node);
311296936Smmel	if (rv != 0)
312296936Smmel		goto fail;
313296936Smmel	rv = as3722_get_version(sc);
314296936Smmel	if (rv != 0)
315296936Smmel		goto fail;
316296936Smmel	rv = as3722_init(sc);
317296936Smmel	if (rv != 0)
318296936Smmel		goto fail;
319296936Smmel	rv = as3722_regulator_attach(sc, node);
320296936Smmel	if (rv != 0)
321296936Smmel		goto fail;
322296936Smmel	rv = as3722_gpio_attach(sc, node);
323296936Smmel	if (rv != 0)
324296936Smmel		goto fail;
325296936Smmel	rv = as3722_rtc_attach(sc, node);
326296936Smmel	if (rv != 0)
327296936Smmel		goto fail;
328296936Smmel
329296936Smmel	fdt_pinctrl_register(dev, NULL);
330296936Smmel	fdt_pinctrl_configure_by_name(dev, "default");
331296936Smmel
332296936Smmel	/* Setup  interrupt. */
333296936Smmel	rv = bus_setup_intr(dev, sc->irq_res, INTR_TYPE_MISC | INTR_MPSAFE,
334296936Smmel	    NULL, as3722_intr, sc, &sc->irq_h);
335296936Smmel	if (rv) {
336296936Smmel		device_printf(dev, "Cannot setup interrupt.\n");
337296936Smmel		goto fail;
338296936Smmel	}
339296936Smmel	return (bus_generic_attach(dev));
340296936Smmel
341296936Smmelfail:
342296936Smmel	if (sc->irq_h != NULL)
343296936Smmel		bus_teardown_intr(dev, sc->irq_res, sc->irq_h);
344296936Smmel	if (sc->irq_res != NULL)
345296936Smmel		bus_release_resource(dev, SYS_RES_IRQ, 0, sc->irq_res);
346296936Smmel	LOCK_DESTROY(sc);
347296936Smmel	return (rv);
348296936Smmel}
349296936Smmel
350296936Smmelstatic int
351296936Smmelas3722_detach(device_t dev)
352296936Smmel{
353296936Smmel	struct as3722_softc *sc;
354296936Smmel
355296936Smmel	sc = device_get_softc(dev);
356296936Smmel	if (sc->irq_h != NULL)
357296936Smmel		bus_teardown_intr(dev, sc->irq_res, sc->irq_h);
358296936Smmel	if (sc->irq_res != NULL)
359296936Smmel		bus_release_resource(dev, SYS_RES_IRQ, 0, sc->irq_res);
360296936Smmel	LOCK_DESTROY(sc);
361296936Smmel
362296936Smmel	return (bus_generic_detach(dev));
363296936Smmel}
364296936Smmel
365296936Smmelstatic phandle_t
366296936Smmelas3722_gpio_get_node(device_t bus, device_t dev)
367296936Smmel{
368296936Smmel
369296936Smmel	/* We only have one child, the GPIO bus, which needs our own node. */
370296936Smmel	return (ofw_bus_get_node(bus));
371296936Smmel}
372296936Smmel
373296936Smmelstatic device_method_t as3722_methods[] = {
374296936Smmel	/* Device interface */
375296936Smmel	DEVMETHOD(device_probe,		as3722_probe),
376296936Smmel	DEVMETHOD(device_attach,	as3722_attach),
377296936Smmel	DEVMETHOD(device_detach,	as3722_detach),
378296936Smmel
379296936Smmel	/* Regdev interface */
380296936Smmel	DEVMETHOD(regdev_map,		as3722_regulator_map),
381296936Smmel
382296936Smmel	/* RTC interface */
383296936Smmel	DEVMETHOD(clock_gettime,	as3722_rtc_gettime),
384296936Smmel	DEVMETHOD(clock_settime,	as3722_rtc_settime),
385296936Smmel
386296936Smmel	/* GPIO protocol interface */
387296936Smmel	DEVMETHOD(gpio_get_bus,		as3722_gpio_get_bus),
388296936Smmel	DEVMETHOD(gpio_pin_max,		as3722_gpio_pin_max),
389296936Smmel	DEVMETHOD(gpio_pin_getname,	as3722_gpio_pin_getname),
390296936Smmel	DEVMETHOD(gpio_pin_getflags,	as3722_gpio_pin_getflags),
391296936Smmel	DEVMETHOD(gpio_pin_getcaps,	as3722_gpio_pin_getcaps),
392296936Smmel	DEVMETHOD(gpio_pin_setflags,	as3722_gpio_pin_setflags),
393296936Smmel	DEVMETHOD(gpio_pin_get,		as3722_gpio_pin_get),
394296936Smmel	DEVMETHOD(gpio_pin_set,		as3722_gpio_pin_set),
395296936Smmel	DEVMETHOD(gpio_pin_toggle,	as3722_gpio_pin_toggle),
396296936Smmel	DEVMETHOD(gpio_map_gpios,	as3722_gpio_map_gpios),
397296936Smmel
398296936Smmel	/* fdt_pinctrl interface */
399296936Smmel	DEVMETHOD(fdt_pinctrl_configure, as3722_pinmux_configure),
400296936Smmel
401296936Smmel	/* ofw_bus interface */
402296936Smmel	DEVMETHOD(ofw_bus_get_node,	as3722_gpio_get_node),
403296936Smmel
404296936Smmel	DEVMETHOD_END
405296936Smmel};
406296936Smmel
407296936Smmelstatic devclass_t as3722_devclass;
408308335Smmelstatic DEFINE_CLASS_0(gpio, as3722_driver, as3722_methods,
409296936Smmel    sizeof(struct as3722_softc));
410296936SmmelEARLY_DRIVER_MODULE(as3722, iicbus, as3722_driver, as3722_devclass,
411308335Smmel    NULL, NULL, 74);
412