Home | History | Annotate | Line # | Download | only in pci
amdpm_smbus.c revision 1.10
      1 /*	$NetBSD: amdpm_smbus.c,v 1.10 2007/02/05 23:38:15 jmcneill 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.10 2007/02/05 23:38:15 jmcneill 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 #ifdef __i386__
     55 #include "opt_xbox.h"
     56 #endif
     57 
     58 #ifdef XBOX
     59 extern int arch_i386_is_xbox;
     60 #endif
     61 
     62 static int       amdpm_smbus_acquire_bus(void *cookie, int flags);
     63 static void      amdpm_smbus_release_bus(void *cookie, int flags);
     64 static int       amdpm_smbus_exec(void *cookie, i2c_op_t op, i2c_addr_t addr,
     65 				  const void *cmd, size_t cmdlen, void *vbuf,
     66 				  size_t buflen, int flags);
     67 static int       amdpm_smbus_check_done(struct amdpm_softc *sc, i2c_op_t op);
     68 static void      amdpm_smbus_clear_gsr(struct amdpm_softc *sc);
     69 static u_int16_t amdpm_smbus_get_gsr(struct amdpm_softc *sc);
     70 static int       amdpm_smbus_send_1(struct amdpm_softc *sc, u_int8_t val, i2c_op_t op);
     71 static int       amdpm_smbus_write_1(struct amdpm_softc *sc, u_int8_t cmd, u_int8_t data, i2c_op_t op);
     72 static int       amdpm_smbus_receive_1(struct amdpm_softc *sc, i2c_op_t op);
     73 static int       amdpm_smbus_read_1(struct amdpm_softc *sc, u_int8_t cmd, i2c_op_t op);
     74 
     75 static int	 amdpm_smbus_intr(void *);
     76 
     77 void
     78 amdpm_smbus_attach(struct amdpm_softc *sc)
     79 {
     80         struct i2cbus_attach_args iba;
     81 	pci_intr_handle_t ih;
     82 	const char *intrstr;
     83 
     84 	/* register with iic */
     85 	sc->sc_i2c.ic_cookie = sc;
     86 	sc->sc_i2c.ic_acquire_bus = amdpm_smbus_acquire_bus;
     87 	sc->sc_i2c.ic_release_bus = amdpm_smbus_release_bus;
     88 	sc->sc_i2c.ic_send_start = NULL;
     89 	sc->sc_i2c.ic_send_stop = NULL;
     90 	sc->sc_i2c.ic_initiate_xfer = NULL;
     91 	sc->sc_i2c.ic_read_byte = NULL;
     92 	sc->sc_i2c.ic_write_byte = NULL;
     93 	sc->sc_i2c.ic_exec = amdpm_smbus_exec;
     94 
     95 	lockinit(&sc->sc_lock, PZERO, "amdpm_smbus", 0, 0);
     96 
     97 #ifdef XBOX
     98 #define XBOX_SMBA	0x8000
     99 #define XBOX_SMSIZE	256
    100 #define XBOX_INTRLINE	12
    101 	/* XXX pci0 dev 1 function 2 "System Management" doesn't probe */
    102 	if (arch_i386_is_xbox) {
    103 		sc->sc_pa->pa_intrline = XBOX_INTRLINE;
    104 
    105 		if (bus_space_map(sc->sc_iot, XBOX_SMBA, XBOX_SMSIZE,
    106 		    0, &sc->sc_sm_ioh) == 0)
    107 			aprint_normal("%s: system management at 0x%04x\n",
    108 			    sc->sc_dev.dv_xname, XBOX_SMBA);
    109 
    110 	}
    111 
    112 	if (pci_intr_map(sc->sc_pa, &ih))
    113 		aprint_error("%s: couldn't map interrupt\n",
    114 		    sc->sc_dev.dv_xname);
    115 	else {
    116 		intrstr = pci_intr_string(sc->sc_pc, ih);
    117 		sc->sc_ih = pci_intr_establish(sc->sc_pc, ih, IPL_BIO,
    118 		    amdpm_smbus_intr, sc);
    119 		if (sc->sc_ih != NULL)
    120 			aprint_normal("%s: interrupting at %s\n",
    121 			    sc->sc_dev.dv_xname, intrstr);
    122 	}
    123 #endif
    124 
    125 	iba.iba_tag = &sc->sc_i2c;
    126 	(void) config_found_ia(&sc->sc_dev, "i2cbus", &iba, iicbus_print);
    127 }
    128 
    129 static int
    130 amdpm_smbus_intr(void *cookie)
    131 {
    132 #ifdef XBOX
    133 	struct amdpm_softc *sc;
    134 	uint32_t status;
    135 
    136 	sc = (struct amdpm_softc *)cookie;
    137 
    138 	if (arch_i386_is_xbox) {
    139 		status = bus_space_read_4(sc->sc_iot, sc->sc_sm_ioh, 0x20);
    140 		bus_space_write_4(sc->sc_iot, sc->sc_sm_ioh, 0x20, status);
    141 
    142 		if (status & 2)
    143 			return iic_smbus_intr(&sc->sc_i2c);
    144 	}
    145 #endif
    146 	return 0;
    147 }
    148 
    149 static int
    150 amdpm_smbus_acquire_bus(void *cookie, int flags)
    151 {
    152 	struct amdpm_softc *sc = cookie;
    153 	int err;
    154 
    155 	err = lockmgr(&sc->sc_lock, LK_EXCLUSIVE, NULL);
    156 
    157 	return err;
    158 }
    159 
    160 static void
    161 amdpm_smbus_release_bus(void *cookie, int flags)
    162 {
    163 	struct amdpm_softc *sc = cookie;
    164 
    165 	lockmgr(&sc->sc_lock, LK_RELEASE, NULL);
    166 
    167 	return;
    168 }
    169 
    170 static int
    171 amdpm_smbus_exec(void *cookie, i2c_op_t op, i2c_addr_t addr, const void *cmd,
    172     size_t cmdlen, void *vbuf, size_t buflen, int flags)
    173 {
    174         struct amdpm_softc *sc  = (struct amdpm_softc *) cookie;
    175 	sc->sc_smbus_slaveaddr  = addr;
    176 	u_int8_t *p = vbuf;
    177 	int rv;
    178 
    179 	if (I2C_OP_READ_P(op) && (cmdlen == 0) && (buflen == 1)) {
    180 	  rv = amdpm_smbus_receive_1(sc, op);
    181 	  if (rv == -1)
    182 		return -1;
    183 	  *p = (u_int8_t)rv;
    184 	  return 0;
    185 	}
    186 
    187 	if ( (I2C_OP_READ_P(op)) && (cmdlen == 1) && (buflen == 1)) {
    188 	  rv = amdpm_smbus_read_1(sc, *(const uint8_t*)cmd, op);
    189 	  if (rv == -1)
    190 		return -1;
    191 	  *p = (u_int8_t)rv;
    192 	  return 0;
    193 	}
    194 
    195 	if ( (I2C_OP_WRITE_P(op)) && (cmdlen == 0) && (buflen == 1)) {
    196 	  return amdpm_smbus_send_1(sc, *(uint8_t*)vbuf, op);
    197 	}
    198 
    199 	if ( (I2C_OP_WRITE_P(op)) && (cmdlen == 1) && (buflen == 1)) {
    200 	  return amdpm_smbus_write_1(sc,  *(const uint8_t*)cmd, *(uint8_t*)vbuf, op);
    201 	}
    202 
    203 	return (-1);
    204 }
    205 
    206 static int
    207 amdpm_smbus_check_done(struct amdpm_softc *sc, i2c_op_t op)
    208 {
    209         int i = 0;
    210 	for (i = 0; i < 1000; i++) {
    211 	  /* check gsr and wait till cycle is done */
    212 	  u_int16_t data = amdpm_smbus_get_gsr(sc);
    213 	  if (data & AMDPM_8111_GSR_CYCLE_DONE) {
    214 	    return (0);
    215 	  }
    216 	  if (!(op & I2C_F_POLL))
    217 	    delay(1);
    218 	}
    219 	return (-1);
    220 }
    221 
    222 
    223 static void
    224 amdpm_smbus_clear_gsr(struct amdpm_softc *sc)
    225 {
    226         /* clear register */
    227         u_int16_t data = 0xFFFF;
    228 	int off = (sc->sc_nforce ? 0xe0 : 0);
    229 	bus_space_write_2(sc->sc_iot, sc->sc_ioh,
    230 	    AMDPM_8111_SMBUS_STAT - off, data);
    231 }
    232 
    233 static u_int16_t
    234 amdpm_smbus_get_gsr(struct amdpm_softc *sc)
    235 {
    236 	int off = (sc->sc_nforce ? 0xe0 : 0);
    237         return (bus_space_read_2(sc->sc_iot, sc->sc_ioh,
    238 	    AMDPM_8111_SMBUS_STAT - off));
    239 }
    240 
    241 static int
    242 amdpm_smbus_send_1(struct amdpm_softc *sc,  u_int8_t val, i2c_op_t op)
    243 {
    244 	u_int16_t data = 0;
    245 	int off = (sc->sc_nforce ? 0xe0 : 0);
    246 
    247         /* first clear gsr */
    248         amdpm_smbus_clear_gsr(sc);
    249 
    250 	/* write smbus slave address to register */
    251 	data = sc->sc_smbus_slaveaddr;
    252 	data <<= 1;
    253 	data |= AMDPM_8111_SMBUS_SEND;
    254 	bus_space_write_1(sc->sc_iot, sc->sc_ioh,
    255 	    AMDPM_8111_SMBUS_HOSTADDR - off, data);
    256 
    257 	data = val;
    258 	/* store data */
    259 	bus_space_write_2(sc->sc_iot, sc->sc_ioh,
    260 	    AMDPM_8111_SMBUS_HOSTDATA - off, data);
    261 	/* host start */
    262 	bus_space_write_2(sc->sc_iot, sc->sc_ioh,
    263 	    AMDPM_8111_SMBUS_CTRL - off,
    264 	    AMDPM_8111_SMBUS_GSR_SB);
    265 
    266 	return(amdpm_smbus_check_done(sc, op));
    267 }
    268 
    269 
    270 static int
    271 amdpm_smbus_write_1(struct amdpm_softc *sc, u_int8_t cmd, u_int8_t val, i2c_op_t op)
    272 {
    273 	u_int16_t data = 0;
    274 	int off = (sc->sc_nforce ? 0xe0 : 0);
    275 
    276         /* first clear gsr */
    277         amdpm_smbus_clear_gsr(sc);
    278 
    279 	data = sc->sc_smbus_slaveaddr;
    280 	data <<= 1;
    281 	data |= AMDPM_8111_SMBUS_WRITE;
    282 	bus_space_write_1(sc->sc_iot, sc->sc_ioh,
    283 	    AMDPM_8111_SMBUS_HOSTADDR - off, data);
    284 
    285 	data = val;
    286 	/* store cmd */
    287 	bus_space_write_1(sc->sc_iot, sc->sc_ioh,
    288 	    AMDPM_8111_SMBUS_HOSTCMD - off, cmd);
    289 	/* store data */
    290 	bus_space_write_2(sc->sc_iot, sc->sc_ioh,
    291 	    AMDPM_8111_SMBUS_HOSTDATA - off, data);
    292 	/* host start */
    293 	bus_space_write_2(sc->sc_iot, sc->sc_ioh,
    294 	    AMDPM_8111_SMBUS_CTRL - off, AMDPM_8111_SMBUS_GSR_WB);
    295 
    296 	return (amdpm_smbus_check_done(sc, op));
    297 }
    298 
    299 static int
    300 amdpm_smbus_receive_1(struct amdpm_softc *sc, i2c_op_t op)
    301 {
    302 	u_int16_t data = 0;
    303 	int off = (sc->sc_nforce ? 0xe0 : 0);
    304 
    305         /* first clear gsr */
    306         amdpm_smbus_clear_gsr(sc);
    307 
    308 	/* write smbus slave address to register */
    309 	data = sc->sc_smbus_slaveaddr;
    310 	data <<= 1;
    311 	data |= AMDPM_8111_SMBUS_RX;
    312 	bus_space_write_1(sc->sc_iot, sc->sc_ioh,
    313 	    AMDPM_8111_SMBUS_HOSTADDR - off, data);
    314 
    315 	/* start smbus cycle */
    316 	bus_space_write_2(sc->sc_iot, sc->sc_ioh,
    317 	    AMDPM_8111_SMBUS_CTRL - off, AMDPM_8111_SMBUS_GSR_RXB);
    318 
    319 	/* check for errors */
    320 	if (amdpm_smbus_check_done(sc, op) < 0)
    321 	  return (-1);
    322 
    323 	/* read data */
    324 	data = bus_space_read_2(sc->sc_iot, sc->sc_ioh,
    325 	    AMDPM_8111_SMBUS_HOSTDATA - off);
    326 	u_int8_t ret = (u_int8_t)(data & 0x00FF);
    327 	return (ret);
    328 }
    329 
    330 static int
    331 amdpm_smbus_read_1(struct amdpm_softc *sc, u_int8_t cmd, i2c_op_t op)
    332 {
    333 	u_int16_t data = 0;
    334 	u_int8_t ret;
    335 	int off = (sc->sc_nforce ? 0xe0 : 0);
    336 
    337         /* first clear gsr */
    338         amdpm_smbus_clear_gsr(sc);
    339 
    340 	/* write smbus slave address to register */
    341 	data = sc->sc_smbus_slaveaddr;
    342 	data <<= 1;
    343 	data |= AMDPM_8111_SMBUS_READ;
    344 	bus_space_write_1(sc->sc_iot, sc->sc_ioh,
    345 	    AMDPM_8111_SMBUS_HOSTADDR - off, data);
    346 
    347 	/* store cmd */
    348 	bus_space_write_1(sc->sc_iot, sc->sc_ioh,
    349 	    AMDPM_8111_SMBUS_HOSTCMD - off, cmd);
    350 	/* host start */
    351 	bus_space_write_2(sc->sc_iot, sc->sc_ioh,
    352 	    AMDPM_8111_SMBUS_CTRL - off, AMDPM_8111_SMBUS_GSR_RB);
    353 
    354 	/* check for errors */
    355 	if (amdpm_smbus_check_done(sc, op) < 0)
    356 	  return (-1);
    357 
    358 	/* store data */
    359 	data = bus_space_read_2(sc->sc_iot, sc->sc_ioh,
    360 	    AMDPM_8111_SMBUS_HOSTDATA - off);
    361 	ret = (u_int8_t)(data & 0x00FF);
    362 	return (ret);
    363 }
    364