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