1 /* $NetBSD: amdpm_smbus.c,v 1.4 2006/06/26 18:21:39 drochner 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.4 2006/06/26 18:21:39 drochner 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 <dev/pci/pcireg.h> 43 #include <dev/pci/pcivar.h> 44 #include <dev/pci/pcidevs.h> 45 46 #include <dev/i2c/i2cvar.h> 47 #include <dev/i2c/i2c_bitbang.h> 48 49 #include <dev/pci/amdpmreg.h> 50 #include <dev/pci/amdpmvar.h> 51 52 #include <dev/pci/amdpm_smbusreg.h> 53 54 static int amdpm_smbus_acquire_bus(void *cookie, int flags); 55 static void amdpm_smbus_release_bus(void *cookie, int flags); 56 static int amdpm_smbus_exec(void *cookie, i2c_op_t op, i2c_addr_t addr, 57 const void *cmd, size_t cmdlen, void *vbuf, 58 size_t buflen, int flags); 59 static int amdpm_smbus_check_done(struct amdpm_softc *sc); 60 static void amdpm_smbus_clear_gsr(struct amdpm_softc *sc); 61 static u_int16_t amdpm_smbus_get_gsr(struct amdpm_softc *sc); 62 static int amdpm_smbus_send_1(struct amdpm_softc *sc, u_int8_t val); 63 static int amdpm_smbus_write_1(struct amdpm_softc *sc, u_int8_t cmd, u_int8_t data); 64 static int amdpm_smbus_receive_1(struct amdpm_softc *sc); 65 static int amdpm_smbus_read_1(struct amdpm_softc *sc, u_int8_t cmd); 66 67 68 void 69 amdpm_smbus_attach(struct amdpm_softc *sc) 70 { 71 struct i2cbus_attach_args iba; 72 73 // register with iic 74 sc->sc_i2c.ic_cookie = sc; 75 sc->sc_i2c.ic_acquire_bus = amdpm_smbus_acquire_bus; 76 sc->sc_i2c.ic_release_bus = amdpm_smbus_release_bus; 77 sc->sc_i2c.ic_send_start = NULL; 78 sc->sc_i2c.ic_send_stop = NULL; 79 sc->sc_i2c.ic_initiate_xfer = NULL; 80 sc->sc_i2c.ic_read_byte = NULL; 81 sc->sc_i2c.ic_write_byte = NULL; 82 sc->sc_i2c.ic_exec = amdpm_smbus_exec; 83 84 lockinit(&sc->sc_lock, PZERO, "amdpm_smbus", 0, 0); 85 86 iba.iba_tag = &sc->sc_i2c; 87 (void) config_found_ia(&sc->sc_dev, "i2cbus", &iba, iicbus_print); 88 } 89 90 static int 91 amdpm_smbus_acquire_bus(void *cookie, int flags) 92 { 93 struct amdpm_softc *sc = cookie; 94 int err; 95 96 err = lockmgr(&sc->sc_lock, LK_EXCLUSIVE, NULL); 97 98 return err; 99 } 100 101 static void 102 amdpm_smbus_release_bus(void *cookie, int flags) 103 { 104 struct amdpm_softc *sc = cookie; 105 106 lockmgr(&sc->sc_lock, LK_RELEASE, NULL); 107 108 return; 109 } 110 111 static int 112 amdpm_smbus_exec(void *cookie, i2c_op_t op, i2c_addr_t addr, const void *cmd, 113 size_t cmdlen, void *vbuf, size_t buflen, int flags) 114 { 115 struct amdpm_softc *sc = (struct amdpm_softc *) cookie; 116 sc->sc_smbus_slaveaddr = addr; 117 118 if (I2C_OP_READ_P(op) && (cmdlen == 0) && (buflen == 1)) { 119 return (amdpm_smbus_receive_1(sc)); 120 } 121 122 if ( (I2C_OP_READ_P(op)) && (cmdlen == 1) && (buflen == 1)) { 123 return (amdpm_smbus_read_1(sc, *(const uint8_t*)cmd)); 124 } 125 126 if ( (I2C_OP_WRITE_P(op)) && (cmdlen == 0) && (buflen == 1)) { 127 return (amdpm_smbus_send_1(sc, *(uint8_t*)vbuf)); 128 } 129 130 if ( (I2C_OP_WRITE_P(op)) && (cmdlen == 1) && (buflen == 1)) { 131 return (amdpm_smbus_write_1(sc, *(const uint8_t*)cmd, *(uint8_t*)vbuf)); 132 } 133 134 return (-1); 135 } 136 137 static int 138 amdpm_smbus_check_done(struct amdpm_softc *sc) 139 { 140 int i = 0; 141 for (i = 0; i < 1000; i++) { 142 /* check gsr and wait till cycle is done */ 143 u_int16_t data = amdpm_smbus_get_gsr(sc); 144 if (data & AMDPM_8111_GSR_CYCLE_DONE) { 145 return (0); 146 } 147 delay(1); 148 } 149 return (-1); 150 } 151 152 153 static void 154 amdpm_smbus_clear_gsr(struct amdpm_softc *sc) 155 { 156 /* clear register */ 157 u_int16_t data = 0xFFFF; 158 bus_space_write_2(sc->sc_iot, sc->sc_ioh, AMDPM_8111_SMBUS_STAT, data); 159 } 160 161 static u_int16_t 162 amdpm_smbus_get_gsr(struct amdpm_softc *sc) 163 { 164 return (bus_space_read_2(sc->sc_iot, sc->sc_ioh, AMDPM_8111_SMBUS_STAT)); 165 } 166 167 static int 168 amdpm_smbus_send_1(struct amdpm_softc *sc, u_int8_t val) 169 { 170 /* first clear gsr */ 171 amdpm_smbus_clear_gsr(sc); 172 173 /* write smbus slave address to register */ 174 u_int16_t data = 0; 175 data = sc->sc_smbus_slaveaddr; 176 data <<= 1; 177 data |= AMDPM_8111_SMBUS_SEND; 178 bus_space_write_1(sc->sc_iot, sc->sc_ioh, AMDPM_8111_SMBUS_HOSTADDR, data); 179 180 data = val; 181 /* store data */ 182 bus_space_write_2(sc->sc_iot, sc->sc_ioh, AMDPM_8111_SMBUS_HOSTDATA, data); 183 /* host start */ 184 bus_space_write_2(sc->sc_iot, sc->sc_ioh, AMDPM_8111_SMBUS_CTRL, 185 AMDPM_8111_SMBUS_GSR_SB); 186 return(amdpm_smbus_check_done(sc)); 187 } 188 189 190 static int 191 amdpm_smbus_write_1(struct amdpm_softc *sc, u_int8_t cmd, u_int8_t val) 192 { 193 /* first clear gsr */ 194 amdpm_smbus_clear_gsr(sc); 195 196 u_int16_t data = 0; 197 data = sc->sc_smbus_slaveaddr; 198 data <<= 1; 199 data |= AMDPM_8111_SMBUS_WRITE; 200 bus_space_write_1(sc->sc_iot, sc->sc_ioh, AMDPM_8111_SMBUS_HOSTADDR, data); 201 202 data = val; 203 /* store cmd */ 204 bus_space_write_1(sc->sc_iot, sc->sc_ioh, AMDPM_8111_SMBUS_HOSTCMD, cmd); 205 /* store data */ 206 bus_space_write_2(sc->sc_iot, sc->sc_ioh, AMDPM_8111_SMBUS_HOSTDATA, data); 207 /* host start */ 208 bus_space_write_2(sc->sc_iot, sc->sc_ioh, AMDPM_8111_SMBUS_CTRL, AMDPM_8111_SMBUS_GSR_WB); 209 210 return (amdpm_smbus_check_done(sc)); 211 } 212 213 static int 214 amdpm_smbus_receive_1(struct amdpm_softc *sc) 215 { 216 /* first clear gsr */ 217 amdpm_smbus_clear_gsr(sc); 218 219 /* write smbus slave address to register */ 220 u_int16_t data = 0; 221 data = sc->sc_smbus_slaveaddr; 222 data <<= 1; 223 data |= AMDPM_8111_SMBUS_RX; 224 bus_space_write_1(sc->sc_iot, sc->sc_ioh, AMDPM_8111_SMBUS_HOSTADDR, data); 225 226 /* start smbus cycle */ 227 bus_space_write_2(sc->sc_iot, sc->sc_ioh, AMDPM_8111_SMBUS_CTRL, AMDPM_8111_SMBUS_GSR_RXB); 228 229 /* check for errors */ 230 if (amdpm_smbus_check_done(sc) < 0) 231 return (-1); 232 233 /* read data */ 234 data = bus_space_read_2(sc->sc_iot, sc->sc_ioh, AMDPM_8111_SMBUS_HOSTDATA); 235 u_int8_t ret = (u_int8_t)(data & 0x00FF); 236 return (ret); 237 } 238 239 static int 240 amdpm_smbus_read_1(struct amdpm_softc *sc, u_int8_t cmd) 241 { 242 /* first clear gsr */ 243 amdpm_smbus_clear_gsr(sc); 244 245 /* write smbus slave address to register */ 246 u_int16_t data = 0; 247 data = sc->sc_smbus_slaveaddr; 248 data <<= 1; 249 data |= AMDPM_8111_SMBUS_READ; 250 bus_space_write_1(sc->sc_iot, sc->sc_ioh, AMDPM_8111_SMBUS_HOSTADDR, data); 251 252 /* store cmd */ 253 bus_space_write_1(sc->sc_iot, sc->sc_ioh, AMDPM_8111_SMBUS_HOSTCMD, cmd); 254 /* host start */ 255 bus_space_write_2(sc->sc_iot, sc->sc_ioh, AMDPM_8111_SMBUS_CTRL, AMDPM_8111_SMBUS_GSR_RB); 256 257 /* check for errors */ 258 if (amdpm_smbus_check_done(sc) < 0) 259 return (-1); 260 261 /* store data */ 262 data = bus_space_read_2(sc->sc_iot, sc->sc_ioh, AMDPM_8111_SMBUS_HOSTDATA); 263 u_int8_t ret = (u_int8_t)(data & 0x00FF); 264 return (ret); 265 } 266