xref: /netbsd-src/sys/dev/pci/amdpm_smbus.c (revision d48f14661dda8638fee055ba15d35bdfb29b9fa8)
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