1 /* $NetBSD: amdpm_smbus.c,v 1.16 2009/02/03 16:27:13 pgoyette Exp $ */ 2 3 /* 4 * Copyright (c) 2005 Anil Gopinath (anil_public@yahoo.com) 5 * All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 3. The name of the author may not be used to endorse or promote products 16 * derived from this software without specific prior written permission. 17 * 18 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 19 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 20 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 21 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 22 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 23 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 24 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED 25 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 26 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 27 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 28 * SUCH DAMAGE. 29 */ 30 31 /* driver for SMBUS 1.0 host controller found in the 32 * AMD-8111 HyperTransport I/O Hub 33 */ 34 #include <sys/cdefs.h> 35 __KERNEL_RCSID(0, "$NetBSD: amdpm_smbus.c,v 1.16 2009/02/03 16:27:13 pgoyette Exp $"); 36 37 #include <sys/param.h> 38 #include <sys/systm.h> 39 #include <sys/kernel.h> 40 #include <sys/device.h> 41 #include <sys/rnd.h> 42 #include <sys/rwlock.h> 43 44 #include <dev/pci/pcireg.h> 45 #include <dev/pci/pcivar.h> 46 #include <dev/pci/pcidevs.h> 47 48 #include <dev/i2c/i2cvar.h> 49 #include <dev/i2c/i2c_bitbang.h> 50 51 #include <dev/pci/amdpmreg.h> 52 #include <dev/pci/amdpmvar.h> 53 54 #include <dev/pci/amdpm_smbusreg.h> 55 56 #ifdef __i386__ 57 #include "opt_xbox.h" 58 #endif 59 60 #ifdef XBOX 61 extern int arch_i386_is_xbox; 62 #endif 63 64 static int amdpm_smbus_acquire_bus(void *, int); 65 static void amdpm_smbus_release_bus(void *, int); 66 static int amdpm_smbus_exec(void *, i2c_op_t, i2c_addr_t, const void *, 67 size_t, void *, size_t, int); 68 static int amdpm_smbus_check_done(struct amdpm_softc *, i2c_op_t); 69 static void amdpm_smbus_clear_gsr(struct amdpm_softc *); 70 static uint16_t amdpm_smbus_get_gsr(struct amdpm_softc *); 71 static int amdpm_smbus_quick(struct amdpm_softc *, i2c_op_t); 72 static int amdpm_smbus_send_1(struct amdpm_softc *, uint8_t, i2c_op_t); 73 static int amdpm_smbus_write_1(struct amdpm_softc *, uint8_t, 74 uint8_t, i2c_op_t); 75 static int amdpm_smbus_receive_1(struct amdpm_softc *, i2c_op_t); 76 static int amdpm_smbus_read_1(struct amdpm_softc *sc, uint8_t, i2c_op_t); 77 78 #ifdef XBOX 79 static int amdpm_smbus_intr(void *); 80 #endif 81 82 void 83 amdpm_smbus_attach(struct amdpm_softc *sc) 84 { 85 struct i2cbus_attach_args iba; 86 #ifdef XBOX 87 pci_intr_handle_t ih; 88 const char *intrstr; 89 #endif 90 91 /* register with iic */ 92 sc->sc_i2c.ic_cookie = sc; 93 sc->sc_i2c.ic_acquire_bus = amdpm_smbus_acquire_bus; 94 sc->sc_i2c.ic_release_bus = amdpm_smbus_release_bus; 95 sc->sc_i2c.ic_send_start = NULL; 96 sc->sc_i2c.ic_send_stop = NULL; 97 sc->sc_i2c.ic_initiate_xfer = NULL; 98 sc->sc_i2c.ic_read_byte = NULL; 99 sc->sc_i2c.ic_write_byte = NULL; 100 sc->sc_i2c.ic_exec = amdpm_smbus_exec; 101 102 rw_init(&sc->sc_rwlock); 103 104 #ifdef XBOX 105 #define XBOX_SMBA 0x8000 106 #define XBOX_SMSIZE 256 107 #define XBOX_INTRLINE 12 108 #define XBOX_REG_ACPI_PM1a_EN 0x02 109 #define XBOX_REG_ACPI_PM1a_EN_TIMER 0x01 110 /* XXX pci0 dev 1 function 2 "System Management" doesn't probe */ 111 if (arch_i386_is_xbox) { 112 uint16_t val; 113 sc->sc_pa->pa_intrline = XBOX_INTRLINE; 114 115 if (bus_space_map(sc->sc_iot, XBOX_SMBA, XBOX_SMSIZE, 116 0, &sc->sc_sm_ioh) == 0) { 117 aprint_normal_dev(&sc->sc_dev, "system management at 0x%04x\n", XBOX_SMBA); 118 119 /* Disable PM ACPI timer SCI interrupt */ 120 val = bus_space_read_2(sc->sc_iot, sc->sc_sm_ioh, 121 XBOX_REG_ACPI_PM1a_EN); 122 bus_space_write_2(sc->sc_iot, sc->sc_sm_ioh, 123 XBOX_REG_ACPI_PM1a_EN, 124 val & ~XBOX_REG_ACPI_PM1a_EN_TIMER); 125 } 126 } 127 128 if (pci_intr_map(sc->sc_pa, &ih)) 129 aprint_error_dev(&sc->sc_dev, "couldn't map interrupt\n"); 130 else { 131 intrstr = pci_intr_string(sc->sc_pc, ih); 132 sc->sc_ih = pci_intr_establish(sc->sc_pc, ih, IPL_BIO, 133 amdpm_smbus_intr, sc); 134 if (sc->sc_ih != NULL) 135 aprint_normal_dev(&sc->sc_dev, "interrupting at %s\n", 136 intrstr); 137 } 138 #endif 139 140 iba.iba_tag = &sc->sc_i2c; 141 (void)config_found_ia(&sc->sc_dev, "i2cbus", &iba, iicbus_print); 142 } 143 144 #ifdef XBOX 145 static int 146 amdpm_smbus_intr(void *cookie) 147 { 148 struct amdpm_softc *sc; 149 uint32_t status; 150 151 sc = (struct amdpm_softc *)cookie; 152 153 if (arch_i386_is_xbox) { 154 status = bus_space_read_4(sc->sc_iot, sc->sc_sm_ioh, 0x20); 155 bus_space_write_4(sc->sc_iot, sc->sc_sm_ioh, 0x20, status); 156 157 if (status & 2) 158 return iic_smbus_intr(&sc->sc_i2c); 159 } 160 161 return 0; 162 } 163 #endif 164 165 static int 166 amdpm_smbus_acquire_bus(void *cookie, int flags) 167 { 168 struct amdpm_softc *sc = cookie; 169 170 rw_enter(&sc->sc_rwlock, RW_WRITER); 171 return 0; 172 } 173 174 static void 175 amdpm_smbus_release_bus(void *cookie, int flags) 176 { 177 struct amdpm_softc *sc = cookie; 178 179 rw_exit(&sc->sc_rwlock); 180 } 181 182 static int 183 amdpm_smbus_exec(void *cookie, i2c_op_t op, i2c_addr_t addr, const void *cmd, 184 size_t cmdlen, void *vbuf, size_t buflen, int flags) 185 { 186 struct amdpm_softc *sc = (struct amdpm_softc *) cookie; 187 sc->sc_smbus_slaveaddr = addr; 188 uint8_t *p = vbuf; 189 int rv; 190 191 if ((cmdlen == 0) && (buflen == 0)) 192 return amdpm_smbus_quick(sc, op); 193 194 if (I2C_OP_READ_P(op) && (cmdlen == 0) && (buflen == 1)) { 195 rv = amdpm_smbus_receive_1(sc, op); 196 if (rv == -1) 197 return -1; 198 *p = (uint8_t)rv; 199 return 0; 200 } 201 202 if ((I2C_OP_READ_P(op)) && (cmdlen == 1) && (buflen == 1)) { 203 rv = amdpm_smbus_read_1(sc, *(const uint8_t *)cmd, op); 204 if (rv == -1) 205 return -1; 206 *p = (uint8_t)rv; 207 return 0; 208 } 209 210 if ((I2C_OP_WRITE_P(op)) && (cmdlen == 0) && (buflen == 1)) 211 return amdpm_smbus_send_1(sc, *(uint8_t*)vbuf, op); 212 213 if ((I2C_OP_WRITE_P(op)) && (cmdlen == 1) && (buflen == 1)) 214 return amdpm_smbus_write_1(sc, 215 *(const uint8_t*)cmd, 216 *(uint8_t*)vbuf, 217 op); 218 219 return -1; 220 } 221 222 static int 223 amdpm_smbus_check_done(struct amdpm_softc *sc, i2c_op_t op) 224 { 225 int i; 226 227 for (i = 0; i < 1000; i++) { 228 /* check gsr and wait till cycle is done */ 229 uint16_t data = amdpm_smbus_get_gsr(sc); 230 if (data & AMDPM_8111_GSR_CYCLE_DONE) 231 return 0; 232 } 233 234 if (!(op & I2C_F_POLL)) 235 delay(1); 236 237 return -1; 238 } 239 240 241 static void 242 amdpm_smbus_clear_gsr(struct amdpm_softc *sc) 243 { 244 /* clear register */ 245 uint16_t data = 0xFFFF; 246 int off = (sc->sc_nforce ? 0xe0 : 0); 247 bus_space_write_2(sc->sc_iot, sc->sc_ioh, 248 AMDPM_8111_SMBUS_STAT - off, data); 249 } 250 251 static uint16_t 252 amdpm_smbus_get_gsr(struct amdpm_softc *sc) 253 { 254 int off = (sc->sc_nforce ? 0xe0 : 0); 255 return bus_space_read_2(sc->sc_iot, sc->sc_ioh, 256 AMDPM_8111_SMBUS_STAT - off); 257 } 258 259 static int 260 amdpm_smbus_quick(struct amdpm_softc *sc, i2c_op_t op) 261 { 262 uint16_t data = 0; 263 int off = (sc->sc_nforce ? 0xe0 : 0); 264 265 /* first clear gsr */ 266 amdpm_smbus_clear_gsr(sc); 267 268 /* write smbus slave address and read/write bit to register */ 269 data = sc->sc_smbus_slaveaddr; 270 data <<= 1; 271 if (I2C_OP_READ_P(op)) 272 data |= AMDPM_8111_SMBUS_READ; 273 274 bus_space_write_1(sc->sc_iot, sc->sc_ioh, 275 AMDPM_8111_SMBUS_HOSTADDR - off, data); 276 277 /* host start */ 278 bus_space_write_2(sc->sc_iot, sc->sc_ioh, 279 AMDPM_8111_SMBUS_CTRL - off, 280 AMDPM_8111_SMBUS_GSR_QUICK); 281 282 return amdpm_smbus_check_done(sc, op); 283 } 284 285 static int 286 amdpm_smbus_send_1(struct amdpm_softc *sc, uint8_t val, i2c_op_t op) 287 { 288 uint16_t data = 0; 289 int off = (sc->sc_nforce ? 0xe0 : 0); 290 291 /* first clear gsr */ 292 amdpm_smbus_clear_gsr(sc); 293 294 /* write smbus slave address to register */ 295 data = sc->sc_smbus_slaveaddr; 296 data <<= 1; 297 data |= AMDPM_8111_SMBUS_SEND; 298 bus_space_write_1(sc->sc_iot, sc->sc_ioh, 299 AMDPM_8111_SMBUS_HOSTADDR - off, data); 300 301 data = val; 302 /* store data */ 303 bus_space_write_2(sc->sc_iot, sc->sc_ioh, 304 AMDPM_8111_SMBUS_HOSTDATA - off, data); 305 /* host start */ 306 bus_space_write_2(sc->sc_iot, sc->sc_ioh, 307 AMDPM_8111_SMBUS_CTRL - off, 308 AMDPM_8111_SMBUS_GSR_SB); 309 310 return amdpm_smbus_check_done(sc, op); 311 } 312 313 314 static int 315 amdpm_smbus_write_1(struct amdpm_softc *sc, uint8_t cmd, uint8_t val, 316 i2c_op_t op) 317 { 318 uint16_t data = 0; 319 int off = (sc->sc_nforce ? 0xe0 : 0); 320 321 /* first clear gsr */ 322 amdpm_smbus_clear_gsr(sc); 323 324 data = sc->sc_smbus_slaveaddr; 325 data <<= 1; 326 data |= AMDPM_8111_SMBUS_WRITE; 327 bus_space_write_1(sc->sc_iot, sc->sc_ioh, 328 AMDPM_8111_SMBUS_HOSTADDR - off, data); 329 330 data = val; 331 /* store cmd */ 332 bus_space_write_1(sc->sc_iot, sc->sc_ioh, 333 AMDPM_8111_SMBUS_HOSTCMD - off, cmd); 334 /* store data */ 335 bus_space_write_2(sc->sc_iot, sc->sc_ioh, 336 AMDPM_8111_SMBUS_HOSTDATA - off, data); 337 /* host start */ 338 bus_space_write_2(sc->sc_iot, sc->sc_ioh, 339 AMDPM_8111_SMBUS_CTRL - off, AMDPM_8111_SMBUS_GSR_WB); 340 341 return amdpm_smbus_check_done(sc, op); 342 } 343 344 static int 345 amdpm_smbus_receive_1(struct amdpm_softc *sc, i2c_op_t op) 346 { 347 uint16_t data = 0; 348 int off = (sc->sc_nforce ? 0xe0 : 0); 349 350 /* first clear gsr */ 351 amdpm_smbus_clear_gsr(sc); 352 353 /* write smbus slave address to register */ 354 data = sc->sc_smbus_slaveaddr; 355 data <<= 1; 356 data |= AMDPM_8111_SMBUS_RX; 357 bus_space_write_1(sc->sc_iot, sc->sc_ioh, 358 AMDPM_8111_SMBUS_HOSTADDR - off, data); 359 360 /* start smbus cycle */ 361 bus_space_write_2(sc->sc_iot, sc->sc_ioh, 362 AMDPM_8111_SMBUS_CTRL - off, AMDPM_8111_SMBUS_GSR_RXB); 363 364 /* check for errors */ 365 if (amdpm_smbus_check_done(sc, op) < 0) 366 return -1; 367 368 /* read data */ 369 data = bus_space_read_2(sc->sc_iot, sc->sc_ioh, 370 AMDPM_8111_SMBUS_HOSTDATA - off); 371 uint8_t ret = (uint8_t)(data & 0x00FF); 372 return ret; 373 } 374 375 static int 376 amdpm_smbus_read_1(struct amdpm_softc *sc, uint8_t cmd, i2c_op_t op) 377 { 378 uint16_t data = 0; 379 uint8_t ret; 380 int off = (sc->sc_nforce ? 0xe0 : 0); 381 382 /* first clear gsr */ 383 amdpm_smbus_clear_gsr(sc); 384 385 /* write smbus slave address to register */ 386 data = sc->sc_smbus_slaveaddr; 387 data <<= 1; 388 data |= AMDPM_8111_SMBUS_READ; 389 bus_space_write_1(sc->sc_iot, sc->sc_ioh, 390 AMDPM_8111_SMBUS_HOSTADDR - off, data); 391 392 /* store cmd */ 393 bus_space_write_1(sc->sc_iot, sc->sc_ioh, 394 AMDPM_8111_SMBUS_HOSTCMD - off, cmd); 395 /* host start */ 396 bus_space_write_2(sc->sc_iot, sc->sc_ioh, 397 AMDPM_8111_SMBUS_CTRL - off, AMDPM_8111_SMBUS_GSR_RB); 398 399 /* check for errors */ 400 if (amdpm_smbus_check_done(sc, op) < 0) 401 return -1; 402 403 /* store data */ 404 data = bus_space_read_2(sc->sc_iot, sc->sc_ioh, 405 AMDPM_8111_SMBUS_HOSTDATA - off); 406 ret = (uint8_t)(data & 0x00FF); 407 return ret; 408 } 409