1 /* $OpenBSD: pinctrl.c,v 1.5 2021/10/24 17:52:26 mpi Exp $ */
2 /*
3 * Copyright (c) 2018, 2019 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 <machine/intr.h>
24 #include <machine/bus.h>
25 #include <machine/fdt.h>
26
27 #include <dev/ofw/openfirm.h>
28 #include <dev/ofw/ofw_misc.h>
29 #include <dev/ofw/ofw_pinctrl.h>
30 #include <dev/ofw/fdt.h>
31
32 #define HREAD2(sc, reg) \
33 (bus_space_read_2((sc)->sc_iot, (sc)->sc_ioh, (reg)))
34 #define HWRITE2(sc, reg, val) \
35 bus_space_write_2((sc)->sc_iot, (sc)->sc_ioh, (reg), (val))
36 #define HREAD4(sc, reg) \
37 (bus_space_read_4((sc)->sc_iot, (sc)->sc_ioh, (reg)))
38 #define HWRITE4(sc, reg, val) \
39 bus_space_write_4((sc)->sc_iot, (sc)->sc_ioh, (reg), (val))
40
41 struct pinctrl_softc {
42 struct device sc_dev;
43 bus_space_tag_t sc_iot;
44 bus_space_handle_t sc_ioh;
45
46 uint32_t sc_reg_width;
47 uint32_t sc_func_mask;
48 uint32_t sc_ncells;
49 };
50
51 int pinctrl_match(struct device *, void *, void *);
52 void pinctrl_attach(struct device *, struct device *, void *);
53
54 const struct cfattach pinctrl_ca = {
55 sizeof (struct pinctrl_softc), pinctrl_match, pinctrl_attach
56 };
57
58 struct cfdriver pinctrl_cd = {
59 NULL, "pinctrl", DV_DULL
60 };
61
62 int pinctrl_pinctrl(uint32_t, void *);
63
64 int
pinctrl_match(struct device * parent,void * match,void * aux)65 pinctrl_match(struct device *parent, void *match, void *aux)
66 {
67 struct fdt_attach_args *faa = aux;
68
69 return (OF_is_compatible(faa->fa_node, "pinctrl-single") ||
70 OF_is_compatible(faa->fa_node, "pinconf-single"));
71 }
72
73 void
pinctrl_attach(struct device * parent,struct device * self,void * aux)74 pinctrl_attach(struct device *parent, struct device *self, void *aux)
75 {
76 struct pinctrl_softc *sc = (struct pinctrl_softc *)self;
77 struct fdt_attach_args *faa = aux;
78
79 if (faa->fa_nreg < 1) {
80 printf(": no registers\n");
81 return;
82 }
83
84 sc->sc_reg_width = OF_getpropint(faa->fa_node,
85 "pinctrl-single,register-width", 0);
86 if (sc->sc_reg_width != 16 &&
87 sc->sc_reg_width != 32) {
88 printf(": unsupported register width\n");
89 return;
90 }
91
92 sc->sc_ncells = OF_getpropint(faa->fa_node, "#pinctrl-cells", 1);
93 sc->sc_func_mask = OF_getpropint(faa->fa_node,
94 "pinctrl-single,function-mask", 0);
95
96 sc->sc_iot = faa->fa_iot;
97 if (bus_space_map(sc->sc_iot, faa->fa_reg[0].addr,
98 faa->fa_reg[0].size, 0, &sc->sc_ioh)) {
99 printf(": can't map registers\n");
100 return;
101 }
102
103 pinctrl_register(faa->fa_node, pinctrl_pinctrl, sc);
104
105 printf("\n");
106 }
107
108 uint32_t
pinctrl_set2(int node,char * setting,uint32_t val)109 pinctrl_set2(int node, char *setting, uint32_t val)
110 {
111 uint32_t values[2];
112
113 if (OF_getpropintarray(node, setting, values, sizeof(values)) !=
114 sizeof(values))
115 return val;
116
117 val &= ~values[1];
118 val |= (values[0] & values[1]);
119 return val;
120 }
121
122 uint32_t
pinctrl_set4(int node,char * setting,uint32_t val)123 pinctrl_set4(int node, char *setting, uint32_t val)
124 {
125 uint32_t values[4];
126
127 if (OF_getpropintarray(node, setting, values, sizeof(values)) !=
128 sizeof(values))
129 return val;
130
131 val &= ~values[3];
132 val |= (values[0] & values[3]);
133 return val;
134 }
135
136 int
pinctrl_pinctrl(uint32_t phandle,void * cookie)137 pinctrl_pinctrl(uint32_t phandle, void *cookie)
138 {
139 struct pinctrl_softc *sc = cookie;
140 uint32_t *pins;
141 int node, len, i;
142
143 node = OF_getnodebyphandle(phandle);
144 if (node == 0)
145 return -1;
146
147 len = OF_getproplen(node, "pinctrl-single,pins");
148 if (len <= 0)
149 return -1;
150
151 pins = malloc(len, M_TEMP, M_WAITOK);
152 OF_getpropintarray(node, "pinctrl-single,pins", pins, len);
153
154 for (i = 0; i < len / sizeof(uint32_t); i += (1 + sc->sc_ncells)) {
155 uint32_t reg = pins[i];
156 uint32_t func = pins[i + 1];
157 uint32_t val = 0;
158
159 if (sc->sc_ncells == 2)
160 func |= pins[i + 2];
161
162 if (sc->sc_reg_width == 16)
163 val = HREAD2(sc, reg);
164 else if (sc->sc_reg_width == 32)
165 val = HREAD4(sc, reg);
166
167 val &= ~sc->sc_func_mask;
168 val |= (func & sc->sc_func_mask);
169
170 val = pinctrl_set2(node, "pinctrl-single,drive-strength", val);
171 val = pinctrl_set4(node, "pinctrl-single,bias-pulldown", val);
172 val = pinctrl_set4(node, "pinctrl-single,bias-pullup", val);
173
174 if (sc->sc_reg_width == 16)
175 HWRITE2(sc, reg, val);
176 else if (sc->sc_reg_width == 32)
177 HWRITE4(sc, reg, val);
178 }
179
180 free(pins, M_TEMP, len);
181 return 0;
182 }
183