xref: /openbsd-src/sys/dev/fdt/pinctrl.c (revision 9fdf0c627b1fec102f212f847a6f7676c1829e65)
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