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