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 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 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 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 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 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 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 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 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 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