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