1ef2ee5d0SMichal Meloun /*-
2ef2ee5d0SMichal Meloun * Copyright (c) 2016 Michal Meloun <mmel@FreeBSD.org>
3ef2ee5d0SMichal Meloun * All rights reserved.
4ef2ee5d0SMichal Meloun *
5ef2ee5d0SMichal Meloun * Redistribution and use in source and binary forms, with or without
6ef2ee5d0SMichal Meloun * modification, are permitted provided that the following conditions
7ef2ee5d0SMichal Meloun * are met:
8ef2ee5d0SMichal Meloun * 1. Redistributions of source code must retain the above copyright
9ef2ee5d0SMichal Meloun * notice, this list of conditions and the following disclaimer.
10ef2ee5d0SMichal Meloun * 2. Redistributions in binary form must reproduce the above copyright
11ef2ee5d0SMichal Meloun * notice, this list of conditions and the following disclaimer in the
12ef2ee5d0SMichal Meloun * documentation and/or other materials provided with the distribution.
13ef2ee5d0SMichal Meloun *
14ef2ee5d0SMichal Meloun * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15ef2ee5d0SMichal Meloun * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16ef2ee5d0SMichal Meloun * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17ef2ee5d0SMichal Meloun * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18ef2ee5d0SMichal Meloun * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19ef2ee5d0SMichal Meloun * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20ef2ee5d0SMichal Meloun * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21ef2ee5d0SMichal Meloun * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22ef2ee5d0SMichal Meloun * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23ef2ee5d0SMichal Meloun * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24ef2ee5d0SMichal Meloun * SUCH DAMAGE.
25ef2ee5d0SMichal Meloun */
26ef2ee5d0SMichal Meloun
27ef2ee5d0SMichal Meloun #include <sys/param.h>
28ef2ee5d0SMichal Meloun #include <sys/systm.h>
29ef2ee5d0SMichal Meloun #include <sys/bus.h>
30ef2ee5d0SMichal Meloun #include <sys/gpio.h>
31ef2ee5d0SMichal Meloun #include <sys/kernel.h>
32ef2ee5d0SMichal Meloun #include <sys/malloc.h>
33ef2ee5d0SMichal Meloun #include <sys/sx.h>
34ef2ee5d0SMichal Meloun
35ef2ee5d0SMichal Meloun #include <machine/bus.h>
36ef2ee5d0SMichal Meloun
37ef2ee5d0SMichal Meloun #include <dev/fdt/fdt_common.h>
38ef2ee5d0SMichal Meloun #include <dev/gpio/gpiobusvar.h>
39ef2ee5d0SMichal Meloun
40ef2ee5d0SMichal Meloun #include "as3722.h"
41ef2ee5d0SMichal Meloun
42ef2ee5d0SMichal Meloun MALLOC_DEFINE(M_AS3722_GPIO, "AS3722 gpio", "AS3722 GPIO");
43ef2ee5d0SMichal Meloun
44ef2ee5d0SMichal Meloun /* AS3722_GPIOx_CONTROL MODE and IOSF definition. */
45ef2ee5d0SMichal Meloun #define AS3722_IOSF_GPIO 0x00
46ef2ee5d0SMichal Meloun #define AS3722_IOSF_INTERRUPT_OUT 0x01
47ef2ee5d0SMichal Meloun #define AS3722_IOSF_VSUP_VBAT_LOW_UNDEBOUNCE_OUT 0x02
48ef2ee5d0SMichal Meloun #define AS3722_IOSF_GPIO_IN_INTERRUPT 0x03
49ef2ee5d0SMichal Meloun #define AS3722_IOSF_PWM_IN 0x04
50ef2ee5d0SMichal Meloun #define AS3722_IOSF_VOLTAGE_IN_STANDBY 0x05
51ef2ee5d0SMichal Meloun #define AS3722_IOSF_OC_PG_SD0 0x06
52ef2ee5d0SMichal Meloun #define AS3722_IOSF_POWERGOOD_OUT 0x07
53ef2ee5d0SMichal Meloun #define AS3722_IOSF_CLK32K_OUT 0x08
54ef2ee5d0SMichal Meloun #define AS3722_IOSF_WATCHDOG_IN 0x09
55ef2ee5d0SMichal Meloun #define AS3722_IOSF_SOFT_RESET_IN 0x0b
56ef2ee5d0SMichal Meloun #define AS3722_IOSF_PWM_OUT 0x0c
57ef2ee5d0SMichal Meloun #define AS3722_IOSF_VSUP_VBAT_LOW_DEBOUNCE_OUT 0x0d
58ef2ee5d0SMichal Meloun #define AS3722_IOSF_OC_PG_SD6 0x0e
59ef2ee5d0SMichal Meloun
60ef2ee5d0SMichal Meloun #define AS3722_MODE_INPUT 0
61ef2ee5d0SMichal Meloun #define AS3722_MODE_PUSH_PULL 1
62ef2ee5d0SMichal Meloun #define AS3722_MODE_OPEN_DRAIN 2
63ef2ee5d0SMichal Meloun #define AS3722_MODE_TRISTATE 3
64ef2ee5d0SMichal Meloun #define AS3722_MODE_INPUT_PULL_UP_LV 4
65ef2ee5d0SMichal Meloun #define AS3722_MODE_INPUT_PULL_DOWN 5
66ef2ee5d0SMichal Meloun #define AS3722_MODE_OPEN_DRAIN_LV 6
67ef2ee5d0SMichal Meloun #define AS3722_MODE_PUSH_PULL_LV 7
68ef2ee5d0SMichal Meloun
69ef2ee5d0SMichal Meloun #define NGPIO 8
70ef2ee5d0SMichal Meloun
71ef2ee5d0SMichal Meloun #define GPIO_LOCK(_sc) sx_slock(&(_sc)->gpio_lock)
72ef2ee5d0SMichal Meloun #define GPIO_UNLOCK(_sc) sx_unlock(&(_sc)->gpio_lock)
73ef2ee5d0SMichal Meloun #define GPIO_ASSERT(_sc) sx_assert(&(_sc)->gpio_lock, SA_LOCKED)
74ef2ee5d0SMichal Meloun
75ef2ee5d0SMichal Meloun #define AS3722_CFG_BIAS_DISABLE 0x0001
76ef2ee5d0SMichal Meloun #define AS3722_CFG_BIAS_PULL_UP 0x0002
77ef2ee5d0SMichal Meloun #define AS3722_CFG_BIAS_PULL_DOWN 0x0004
78ef2ee5d0SMichal Meloun #define AS3722_CFG_BIAS_HIGH_IMPEDANCE 0x0008
79ef2ee5d0SMichal Meloun #define AS3722_CFG_OPEN_DRAIN 0x0010
80ef2ee5d0SMichal Meloun
81ef2ee5d0SMichal Meloun static const struct {
82ef2ee5d0SMichal Meloun const char *name;
83ef2ee5d0SMichal Meloun int config; /* AS3722_CFG_ */
84ef2ee5d0SMichal Meloun } as3722_cfg_names[] = {
85ef2ee5d0SMichal Meloun {"bias-disable", AS3722_CFG_BIAS_DISABLE},
86ef2ee5d0SMichal Meloun {"bias-pull-up", AS3722_CFG_BIAS_PULL_UP},
87ef2ee5d0SMichal Meloun {"bias-pull-down", AS3722_CFG_BIAS_PULL_DOWN},
88ef2ee5d0SMichal Meloun {"bias-high-impedance", AS3722_CFG_BIAS_HIGH_IMPEDANCE},
89ef2ee5d0SMichal Meloun {"drive-open-drain", AS3722_CFG_OPEN_DRAIN},
90ef2ee5d0SMichal Meloun };
91ef2ee5d0SMichal Meloun
92ef2ee5d0SMichal Meloun static struct {
93ef2ee5d0SMichal Meloun const char *name;
94ef2ee5d0SMichal Meloun int fnc_val;
95ef2ee5d0SMichal Meloun } as3722_fnc_table[] = {
96ef2ee5d0SMichal Meloun {"gpio", AS3722_IOSF_GPIO},
97ef2ee5d0SMichal Meloun {"interrupt-out", AS3722_IOSF_INTERRUPT_OUT},
98ef2ee5d0SMichal Meloun {"vsup-vbat-low-undebounce-out", AS3722_IOSF_VSUP_VBAT_LOW_UNDEBOUNCE_OUT},
99ef2ee5d0SMichal Meloun {"gpio-in-interrupt", AS3722_IOSF_GPIO_IN_INTERRUPT},
100ef2ee5d0SMichal Meloun {"pwm-in", AS3722_IOSF_PWM_IN},
101ef2ee5d0SMichal Meloun {"voltage-in-standby", AS3722_IOSF_VOLTAGE_IN_STANDBY},
102ef2ee5d0SMichal Meloun {"oc-pg-sd0", AS3722_IOSF_OC_PG_SD0},
103ef2ee5d0SMichal Meloun {"powergood-out", AS3722_IOSF_POWERGOOD_OUT},
104ef2ee5d0SMichal Meloun {"clk32k-out", AS3722_IOSF_CLK32K_OUT},
105ef2ee5d0SMichal Meloun {"watchdog-in", AS3722_IOSF_WATCHDOG_IN},
106ef2ee5d0SMichal Meloun {"soft-reset-in", AS3722_IOSF_SOFT_RESET_IN},
107ef2ee5d0SMichal Meloun {"pwm-out", AS3722_IOSF_PWM_OUT},
108ef2ee5d0SMichal Meloun {"vsup-vbat-low-debounce-out", AS3722_IOSF_VSUP_VBAT_LOW_DEBOUNCE_OUT},
109ef2ee5d0SMichal Meloun {"oc-pg-sd6", AS3722_IOSF_OC_PG_SD6},
110ef2ee5d0SMichal Meloun };
111ef2ee5d0SMichal Meloun
112ef2ee5d0SMichal Meloun struct as3722_pincfg {
113ef2ee5d0SMichal Meloun char *function;
114ef2ee5d0SMichal Meloun int flags;
115ef2ee5d0SMichal Meloun };
116ef2ee5d0SMichal Meloun
117ef2ee5d0SMichal Meloun struct as3722_gpio_pin {
118ef2ee5d0SMichal Meloun int pin_caps;
119ef2ee5d0SMichal Meloun uint8_t pin_ctrl_reg;
120ef2ee5d0SMichal Meloun char pin_name[GPIOMAXNAME];
121ef2ee5d0SMichal Meloun int pin_cfg_flags;
122ef2ee5d0SMichal Meloun };
123ef2ee5d0SMichal Meloun
124ef2ee5d0SMichal Meloun /* --------------------------------------------------------------------------
125ef2ee5d0SMichal Meloun *
126ef2ee5d0SMichal Meloun * Pinmux functions.
127ef2ee5d0SMichal Meloun */
128ef2ee5d0SMichal Meloun static int
as3722_pinmux_get_function(struct as3722_softc * sc,char * name)129ef2ee5d0SMichal Meloun as3722_pinmux_get_function(struct as3722_softc *sc, char *name)
130ef2ee5d0SMichal Meloun {
131ef2ee5d0SMichal Meloun int i;
132ef2ee5d0SMichal Meloun
133ef2ee5d0SMichal Meloun for (i = 0; i < nitems(as3722_fnc_table); i++) {
134ef2ee5d0SMichal Meloun if (strcmp(as3722_fnc_table[i].name, name) == 0)
135ef2ee5d0SMichal Meloun return (as3722_fnc_table[i].fnc_val);
136ef2ee5d0SMichal Meloun }
137ef2ee5d0SMichal Meloun return (-1);
138ef2ee5d0SMichal Meloun }
139ef2ee5d0SMichal Meloun
140ef2ee5d0SMichal Meloun static int
as3722_pinmux_config_node(struct as3722_softc * sc,char * pin_name,struct as3722_pincfg * cfg)141ef2ee5d0SMichal Meloun as3722_pinmux_config_node(struct as3722_softc *sc, char *pin_name,
142ef2ee5d0SMichal Meloun struct as3722_pincfg *cfg)
143ef2ee5d0SMichal Meloun {
144ef2ee5d0SMichal Meloun uint8_t ctrl;
145ef2ee5d0SMichal Meloun int rv, fnc, pin;
146ef2ee5d0SMichal Meloun
147ef2ee5d0SMichal Meloun for (pin = 0; pin < sc->gpio_npins; pin++) {
148ef2ee5d0SMichal Meloun if (strcmp(sc->gpio_pins[pin]->pin_name, pin_name) == 0)
149ef2ee5d0SMichal Meloun break;
150ef2ee5d0SMichal Meloun }
151ef2ee5d0SMichal Meloun if (pin >= sc->gpio_npins) {
152ef2ee5d0SMichal Meloun device_printf(sc->dev, "Unknown pin: %s\n", pin_name);
153ef2ee5d0SMichal Meloun return (ENXIO);
154ef2ee5d0SMichal Meloun }
155ef2ee5d0SMichal Meloun
156ef2ee5d0SMichal Meloun ctrl = sc->gpio_pins[pin]->pin_ctrl_reg;
157ef2ee5d0SMichal Meloun sc->gpio_pins[pin]->pin_cfg_flags = cfg->flags;
158ef2ee5d0SMichal Meloun if (cfg->function != NULL) {
159ef2ee5d0SMichal Meloun fnc = as3722_pinmux_get_function(sc, cfg->function);
160ef2ee5d0SMichal Meloun if (fnc == -1) {
161ef2ee5d0SMichal Meloun device_printf(sc->dev,
162ef2ee5d0SMichal Meloun "Unknown function %s for pin %s\n", cfg->function,
163ef2ee5d0SMichal Meloun sc->gpio_pins[pin]->pin_name);
164ef2ee5d0SMichal Meloun return (ENXIO);
165ef2ee5d0SMichal Meloun }
166ef2ee5d0SMichal Meloun switch (fnc) {
167ef2ee5d0SMichal Meloun case AS3722_IOSF_INTERRUPT_OUT:
168ef2ee5d0SMichal Meloun case AS3722_IOSF_VSUP_VBAT_LOW_UNDEBOUNCE_OUT:
169ef2ee5d0SMichal Meloun case AS3722_IOSF_OC_PG_SD0:
170ef2ee5d0SMichal Meloun case AS3722_IOSF_POWERGOOD_OUT:
171ef2ee5d0SMichal Meloun case AS3722_IOSF_CLK32K_OUT:
172ef2ee5d0SMichal Meloun case AS3722_IOSF_PWM_OUT:
173ef2ee5d0SMichal Meloun case AS3722_IOSF_OC_PG_SD6:
174ef2ee5d0SMichal Meloun ctrl &= ~(AS3722_GPIO_MODE_MASK <<
175ef2ee5d0SMichal Meloun AS3722_GPIO_MODE_SHIFT);
176ef2ee5d0SMichal Meloun ctrl |= AS3722_MODE_PUSH_PULL << AS3722_GPIO_MODE_SHIFT;
177ef2ee5d0SMichal Meloun /* XXX Handle flags (OC + pullup) */
178ef2ee5d0SMichal Meloun break;
179ef2ee5d0SMichal Meloun case AS3722_IOSF_GPIO_IN_INTERRUPT:
180ef2ee5d0SMichal Meloun case AS3722_IOSF_PWM_IN:
181ef2ee5d0SMichal Meloun case AS3722_IOSF_VOLTAGE_IN_STANDBY:
182ef2ee5d0SMichal Meloun case AS3722_IOSF_WATCHDOG_IN:
183ef2ee5d0SMichal Meloun case AS3722_IOSF_SOFT_RESET_IN:
184ef2ee5d0SMichal Meloun ctrl &= ~(AS3722_GPIO_MODE_MASK <<
185ef2ee5d0SMichal Meloun AS3722_GPIO_MODE_SHIFT);
186ef2ee5d0SMichal Meloun ctrl |= AS3722_MODE_INPUT << AS3722_GPIO_MODE_SHIFT;
187ef2ee5d0SMichal Meloun /* XXX Handle flags (pulldown + pullup) */
188ef2ee5d0SMichal Meloun
189ef2ee5d0SMichal Meloun default:
190ef2ee5d0SMichal Meloun break;
191ef2ee5d0SMichal Meloun }
192ef2ee5d0SMichal Meloun ctrl &= ~(AS3722_GPIO_IOSF_MASK << AS3722_GPIO_IOSF_SHIFT);
193ef2ee5d0SMichal Meloun ctrl |= fnc << AS3722_GPIO_IOSF_SHIFT;
194ef2ee5d0SMichal Meloun }
195ef2ee5d0SMichal Meloun rv = 0;
196ef2ee5d0SMichal Meloun if (ctrl != sc->gpio_pins[pin]->pin_ctrl_reg) {
197ef2ee5d0SMichal Meloun rv = WR1(sc, AS3722_GPIO0_CONTROL + pin, ctrl);
198ef2ee5d0SMichal Meloun sc->gpio_pins[pin]->pin_ctrl_reg = ctrl;
199ef2ee5d0SMichal Meloun }
200ef2ee5d0SMichal Meloun return (rv);
201ef2ee5d0SMichal Meloun }
202ef2ee5d0SMichal Meloun
203ef2ee5d0SMichal Meloun static int
as3722_pinmux_read_node(struct as3722_softc * sc,phandle_t node,struct as3722_pincfg * cfg,char ** pins,int * lpins)204ef2ee5d0SMichal Meloun as3722_pinmux_read_node(struct as3722_softc *sc, phandle_t node,
205ef2ee5d0SMichal Meloun struct as3722_pincfg *cfg, char **pins, int *lpins)
206ef2ee5d0SMichal Meloun {
207ef2ee5d0SMichal Meloun int rv, i;
208ef2ee5d0SMichal Meloun
209217d17bcSOleksandr Tymoshenko *lpins = OF_getprop_alloc(node, "pins", (void **)pins);
210ef2ee5d0SMichal Meloun if (*lpins <= 0)
211ef2ee5d0SMichal Meloun return (ENOENT);
212ef2ee5d0SMichal Meloun
213ef2ee5d0SMichal Meloun /* Read function (mux) settings. */
214217d17bcSOleksandr Tymoshenko rv = OF_getprop_alloc(node, "function", (void **)&cfg->function);
215ef2ee5d0SMichal Meloun if (rv <= 0)
216ef2ee5d0SMichal Meloun cfg->function = NULL;
217ef2ee5d0SMichal Meloun
218ef2ee5d0SMichal Meloun /* Read boolean properties. */
219ef2ee5d0SMichal Meloun for (i = 0; i < nitems(as3722_cfg_names); i++) {
220ef2ee5d0SMichal Meloun if (OF_hasprop(node, as3722_cfg_names[i].name))
221ef2ee5d0SMichal Meloun cfg->flags |= as3722_cfg_names[i].config;
222ef2ee5d0SMichal Meloun }
223ef2ee5d0SMichal Meloun return (0);
224ef2ee5d0SMichal Meloun }
225ef2ee5d0SMichal Meloun
226ef2ee5d0SMichal Meloun static int
as3722_pinmux_process_node(struct as3722_softc * sc,phandle_t node)227ef2ee5d0SMichal Meloun as3722_pinmux_process_node(struct as3722_softc *sc, phandle_t node)
228ef2ee5d0SMichal Meloun {
229ef2ee5d0SMichal Meloun struct as3722_pincfg cfg;
230ef2ee5d0SMichal Meloun char *pins, *pname;
231ef2ee5d0SMichal Meloun int i, len, lpins, rv;
232ef2ee5d0SMichal Meloun
233ef2ee5d0SMichal Meloun rv = as3722_pinmux_read_node(sc, node, &cfg, &pins, &lpins);
234ef2ee5d0SMichal Meloun if (rv != 0)
235ef2ee5d0SMichal Meloun return (rv);
236ef2ee5d0SMichal Meloun
237ef2ee5d0SMichal Meloun len = 0;
238ef2ee5d0SMichal Meloun pname = pins;
239ef2ee5d0SMichal Meloun do {
240ef2ee5d0SMichal Meloun i = strlen(pname) + 1;
241ef2ee5d0SMichal Meloun rv = as3722_pinmux_config_node(sc, pname, &cfg);
242ef2ee5d0SMichal Meloun if (rv != 0) {
243ef2ee5d0SMichal Meloun device_printf(sc->dev,
244ef2ee5d0SMichal Meloun "Cannot configure pin: %s: %d\n", pname, rv);
245ef2ee5d0SMichal Meloun }
246ef2ee5d0SMichal Meloun len += i;
247ef2ee5d0SMichal Meloun pname += i;
248ef2ee5d0SMichal Meloun } while (len < lpins);
249ef2ee5d0SMichal Meloun
250ef2ee5d0SMichal Meloun if (pins != NULL)
251bebd5269SOleksandr Tymoshenko OF_prop_free(pins);
252ef2ee5d0SMichal Meloun if (cfg.function != NULL)
253bebd5269SOleksandr Tymoshenko OF_prop_free(cfg.function);
254ef2ee5d0SMichal Meloun
255ef2ee5d0SMichal Meloun return (rv);
256ef2ee5d0SMichal Meloun }
257ef2ee5d0SMichal Meloun
as3722_pinmux_configure(device_t dev,phandle_t cfgxref)258ef2ee5d0SMichal Meloun int as3722_pinmux_configure(device_t dev, phandle_t cfgxref)
259ef2ee5d0SMichal Meloun {
260ef2ee5d0SMichal Meloun struct as3722_softc *sc;
261ef2ee5d0SMichal Meloun phandle_t node, cfgnode;
262ef2ee5d0SMichal Meloun int rv;
263ef2ee5d0SMichal Meloun
264ef2ee5d0SMichal Meloun sc = device_get_softc(dev);
265ef2ee5d0SMichal Meloun cfgnode = OF_node_from_xref(cfgxref);
266ef2ee5d0SMichal Meloun
267ef2ee5d0SMichal Meloun for (node = OF_child(cfgnode); node != 0; node = OF_peer(node)) {
2687bc28467SAndrew Turner if (!ofw_bus_node_status_okay(node))
269ef2ee5d0SMichal Meloun continue;
270ef2ee5d0SMichal Meloun rv = as3722_pinmux_process_node(sc, node);
271ef2ee5d0SMichal Meloun if (rv != 0)
272ef2ee5d0SMichal Meloun device_printf(dev, "Failed to process pinmux");
273ef2ee5d0SMichal Meloun }
274ef2ee5d0SMichal Meloun return (0);
275ef2ee5d0SMichal Meloun }
276ef2ee5d0SMichal Meloun
277ef2ee5d0SMichal Meloun /* --------------------------------------------------------------------------
278ef2ee5d0SMichal Meloun *
279ef2ee5d0SMichal Meloun * GPIO
280ef2ee5d0SMichal Meloun */
281ef2ee5d0SMichal Meloun device_t
as3722_gpio_get_bus(device_t dev)282ef2ee5d0SMichal Meloun as3722_gpio_get_bus(device_t dev)
283ef2ee5d0SMichal Meloun {
284ef2ee5d0SMichal Meloun struct as3722_softc *sc;
285ef2ee5d0SMichal Meloun
286ef2ee5d0SMichal Meloun sc = device_get_softc(dev);
287ef2ee5d0SMichal Meloun return (sc->gpio_busdev);
288ef2ee5d0SMichal Meloun }
289ef2ee5d0SMichal Meloun
290ef2ee5d0SMichal Meloun int
as3722_gpio_pin_max(device_t dev,int * maxpin)291ef2ee5d0SMichal Meloun as3722_gpio_pin_max(device_t dev, int *maxpin)
292ef2ee5d0SMichal Meloun {
293ef2ee5d0SMichal Meloun
294ef2ee5d0SMichal Meloun *maxpin = NGPIO - 1;
295ef2ee5d0SMichal Meloun return (0);
296ef2ee5d0SMichal Meloun }
297ef2ee5d0SMichal Meloun
298ef2ee5d0SMichal Meloun int
as3722_gpio_pin_getcaps(device_t dev,uint32_t pin,uint32_t * caps)299ef2ee5d0SMichal Meloun as3722_gpio_pin_getcaps(device_t dev, uint32_t pin, uint32_t *caps)
300ef2ee5d0SMichal Meloun {
301ef2ee5d0SMichal Meloun struct as3722_softc *sc;
302ef2ee5d0SMichal Meloun
303ef2ee5d0SMichal Meloun sc = device_get_softc(dev);
304ef2ee5d0SMichal Meloun if (pin >= sc->gpio_npins)
305ef2ee5d0SMichal Meloun return (EINVAL);
306ef2ee5d0SMichal Meloun GPIO_LOCK(sc);
307ef2ee5d0SMichal Meloun *caps = sc->gpio_pins[pin]->pin_caps;
308ef2ee5d0SMichal Meloun GPIO_UNLOCK(sc);
309ef2ee5d0SMichal Meloun return (0);
310ef2ee5d0SMichal Meloun }
311ef2ee5d0SMichal Meloun
312ef2ee5d0SMichal Meloun int
as3722_gpio_pin_getname(device_t dev,uint32_t pin,char * name)313ef2ee5d0SMichal Meloun as3722_gpio_pin_getname(device_t dev, uint32_t pin, char *name)
314ef2ee5d0SMichal Meloun {
315ef2ee5d0SMichal Meloun struct as3722_softc *sc;
316ef2ee5d0SMichal Meloun
317ef2ee5d0SMichal Meloun sc = device_get_softc(dev);
318ef2ee5d0SMichal Meloun if (pin >= sc->gpio_npins)
319ef2ee5d0SMichal Meloun return (EINVAL);
320ef2ee5d0SMichal Meloun GPIO_LOCK(sc);
321ef2ee5d0SMichal Meloun memcpy(name, sc->gpio_pins[pin]->pin_name, GPIOMAXNAME);
322ef2ee5d0SMichal Meloun GPIO_UNLOCK(sc);
323ef2ee5d0SMichal Meloun return (0);
324ef2ee5d0SMichal Meloun }
325ef2ee5d0SMichal Meloun
326ef2ee5d0SMichal Meloun int
as3722_gpio_pin_getflags(device_t dev,uint32_t pin,uint32_t * out_flags)327ef2ee5d0SMichal Meloun as3722_gpio_pin_getflags(device_t dev, uint32_t pin, uint32_t *out_flags)
328ef2ee5d0SMichal Meloun {
329ef2ee5d0SMichal Meloun struct as3722_softc *sc;
330ef2ee5d0SMichal Meloun uint8_t tmp, mode, iosf;
331ef2ee5d0SMichal Meloun uint32_t flags;
332ef2ee5d0SMichal Meloun bool inverted;
333ef2ee5d0SMichal Meloun
334ef2ee5d0SMichal Meloun sc = device_get_softc(dev);
335ef2ee5d0SMichal Meloun if (pin >= sc->gpio_npins)
336ef2ee5d0SMichal Meloun return (EINVAL);
337ef2ee5d0SMichal Meloun
338ef2ee5d0SMichal Meloun GPIO_LOCK(sc);
339ef2ee5d0SMichal Meloun tmp = sc->gpio_pins[pin]->pin_ctrl_reg;
340ef2ee5d0SMichal Meloun GPIO_UNLOCK(sc);
341ef2ee5d0SMichal Meloun iosf = (tmp >> AS3722_GPIO_IOSF_SHIFT) & AS3722_GPIO_IOSF_MASK;
342ef2ee5d0SMichal Meloun mode = (tmp >> AS3722_GPIO_MODE_SHIFT) & AS3722_GPIO_MODE_MASK;
343ef2ee5d0SMichal Meloun inverted = (tmp & AS3722_GPIO_INVERT) != 0;
344ef2ee5d0SMichal Meloun /* Is pin in GPIO mode ? */
345ef2ee5d0SMichal Meloun if (iosf != AS3722_IOSF_GPIO)
346ef2ee5d0SMichal Meloun return (ENXIO);
347ef2ee5d0SMichal Meloun
348ef2ee5d0SMichal Meloun flags = 0;
349ef2ee5d0SMichal Meloun switch (mode) {
350ef2ee5d0SMichal Meloun case AS3722_MODE_INPUT:
351ef2ee5d0SMichal Meloun flags = GPIO_PIN_INPUT;
352ef2ee5d0SMichal Meloun break;
353ef2ee5d0SMichal Meloun case AS3722_MODE_PUSH_PULL:
354ef2ee5d0SMichal Meloun case AS3722_MODE_PUSH_PULL_LV:
355ef2ee5d0SMichal Meloun flags = GPIO_PIN_OUTPUT;
356ef2ee5d0SMichal Meloun break;
357ef2ee5d0SMichal Meloun case AS3722_MODE_OPEN_DRAIN:
358ef2ee5d0SMichal Meloun case AS3722_MODE_OPEN_DRAIN_LV:
359ef2ee5d0SMichal Meloun flags = GPIO_PIN_INPUT | GPIO_PIN_OUTPUT | GPIO_PIN_OPENDRAIN;
360ef2ee5d0SMichal Meloun break;
361ef2ee5d0SMichal Meloun case AS3722_MODE_TRISTATE:
362ef2ee5d0SMichal Meloun flags = GPIO_PIN_TRISTATE;
363ef2ee5d0SMichal Meloun break;
364ef2ee5d0SMichal Meloun case AS3722_MODE_INPUT_PULL_UP_LV:
365ef2ee5d0SMichal Meloun flags = GPIO_PIN_INPUT | GPIO_PIN_PULLUP;
366ef2ee5d0SMichal Meloun break;
367ef2ee5d0SMichal Meloun
368ef2ee5d0SMichal Meloun case AS3722_MODE_INPUT_PULL_DOWN:
369ef2ee5d0SMichal Meloun flags = GPIO_PIN_OUTPUT | GPIO_PIN_PULLDOWN;
370ef2ee5d0SMichal Meloun break;
371ef2ee5d0SMichal Meloun }
372ef2ee5d0SMichal Meloun if (inverted)
373ef2ee5d0SMichal Meloun flags |= GPIO_PIN_INVIN | GPIO_PIN_INVOUT;
374ef2ee5d0SMichal Meloun *out_flags = flags;
375ef2ee5d0SMichal Meloun return (0);
376ef2ee5d0SMichal Meloun }
377ef2ee5d0SMichal Meloun
378ef2ee5d0SMichal Meloun static int
as3722_gpio_get_mode(struct as3722_softc * sc,uint32_t pin,uint32_t gpio_flags)379ef2ee5d0SMichal Meloun as3722_gpio_get_mode(struct as3722_softc *sc, uint32_t pin, uint32_t gpio_flags)
380ef2ee5d0SMichal Meloun {
381ef2ee5d0SMichal Meloun int flags;
382ef2ee5d0SMichal Meloun
383ef2ee5d0SMichal Meloun flags = sc->gpio_pins[pin]->pin_cfg_flags;
384ef2ee5d0SMichal Meloun
385ef2ee5d0SMichal Meloun /* Tristate mode. */
386ef2ee5d0SMichal Meloun if (flags & AS3722_CFG_BIAS_HIGH_IMPEDANCE ||
387ef2ee5d0SMichal Meloun gpio_flags & GPIO_PIN_TRISTATE)
388ef2ee5d0SMichal Meloun return (AS3722_MODE_TRISTATE);
389ef2ee5d0SMichal Meloun
390ef2ee5d0SMichal Meloun /* Open drain modes. */
391ef2ee5d0SMichal Meloun if (flags & AS3722_CFG_OPEN_DRAIN || gpio_flags & GPIO_PIN_OPENDRAIN) {
392ef2ee5d0SMichal Meloun /* Only pull up have effect */
393ef2ee5d0SMichal Meloun if (flags & AS3722_CFG_BIAS_PULL_UP ||
394ef2ee5d0SMichal Meloun gpio_flags & GPIO_PIN_PULLUP)
395ef2ee5d0SMichal Meloun return (AS3722_MODE_OPEN_DRAIN_LV);
396ef2ee5d0SMichal Meloun return (AS3722_MODE_OPEN_DRAIN);
397ef2ee5d0SMichal Meloun }
398ef2ee5d0SMichal Meloun /* Input modes. */
399ef2ee5d0SMichal Meloun if (gpio_flags & GPIO_PIN_INPUT) {
400ef2ee5d0SMichal Meloun /* Accept pull up or pull down. */
401ef2ee5d0SMichal Meloun if (flags & AS3722_CFG_BIAS_PULL_UP ||
402ef2ee5d0SMichal Meloun gpio_flags & GPIO_PIN_PULLUP)
403ef2ee5d0SMichal Meloun return (AS3722_MODE_INPUT_PULL_UP_LV);
404ef2ee5d0SMichal Meloun
405ef2ee5d0SMichal Meloun if (flags & AS3722_CFG_BIAS_PULL_DOWN ||
406ef2ee5d0SMichal Meloun gpio_flags & GPIO_PIN_PULLDOWN)
407ef2ee5d0SMichal Meloun return (AS3722_MODE_INPUT_PULL_DOWN);
408ef2ee5d0SMichal Meloun return (AS3722_MODE_INPUT);
409ef2ee5d0SMichal Meloun }
410ef2ee5d0SMichal Meloun /*
411ef2ee5d0SMichal Meloun * Output modes.
412ef2ee5d0SMichal Meloun * Pull down is used as indicator of low voltage output.
413ef2ee5d0SMichal Meloun */
414ef2ee5d0SMichal Meloun if (flags & AS3722_CFG_BIAS_PULL_DOWN ||
415ef2ee5d0SMichal Meloun gpio_flags & GPIO_PIN_PULLDOWN)
416ef2ee5d0SMichal Meloun return (AS3722_MODE_PUSH_PULL_LV);
417ef2ee5d0SMichal Meloun return (AS3722_MODE_PUSH_PULL);
418ef2ee5d0SMichal Meloun }
419ef2ee5d0SMichal Meloun
420ef2ee5d0SMichal Meloun int
as3722_gpio_pin_setflags(device_t dev,uint32_t pin,uint32_t flags)421ef2ee5d0SMichal Meloun as3722_gpio_pin_setflags(device_t dev, uint32_t pin, uint32_t flags)
422ef2ee5d0SMichal Meloun {
423ef2ee5d0SMichal Meloun struct as3722_softc *sc;
424ef2ee5d0SMichal Meloun uint8_t ctrl, mode, iosf;
425ef2ee5d0SMichal Meloun int rv;
426ef2ee5d0SMichal Meloun
427ef2ee5d0SMichal Meloun sc = device_get_softc(dev);
428ef2ee5d0SMichal Meloun if (pin >= sc->gpio_npins)
429ef2ee5d0SMichal Meloun return (EINVAL);
430ef2ee5d0SMichal Meloun
431ef2ee5d0SMichal Meloun GPIO_LOCK(sc);
432ef2ee5d0SMichal Meloun ctrl = sc->gpio_pins[pin]->pin_ctrl_reg;
433ef2ee5d0SMichal Meloun iosf = (ctrl >> AS3722_GPIO_IOSF_SHIFT) & AS3722_GPIO_IOSF_MASK;
434ef2ee5d0SMichal Meloun /* Is pin in GPIO mode ? */
435ef2ee5d0SMichal Meloun if (iosf != AS3722_IOSF_GPIO) {
436ef2ee5d0SMichal Meloun GPIO_UNLOCK(sc);
437ef2ee5d0SMichal Meloun return (ENXIO);
438ef2ee5d0SMichal Meloun }
439ef2ee5d0SMichal Meloun mode = as3722_gpio_get_mode(sc, pin, flags);
440ef2ee5d0SMichal Meloun ctrl &= ~(AS3722_GPIO_MODE_MASK << AS3722_GPIO_MODE_SHIFT);
441*dca7f696SJohn Baldwin ctrl |= mode << AS3722_GPIO_MODE_SHIFT;
442ef2ee5d0SMichal Meloun rv = 0;
443ef2ee5d0SMichal Meloun if (ctrl != sc->gpio_pins[pin]->pin_ctrl_reg) {
444ef2ee5d0SMichal Meloun rv = WR1(sc, AS3722_GPIO0_CONTROL + pin, ctrl);
445ef2ee5d0SMichal Meloun sc->gpio_pins[pin]->pin_ctrl_reg = ctrl;
446ef2ee5d0SMichal Meloun }
447ef2ee5d0SMichal Meloun GPIO_UNLOCK(sc);
448ef2ee5d0SMichal Meloun return (rv);
449ef2ee5d0SMichal Meloun }
450ef2ee5d0SMichal Meloun
451ef2ee5d0SMichal Meloun int
as3722_gpio_pin_set(device_t dev,uint32_t pin,uint32_t val)452ef2ee5d0SMichal Meloun as3722_gpio_pin_set(device_t dev, uint32_t pin, uint32_t val)
453ef2ee5d0SMichal Meloun {
454ef2ee5d0SMichal Meloun struct as3722_softc *sc;
455ef2ee5d0SMichal Meloun uint8_t tmp;
456ef2ee5d0SMichal Meloun int rv;
457ef2ee5d0SMichal Meloun
458ef2ee5d0SMichal Meloun sc = device_get_softc(dev);
459ef2ee5d0SMichal Meloun if (pin >= sc->gpio_npins)
460ef2ee5d0SMichal Meloun return (EINVAL);
461ef2ee5d0SMichal Meloun
462ef2ee5d0SMichal Meloun tmp = (val != 0) ? 1 : 0;
463ef2ee5d0SMichal Meloun if (sc->gpio_pins[pin]->pin_ctrl_reg & AS3722_GPIO_INVERT)
464ef2ee5d0SMichal Meloun tmp ^= 1;
465ef2ee5d0SMichal Meloun
466ef2ee5d0SMichal Meloun GPIO_LOCK(sc);
467ef2ee5d0SMichal Meloun rv = RM1(sc, AS3722_GPIO_SIGNAL_OUT, (1 << pin), (tmp << pin));
468ef2ee5d0SMichal Meloun GPIO_UNLOCK(sc);
469ef2ee5d0SMichal Meloun return (rv);
470ef2ee5d0SMichal Meloun }
471ef2ee5d0SMichal Meloun
472ef2ee5d0SMichal Meloun int
as3722_gpio_pin_get(device_t dev,uint32_t pin,uint32_t * val)473ef2ee5d0SMichal Meloun as3722_gpio_pin_get(device_t dev, uint32_t pin, uint32_t *val)
474ef2ee5d0SMichal Meloun {
475ef2ee5d0SMichal Meloun struct as3722_softc *sc;
476ef2ee5d0SMichal Meloun uint8_t tmp, mode, ctrl;
477ef2ee5d0SMichal Meloun int rv;
478ef2ee5d0SMichal Meloun
479ef2ee5d0SMichal Meloun sc = device_get_softc(dev);
480ef2ee5d0SMichal Meloun if (pin >= sc->gpio_npins)
481ef2ee5d0SMichal Meloun return (EINVAL);
482ef2ee5d0SMichal Meloun
483ef2ee5d0SMichal Meloun GPIO_LOCK(sc);
484ef2ee5d0SMichal Meloun ctrl = sc->gpio_pins[pin]->pin_ctrl_reg;
485ef2ee5d0SMichal Meloun mode = (ctrl >> AS3722_GPIO_MODE_SHIFT) & AS3722_GPIO_MODE_MASK;
486ef2ee5d0SMichal Meloun if ((mode == AS3722_MODE_PUSH_PULL) ||
487ef2ee5d0SMichal Meloun (mode == AS3722_MODE_PUSH_PULL_LV))
488ef2ee5d0SMichal Meloun rv = RD1(sc, AS3722_GPIO_SIGNAL_OUT, &tmp);
489ef2ee5d0SMichal Meloun else
490ef2ee5d0SMichal Meloun rv = RD1(sc, AS3722_GPIO_SIGNAL_IN, &tmp);
491ef2ee5d0SMichal Meloun GPIO_UNLOCK(sc);
492ef2ee5d0SMichal Meloun if (rv != 0)
493ef2ee5d0SMichal Meloun return (rv);
494ef2ee5d0SMichal Meloun
495ef2ee5d0SMichal Meloun *val = tmp & (1 << pin) ? 1 : 0;
496ef2ee5d0SMichal Meloun if (ctrl & AS3722_GPIO_INVERT)
497ef2ee5d0SMichal Meloun *val ^= 1;
498ef2ee5d0SMichal Meloun return (0);
499ef2ee5d0SMichal Meloun }
500ef2ee5d0SMichal Meloun
501ef2ee5d0SMichal Meloun int
as3722_gpio_pin_toggle(device_t dev,uint32_t pin)502ef2ee5d0SMichal Meloun as3722_gpio_pin_toggle(device_t dev, uint32_t pin)
503ef2ee5d0SMichal Meloun {
504ef2ee5d0SMichal Meloun struct as3722_softc *sc;
505ef2ee5d0SMichal Meloun uint8_t tmp;
506ef2ee5d0SMichal Meloun int rv;
507ef2ee5d0SMichal Meloun
508ef2ee5d0SMichal Meloun sc = device_get_softc(dev);
509ef2ee5d0SMichal Meloun if (pin >= sc->gpio_npins)
510ef2ee5d0SMichal Meloun return (EINVAL);
511ef2ee5d0SMichal Meloun
512ef2ee5d0SMichal Meloun GPIO_LOCK(sc);
513ef2ee5d0SMichal Meloun rv = RD1(sc, AS3722_GPIO_SIGNAL_OUT, &tmp);
514ef2ee5d0SMichal Meloun if (rv != 0) {
515ef2ee5d0SMichal Meloun GPIO_UNLOCK(sc);
516ef2ee5d0SMichal Meloun return (rv);
517ef2ee5d0SMichal Meloun }
518ef2ee5d0SMichal Meloun tmp ^= (1 <<pin);
519ef2ee5d0SMichal Meloun rv = RM1(sc, AS3722_GPIO_SIGNAL_OUT, (1 << pin), tmp);
520ef2ee5d0SMichal Meloun GPIO_UNLOCK(sc);
521ef2ee5d0SMichal Meloun return (0);
522ef2ee5d0SMichal Meloun }
523ef2ee5d0SMichal Meloun
524ef2ee5d0SMichal Meloun int
as3722_gpio_map_gpios(device_t dev,phandle_t pdev,phandle_t gparent,int gcells,pcell_t * gpios,uint32_t * pin,uint32_t * flags)525ef2ee5d0SMichal Meloun as3722_gpio_map_gpios(device_t dev, phandle_t pdev, phandle_t gparent,
526ef2ee5d0SMichal Meloun int gcells, pcell_t *gpios, uint32_t *pin, uint32_t *flags)
527ef2ee5d0SMichal Meloun {
528ef2ee5d0SMichal Meloun
529ef2ee5d0SMichal Meloun if (gcells != 2)
530ef2ee5d0SMichal Meloun return (ERANGE);
531ef2ee5d0SMichal Meloun *pin = gpios[0];
532ef2ee5d0SMichal Meloun *flags= gpios[1];
533ef2ee5d0SMichal Meloun return (0);
534ef2ee5d0SMichal Meloun }
535ef2ee5d0SMichal Meloun
536ef2ee5d0SMichal Meloun int
as3722_gpio_attach(struct as3722_softc * sc,phandle_t node)537ef2ee5d0SMichal Meloun as3722_gpio_attach(struct as3722_softc *sc, phandle_t node)
538ef2ee5d0SMichal Meloun {
539ef2ee5d0SMichal Meloun struct as3722_gpio_pin *pin;
540ef2ee5d0SMichal Meloun int i, rv;
541ef2ee5d0SMichal Meloun
542ef2ee5d0SMichal Meloun sx_init(&sc->gpio_lock, "AS3722 GPIO lock");
543ef2ee5d0SMichal Meloun sc->gpio_npins = NGPIO;
544ef2ee5d0SMichal Meloun sc->gpio_pins = malloc(sizeof(struct as3722_gpio_pin *) *
545ef2ee5d0SMichal Meloun sc->gpio_npins, M_AS3722_GPIO, M_WAITOK | M_ZERO);
546ef2ee5d0SMichal Meloun
547ef2ee5d0SMichal Meloun sc->gpio_busdev = gpiobus_attach_bus(sc->dev);
548ef2ee5d0SMichal Meloun if (sc->gpio_busdev == NULL)
549ef2ee5d0SMichal Meloun return (ENXIO);
550ef2ee5d0SMichal Meloun for (i = 0; i < sc->gpio_npins; i++) {
551ef2ee5d0SMichal Meloun sc->gpio_pins[i] = malloc(sizeof(struct as3722_gpio_pin),
552ef2ee5d0SMichal Meloun M_AS3722_GPIO, M_WAITOK | M_ZERO);
553ef2ee5d0SMichal Meloun pin = sc->gpio_pins[i];
554ef2ee5d0SMichal Meloun sprintf(pin->pin_name, "gpio%d", i);
555ef2ee5d0SMichal Meloun pin->pin_caps = GPIO_PIN_INPUT | GPIO_PIN_OUTPUT |
556ef2ee5d0SMichal Meloun GPIO_PIN_OPENDRAIN | GPIO_PIN_PUSHPULL | GPIO_PIN_TRISTATE |
557ef2ee5d0SMichal Meloun GPIO_PIN_PULLUP | GPIO_PIN_PULLDOWN | GPIO_PIN_INVIN |
558ef2ee5d0SMichal Meloun GPIO_PIN_INVOUT;
559ef2ee5d0SMichal Meloun rv = RD1(sc, AS3722_GPIO0_CONTROL + i, &pin->pin_ctrl_reg);
560ef2ee5d0SMichal Meloun if (rv != 0) {
561ef2ee5d0SMichal Meloun device_printf(sc->dev,
562ef2ee5d0SMichal Meloun "Cannot read configuration for pin %s\n",
563ef2ee5d0SMichal Meloun sc->gpio_pins[i]->pin_name);
564ef2ee5d0SMichal Meloun }
565ef2ee5d0SMichal Meloun }
566ef2ee5d0SMichal Meloun return (0);
567ef2ee5d0SMichal Meloun }
568