Line data Source code
1 : /* $OpenBSD: amdiic.c,v 1.11 2013/10/01 20:06:00 sf Exp $ */
2 :
3 : /*
4 : * Copyright (c) 2005 Alexander Yurchenko <grange@openbsd.org>
5 : *
6 : * Permission to use, copy, modify, and distribute this software for any
7 : * purpose with or without fee is hereby granted, provided that the above
8 : * copyright notice and this permission notice appear in all copies.
9 : *
10 : * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11 : * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12 : * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13 : * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14 : * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15 : * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16 : * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17 : */
18 :
19 : /*
20 : * AMD-8111 SMBus controller driver.
21 : */
22 :
23 : #include <sys/param.h>
24 : #include <sys/systm.h>
25 : #include <sys/device.h>
26 : #include <sys/kernel.h>
27 : #include <sys/rwlock.h>
28 :
29 : #include <machine/bus.h>
30 :
31 : #include <dev/pci/pcidevs.h>
32 : #include <dev/pci/pcireg.h>
33 : #include <dev/pci/pcivar.h>
34 :
35 : #include <dev/i2c/i2cvar.h>
36 :
37 : #ifdef AMDIIC_DEBUG
38 : #define DPRINTF(x) printf x
39 : #else
40 : #define DPRINTF(x)
41 : #endif
42 :
43 : #define AMDIIC_DELAY 100
44 : #define AMDIIC_TIMEOUT 1
45 :
46 : /* PCI configuration registers */
47 : #define AMD8111_SMB_BASE 0x10 /* SMBus base address */
48 : #define AMD8111_SMB_MISC 0x48 /* miscellaneous control */
49 : #define AMD8111_SMB_MISC_SU (1 << 0) /* 16x clock speed-up */
50 : #define AMD8111_SMB_MISC_INTEN (1 << 1) /* PCI IRQ enabled */
51 : #define AMD8111_SMB_MISC_SCIEN (1 << 2) /* SCI enabled */
52 :
53 : /* SMBus I/O registers */
54 : #define AMD8111_SMB_SC_DATA 0x00 /* data port */
55 : #define AMD8111_SMB_SC_ST 0x04 /* status */
56 : #define AMD8111_SMB_SC_ST_OBF (1 << 0) /* output buffer full */
57 : #define AMD8111_SMB_SC_ST_IBF (1 << 1) /* input buffer full */
58 : #define AMD8111_SMB_SC_ST_CMD (1 << 3) /* command byte */
59 : #define AMD8111_SMB_SC_ST_BITS "\020\001OBF\002IBF\004CMD"
60 : #define AMD8111_SMB_SC_CMD 0x04 /* command port */
61 : #define AMD8111_SMB_SC_CMD_RD 0x80 /* read */
62 : #define AMD8111_SMB_SC_CMD_WR 0x81 /* write */
63 : #define AMD8111_SMB_SC_IC 0x08 /* interrupt control */
64 :
65 : /* Host controller interface registers */
66 : #define AMD8111_SMB_PROTO 0x00 /* protocol */
67 : #define AMD8111_SMB_PROTO_READ 0x01 /* read direction */
68 : #define AMD8111_SMB_PROTO_QUICK 0x02 /* QUICK command */
69 : #define AMD8111_SMB_PROTO_BYTE 0x04 /* BYTE command */
70 : #define AMD8111_SMB_PROTO_BDATA 0x06 /* BYTE DATA command */
71 : #define AMD8111_SMB_PROTO_WDATA 0x08 /* WORD DATA command */
72 : #define AMD8111_SMB_STAT 0x01 /* status */
73 : #define AMD8111_SMB_STAT_MASK 0x1f
74 : #define AMD8111_SMB_STAT_DONE (1 << 7) /* command completion */
75 : #define AMD8111_SMB_ADDR 0x02 /* address */
76 : #define AMD8111_SMB_ADDR_SHIFT 1
77 : #define AMD8111_SMB_CMD 0x03 /* SMBus command */
78 : #define AMD8111_SMB_DATA(x) (0x04 + (x)) /* SMBus data */
79 :
80 : struct amdiic_softc {
81 : struct device sc_dev;
82 :
83 : bus_space_tag_t sc_iot;
84 : bus_space_handle_t sc_ioh;
85 : void * sc_ih;
86 : int sc_poll;
87 :
88 : struct i2c_controller sc_i2c_tag;
89 : struct rwlock sc_i2c_lock;
90 : struct {
91 : i2c_op_t op;
92 : void * buf;
93 : size_t len;
94 : int flags;
95 : volatile int error;
96 : } sc_i2c_xfer;
97 : };
98 :
99 : int amdiic_match(struct device *, void *, void *);
100 : void amdiic_attach(struct device *, struct device *, void *);
101 :
102 : int amdiic_read(struct amdiic_softc *, u_int8_t);
103 : int amdiic_write(struct amdiic_softc *, u_int8_t, u_int8_t);
104 : int amdiic_wait(struct amdiic_softc *, int);
105 :
106 : int amdiic_i2c_acquire_bus(void *, int);
107 : void amdiic_i2c_release_bus(void *, int);
108 : int amdiic_i2c_exec(void *, i2c_op_t, i2c_addr_t, const void *, size_t,
109 : void *, size_t, int);
110 :
111 : int amdiic_intr(void *);
112 :
113 : struct cfattach amdiic_ca = {
114 : sizeof(struct amdiic_softc),
115 : amdiic_match,
116 : amdiic_attach
117 : };
118 :
119 : struct cfdriver amdiic_cd = {
120 : NULL, "amdiic", DV_DULL
121 : };
122 :
123 : const struct pci_matchid amdiic_ids[] = {
124 : { PCI_VENDOR_AMD, PCI_PRODUCT_AMD_8111_SMB }
125 : };
126 :
127 : int
128 0 : amdiic_match(struct device *parent, void *match, void *aux)
129 : {
130 0 : return (pci_matchbyid(aux, amdiic_ids,
131 : sizeof(amdiic_ids) / sizeof(amdiic_ids[0])));
132 : }
133 :
134 : void
135 0 : amdiic_attach(struct device *parent, struct device *self, void *aux)
136 : {
137 0 : struct amdiic_softc *sc = (struct amdiic_softc *)self;
138 0 : struct pci_attach_args *pa = aux;
139 0 : struct i2cbus_attach_args iba;
140 : pcireg_t conf;
141 0 : bus_size_t iosize;
142 0 : pci_intr_handle_t ih;
143 : const char *intrstr = NULL;
144 :
145 : /* Map I/O space */
146 0 : if (pci_mapreg_map(pa, AMD8111_SMB_BASE, PCI_MAPREG_TYPE_IO, 0,
147 0 : &sc->sc_iot, &sc->sc_ioh, NULL, &iosize, 0)) {
148 0 : printf(": can't map i/o space\n");
149 0 : return;
150 : }
151 :
152 : /* Read configuration */
153 0 : conf = pci_conf_read(pa->pa_pc, pa->pa_tag, AMD8111_SMB_MISC);
154 : DPRINTF((": conf 0x%08x", conf));
155 :
156 0 : sc->sc_poll = 1;
157 0 : if (conf & AMD8111_SMB_MISC_SCIEN) {
158 : /* No PCI IRQ */
159 0 : printf(": SCI");
160 0 : } else if (conf & AMD8111_SMB_MISC_INTEN) {
161 : /* Install interrupt handler */
162 0 : if (pci_intr_map(pa, &ih) == 0) {
163 0 : intrstr = pci_intr_string(pa->pa_pc, ih);
164 0 : sc->sc_ih = pci_intr_establish(pa->pa_pc, ih, IPL_BIO,
165 0 : amdiic_intr, sc, sc->sc_dev.dv_xname);
166 0 : if (sc->sc_ih != NULL) {
167 0 : printf(": %s", intrstr);
168 0 : sc->sc_poll = 0;
169 0 : }
170 : }
171 0 : if (sc->sc_poll)
172 0 : printf(": polling");
173 : }
174 :
175 0 : printf("\n");
176 :
177 : /* Attach I2C bus */
178 0 : rw_init(&sc->sc_i2c_lock, "iiclk");
179 0 : sc->sc_i2c_tag.ic_cookie = sc;
180 0 : sc->sc_i2c_tag.ic_acquire_bus = amdiic_i2c_acquire_bus;
181 0 : sc->sc_i2c_tag.ic_release_bus = amdiic_i2c_release_bus;
182 0 : sc->sc_i2c_tag.ic_exec = amdiic_i2c_exec;
183 :
184 0 : bzero(&iba, sizeof(iba));
185 0 : iba.iba_name = "iic";
186 0 : iba.iba_tag = &sc->sc_i2c_tag;
187 0 : config_found(self, &iba, iicbus_print);
188 :
189 0 : return;
190 0 : }
191 :
192 : int
193 0 : amdiic_read(struct amdiic_softc *sc, u_int8_t reg)
194 : {
195 0 : if (amdiic_wait(sc, 0))
196 0 : return (-1);
197 0 : bus_space_write_1(sc->sc_iot, sc->sc_ioh, AMD8111_SMB_SC_CMD,
198 : AMD8111_SMB_SC_CMD_RD);
199 0 : if (amdiic_wait(sc, 0))
200 0 : return (-1);
201 0 : bus_space_write_1(sc->sc_iot, sc->sc_ioh, AMD8111_SMB_SC_DATA, reg);
202 0 : if (amdiic_wait(sc, 1))
203 0 : return (-1);
204 :
205 0 : return (bus_space_read_1(sc->sc_iot, sc->sc_ioh, AMD8111_SMB_SC_DATA));
206 0 : }
207 :
208 : int
209 0 : amdiic_write(struct amdiic_softc *sc, u_int8_t reg, u_int8_t val)
210 : {
211 0 : if (amdiic_wait(sc, 0))
212 0 : return (-1);
213 0 : bus_space_write_1(sc->sc_iot, sc->sc_ioh, AMD8111_SMB_SC_CMD,
214 : AMD8111_SMB_SC_CMD_WR);
215 0 : if (amdiic_wait(sc, 0))
216 0 : return (-1);
217 0 : bus_space_write_1(sc->sc_iot, sc->sc_ioh, AMD8111_SMB_SC_DATA, reg);
218 0 : if (amdiic_wait(sc, 0))
219 0 : return (-1);
220 0 : bus_space_write_1(sc->sc_iot, sc->sc_ioh, AMD8111_SMB_SC_DATA, val);
221 :
222 0 : return (0);
223 0 : }
224 :
225 : int
226 0 : amdiic_wait(struct amdiic_softc *sc, int output)
227 : {
228 : int retries;
229 : u_int8_t st;
230 :
231 0 : for (retries = 100; retries > 0; retries--) {
232 0 : st = bus_space_read_1(sc->sc_iot, sc->sc_ioh,
233 : AMD8111_SMB_SC_ST);
234 0 : if (output && (st & AMD8111_SMB_SC_ST_OBF))
235 0 : return (0);
236 0 : if (!output && (st & AMD8111_SMB_SC_ST_IBF) == 0)
237 0 : return (0);
238 0 : DELAY(1);
239 : }
240 : DPRINTF(("%s: %s wait timeout: st 0x%b\n", sc->sc_dev.dv_xname,
241 : (output ? "output" : "input"), st));
242 :
243 0 : return (1);
244 0 : }
245 :
246 : int
247 0 : amdiic_i2c_acquire_bus(void *cookie, int flags)
248 : {
249 0 : struct amdiic_softc *sc = cookie;
250 :
251 0 : if (cold || sc->sc_poll || (flags & I2C_F_POLL))
252 0 : return (0);
253 :
254 0 : return (rw_enter(&sc->sc_i2c_lock, RW_WRITE | RW_INTR));
255 0 : }
256 :
257 : void
258 0 : amdiic_i2c_release_bus(void *cookie, int flags)
259 : {
260 0 : struct amdiic_softc *sc = cookie;
261 :
262 0 : if (cold || sc->sc_poll || (flags & I2C_F_POLL))
263 0 : return;
264 :
265 0 : rw_exit(&sc->sc_i2c_lock);
266 0 : }
267 :
268 : int
269 0 : amdiic_i2c_exec(void *cookie, i2c_op_t op, i2c_addr_t addr,
270 : const void *cmdbuf, size_t cmdlen, void *buf, size_t len, int flags)
271 : {
272 0 : struct amdiic_softc *sc = cookie;
273 : u_int8_t *b;
274 : u_int8_t proto, st;
275 : int retries;
276 :
277 : DPRINTF(("%s: exec: op %d, addr 0x%02x, cmdlen %d, len %d, "
278 : "flags 0x%02x\n", sc->sc_dev.dv_xname, op, addr,
279 : cmdlen, len, flags));
280 :
281 0 : if (cold || sc->sc_poll)
282 0 : flags |= I2C_F_POLL;
283 :
284 0 : if (!I2C_OP_STOP_P(op) || cmdlen > 1 || len > 2)
285 0 : return (1);
286 :
287 : /* Setup transfer */
288 0 : sc->sc_i2c_xfer.op = op;
289 0 : sc->sc_i2c_xfer.buf = buf;
290 0 : sc->sc_i2c_xfer.len = len;
291 0 : sc->sc_i2c_xfer.flags = flags;
292 0 : sc->sc_i2c_xfer.error = 0;
293 :
294 : /* Set slave address */
295 0 : if (amdiic_write(sc, AMD8111_SMB_ADDR,
296 0 : addr << AMD8111_SMB_ADDR_SHIFT) == -1)
297 0 : return (1);
298 :
299 : b = (void *)cmdbuf;
300 0 : if (cmdlen > 0)
301 : /* Set command byte */
302 0 : if (amdiic_write(sc, AMD8111_SMB_CMD, b[0]) == -1)
303 0 : return (1);
304 :
305 0 : if (I2C_OP_WRITE_P(op)) {
306 : /* Write data */
307 : b = buf;
308 0 : if (len > 0)
309 0 : if (amdiic_write(sc, AMD8111_SMB_DATA(0), b[0]) == -1)
310 0 : return (1);
311 0 : if (len > 1)
312 0 : if (amdiic_write(sc, AMD8111_SMB_DATA(1), b[1]) == -1)
313 0 : return (1);
314 : }
315 :
316 : /* Set SMBus command */
317 0 : if (len == 0)
318 0 : proto = AMD8111_SMB_PROTO_BYTE;
319 0 : else if (len == 1)
320 0 : proto = AMD8111_SMB_PROTO_BDATA;
321 0 : else if (len == 2)
322 : proto = AMD8111_SMB_PROTO_WDATA;
323 : else
324 0 : panic("%s: unexpected len %zd", __func__, len);
325 :
326 : /* Set direction */
327 0 : if (I2C_OP_READ_P(op))
328 0 : proto |= AMD8111_SMB_PROTO_READ;
329 :
330 : /* Start transaction */
331 0 : amdiic_write(sc, AMD8111_SMB_PROTO, proto);
332 :
333 0 : if (flags & I2C_F_POLL) {
334 : /* Poll for completion */
335 0 : DELAY(AMDIIC_DELAY);
336 0 : for (retries = 1000; retries > 0; retries--) {
337 0 : st = amdiic_read(sc, AMD8111_SMB_STAT);
338 0 : if (st != 0)
339 : break;
340 0 : DELAY(AMDIIC_DELAY);
341 : }
342 0 : if (st == 0) {
343 0 : printf("%s: exec: op %d, addr 0x%02x, cmdlen %zu, "
344 : "len %zu, flags 0x%02x: timeout\n",
345 0 : sc->sc_dev.dv_xname, op, addr, cmdlen, len, flags);
346 0 : return (1);
347 : }
348 0 : amdiic_intr(sc);
349 0 : } else {
350 : /* Wait for interrupt */
351 0 : if (tsleep(sc, PRIBIO, "amdiic", AMDIIC_TIMEOUT * hz))
352 0 : return (1);
353 : }
354 :
355 0 : if (sc->sc_i2c_xfer.error)
356 0 : return (1);
357 :
358 0 : return (0);
359 0 : }
360 :
361 : int
362 0 : amdiic_intr(void *arg)
363 : {
364 0 : struct amdiic_softc *sc = arg;
365 : int st;
366 : u_int8_t *b;
367 : size_t len;
368 :
369 : /* Read status */
370 0 : if ((st = amdiic_read(sc, AMD8111_SMB_STAT)) == -1)
371 0 : return (-1);
372 0 : if (st == 0)
373 : /* Interrupt was not for us */
374 0 : return (0);
375 :
376 : DPRINTF(("%s: intr: st 0x%02x\n", sc->sc_dev.dv_xname, st));
377 :
378 : /* Check for errors */
379 0 : if ((st & AMD8111_SMB_STAT_MASK) != 0) {
380 0 : sc->sc_i2c_xfer.error = 1;
381 0 : goto done;
382 : }
383 :
384 0 : if (st & AMD8111_SMB_STAT_DONE) {
385 0 : if (I2C_OP_WRITE_P(sc->sc_i2c_xfer.op))
386 : goto done;
387 :
388 : /* Read data */
389 0 : b = sc->sc_i2c_xfer.buf;
390 0 : len = sc->sc_i2c_xfer.len;
391 0 : if (len > 0)
392 0 : b[0] = amdiic_read(sc, AMD8111_SMB_DATA(0));
393 0 : if (len > 1)
394 0 : b[1] = amdiic_read(sc, AMD8111_SMB_DATA(1));
395 : }
396 :
397 : done:
398 0 : if ((sc->sc_i2c_xfer.flags & I2C_F_POLL) == 0)
399 0 : wakeup(sc);
400 0 : return (1);
401 0 : }
|