1 /* $OpenBSD: qciic_fdt.c,v 1.2 2024/10/17 17:58:58 kettenis Exp $ */
2 /*
3 * Copyright (c) 2022 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/bus.h>
23 #include <machine/intr.h>
24 #include <machine/fdt.h>
25
26 #include <dev/ofw/openfirm.h>
27 #include <dev/ofw/ofw_gpio.h>
28 #include <dev/ofw/ofw_pinctrl.h>
29 #include <dev/ofw/fdt.h>
30
31 #define _I2C_PRIVATE
32 #include <dev/i2c/i2cvar.h>
33
34 /* Registers */
35 #define GENI_I2C_TX_TRANS_LEN 0x26c
36 #define GENI_I2C_RX_TRANS_LEN 0x270
37 #define GENI_M_CMD0 0x600
38 #define GENI_M_CMD0_OPCODE_I2C_WRITE (0x1 << 27)
39 #define GENI_M_CMD0_OPCODE_I2C_READ (0x2 << 27)
40 #define GENI_M_CMD0_SLV_ADDR_SHIFT 9
41 #define GENI_M_CMD0_STOP_STRETCH (1 << 2)
42 #define GENI_M_IRQ_STATUS 0x610
43 #define GENI_M_IRQ_CLEAR 0x618
44 #define GENI_M_IRQ_CMD_DONE (1 << 0)
45 #define GENI_TX_FIFO 0x700
46 #define GENI_RX_FIFO 0x780
47 #define GENI_TX_FIFO_STATUS 0x800
48 #define GENI_RX_FIFO_STATUS 0x804
49 #define GENI_RX_FIFO_STATUS_WC(val) ((val) & 0xffffff)
50
51 #define HREAD4(sc, reg) \
52 (bus_space_read_4((sc)->sc_iot, (sc)->sc_ioh, (reg)))
53 #define HWRITE4(sc, reg, val) \
54 bus_space_write_4((sc)->sc_iot, (sc)->sc_ioh, (reg), (val))
55
56 struct qciic_fdt_softc {
57 struct device sc_dev;
58 bus_space_tag_t sc_iot;
59 bus_space_handle_t sc_ioh;
60
61 int sc_node;
62 struct device *sc_iic;
63
64 struct i2c_controller sc_ic;
65 };
66
67 int qciic_fdt_match(struct device *, void *, void *);
68 void qciic_fdt_attach(struct device *, struct device *, void *);
69
70 const struct cfattach qciic_fdt_ca = {
71 sizeof (struct qciic_fdt_softc), qciic_fdt_match, qciic_fdt_attach
72 };
73
74 int qciic_fdt_acquire_bus(void *, int);
75 void qciic_fdt_release_bus(void *, int);
76 int qciic_fdt_exec(void *, i2c_op_t, i2c_addr_t, const void *, size_t,
77 void *, size_t, int);
78
79 void *qciic_fdt_i2c_intr_establish(void *, void *, int, int (*)(void *),
80 void *, const char *);
81 void qciic_fdt_i2c_intr_disestablish(void *, void *);
82 const char *qciic_fdt_i2c_intr_string(void *, void *);
83
84 void qciic_fdt_bus_scan(struct device *, struct i2cbus_attach_args *,
85 void *);
86
87 int
qciic_fdt_match(struct device * parent,void * match,void * aux)88 qciic_fdt_match(struct device *parent, void *match, void *aux)
89 {
90 struct fdt_attach_args *faa = aux;
91
92 return OF_is_compatible(faa->fa_node, "qcom,geni-i2c");
93 }
94
95 void
qciic_fdt_attach(struct device * parent,struct device * self,void * aux)96 qciic_fdt_attach(struct device *parent, struct device *self, void *aux)
97 {
98 struct qciic_fdt_softc *sc = (struct qciic_fdt_softc *)self;
99 struct fdt_attach_args *faa = aux;
100 struct i2cbus_attach_args iba;
101
102 sc->sc_node = faa->fa_node;
103
104 sc->sc_iot = faa->fa_iot;
105 if (bus_space_map(sc->sc_iot, faa->fa_reg[0].addr, faa->fa_reg[0].size,
106 0, &sc->sc_ioh)) {
107 printf(": can't map registers\n");
108 return;
109 }
110
111 printf("\n");
112
113 pinctrl_byname(sc->sc_node, "default");
114
115 sc->sc_ic.ic_cookie = sc;
116 sc->sc_ic.ic_acquire_bus = qciic_fdt_acquire_bus;
117 sc->sc_ic.ic_release_bus = qciic_fdt_release_bus;
118 sc->sc_ic.ic_exec = qciic_fdt_exec;
119 sc->sc_ic.ic_intr_establish = qciic_fdt_i2c_intr_establish;
120 sc->sc_ic.ic_intr_disestablish = qciic_fdt_i2c_intr_disestablish;
121 sc->sc_ic.ic_intr_string = qciic_fdt_i2c_intr_string;
122
123 memset(&iba, 0, sizeof(iba));
124 iba.iba_name = "iic";
125 iba.iba_tag = &sc->sc_ic;
126 iba.iba_bus_scan = qciic_fdt_bus_scan;
127 iba.iba_bus_scan_arg = &sc->sc_node;
128 config_found(&sc->sc_dev, &iba, iicbus_print);
129 }
130
131 int
qciic_fdt_acquire_bus(void * cookie,int flags)132 qciic_fdt_acquire_bus(void *cookie, int flags)
133 {
134 return 0;
135 }
136
137 void
qciic_fdt_release_bus(void * cookie,int flags)138 qciic_fdt_release_bus(void *cookie, int flags)
139 {
140 }
141
142 int
qciic_fdt_wait(struct qciic_fdt_softc * sc,uint32_t bits)143 qciic_fdt_wait(struct qciic_fdt_softc *sc, uint32_t bits)
144 {
145 uint32_t stat;
146 int timo;
147
148 for (timo = 50000; timo > 0; timo--) {
149 stat = HREAD4(sc, GENI_M_IRQ_STATUS);
150 if (stat & bits)
151 break;
152 delay(10);
153 }
154 if (timo == 0)
155 return ETIMEDOUT;
156
157 return 0;
158 }
159
160 int
qciic_fdt_read(struct qciic_fdt_softc * sc,uint8_t * buf,size_t len)161 qciic_fdt_read(struct qciic_fdt_softc *sc, uint8_t *buf, size_t len)
162 {
163 uint32_t stat, word;
164 int timo, i;
165
166 word = 0;
167 for (i = 0; i < len; i++) {
168 if ((i % 4) == 0) {
169 for (timo = 50000; timo > 0; timo--) {
170 stat = HREAD4(sc, GENI_RX_FIFO_STATUS);
171 if (GENI_RX_FIFO_STATUS_WC(stat) > 0)
172 break;
173 delay(10);
174 }
175 if (timo == 0)
176 return ETIMEDOUT;
177 word = HREAD4(sc, GENI_RX_FIFO);
178 }
179 buf[i] = word >> ((i % 4) * 8);
180 }
181
182 return 0;
183 }
184
185 int
qciic_fdt_write(struct qciic_fdt_softc * sc,const uint8_t * buf,size_t len)186 qciic_fdt_write(struct qciic_fdt_softc *sc, const uint8_t *buf, size_t len)
187 {
188 uint32_t stat, word;
189 int timo, i;
190
191 word = 0;
192 for (i = 0; i < len; i++) {
193 word |= buf[i] << ((i % 4) * 8);
194 if ((i % 4) == 3 || i == (len - 1)) {
195 for (timo = 50000; timo > 0; timo--) {
196 stat = HREAD4(sc, GENI_TX_FIFO_STATUS);
197 if (stat < 16)
198 break;
199 delay(10);
200 }
201 if (timo == 0)
202 return ETIMEDOUT;
203 HWRITE4(sc, GENI_TX_FIFO, word);
204 word = 0;
205 }
206 }
207
208 return 0;
209 }
210
211 int
qciic_fdt_exec(void * cookie,i2c_op_t op,i2c_addr_t addr,const void * cmd,size_t cmdlen,void * buf,size_t buflen,int flags)212 qciic_fdt_exec(void *cookie, i2c_op_t op, i2c_addr_t addr, const void *cmd,
213 size_t cmdlen, void *buf, size_t buflen, int flags)
214 {
215 struct qciic_fdt_softc *sc = cookie;
216 uint32_t m_cmd, m_param, stat;
217 int error;
218
219 m_param = addr << GENI_M_CMD0_SLV_ADDR_SHIFT;
220 m_param |= GENI_M_CMD0_STOP_STRETCH;
221
222 if (buflen == 0 && I2C_OP_STOP_P(op))
223 m_param &= ~GENI_M_CMD0_STOP_STRETCH;
224
225 if (cmdlen > 0) {
226 stat = HREAD4(sc, GENI_M_IRQ_STATUS);
227 HWRITE4(sc, GENI_M_IRQ_CLEAR, stat);
228 HWRITE4(sc, GENI_I2C_TX_TRANS_LEN, cmdlen);
229 m_cmd = GENI_M_CMD0_OPCODE_I2C_WRITE | m_param;
230 HWRITE4(sc, GENI_M_CMD0, m_cmd);
231
232 error = qciic_fdt_write(sc, cmd, cmdlen);
233 if (error)
234 return error;
235
236 error = qciic_fdt_wait(sc, GENI_M_IRQ_CMD_DONE);
237 if (error)
238 return error;
239 }
240
241 if (buflen == 0)
242 return 0;
243
244 if (I2C_OP_STOP_P(op))
245 m_param &= ~GENI_M_CMD0_STOP_STRETCH;
246
247 if (I2C_OP_READ_P(op)) {
248 stat = HREAD4(sc, GENI_M_IRQ_STATUS);
249 HWRITE4(sc, GENI_M_IRQ_CLEAR, stat);
250 HWRITE4(sc, GENI_I2C_RX_TRANS_LEN, buflen);
251 m_cmd = GENI_M_CMD0_OPCODE_I2C_READ | m_param;
252 HWRITE4(sc, GENI_M_CMD0, m_cmd);
253
254 error = qciic_fdt_read(sc, buf, buflen);
255 if (error)
256 return error;
257
258 error = qciic_fdt_wait(sc, GENI_M_IRQ_CMD_DONE);
259 if (error)
260 return error;
261 } else {
262 stat = HREAD4(sc, GENI_M_IRQ_STATUS);
263 HWRITE4(sc, GENI_M_IRQ_CLEAR, stat);
264 HWRITE4(sc, GENI_I2C_TX_TRANS_LEN, buflen);
265 m_cmd = GENI_M_CMD0_OPCODE_I2C_WRITE | m_param;
266 HWRITE4(sc, GENI_M_CMD0, m_cmd);
267
268 error = qciic_fdt_write(sc, buf, buflen);
269 if (error)
270 return error;
271
272 error = qciic_fdt_wait(sc, GENI_M_IRQ_CMD_DONE);
273 if (error)
274 return error;
275 }
276
277 return 0;
278 }
279
280 void *
qciic_fdt_i2c_intr_establish(void * cookie,void * ih,int level,int (* func)(void *),void * arg,const char * name)281 qciic_fdt_i2c_intr_establish(void *cookie, void *ih, int level,
282 int (*func)(void *), void *arg, const char *name)
283 {
284 int node = *(int *)ih;
285
286 return fdt_intr_establish(node, level, func, arg, (char *)name);
287 }
288
289 void
qciic_fdt_i2c_intr_disestablish(void * cookie,void * ih)290 qciic_fdt_i2c_intr_disestablish(void *cookie, void *ih)
291 {
292 fdt_intr_disestablish(ih);
293 }
294
295 const char *
qciic_fdt_i2c_intr_string(void * cookie,void * ih)296 qciic_fdt_i2c_intr_string(void *cookie, void *ih)
297 {
298 static char irqstr[64];
299
300 snprintf(irqstr, sizeof(irqstr), "irq");
301
302 return irqstr;
303 }
304
305 void
qciic_fdt_bus_scan(struct device * self,struct i2cbus_attach_args * iba,void * aux)306 qciic_fdt_bus_scan(struct device *self, struct i2cbus_attach_args *iba, void *aux)
307 {
308 int iba_node = *(int *)aux;
309 extern int iic_print(void *, const char *);
310 struct i2c_attach_args ia;
311 char name[32], status[32];
312 uint32_t reg[1];
313 int node;
314
315 for (node = OF_child(iba_node); node; node = OF_peer(node)) {
316 memset(name, 0, sizeof(name));
317 memset(status, 0, sizeof(status));
318 memset(reg, 0, sizeof(reg));
319
320 if (OF_getprop(node, "compatible", name, sizeof(name)) == -1)
321 continue;
322 if (name[0] == '\0')
323 continue;
324
325 if (OF_getprop(node, "status", status, sizeof(status)) > 0 &&
326 strcmp(status, "disabled") == 0)
327 continue;
328
329 if (OF_getprop(node, "reg", ®, sizeof(reg)) != sizeof(reg))
330 continue;
331
332 memset(&ia, 0, sizeof(ia));
333 ia.ia_tag = iba->iba_tag;
334 ia.ia_addr = bemtoh32(®[0]);
335 ia.ia_name = name;
336 ia.ia_cookie = &node;
337 ia.ia_intr = &node;
338
339 /* Quirk for ihidev(4) */
340 if (strcmp(name, "hid-over-i2c") == 0) {
341 ia.ia_name = "ihidev";
342 ia.ia_size = OF_getpropint(node, "hid-descr-addr", 0);
343 ia.ia_cookie = name;
344 }
345
346 config_found(self, &ia, iic_print);
347 }
348 }
349