xref: /openbsd-src/sys/arch/sparc64/dev/mgiic.c (revision 2b0358df1d88d06ef4139321dd05bd5e05d91eaf)
1 /*	$OpenBSD: mgiic.c,v 1.2 2008/04/22 01:44:19 deraadt 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 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
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
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
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
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
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 	if (len > 0) {
189 		if (I2C_OP_WRITE_P(op))
190 			ret = mgiic_xmit(sc, addr & 0x7f, buf, len);
191 		else
192 			ret = mgiic_recv(sc, addr & 0x7f, buf, len);
193 	}
194 done:
195 	printf("e%d\n", ret);
196 	return (ret);
197 }
198 
199 int
200 mgiic_xmit(struct mgiic_softc *sc, u_int8_t addr, const u_int8_t *buf,
201     size_t len)
202 {
203 	int err = 1, i = 0;
204 
205 top:
206 	printf("xmit s%02x STA ", mgiic_read(sc, MGSTATUS));
207 	mgiic_control(sc, MGCONTROL_STA, MGCONTROL_IFLG);
208 
209 	if (mgiic_poll(sc))
210 		goto bail;
211 	printf("s%02x ", mgiic_read(sc, MGSTATUS));
212 	if (mgiic_read(sc, MGSTATUS) != MGSTATUS_STARTSENT)
213 		goto bail;
214 
215 	mgiic_write(sc, MGDATA, addr << 1);
216 	printf("a%02x ", addr << 1);
217 	mgiic_control(sc, 0, MGCONTROL_IFLG);
218 
219 	while (i < len) {
220 		if (mgiic_poll(sc))
221 			 goto bail;
222 		printf("s%02x ", mgiic_read(sc, MGSTATUS));
223 		switch (mgiic_read(sc, MGSTATUS)) {
224 		case MGSTATUS_ADDR_W_ACKR:
225 		case MGSTATUS_MDATA_ACKR:
226 			mgiic_write(sc, MGDATA, buf[i]);
227 			printf("w%02x ", buf[i]);
228 			i++;
229 			mgiic_control(sc, 0, MGCONTROL_IFLG);
230 			break;
231 		case MGSTATUS_ADDR_W_NOACKR:
232 		case MGSTATUS_MDATA_NOACKR:
233 			mgiic_write(sc, MGDATA, buf[i]);
234 			printf("w%02x ", buf[i]);
235 			mgiic_control(sc, 0, MGCONTROL_IFLG);
236 			break;
237 		case MGSTATUS_BUSERR:
238 			mgiic_control(sc, MGCONTROL_STP, MGCONTROL_IFLG);
239 			i = 0;
240 			if (mgiic_poll(sc))
241 				goto bail;
242 			goto top;
243 		case MGSTATUS_IDLE:
244 		default:
245 			err = 1;
246 			goto bail;
247 		}
248 	}
249 	printf("OK ");
250 	err = 0;
251 bail:
252 	if (err)
253 		printf("BAIL STP s%02x\n", mgiic_read(sc, MGSTATUS));
254 	mgiic_control(sc, MGCONTROL_STP, MGCONTROL_IFLG);
255 	while (mgiic_read(sc, MGSTATUS) != MGSTATUS_IDLE)
256 		;
257 	printf("s%02x\n", mgiic_read(sc, MGSTATUS));
258 	return (err);
259 }
260 
261 int
262 mgiic_recv(struct mgiic_softc *sc, u_int8_t addr, u_int8_t *buf, size_t len)
263 {
264 	int err = 1, i = 0;
265 
266 	printf("recv s%02x ", mgiic_read(sc, MGSTATUS));
267 	mgiic_control(sc, MGCONTROL_STA, MGCONTROL_IFLG);
268 	if (mgiic_poll(sc))
269 		goto bail;
270 
271 	printf("s%02x ", mgiic_read(sc, MGSTATUS));
272 	if (mgiic_read(sc, MGSTATUS) != MGSTATUS_STARTSENT)
273 		goto bail;
274 
275 re_address:
276 	mgiic_write(sc, MGDATA, (addr << 1) | 0x01);
277 	printf("a%02x ", (addr << 1) | 0x01);
278 	mgiic_control(sc, 0, MGCONTROL_IFLG);
279 
280 	while (i < len) {
281 		if (mgiic_poll(sc))
282 			goto bail;
283 		printf("s%02x ", mgiic_read(sc, MGSTATUS));
284 		switch (mgiic_read(sc, MGSTATUS)) {
285 		case MGSTATUS_ADDR_R_ACKR:
286 			if (len - i > 1)
287 				mgiic_control(sc, MGCONTROL_AAK, MGCONTROL_IFLG);
288 			else
289 				mgiic_control(sc, 0, MGCONTROL_IFLG);
290 			break;
291 		case MGSTATUS_ADDR_R_NOACKR:
292 			mgiic_control(sc, MGCONTROL_STA, MGCONTROL_IFLG);
293 			break;
294 		case MGSTATUS_REPEATSTART:
295 			goto re_address;
296 		case MGSTATUS_MDATA_ACKT:
297 			buf[i] = mgiic_read(sc, MGDATA);
298 			printf("r%02x ", buf[i]);
299 			i++;
300 			if (len - i > 1)
301 				mgiic_control(sc, MGCONTROL_AAK, MGCONTROL_IFLG);
302 			else
303 				mgiic_control(sc, 0, MGCONTROL_IFLG|MGCONTROL_AAK);
304 			break;
305 		case MGSTATUS_MDATA_NOACKT:
306 			buf[i] = mgiic_read(sc, MGDATA);
307 			printf("r%02x ", buf[i]);
308 			i++;
309 			if (len == i) {
310 				printf("DONE ");
311 				err = 0;
312 				goto bail;
313 			}
314 			printf("SHORT ");
315 			goto bail;
316 			break;
317 		default:
318 			printf("BAD");
319 			goto bail;
320 		}
321 	}
322 	printf("OK ");
323 	err = 0;
324 bail:
325 	if (err)
326 		printf("BAIL STP s%02x\n", mgiic_read(sc, MGSTATUS));
327 	mgiic_control(sc, MGCONTROL_STP, MGCONTROL_IFLG | MGCONTROL_AAK);
328 	while (mgiic_read(sc, MGSTATUS) != MGSTATUS_IDLE)
329 		;
330 	printf("s%02x\n", mgiic_read(sc, MGSTATUS));
331 	return (err);
332 }
333 
334 volatile u_int8_t
335 mgiic_read(struct mgiic_softc *sc, bus_size_t r)
336 {
337 	bus_space_barrier(sc->sc_bt, sc->sc_regh, r, 8,
338 	    BUS_SPACE_BARRIER_READ);
339 	return (bus_space_read_8(sc->sc_bt, sc->sc_regh, r)) & 0xff;
340 }
341 
342 volatile void
343 mgiic_write(struct mgiic_softc *sc, bus_size_t r, u_int8_t v)
344 {
345 	u_int64_t val = v;
346 
347 	bus_space_write_8(sc->sc_bt, sc->sc_regh, r, val);
348 	bus_space_barrier(sc->sc_bt, sc->sc_regh, r, 8,
349 	    BUS_SPACE_BARRIER_WRITE);
350 }
351 
352 volatile void
353 mgiic_control(struct mgiic_softc *sc, u_int8_t on, u_int8_t off)
354 {
355 	u_int8_t val;
356 
357 	val = (mgiic_read(sc, MGCONTROL) | on) & ~off;
358 	mgiic_write(sc, MGCONTROL, val);
359 }
360 
361 int
362 mgiic_poll(struct mgiic_softc *sc)
363 {
364 	int		i;
365 
366 	for (i = 0; i < 1000; i++) {
367 		if (mgiic_read(sc, MGCONTROL) & MGCONTROL_IFLG)
368 			return (0);
369 		delay(100);
370 	}
371 	return (1);
372 }
373