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