1 1.26 thorpej /* $NetBSD: amdpm_smbus.c,v 1.26 2025/09/15 13:23:03 thorpej Exp $ */ 2 1.1 tls 3 1.1 tls /* 4 1.1 tls * Copyright (c) 2005 Anil Gopinath (anil_public (at) yahoo.com) 5 1.1 tls * All rights reserved. 6 1.1 tls * 7 1.1 tls * Redistribution and use in source and binary forms, with or without 8 1.1 tls * modification, are permitted provided that the following conditions 9 1.1 tls * are met: 10 1.1 tls * 1. Redistributions of source code must retain the above copyright 11 1.1 tls * notice, this list of conditions and the following disclaimer. 12 1.1 tls * 2. Redistributions in binary form must reproduce the above copyright 13 1.1 tls * notice, this list of conditions and the following disclaimer in the 14 1.1 tls * documentation and/or other materials provided with the distribution. 15 1.1 tls * 3. The name of the author may not be used to endorse or promote products 16 1.1 tls * derived from this software without specific prior written permission. 17 1.1 tls * 18 1.1 tls * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 19 1.1 tls * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 20 1.1 tls * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 21 1.1 tls * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 22 1.1 tls * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 23 1.1 tls * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 24 1.1 tls * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED 25 1.1 tls * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 26 1.1 tls * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 27 1.1 tls * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 28 1.1 tls * SUCH DAMAGE. 29 1.1 tls */ 30 1.1 tls 31 1.1 tls /* driver for SMBUS 1.0 host controller found in the 32 1.1 tls * AMD-8111 HyperTransport I/O Hub 33 1.1 tls */ 34 1.2 xtraeme #include <sys/cdefs.h> 35 1.26 thorpej __KERNEL_RCSID(0, "$NetBSD: amdpm_smbus.c,v 1.26 2025/09/15 13:23:03 thorpej Exp $"); 36 1.2 xtraeme 37 1.1 tls #include <sys/param.h> 38 1.1 tls #include <sys/systm.h> 39 1.1 tls #include <sys/kernel.h> 40 1.1 tls #include <sys/device.h> 41 1.14 xtraeme 42 1.1 tls #include <dev/pci/pcireg.h> 43 1.1 tls #include <dev/pci/pcivar.h> 44 1.1 tls #include <dev/pci/pcidevs.h> 45 1.1 tls 46 1.1 tls #include <dev/i2c/i2cvar.h> 47 1.1 tls #include <dev/i2c/i2c_bitbang.h> 48 1.1 tls 49 1.1 tls #include <dev/pci/amdpmreg.h> 50 1.1 tls #include <dev/pci/amdpmvar.h> 51 1.1 tls 52 1.1 tls #include <dev/pci/amdpm_smbusreg.h> 53 1.1 tls 54 1.13 xtraeme static int amdpm_smbus_exec(void *, i2c_op_t, i2c_addr_t, const void *, 55 1.13 xtraeme size_t, void *, size_t, int); 56 1.13 xtraeme static int amdpm_smbus_check_done(struct amdpm_softc *, i2c_op_t); 57 1.13 xtraeme static void amdpm_smbus_clear_gsr(struct amdpm_softc *); 58 1.13 xtraeme static uint16_t amdpm_smbus_get_gsr(struct amdpm_softc *); 59 1.16 pgoyette static int amdpm_smbus_quick(struct amdpm_softc *, i2c_op_t); 60 1.13 xtraeme static int amdpm_smbus_send_1(struct amdpm_softc *, uint8_t, i2c_op_t); 61 1.13 xtraeme static int amdpm_smbus_write_1(struct amdpm_softc *, uint8_t, 62 1.13 xtraeme uint8_t, i2c_op_t); 63 1.13 xtraeme static int amdpm_smbus_receive_1(struct amdpm_softc *, i2c_op_t); 64 1.13 xtraeme static int amdpm_smbus_read_1(struct amdpm_softc *sc, uint8_t, i2c_op_t); 65 1.1 tls 66 1.1 tls void 67 1.1 tls amdpm_smbus_attach(struct amdpm_softc *sc) 68 1.1 tls { 69 1.26 thorpej 70 1.5 jmcneill /* register with iic */ 71 1.23 thorpej iic_tag_init(&sc->sc_i2c); 72 1.1 tls sc->sc_i2c.ic_cookie = sc; 73 1.1 tls sc->sc_i2c.ic_exec = amdpm_smbus_exec; 74 1.3 xtraeme 75 1.26 thorpej iicbus_attach(sc->sc_dev, &sc->sc_i2c); 76 1.1 tls } 77 1.1 tls 78 1.10 jmcneill static int 79 1.1 tls amdpm_smbus_exec(void *cookie, i2c_op_t op, i2c_addr_t addr, const void *cmd, 80 1.13 xtraeme size_t cmdlen, void *vbuf, size_t buflen, int flags) 81 1.1 tls { 82 1.1 tls struct amdpm_softc *sc = (struct amdpm_softc *) cookie; 83 1.1 tls sc->sc_smbus_slaveaddr = addr; 84 1.13 xtraeme uint8_t *p = vbuf; 85 1.9 jmcneill int rv; 86 1.1 tls 87 1.16 pgoyette if ((cmdlen == 0) && (buflen == 0)) 88 1.16 pgoyette return amdpm_smbus_quick(sc, op); 89 1.16 pgoyette 90 1.1 tls if (I2C_OP_READ_P(op) && (cmdlen == 0) && (buflen == 1)) { 91 1.13 xtraeme rv = amdpm_smbus_receive_1(sc, op); 92 1.13 xtraeme if (rv == -1) 93 1.13 xtraeme return -1; 94 1.13 xtraeme *p = (uint8_t)rv; 95 1.13 xtraeme return 0; 96 1.1 tls } 97 1.1 tls 98 1.13 xtraeme if ((I2C_OP_READ_P(op)) && (cmdlen == 1) && (buflen == 1)) { 99 1.13 xtraeme rv = amdpm_smbus_read_1(sc, *(const uint8_t *)cmd, op); 100 1.13 xtraeme if (rv == -1) 101 1.13 xtraeme return -1; 102 1.13 xtraeme *p = (uint8_t)rv; 103 1.13 xtraeme return 0; 104 1.1 tls } 105 1.1 tls 106 1.13 xtraeme if ((I2C_OP_WRITE_P(op)) && (cmdlen == 0) && (buflen == 1)) 107 1.13 xtraeme return amdpm_smbus_send_1(sc, *(uint8_t*)vbuf, op); 108 1.1 tls 109 1.13 xtraeme if ((I2C_OP_WRITE_P(op)) && (cmdlen == 1) && (buflen == 1)) 110 1.13 xtraeme return amdpm_smbus_write_1(sc, 111 1.13 xtraeme *(const uint8_t*)cmd, 112 1.13 xtraeme *(uint8_t*)vbuf, 113 1.13 xtraeme op); 114 1.1 tls 115 1.13 xtraeme return -1; 116 1.1 tls } 117 1.1 tls 118 1.1 tls static int 119 1.10 jmcneill amdpm_smbus_check_done(struct amdpm_softc *sc, i2c_op_t op) 120 1.1 tls { 121 1.13 xtraeme int i; 122 1.13 xtraeme 123 1.1 tls for (i = 0; i < 1000; i++) { 124 1.13 xtraeme /* check gsr and wait till cycle is done */ 125 1.13 xtraeme uint16_t data = amdpm_smbus_get_gsr(sc); 126 1.13 xtraeme if (data & AMDPM_8111_GSR_CYCLE_DONE) 127 1.13 xtraeme return 0; 128 1.13 xtraeme } 129 1.13 xtraeme 130 1.13 xtraeme if (!(op & I2C_F_POLL)) 131 1.10 jmcneill delay(1); 132 1.13 xtraeme 133 1.13 xtraeme return -1; 134 1.1 tls } 135 1.1 tls 136 1.1 tls 137 1.1 tls static void 138 1.1 tls amdpm_smbus_clear_gsr(struct amdpm_softc *sc) 139 1.1 tls { 140 1.1 tls /* clear register */ 141 1.13 xtraeme uint16_t data = 0xFFFF; 142 1.8 jmcneill int off = (sc->sc_nforce ? 0xe0 : 0); 143 1.8 jmcneill bus_space_write_2(sc->sc_iot, sc->sc_ioh, 144 1.8 jmcneill AMDPM_8111_SMBUS_STAT - off, data); 145 1.1 tls } 146 1.1 tls 147 1.13 xtraeme static uint16_t 148 1.1 tls amdpm_smbus_get_gsr(struct amdpm_softc *sc) 149 1.1 tls { 150 1.8 jmcneill int off = (sc->sc_nforce ? 0xe0 : 0); 151 1.13 xtraeme return bus_space_read_2(sc->sc_iot, sc->sc_ioh, 152 1.13 xtraeme AMDPM_8111_SMBUS_STAT - off); 153 1.1 tls } 154 1.1 tls 155 1.1 tls static int 156 1.16 pgoyette amdpm_smbus_quick(struct amdpm_softc *sc, i2c_op_t op) 157 1.16 pgoyette { 158 1.16 pgoyette uint16_t data = 0; 159 1.16 pgoyette int off = (sc->sc_nforce ? 0xe0 : 0); 160 1.16 pgoyette 161 1.16 pgoyette /* first clear gsr */ 162 1.16 pgoyette amdpm_smbus_clear_gsr(sc); 163 1.16 pgoyette 164 1.16 pgoyette /* write smbus slave address and read/write bit to register */ 165 1.16 pgoyette data = sc->sc_smbus_slaveaddr; 166 1.16 pgoyette data <<= 1; 167 1.16 pgoyette if (I2C_OP_READ_P(op)) 168 1.16 pgoyette data |= AMDPM_8111_SMBUS_READ; 169 1.16 pgoyette 170 1.16 pgoyette bus_space_write_1(sc->sc_iot, sc->sc_ioh, 171 1.16 pgoyette AMDPM_8111_SMBUS_HOSTADDR - off, data); 172 1.16 pgoyette 173 1.16 pgoyette /* host start */ 174 1.16 pgoyette bus_space_write_2(sc->sc_iot, sc->sc_ioh, 175 1.16 pgoyette AMDPM_8111_SMBUS_CTRL - off, 176 1.16 pgoyette AMDPM_8111_SMBUS_GSR_QUICK); 177 1.16 pgoyette 178 1.16 pgoyette return amdpm_smbus_check_done(sc, op); 179 1.16 pgoyette } 180 1.16 pgoyette 181 1.16 pgoyette static int 182 1.13 xtraeme amdpm_smbus_send_1(struct amdpm_softc *sc, uint8_t val, i2c_op_t op) 183 1.1 tls { 184 1.13 xtraeme uint16_t data = 0; 185 1.8 jmcneill int off = (sc->sc_nforce ? 0xe0 : 0); 186 1.8 jmcneill 187 1.13 xtraeme /* first clear gsr */ 188 1.13 xtraeme amdpm_smbus_clear_gsr(sc); 189 1.1 tls 190 1.1 tls /* write smbus slave address to register */ 191 1.1 tls data = sc->sc_smbus_slaveaddr; 192 1.1 tls data <<= 1; 193 1.1 tls data |= AMDPM_8111_SMBUS_SEND; 194 1.8 jmcneill bus_space_write_1(sc->sc_iot, sc->sc_ioh, 195 1.8 jmcneill AMDPM_8111_SMBUS_HOSTADDR - off, data); 196 1.1 tls 197 1.1 tls data = val; 198 1.1 tls /* store data */ 199 1.8 jmcneill bus_space_write_2(sc->sc_iot, sc->sc_ioh, 200 1.8 jmcneill AMDPM_8111_SMBUS_HOSTDATA - off, data); 201 1.1 tls /* host start */ 202 1.8 jmcneill bus_space_write_2(sc->sc_iot, sc->sc_ioh, 203 1.8 jmcneill AMDPM_8111_SMBUS_CTRL - off, 204 1.8 jmcneill AMDPM_8111_SMBUS_GSR_SB); 205 1.8 jmcneill 206 1.13 xtraeme return amdpm_smbus_check_done(sc, op); 207 1.1 tls } 208 1.1 tls 209 1.1 tls 210 1.1 tls static int 211 1.13 xtraeme amdpm_smbus_write_1(struct amdpm_softc *sc, uint8_t cmd, uint8_t val, 212 1.13 xtraeme i2c_op_t op) 213 1.1 tls { 214 1.13 xtraeme uint16_t data = 0; 215 1.8 jmcneill int off = (sc->sc_nforce ? 0xe0 : 0); 216 1.8 jmcneill 217 1.13 xtraeme /* first clear gsr */ 218 1.13 xtraeme amdpm_smbus_clear_gsr(sc); 219 1.1 tls 220 1.1 tls data = sc->sc_smbus_slaveaddr; 221 1.1 tls data <<= 1; 222 1.1 tls data |= AMDPM_8111_SMBUS_WRITE; 223 1.8 jmcneill bus_space_write_1(sc->sc_iot, sc->sc_ioh, 224 1.8 jmcneill AMDPM_8111_SMBUS_HOSTADDR - off, data); 225 1.1 tls 226 1.1 tls data = val; 227 1.1 tls /* store cmd */ 228 1.8 jmcneill bus_space_write_1(sc->sc_iot, sc->sc_ioh, 229 1.8 jmcneill AMDPM_8111_SMBUS_HOSTCMD - off, cmd); 230 1.1 tls /* store data */ 231 1.8 jmcneill bus_space_write_2(sc->sc_iot, sc->sc_ioh, 232 1.8 jmcneill AMDPM_8111_SMBUS_HOSTDATA - off, data); 233 1.1 tls /* host start */ 234 1.8 jmcneill bus_space_write_2(sc->sc_iot, sc->sc_ioh, 235 1.8 jmcneill AMDPM_8111_SMBUS_CTRL - off, AMDPM_8111_SMBUS_GSR_WB); 236 1.1 tls 237 1.13 xtraeme return amdpm_smbus_check_done(sc, op); 238 1.1 tls } 239 1.1 tls 240 1.1 tls static int 241 1.10 jmcneill amdpm_smbus_receive_1(struct amdpm_softc *sc, i2c_op_t op) 242 1.1 tls { 243 1.13 xtraeme uint16_t data = 0; 244 1.8 jmcneill int off = (sc->sc_nforce ? 0xe0 : 0); 245 1.8 jmcneill 246 1.13 xtraeme /* first clear gsr */ 247 1.13 xtraeme amdpm_smbus_clear_gsr(sc); 248 1.1 tls 249 1.1 tls /* write smbus slave address to register */ 250 1.1 tls data = sc->sc_smbus_slaveaddr; 251 1.1 tls data <<= 1; 252 1.1 tls data |= AMDPM_8111_SMBUS_RX; 253 1.8 jmcneill bus_space_write_1(sc->sc_iot, sc->sc_ioh, 254 1.8 jmcneill AMDPM_8111_SMBUS_HOSTADDR - off, data); 255 1.1 tls 256 1.1 tls /* start smbus cycle */ 257 1.8 jmcneill bus_space_write_2(sc->sc_iot, sc->sc_ioh, 258 1.8 jmcneill AMDPM_8111_SMBUS_CTRL - off, AMDPM_8111_SMBUS_GSR_RXB); 259 1.1 tls 260 1.1 tls /* check for errors */ 261 1.10 jmcneill if (amdpm_smbus_check_done(sc, op) < 0) 262 1.13 xtraeme return -1; 263 1.1 tls 264 1.1 tls /* read data */ 265 1.8 jmcneill data = bus_space_read_2(sc->sc_iot, sc->sc_ioh, 266 1.8 jmcneill AMDPM_8111_SMBUS_HOSTDATA - off); 267 1.13 xtraeme uint8_t ret = (uint8_t)(data & 0x00FF); 268 1.13 xtraeme return ret; 269 1.1 tls } 270 1.1 tls 271 1.1 tls static int 272 1.13 xtraeme amdpm_smbus_read_1(struct amdpm_softc *sc, uint8_t cmd, i2c_op_t op) 273 1.8 jmcneill { 274 1.13 xtraeme uint16_t data = 0; 275 1.13 xtraeme uint8_t ret; 276 1.8 jmcneill int off = (sc->sc_nforce ? 0xe0 : 0); 277 1.8 jmcneill 278 1.13 xtraeme /* first clear gsr */ 279 1.13 xtraeme amdpm_smbus_clear_gsr(sc); 280 1.1 tls 281 1.1 tls /* write smbus slave address to register */ 282 1.1 tls data = sc->sc_smbus_slaveaddr; 283 1.1 tls data <<= 1; 284 1.1 tls data |= AMDPM_8111_SMBUS_READ; 285 1.8 jmcneill bus_space_write_1(sc->sc_iot, sc->sc_ioh, 286 1.8 jmcneill AMDPM_8111_SMBUS_HOSTADDR - off, data); 287 1.1 tls 288 1.1 tls /* store cmd */ 289 1.8 jmcneill bus_space_write_1(sc->sc_iot, sc->sc_ioh, 290 1.8 jmcneill AMDPM_8111_SMBUS_HOSTCMD - off, cmd); 291 1.1 tls /* host start */ 292 1.8 jmcneill bus_space_write_2(sc->sc_iot, sc->sc_ioh, 293 1.8 jmcneill AMDPM_8111_SMBUS_CTRL - off, AMDPM_8111_SMBUS_GSR_RB); 294 1.1 tls 295 1.1 tls /* check for errors */ 296 1.10 jmcneill if (amdpm_smbus_check_done(sc, op) < 0) 297 1.13 xtraeme return -1; 298 1.1 tls 299 1.1 tls /* store data */ 300 1.8 jmcneill data = bus_space_read_2(sc->sc_iot, sc->sc_ioh, 301 1.8 jmcneill AMDPM_8111_SMBUS_HOSTDATA - off); 302 1.13 xtraeme ret = (uint8_t)(data & 0x00FF); 303 1.13 xtraeme return ret; 304 1.1 tls } 305