1 /* $OpenBSD: mgiic.c,v 1.4 2021/10/24 17:05:03 mpi Exp $ */
2 /*
3 * Copyright (c) 2008 Theo de Raadt <deraadt@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/device.h>
20 #include <sys/errno.h>
21 #include <sys/malloc.h>
22 #include <sys/systm.h>
23 #include <sys/rwlock.h>
24
25 #include <uvm/uvm_extern.h>
26
27 #include <machine/bus.h>
28 #include <machine/autoconf.h>
29 #include <machine/openfirm.h>
30
31 #include <dev/i2c/i2cvar.h>
32 #include <sparc64/dev/ofwi2cvar.h>
33
34 #define MGSLAVEADDR 0x00
35 #define MGSLAVEXADDR 0x08
36 #define MGDATA 0x10
37 #define MGCONTROL 0x18
38 #define MGCONTROL_IEN 0x80
39 #define MGCONTROL_ENAB 0x40
40 #define MGCONTROL_STA 0x20
41 #define MGCONTROL_STP 0x10
42 #define MGCONTROL_IFLG 0x08
43 #define MGCONTROL_AAK 0x04
44 #define MGSTATUS 0x20
45 #define MGSTATUS_BUSERR 0x00
46 #define MGSTATUS_STARTSENT 0x08
47 #define MGSTATUS_REPEATSTART 0x10
48 #define MGSTATUS_ADDR_W_ACKR 0x18
49 #define MGSTATUS_ADDR_W_NOACKR 0x20
50 #define MGSTATUS_MDATA_ACKR 0x28
51 #define MGSTATUS_MDATA_NOACKR 0x30
52 #define MGSTATUS_ARBLOST 0x38
53 #define MGSTATUS_ADDR_R_ACKR 0x40
54 #define MGSTATUS_ADDR_R_NOACKR 0x48
55 #define MGSTATUS_MDATA_ACKT 0x50
56 #define MGSTATUS_MDATA_NOACKT 0x58
57 #define MGSTATUS_SADDR_W_ACKT 0x60
58 #define MGSTATUS_ARBLOST_SLW_ACKT 0x68
59 #define MGSTATUS_GC_TACK 0x70
60 #define MGSTATUS_ARBLOST_GC_ACKT 0x78
61 #define MGSTATUS_IDLE 0xf8
62 #define MGCLOCKCONTROL 0x28
63 #define MGSOFTRESET 0x30
64
65 struct mgiic_softc {
66 struct device sc_dev;
67
68 bus_space_tag_t sc_bt;
69 bus_space_handle_t sc_regh;
70
71
72 int sc_poll;
73
74 struct i2c_controller sc_i2c;
75 struct rwlock sc_lock;
76 };
77
78 int mgiic_match(struct device *, void *, void *);
79 void mgiic_attach(struct device *, struct device *, void *);
80
81 struct cfdriver mgiic_cd = {
82 NULL, "mgiic", DV_DULL
83 };
84
85 const struct cfattach mgiic_ca = {
86 sizeof(struct mgiic_softc), mgiic_match, mgiic_attach
87 };
88
89 int mgiic_i2c_acquire_bus(void *, int);
90 void mgiic_i2c_release_bus(void *, int);
91 int mgiic_i2c_exec(void *, i2c_op_t, i2c_addr_t, const void *,
92 size_t, void *, size_t, int);
93
94 int mgiic_xmit(struct mgiic_softc *, u_int8_t, const u_int8_t *,
95 size_t);
96 int mgiic_recv(struct mgiic_softc *, u_int8_t, u_int8_t *, size_t);
97 volatile u_int8_t mgiic_read(struct mgiic_softc *, bus_size_t);
98 volatile void mgiic_write(struct mgiic_softc *, bus_size_t, u_int8_t);
99 volatile void mgiic_control(struct mgiic_softc *, u_int8_t, u_int8_t);
100 int mgiic_poll(struct mgiic_softc *);
101
102 int
mgiic_match(struct device * parent,void * match,void * aux)103 mgiic_match(struct device *parent, void *match, void *aux)
104 {
105 struct mainbus_attach_args *ma = aux;
106 char compat[32];
107
108 if (strcmp(ma->ma_name, "i2c") != 0)
109 return (0);
110 if (OF_getprop(ma->ma_node, "compatible", compat, sizeof(compat)) == -1)
111 return (0);
112 if (strcmp(compat, "fire-i2c") == 0)
113 return (1);
114 return (0);
115 }
116
117 void
mgiic_attach(struct device * parent,struct device * self,void * aux)118 mgiic_attach(struct device *parent, struct device *self, void *aux)
119 {
120 struct mgiic_softc *sc = (struct mgiic_softc *)self;
121 struct mainbus_attach_args *ma = aux;
122 struct i2cbus_attach_args iba;
123
124 sc->sc_bt = ma->ma_bustag;
125
126 if (bus_space_map(sc->sc_bt, ma->ma_reg[0].ur_paddr,
127 ma->ma_reg[0].ur_len, 0, &sc->sc_regh)) {
128 printf(": failed to map preg\n");
129 return;
130 }
131
132 rw_init(&sc->sc_lock, "iiclk");
133 sc->sc_i2c.ic_cookie = sc;
134 sc->sc_i2c.ic_acquire_bus = mgiic_i2c_acquire_bus;
135 sc->sc_i2c.ic_release_bus = mgiic_i2c_release_bus;
136 sc->sc_i2c.ic_exec = mgiic_i2c_exec;
137
138 printf("\n");
139
140 bzero(&iba, sizeof(iba));
141 iba.iba_name = "iic";
142 iba.iba_tag = &sc->sc_i2c;
143 iba.iba_bus_scan = ofwiic_scan;
144 iba.iba_bus_scan_arg = &ma->ma_node;
145 config_found(&sc->sc_dev, &iba, iicbus_print);
146 }
147
148 int
mgiic_i2c_acquire_bus(void * arg,int flags)149 mgiic_i2c_acquire_bus(void *arg, int flags)
150 {
151 struct mgiic_softc *sc = arg;
152
153 if (cold || sc->sc_poll || (flags & I2C_F_POLL))
154 return (0);
155
156 return (rw_enter(&sc->sc_lock, RW_WRITE | RW_INTR));
157 }
158
159 void
mgiic_i2c_release_bus(void * arg,int flags)160 mgiic_i2c_release_bus(void *arg, int flags)
161 {
162 struct mgiic_softc *sc = arg;
163
164 if (cold || sc->sc_poll || (flags & I2C_F_POLL))
165 return;
166
167 rw_exit(&sc->sc_lock);
168 }
169
170 int
mgiic_i2c_exec(void * arg,i2c_op_t op,i2c_addr_t addr,const void * cmdbuf,size_t cmdlen,void * buf,size_t len,int flags)171 mgiic_i2c_exec(void *arg, i2c_op_t op, i2c_addr_t addr,
172 const void *cmdbuf, size_t cmdlen, void *buf, size_t len, int flags)
173 {
174 struct mgiic_softc *sc = arg;
175 int ret = 0;
176
177 if (addr & ~0x7f)
178 return (1);
179
180 if (cold || sc->sc_poll)
181 flags |= I2C_F_POLL;
182
183 if (cmdlen > 0) {
184 ret = mgiic_xmit(sc, addr & 0x7f, cmdbuf, cmdlen);
185 if (ret != 0)
186 goto done;
187 }
188
189 if (len > 0) {
190 if (I2C_OP_WRITE_P(op))
191 ret = mgiic_xmit(sc, addr & 0x7f, buf, len);
192 else
193 ret = mgiic_recv(sc, addr & 0x7f, buf, len);
194 }
195 done:
196 printf("e%d\n", ret);
197 return (ret);
198 }
199
200 int
mgiic_xmit(struct mgiic_softc * sc,u_int8_t addr,const u_int8_t * buf,size_t len)201 mgiic_xmit(struct mgiic_softc *sc, u_int8_t addr, const u_int8_t *buf,
202 size_t len)
203 {
204 int err = 1, i = 0;
205
206 top:
207 printf("xmit s%02x STA ", mgiic_read(sc, MGSTATUS));
208 mgiic_control(sc, MGCONTROL_STA, MGCONTROL_IFLG);
209
210 if (mgiic_poll(sc))
211 goto bail;
212 printf("s%02x ", mgiic_read(sc, MGSTATUS));
213 if (mgiic_read(sc, MGSTATUS) != MGSTATUS_STARTSENT)
214 goto bail;
215
216 mgiic_write(sc, MGDATA, addr << 1);
217 printf("a%02x ", addr << 1);
218 mgiic_control(sc, 0, MGCONTROL_IFLG);
219
220 while (i < len) {
221 if (mgiic_poll(sc))
222 goto bail;
223 printf("s%02x ", mgiic_read(sc, MGSTATUS));
224 switch (mgiic_read(sc, MGSTATUS)) {
225 case MGSTATUS_ADDR_W_ACKR:
226 case MGSTATUS_MDATA_ACKR:
227 mgiic_write(sc, MGDATA, buf[i]);
228 printf("w%02x ", buf[i]);
229 i++;
230 mgiic_control(sc, 0, MGCONTROL_IFLG);
231 break;
232 case MGSTATUS_ADDR_W_NOACKR:
233 case MGSTATUS_MDATA_NOACKR:
234 mgiic_write(sc, MGDATA, buf[i]);
235 printf("w%02x ", buf[i]);
236 mgiic_control(sc, 0, MGCONTROL_IFLG);
237 break;
238 case MGSTATUS_BUSERR:
239 mgiic_control(sc, MGCONTROL_STP, MGCONTROL_IFLG);
240 i = 0;
241 if (mgiic_poll(sc))
242 goto bail;
243 goto top;
244 case MGSTATUS_IDLE:
245 default:
246 err = 1;
247 goto bail;
248 }
249 }
250 printf("OK ");
251 err = 0;
252 bail:
253 if (err)
254 printf("BAIL STP s%02x\n", mgiic_read(sc, MGSTATUS));
255 mgiic_control(sc, MGCONTROL_STP, MGCONTROL_IFLG);
256 while (mgiic_read(sc, MGSTATUS) != MGSTATUS_IDLE)
257 ;
258 printf("s%02x\n", mgiic_read(sc, MGSTATUS));
259 return (err);
260 }
261
262 int
mgiic_recv(struct mgiic_softc * sc,u_int8_t addr,u_int8_t * buf,size_t len)263 mgiic_recv(struct mgiic_softc *sc, u_int8_t addr, u_int8_t *buf, size_t len)
264 {
265 int err = 1, i = 0;
266
267 printf("recv s%02x ", mgiic_read(sc, MGSTATUS));
268 mgiic_control(sc, MGCONTROL_STA, MGCONTROL_IFLG);
269 if (mgiic_poll(sc))
270 goto bail;
271
272 printf("s%02x ", mgiic_read(sc, MGSTATUS));
273 if (mgiic_read(sc, MGSTATUS) != MGSTATUS_STARTSENT)
274 goto bail;
275
276 re_address:
277 mgiic_write(sc, MGDATA, (addr << 1) | 0x01);
278 printf("a%02x ", (addr << 1) | 0x01);
279 mgiic_control(sc, 0, MGCONTROL_IFLG);
280
281 while (i < len) {
282 if (mgiic_poll(sc))
283 goto bail;
284 printf("s%02x ", mgiic_read(sc, MGSTATUS));
285 switch (mgiic_read(sc, MGSTATUS)) {
286 case MGSTATUS_ADDR_R_ACKR:
287 if (len - i > 1)
288 mgiic_control(sc, MGCONTROL_AAK, MGCONTROL_IFLG);
289 else
290 mgiic_control(sc, 0, MGCONTROL_IFLG);
291 break;
292 case MGSTATUS_ADDR_R_NOACKR:
293 mgiic_control(sc, MGCONTROL_STA, MGCONTROL_IFLG);
294 break;
295 case MGSTATUS_REPEATSTART:
296 goto re_address;
297 case MGSTATUS_MDATA_ACKT:
298 buf[i] = mgiic_read(sc, MGDATA);
299 printf("r%02x ", buf[i]);
300 i++;
301 if (len - i > 1)
302 mgiic_control(sc, MGCONTROL_AAK, MGCONTROL_IFLG);
303 else
304 mgiic_control(sc, 0, MGCONTROL_IFLG|MGCONTROL_AAK);
305 break;
306 case MGSTATUS_MDATA_NOACKT:
307 buf[i] = mgiic_read(sc, MGDATA);
308 printf("r%02x ", buf[i]);
309 i++;
310 if (len == i) {
311 printf("DONE ");
312 err = 0;
313 goto bail;
314 }
315 printf("SHORT ");
316 goto bail;
317 break;
318 default:
319 printf("BAD");
320 goto bail;
321 }
322 }
323 printf("OK ");
324 err = 0;
325 bail:
326 if (err)
327 printf("BAIL STP s%02x\n", mgiic_read(sc, MGSTATUS));
328 mgiic_control(sc, MGCONTROL_STP, MGCONTROL_IFLG | MGCONTROL_AAK);
329 while (mgiic_read(sc, MGSTATUS) != MGSTATUS_IDLE)
330 ;
331 printf("s%02x\n", mgiic_read(sc, MGSTATUS));
332 return (err);
333 }
334
335 volatile u_int8_t
mgiic_read(struct mgiic_softc * sc,bus_size_t r)336 mgiic_read(struct mgiic_softc *sc, bus_size_t r)
337 {
338 bus_space_barrier(sc->sc_bt, sc->sc_regh, r, 8,
339 BUS_SPACE_BARRIER_READ);
340 return (bus_space_read_8(sc->sc_bt, sc->sc_regh, r)) & 0xff;
341 }
342
343 volatile void
mgiic_write(struct mgiic_softc * sc,bus_size_t r,u_int8_t v)344 mgiic_write(struct mgiic_softc *sc, bus_size_t r, u_int8_t v)
345 {
346 u_int64_t val = v;
347
348 bus_space_write_8(sc->sc_bt, sc->sc_regh, r, val);
349 bus_space_barrier(sc->sc_bt, sc->sc_regh, r, 8,
350 BUS_SPACE_BARRIER_WRITE);
351 }
352
353 volatile void
mgiic_control(struct mgiic_softc * sc,u_int8_t on,u_int8_t off)354 mgiic_control(struct mgiic_softc *sc, u_int8_t on, u_int8_t off)
355 {
356 u_int8_t val;
357
358 val = (mgiic_read(sc, MGCONTROL) | on) & ~off;
359 mgiic_write(sc, MGCONTROL, val);
360 }
361
362 int
mgiic_poll(struct mgiic_softc * sc)363 mgiic_poll(struct mgiic_softc *sc)
364 {
365 int i;
366
367 for (i = 0; i < 1000; i++) {
368 if (mgiic_read(sc, MGCONTROL) & MGCONTROL_IFLG)
369 return (0);
370 delay(100);
371 }
372 return (1);
373 }
374