xref: /netbsd-src/sys/dev/pci/amdpm_smbus.c (revision eb961d0e02b7a46a9acfa877b02df48df6637278)
1 /*	$NetBSD: amdpm_smbus.c,v 1.2 2006/02/19 08:49:45 xtraeme 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.2 2006/02/19 08:49:45 xtraeme 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 	iba.iba_name = "iic";
85 	iba.iba_tag = &sc->sc_i2c;
86 	(void) config_found(&sc->sc_dev, &iba, iicbus_print);
87 }
88 
89 static int
90 amdpm_smbus_acquire_bus(void *cookie, int flags)
91 {
92 	return (0);
93 }
94 
95 static void
96 amdpm_smbus_release_bus(void *cookie, int flags)
97 {
98 }
99 
100 static int
101 amdpm_smbus_exec(void *cookie, i2c_op_t op, i2c_addr_t addr, const void *cmd,
102     size_t cmdlen, void *vbuf, size_t buflen, int flags)
103 {
104         struct amdpm_softc *sc  = (struct amdpm_softc *) cookie;
105 	sc->sc_smbus_slaveaddr  = addr;
106 
107 	if (I2C_OP_READ_P(op) && (cmdlen == 0) && (buflen == 1)) {
108 	  return (amdpm_smbus_receive_1(sc));
109 	}
110 
111 	if ( (I2C_OP_READ_P(op)) && (cmdlen == 1) && (buflen == 1)) {
112 	  return (amdpm_smbus_read_1(sc, *(const uint8_t*)cmd));
113 	}
114 
115 	if ( (I2C_OP_WRITE_P(op)) && (cmdlen == 0) && (buflen == 1)) {
116 	  return (amdpm_smbus_send_1(sc, *(uint8_t*)vbuf));
117 	}
118 
119 	if ( (I2C_OP_WRITE_P(op)) && (cmdlen == 1) && (buflen == 1)) {
120 	  return (amdpm_smbus_write_1(sc,  *(const uint8_t*)cmd, *(uint8_t*)vbuf));
121 	}
122 
123 	return (-1);
124 }
125 
126 static int
127 amdpm_smbus_check_done(struct amdpm_softc *sc)
128 {
129         int i = 0;
130 	for (i = 0; i < 1000; i++) {
131 	  /* check gsr and wait till cycle is done */
132 	  u_int16_t data = amdpm_smbus_get_gsr(sc);
133 	  if (data & AMDPM_8111_GSR_CYCLE_DONE) {
134 	    return (0);
135 	  }
136 	  delay(1);
137 	}
138 	return (-1);
139 }
140 
141 
142 static void
143 amdpm_smbus_clear_gsr(struct amdpm_softc *sc)
144 {
145         /* clear register */
146         u_int16_t data = 0xFFFF;
147 	bus_space_write_2(sc->sc_iot, sc->sc_ioh, AMDPM_8111_SMBUS_STAT, data);
148 }
149 
150 static u_int16_t
151 amdpm_smbus_get_gsr(struct amdpm_softc *sc)
152 {
153         return (bus_space_read_2(sc->sc_iot, sc->sc_ioh, AMDPM_8111_SMBUS_STAT));
154 }
155 
156 static int
157 amdpm_smbus_send_1(struct amdpm_softc *sc,  u_int8_t val)
158 {
159         /* first clear gsr */
160         amdpm_smbus_clear_gsr(sc);
161 
162 	/* write smbus slave address to register */
163 	u_int16_t data = 0;
164 	data = sc->sc_smbus_slaveaddr;
165 	data <<= 1;
166 	data |= AMDPM_8111_SMBUS_SEND;
167 	bus_space_write_1(sc->sc_iot, sc->sc_ioh, AMDPM_8111_SMBUS_HOSTADDR, data);
168 
169 	data = val;
170 	/* store data */
171 	bus_space_write_2(sc->sc_iot, sc->sc_ioh, AMDPM_8111_SMBUS_HOSTDATA, data);
172 	/* host start */
173 	bus_space_write_2(sc->sc_iot, sc->sc_ioh, AMDPM_8111_SMBUS_CTRL,
174 			  AMDPM_8111_SMBUS_GSR_SB);
175 	return(amdpm_smbus_check_done(sc));
176 }
177 
178 
179 static int
180 amdpm_smbus_write_1(struct amdpm_softc *sc, u_int8_t cmd, u_int8_t val)
181 {
182         /* first clear gsr */
183         amdpm_smbus_clear_gsr(sc);
184 
185 	u_int16_t data = 0;
186 	data = sc->sc_smbus_slaveaddr;
187 	data <<= 1;
188 	data |= AMDPM_8111_SMBUS_WRITE;
189 	bus_space_write_1(sc->sc_iot, sc->sc_ioh, AMDPM_8111_SMBUS_HOSTADDR, data);
190 
191 	data = val;
192 	/* store cmd */
193 	bus_space_write_1(sc->sc_iot, sc->sc_ioh, AMDPM_8111_SMBUS_HOSTCMD, cmd);
194 	/* store data */
195 	bus_space_write_2(sc->sc_iot, sc->sc_ioh, AMDPM_8111_SMBUS_HOSTDATA, data);
196 	/* host start */
197 	bus_space_write_2(sc->sc_iot, sc->sc_ioh, AMDPM_8111_SMBUS_CTRL, AMDPM_8111_SMBUS_GSR_WB);
198 
199 	return (amdpm_smbus_check_done(sc));
200 }
201 
202 static int
203 amdpm_smbus_receive_1(struct amdpm_softc *sc)
204 {
205         /* first clear gsr */
206         amdpm_smbus_clear_gsr(sc);
207 
208 	/* write smbus slave address to register */
209 	u_int16_t data = 0;
210 	data = sc->sc_smbus_slaveaddr;
211 	data <<= 1;
212 	data |= AMDPM_8111_SMBUS_RX;
213 	bus_space_write_1(sc->sc_iot, sc->sc_ioh, AMDPM_8111_SMBUS_HOSTADDR, data);
214 
215 	/* start smbus cycle */
216 	bus_space_write_2(sc->sc_iot, sc->sc_ioh, AMDPM_8111_SMBUS_CTRL, AMDPM_8111_SMBUS_GSR_RXB);
217 
218 	/* check for errors */
219 	if (amdpm_smbus_check_done(sc) < 0)
220 	  return (-1);
221 
222 	/* read data */
223 	data = bus_space_read_2(sc->sc_iot, sc->sc_ioh, AMDPM_8111_SMBUS_HOSTDATA);
224 	u_int8_t ret = (u_int8_t)(data & 0x00FF);
225 	return (ret);
226 }
227 
228 static int
229 amdpm_smbus_read_1(struct amdpm_softc *sc, u_int8_t cmd)
230 {
231         /* first clear gsr */
232         amdpm_smbus_clear_gsr(sc);
233 
234 	/* write smbus slave address to register */
235 	u_int16_t data = 0;
236 	data = sc->sc_smbus_slaveaddr;
237 	data <<= 1;
238 	data |= AMDPM_8111_SMBUS_READ;
239 	bus_space_write_1(sc->sc_iot, sc->sc_ioh, AMDPM_8111_SMBUS_HOSTADDR, data);
240 
241 	/* store cmd */
242 	bus_space_write_1(sc->sc_iot, sc->sc_ioh, AMDPM_8111_SMBUS_HOSTCMD, cmd);
243 	/* host start */
244 	bus_space_write_2(sc->sc_iot, sc->sc_ioh, AMDPM_8111_SMBUS_CTRL, AMDPM_8111_SMBUS_GSR_RB);
245 
246 	/* check for errors */
247 	if (amdpm_smbus_check_done(sc) < 0)
248 	  return (-1);
249 
250 	/* store data */
251 	data = bus_space_read_2(sc->sc_iot, sc->sc_ioh, AMDPM_8111_SMBUS_HOSTDATA);
252 	u_int8_t ret = (u_int8_t)(data & 0x00FF);
253 	return (ret);
254 }
255