xref: /openbsd/sys/dev/fdt/bcm2835_bsc.c (revision 3cab2bb3)
1 /*	$OpenBSD: bcm2835_bsc.c,v 1.1 2020/04/25 22:28:12 kettenis Exp $	*/
2 /*
3  * Copyright (c) 2020 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 
22 #include <machine/intr.h>
23 #include <machine/bus.h>
24 #include <machine/fdt.h>
25 
26 #define _I2C_PRIVATE
27 #include <dev/i2c/i2cvar.h>
28 
29 #include <dev/ofw/openfirm.h>
30 #include <dev/ofw/ofw_clock.h>
31 #include <dev/ofw/ofw_pinctrl.h>
32 #include <dev/ofw/fdt.h>
33 
34 /* Registers. */
35 #define BSC_C		0x00
36 #define  BSC_C_I2CEN	(1 << 15)
37 #define  BSC_C_INTR	(1 << 10)
38 #define  BSC_C_INTT	(1 << 9)
39 #define  BSC_C_INTD	(1 << 8)
40 #define  BSC_C_ST	(1 << 7)
41 #define  BSC_C_CLEAR	(0x3 << 4)
42 #define  BSC_C_READ	(1 << 0)
43 #define BSC_S		0x04
44 #define  BSC_S_CLKT	(1 << 9)
45 #define  BSC_S_ERR	(1 << 8)
46 #define  BSC_S_RXF	(1 << 7)
47 #define  BSC_S_TXE	(1 << 6)
48 #define  BSC_S_RXD	(1 << 5)
49 #define  BSC_S_TXD	(1 << 4)
50 #define  BSC_S_RXR	(1 << 3)
51 #define  BSC_S_TXW	(1 << 2)
52 #define  BSC_S_DONE	(1 << 1)
53 #define  BSC_S_TA	(1 << 0)
54 #define BSC_DLEN	0x08
55 #define BSC_A		0x0c
56 #define BSC_FIFO	0x10
57 #define BSC_DIV		0x14
58 #define BSC_DEL		0x18
59 #define  BSC_DEL_FEDL_SHIFT	16
60 #define  BSC_DEL_REDL_SHIFT	0
61 #define BSC_CLKT	0x1c
62 
63 #define HREAD4(sc, reg)							\
64 	(bus_space_read_4((sc)->sc_iot, (sc)->sc_ioh, (reg)))
65 #define HWRITE4(sc, reg, val)						\
66 	bus_space_write_4((sc)->sc_iot, (sc)->sc_ioh, (reg), (val))
67 
68 struct bcmbsc_softc {
69 	struct device		sc_dev;
70 	bus_space_tag_t		sc_iot;
71 	bus_space_handle_t	sc_ioh;
72 
73 	int			sc_node;
74 	struct i2c_controller	sc_ic;
75 };
76 
77 int	bcmbsc_match(struct device *, void *, void *);
78 void	bcmbsc_attach(struct device *, struct device *, void *);
79 
80 struct cfattach	bcmbsc_ca = {
81 	sizeof (struct bcmbsc_softc), bcmbsc_match, bcmbsc_attach
82 };
83 
84 struct cfdriver bcmbsc_cd = {
85 	NULL, "bcmbsc", DV_DULL
86 };
87 
88 int	bcmbsc_acquire_bus(void *, int);
89 void	bcmbsc_release_bus(void *, int);
90 int	bcmbsc_exec(void *, i2c_op_t, i2c_addr_t, const void *, size_t,
91 	    void *, size_t, int);
92 
93 void	bcmbsc_bus_scan(struct device *, struct i2cbus_attach_args *, void *);
94 
95 int
96 bcmbsc_match(struct device *parent, void *match, void *aux)
97 {
98 	struct fdt_attach_args *faa = aux;
99 
100 	return OF_is_compatible(faa->fa_node, "brcm,bcm2835-i2c");
101 }
102 
103 void
104 bcmbsc_attach(struct device *parent, struct device *self, void *aux)
105 {
106 	struct bcmbsc_softc *sc = (struct bcmbsc_softc *)self;
107 	struct fdt_attach_args *faa = aux;
108 	struct i2cbus_attach_args iba;
109 	uint32_t clock_speed, bus_speed;
110 	uint32_t div, fedl, redl;
111 
112 	if (faa->fa_nreg < 1) {
113 		printf(": no registers\n");
114 		return;
115 	}
116 
117 	sc->sc_iot = faa->fa_iot;
118 	sc->sc_node = faa->fa_node;
119 
120 	if (bus_space_map(sc->sc_iot, faa->fa_reg[0].addr,
121 	    faa->fa_reg[0].size, 0, &sc->sc_ioh)) {
122 		printf(": can't map registers\n");
123 		return;
124 	}
125 
126 	printf("\n");
127 
128 	pinctrl_byname(sc->sc_node, "default");
129 
130 	clock_enable_all(sc->sc_node);
131 
132 	clock_speed = clock_get_frequency(sc->sc_node, NULL);
133 	bus_speed = OF_getpropint(sc->sc_node, "clock-frequency", 100000);
134 
135 	div = clock_speed / bus_speed;
136 	if (div & 1)
137 		div++;
138 	fedl = MAX(div / 16, 1);
139 	redl = MAX(div / 4, 1);
140 	HWRITE4(sc, BSC_DIV, div);
141 	HWRITE4(sc, BSC_DEL, (fedl << BSC_DEL_FEDL_SHIFT) |
142 	    (redl << BSC_DEL_REDL_SHIFT));
143 
144 	sc->sc_ic.ic_cookie = sc;
145 	sc->sc_ic.ic_acquire_bus = bcmbsc_acquire_bus;
146 	sc->sc_ic.ic_release_bus = bcmbsc_release_bus;
147 	sc->sc_ic.ic_exec = bcmbsc_exec;
148 
149 	/* Configure its children */
150 	memset(&iba, 0, sizeof(iba));
151 	iba.iba_name = "iic";
152 	iba.iba_tag = &sc->sc_ic;
153 	iba.iba_bus_scan = bcmbsc_bus_scan;
154 	iba.iba_bus_scan_arg = &sc->sc_node;
155 
156 	config_found(&sc->sc_dev, &iba, iicbus_print);
157 }
158 
159 int
160 bcmbsc_acquire_bus(void *cookie, int flags)
161 {
162 	struct bcmbsc_softc *sc = cookie;
163 
164 	HWRITE4(sc, BSC_S, HREAD4(sc, BSC_S));
165 	HWRITE4(sc, BSC_C, BSC_C_I2CEN | BSC_C_CLEAR);
166 	return 0;
167 }
168 
169 void
170 bcmbsc_release_bus(void *cookie, int flags)
171 {
172 	struct bcmbsc_softc *sc = cookie;
173 
174 	HWRITE4(sc, BSC_C, BSC_C_CLEAR);
175 }
176 
177 int
178 bcmbsc_wait(struct bcmbsc_softc *sc, uint32_t mask, uint32_t value)
179 {
180 	uint32_t stat;
181 	int timo;
182 
183 	for (timo = 10000; timo > 0; timo--) {
184 		stat = HREAD4(sc, BSC_S);
185 		if ((stat & mask) == value)
186 			return 0;
187 		if (stat & BSC_S_CLKT)
188 			return ETIMEDOUT;
189 		if (stat & BSC_S_ERR)
190 			return EIO;
191 		delay(1);
192 	}
193 
194 	return ETIMEDOUT;
195 }
196 
197 int
198 bcmbsc_read(struct bcmbsc_softc *sc, uint8_t *buf, size_t buflen)
199 {
200 	int i, error;
201 
202 	for (i = 0; i < buflen; i++) {
203 		error = bcmbsc_wait(sc, BSC_S_RXD, BSC_S_RXD);
204 		if (error)
205 			return error;
206 		buf[i] = HREAD4(sc, BSC_FIFO);
207 	}
208 
209 	return 0;
210 }
211 
212 int
213 bcmbsc_write(struct bcmbsc_softc *sc, const uint8_t *buf, size_t buflen)
214 {
215 	int i, error;
216 
217 	for (i = 0; i < buflen; i++) {
218 		error = bcmbsc_wait(sc, BSC_S_TXD, BSC_S_TXD);
219 		if (error)
220 			return error;
221 		HWRITE4(sc, BSC_FIFO, buf[i]);
222 	}
223 
224 	return 0;
225 }
226 
227 int
228 bcmbsc_exec(void *cookie, i2c_op_t op, i2c_addr_t addr, const void *cmd,
229     size_t cmdlen, void *buf, size_t buflen, int flags)
230 {
231 	struct bcmbsc_softc *sc = cookie;
232 	uint32_t ctrl = BSC_C_I2CEN | BSC_C_ST;
233 	int error;
234 
235 	if (cmdlen + buflen > 65535)
236 		return EINVAL;
237 
238 	HWRITE4(sc, BSC_A, addr);
239 
240 	if (I2C_OP_READ_P(op))
241 		HWRITE4(sc, BSC_DLEN, cmdlen);
242 	else
243 		HWRITE4(sc, BSC_DLEN, cmdlen + buflen);
244 
245 	if (cmdlen > 0) {
246 		HWRITE4(sc, BSC_C, ctrl);
247 		error = bcmbsc_write(sc, cmd, cmdlen);
248 		if (error)
249 			return error;
250 		if (I2C_OP_READ_P(op))
251 			bcmbsc_wait(sc, BSC_S_DONE, BSC_S_DONE);
252 	}
253 
254 	if (I2C_OP_READ_P(op)) {
255 		HWRITE4(sc, BSC_DLEN, buflen);
256 		HWRITE4(sc, BSC_C, ctrl | BSC_C_READ);
257 		error = bcmbsc_read(sc, buf, buflen);
258 		if (error)
259 			return error;
260 	} else {
261 		if (cmdlen == 0)
262 			HWRITE4(sc, BSC_C, ctrl);
263 		error = bcmbsc_write(sc, buf, buflen);
264 		if (error)
265 			return error;
266 	}
267 
268 	return bcmbsc_wait(sc, BSC_S_DONE | BSC_S_TA, BSC_S_DONE);
269 }
270 
271 void
272 bcmbsc_bus_scan(struct device *self, struct i2cbus_attach_args *iba, void *arg)
273 {
274 	int iba_node = *(int *)arg;
275 	struct i2c_attach_args ia;
276 	char name[32];
277 	uint32_t reg[1];
278 	int node;
279 
280 	for (node = OF_child(iba_node); node; node = OF_peer(node)) {
281 		memset(name, 0, sizeof(name));
282 		memset(reg, 0, sizeof(reg));
283 
284 		if (OF_getprop(node, "compatible", name, sizeof(name)) == -1)
285 			continue;
286 		if (name[0] == '\0')
287 			continue;
288 
289 		if (OF_getprop(node, "reg", &reg, sizeof(reg)) != sizeof(reg))
290 			continue;
291 
292 		memset(&ia, 0, sizeof(ia));
293 		ia.ia_tag = iba->iba_tag;
294 		ia.ia_addr = bemtoh32(&reg[0]);
295 		ia.ia_name = name;
296 		ia.ia_cookie = &node;
297 		config_found(self, &ia, iic_print);
298 	}
299 }
300