1 /* $NetBSD: amdpm_smbus.c,v 1.26 2025/09/15 13:23:03 thorpej Exp $ */ 2 3 /* 4 * Copyright (c) 2005 Anil Gopinath (anil_public (at) 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.26 2025/09/15 13:23:03 thorpej Exp $"); 36 37 #include <sys/param.h> 38 #include <sys/systm.h> 39 #include <sys/kernel.h> 40 #include <sys/device.h> 41 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_exec(void *, i2c_op_t, i2c_addr_t, const void *, 55 size_t, void *, size_t, int); 56 static int amdpm_smbus_check_done(struct amdpm_softc *, i2c_op_t); 57 static void amdpm_smbus_clear_gsr(struct amdpm_softc *); 58 static uint16_t amdpm_smbus_get_gsr(struct amdpm_softc *); 59 static int amdpm_smbus_quick(struct amdpm_softc *, i2c_op_t); 60 static int amdpm_smbus_send_1(struct amdpm_softc *, uint8_t, i2c_op_t); 61 static int amdpm_smbus_write_1(struct amdpm_softc *, uint8_t, 62 uint8_t, i2c_op_t); 63 static int amdpm_smbus_receive_1(struct amdpm_softc *, i2c_op_t); 64 static int amdpm_smbus_read_1(struct amdpm_softc *sc, uint8_t, i2c_op_t); 65 66 void 67 amdpm_smbus_attach(struct amdpm_softc *sc) 68 { 69 70 /* register with iic */ 71 iic_tag_init(&sc->sc_i2c); 72 sc->sc_i2c.ic_cookie = sc; 73 sc->sc_i2c.ic_exec = amdpm_smbus_exec; 74 75 iicbus_attach(sc->sc_dev, &sc->sc_i2c); 76 } 77 78 static int 79 amdpm_smbus_exec(void *cookie, i2c_op_t op, i2c_addr_t addr, const void *cmd, 80 size_t cmdlen, void *vbuf, size_t buflen, int flags) 81 { 82 struct amdpm_softc *sc = (struct amdpm_softc *) cookie; 83 sc->sc_smbus_slaveaddr = addr; 84 uint8_t *p = vbuf; 85 int rv; 86 87 if ((cmdlen == 0) && (buflen == 0)) 88 return amdpm_smbus_quick(sc, op); 89 90 if (I2C_OP_READ_P(op) && (cmdlen == 0) && (buflen == 1)) { 91 rv = amdpm_smbus_receive_1(sc, op); 92 if (rv == -1) 93 return -1; 94 *p = (uint8_t)rv; 95 return 0; 96 } 97 98 if ((I2C_OP_READ_P(op)) && (cmdlen == 1) && (buflen == 1)) { 99 rv = amdpm_smbus_read_1(sc, *(const uint8_t *)cmd, op); 100 if (rv == -1) 101 return -1; 102 *p = (uint8_t)rv; 103 return 0; 104 } 105 106 if ((I2C_OP_WRITE_P(op)) && (cmdlen == 0) && (buflen == 1)) 107 return amdpm_smbus_send_1(sc, *(uint8_t*)vbuf, op); 108 109 if ((I2C_OP_WRITE_P(op)) && (cmdlen == 1) && (buflen == 1)) 110 return amdpm_smbus_write_1(sc, 111 *(const uint8_t*)cmd, 112 *(uint8_t*)vbuf, 113 op); 114 115 return -1; 116 } 117 118 static int 119 amdpm_smbus_check_done(struct amdpm_softc *sc, i2c_op_t op) 120 { 121 int i; 122 123 for (i = 0; i < 1000; i++) { 124 /* check gsr and wait till cycle is done */ 125 uint16_t data = amdpm_smbus_get_gsr(sc); 126 if (data & AMDPM_8111_GSR_CYCLE_DONE) 127 return 0; 128 } 129 130 if (!(op & I2C_F_POLL)) 131 delay(1); 132 133 return -1; 134 } 135 136 137 static void 138 amdpm_smbus_clear_gsr(struct amdpm_softc *sc) 139 { 140 /* clear register */ 141 uint16_t data = 0xFFFF; 142 int off = (sc->sc_nforce ? 0xe0 : 0); 143 bus_space_write_2(sc->sc_iot, sc->sc_ioh, 144 AMDPM_8111_SMBUS_STAT - off, data); 145 } 146 147 static uint16_t 148 amdpm_smbus_get_gsr(struct amdpm_softc *sc) 149 { 150 int off = (sc->sc_nforce ? 0xe0 : 0); 151 return bus_space_read_2(sc->sc_iot, sc->sc_ioh, 152 AMDPM_8111_SMBUS_STAT - off); 153 } 154 155 static int 156 amdpm_smbus_quick(struct amdpm_softc *sc, i2c_op_t op) 157 { 158 uint16_t data = 0; 159 int off = (sc->sc_nforce ? 0xe0 : 0); 160 161 /* first clear gsr */ 162 amdpm_smbus_clear_gsr(sc); 163 164 /* write smbus slave address and read/write bit to register */ 165 data = sc->sc_smbus_slaveaddr; 166 data <<= 1; 167 if (I2C_OP_READ_P(op)) 168 data |= AMDPM_8111_SMBUS_READ; 169 170 bus_space_write_1(sc->sc_iot, sc->sc_ioh, 171 AMDPM_8111_SMBUS_HOSTADDR - off, data); 172 173 /* host start */ 174 bus_space_write_2(sc->sc_iot, sc->sc_ioh, 175 AMDPM_8111_SMBUS_CTRL - off, 176 AMDPM_8111_SMBUS_GSR_QUICK); 177 178 return amdpm_smbus_check_done(sc, op); 179 } 180 181 static int 182 amdpm_smbus_send_1(struct amdpm_softc *sc, uint8_t val, i2c_op_t op) 183 { 184 uint16_t data = 0; 185 int off = (sc->sc_nforce ? 0xe0 : 0); 186 187 /* first clear gsr */ 188 amdpm_smbus_clear_gsr(sc); 189 190 /* write smbus slave address to register */ 191 data = sc->sc_smbus_slaveaddr; 192 data <<= 1; 193 data |= AMDPM_8111_SMBUS_SEND; 194 bus_space_write_1(sc->sc_iot, sc->sc_ioh, 195 AMDPM_8111_SMBUS_HOSTADDR - off, data); 196 197 data = val; 198 /* store data */ 199 bus_space_write_2(sc->sc_iot, sc->sc_ioh, 200 AMDPM_8111_SMBUS_HOSTDATA - off, data); 201 /* host start */ 202 bus_space_write_2(sc->sc_iot, sc->sc_ioh, 203 AMDPM_8111_SMBUS_CTRL - off, 204 AMDPM_8111_SMBUS_GSR_SB); 205 206 return amdpm_smbus_check_done(sc, op); 207 } 208 209 210 static int 211 amdpm_smbus_write_1(struct amdpm_softc *sc, uint8_t cmd, uint8_t val, 212 i2c_op_t op) 213 { 214 uint16_t data = 0; 215 int off = (sc->sc_nforce ? 0xe0 : 0); 216 217 /* first clear gsr */ 218 amdpm_smbus_clear_gsr(sc); 219 220 data = sc->sc_smbus_slaveaddr; 221 data <<= 1; 222 data |= AMDPM_8111_SMBUS_WRITE; 223 bus_space_write_1(sc->sc_iot, sc->sc_ioh, 224 AMDPM_8111_SMBUS_HOSTADDR - off, data); 225 226 data = val; 227 /* store cmd */ 228 bus_space_write_1(sc->sc_iot, sc->sc_ioh, 229 AMDPM_8111_SMBUS_HOSTCMD - off, cmd); 230 /* store data */ 231 bus_space_write_2(sc->sc_iot, sc->sc_ioh, 232 AMDPM_8111_SMBUS_HOSTDATA - off, data); 233 /* host start */ 234 bus_space_write_2(sc->sc_iot, sc->sc_ioh, 235 AMDPM_8111_SMBUS_CTRL - off, AMDPM_8111_SMBUS_GSR_WB); 236 237 return amdpm_smbus_check_done(sc, op); 238 } 239 240 static int 241 amdpm_smbus_receive_1(struct amdpm_softc *sc, i2c_op_t op) 242 { 243 uint16_t data = 0; 244 int off = (sc->sc_nforce ? 0xe0 : 0); 245 246 /* first clear gsr */ 247 amdpm_smbus_clear_gsr(sc); 248 249 /* write smbus slave address to register */ 250 data = sc->sc_smbus_slaveaddr; 251 data <<= 1; 252 data |= AMDPM_8111_SMBUS_RX; 253 bus_space_write_1(sc->sc_iot, sc->sc_ioh, 254 AMDPM_8111_SMBUS_HOSTADDR - off, data); 255 256 /* start smbus cycle */ 257 bus_space_write_2(sc->sc_iot, sc->sc_ioh, 258 AMDPM_8111_SMBUS_CTRL - off, AMDPM_8111_SMBUS_GSR_RXB); 259 260 /* check for errors */ 261 if (amdpm_smbus_check_done(sc, op) < 0) 262 return -1; 263 264 /* read data */ 265 data = bus_space_read_2(sc->sc_iot, sc->sc_ioh, 266 AMDPM_8111_SMBUS_HOSTDATA - off); 267 uint8_t ret = (uint8_t)(data & 0x00FF); 268 return ret; 269 } 270 271 static int 272 amdpm_smbus_read_1(struct amdpm_softc *sc, uint8_t cmd, i2c_op_t op) 273 { 274 uint16_t data = 0; 275 uint8_t ret; 276 int off = (sc->sc_nforce ? 0xe0 : 0); 277 278 /* first clear gsr */ 279 amdpm_smbus_clear_gsr(sc); 280 281 /* write smbus slave address to register */ 282 data = sc->sc_smbus_slaveaddr; 283 data <<= 1; 284 data |= AMDPM_8111_SMBUS_READ; 285 bus_space_write_1(sc->sc_iot, sc->sc_ioh, 286 AMDPM_8111_SMBUS_HOSTADDR - off, data); 287 288 /* store cmd */ 289 bus_space_write_1(sc->sc_iot, sc->sc_ioh, 290 AMDPM_8111_SMBUS_HOSTCMD - off, cmd); 291 /* host start */ 292 bus_space_write_2(sc->sc_iot, sc->sc_ioh, 293 AMDPM_8111_SMBUS_CTRL - off, AMDPM_8111_SMBUS_GSR_RB); 294 295 /* check for errors */ 296 if (amdpm_smbus_check_done(sc, op) < 0) 297 return -1; 298 299 /* store data */ 300 data = bus_space_read_2(sc->sc_iot, sc->sc_ioh, 301 AMDPM_8111_SMBUS_HOSTDATA - off); 302 ret = (uint8_t)(data & 0x00FF); 303 return ret; 304 } 305