Line data Source code
1 : /* $OpenBSD: piixpm.c,v 1.39 2013/10/01 20:06:02 sf Exp $ */
2 :
3 : /*
4 : * Copyright (c) 2005, 2006 Alexander Yurchenko <grange@openbsd.org>
5 : *
6 : * Permission to use, copy, modify, and distribute this software for any
7 : * purpose with or without fee is hereby granted, provided that the above
8 : * copyright notice and this permission notice appear in all copies.
9 : *
10 : * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11 : * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12 : * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13 : * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14 : * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15 : * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16 : * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17 : */
18 :
19 : /*
20 : * Intel PIIX and compatible Power Management controller driver.
21 : */
22 :
23 : #include <sys/param.h>
24 : #include <sys/systm.h>
25 : #include <sys/device.h>
26 : #include <sys/kernel.h>
27 : #include <sys/rwlock.h>
28 :
29 : #include <machine/bus.h>
30 :
31 : #include <dev/pci/pcidevs.h>
32 : #include <dev/pci/pcireg.h>
33 : #include <dev/pci/pcivar.h>
34 :
35 : #include <dev/pci/piixreg.h>
36 :
37 : #include <dev/i2c/i2cvar.h>
38 :
39 : #ifdef PIIXPM_DEBUG
40 : #define DPRINTF(x) printf x
41 : #else
42 : #define DPRINTF(x)
43 : #endif
44 :
45 : #define PIIXPM_DELAY 200
46 : #define PIIXPM_TIMEOUT 1
47 :
48 : struct piixpm_softc {
49 : struct device sc_dev;
50 :
51 : bus_space_tag_t sc_iot;
52 : bus_space_handle_t sc_ioh;
53 : void * sc_ih;
54 : int sc_poll;
55 :
56 : struct i2c_controller sc_i2c_tag;
57 : struct rwlock sc_i2c_lock;
58 : struct {
59 : i2c_op_t op;
60 : void * buf;
61 : size_t len;
62 : int flags;
63 : volatile int error;
64 : } sc_i2c_xfer;
65 : };
66 :
67 : int piixpm_match(struct device *, void *, void *);
68 : void piixpm_attach(struct device *, struct device *, void *);
69 :
70 : int piixpm_i2c_acquire_bus(void *, int);
71 : void piixpm_i2c_release_bus(void *, int);
72 : int piixpm_i2c_exec(void *, i2c_op_t, i2c_addr_t, const void *, size_t,
73 : void *, size_t, int);
74 :
75 : int piixpm_intr(void *);
76 :
77 : struct cfattach piixpm_ca = {
78 : sizeof(struct piixpm_softc),
79 : piixpm_match,
80 : piixpm_attach
81 : };
82 :
83 : struct cfdriver piixpm_cd = {
84 : NULL, "piixpm", DV_DULL
85 : };
86 :
87 : const struct pci_matchid piixpm_ids[] = {
88 : { PCI_VENDOR_AMD, PCI_PRODUCT_AMD_HUDSON2_SMB },
89 :
90 : { PCI_VENDOR_ATI, PCI_PRODUCT_ATI_SB200_SMB },
91 : { PCI_VENDOR_ATI, PCI_PRODUCT_ATI_SB300_SMB },
92 : { PCI_VENDOR_ATI, PCI_PRODUCT_ATI_SB400_SMB },
93 : { PCI_VENDOR_ATI, PCI_PRODUCT_ATI_SBX00_SMB },
94 :
95 : { PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_82371AB_PM },
96 : { PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_82440MX_PM },
97 :
98 : { PCI_VENDOR_RCC, PCI_PRODUCT_RCC_CSB5 },
99 : { PCI_VENDOR_RCC, PCI_PRODUCT_RCC_CSB6 },
100 : { PCI_VENDOR_RCC, PCI_PRODUCT_RCC_HT_1000 },
101 : { PCI_VENDOR_RCC, PCI_PRODUCT_RCC_HT_1100 },
102 : { PCI_VENDOR_RCC, PCI_PRODUCT_RCC_OSB4 },
103 :
104 : { PCI_VENDOR_SMSC, PCI_PRODUCT_SMSC_VICTORY66_PM }
105 : };
106 :
107 : int
108 0 : piixpm_match(struct device *parent, void *match, void *aux)
109 : {
110 0 : return (pci_matchbyid(aux, piixpm_ids,
111 : sizeof(piixpm_ids) / sizeof(piixpm_ids[0])));
112 : }
113 :
114 : void
115 0 : piixpm_attach(struct device *parent, struct device *self, void *aux)
116 : {
117 0 : struct piixpm_softc *sc = (struct piixpm_softc *)self;
118 0 : struct pci_attach_args *pa = aux;
119 0 : bus_space_handle_t ioh;
120 : u_int16_t smb0en;
121 : bus_addr_t base;
122 : pcireg_t conf;
123 0 : pci_intr_handle_t ih;
124 : const char *intrstr = NULL;
125 0 : struct i2cbus_attach_args iba;
126 :
127 0 : sc->sc_iot = pa->pa_iot;
128 :
129 0 : if ((PCI_VENDOR(pa->pa_id) == PCI_VENDOR_AMD &&
130 0 : PCI_PRODUCT(pa->pa_id) == PCI_PRODUCT_AMD_HUDSON2_SMB) ||
131 0 : (PCI_VENDOR(pa->pa_id) == PCI_VENDOR_ATI &&
132 0 : PCI_PRODUCT(pa->pa_id) == PCI_PRODUCT_ATI_SBX00_SMB &&
133 0 : PCI_REVISION(pa->pa_class) >= 0x40)) {
134 : /*
135 : * On the AMD SB800+, the SMBus I/O registers are well
136 : * hidden. We need to look at the "SMBus0En" Power
137 : * Management register to find out where they live.
138 : * We use indirect IO access through the index/data
139 : * pair at 0xcd6/0xcd7 to access "SMBus0En". Since
140 : * the index/data pair may be needed by other drivers,
141 : * we only map them for the duration that we actually
142 : * need them.
143 : */
144 0 : if (bus_space_map(sc->sc_iot, SB800_PMREG_BASE,
145 0 : SB800_PMREG_SIZE, 0, &ioh) != 0) {
146 0 : printf(": can't map i/o space\n");
147 0 : return;
148 : }
149 :
150 : /* Read "SmBus0En" */
151 0 : bus_space_write_1(sc->sc_iot, ioh, 0, SB800_PMREG_SMB0EN);
152 0 : smb0en = bus_space_read_1(sc->sc_iot, ioh, 1);
153 0 : bus_space_write_1(sc->sc_iot, ioh, 0, SB800_PMREG_SMB0EN + 1);
154 0 : smb0en |= (bus_space_read_1(sc->sc_iot, ioh, 1) << 8);
155 :
156 0 : bus_space_unmap(sc->sc_iot, ioh, SB800_PMREG_SIZE);
157 :
158 0 : if ((smb0en & SB800_SMB0EN_EN) == 0) {
159 0 : printf(": SMBus disabled\n");
160 0 : return;
161 : }
162 :
163 : /* Map I/O space */
164 0 : base = smb0en & SB800_SMB0EN_BASE_MASK;
165 0 : if (base == 0 || bus_space_map(sc->sc_iot, base,
166 0 : SB800_SMB_SIZE, 0, &sc->sc_ioh)) {
167 0 : printf(": can't map i/o space");
168 0 : return;
169 : }
170 :
171 : /* Read configuration */
172 0 : conf = bus_space_read_1(sc->sc_iot, sc->sc_ioh, SB800_SMB_HOSTC);
173 0 : if (conf & SB800_SMB_HOSTC_SMI)
174 0 : conf = PIIX_SMB_HOSTC_SMI;
175 : else
176 : conf = PIIX_SMB_HOSTC_IRQ;
177 : } else {
178 : /* Read configuration */
179 0 : conf = pci_conf_read(pa->pa_pc, pa->pa_tag, PIIX_SMB_HOSTC);
180 : DPRINTF((": conf 0x%08x", conf));
181 :
182 0 : if ((conf & PIIX_SMB_HOSTC_HSTEN) == 0) {
183 0 : printf(": SMBus disabled\n");
184 0 : return;
185 : }
186 :
187 : /* Map I/O space */
188 0 : base = pci_conf_read(pa->pa_pc, pa->pa_tag, PIIX_SMB_BASE) &
189 : PIIX_SMB_BASE_MASK;
190 0 : if (base == 0 || bus_space_map(sc->sc_iot, base,
191 0 : PIIX_SMB_SIZE, 0, &sc->sc_ioh)) {
192 0 : printf(": can't map i/o space\n");
193 0 : return;
194 : }
195 : }
196 :
197 0 : sc->sc_poll = 1;
198 0 : if ((conf & PIIX_SMB_HOSTC_INTMASK) == PIIX_SMB_HOSTC_SMI) {
199 : /* No PCI IRQ */
200 0 : printf(": SMI");
201 0 : } else {
202 0 : if ((conf & PIIX_SMB_HOSTC_INTMASK) == PIIX_SMB_HOSTC_IRQ) {
203 : /* Install interrupt handler */
204 0 : if (pci_intr_map(pa, &ih) == 0) {
205 0 : intrstr = pci_intr_string(pa->pa_pc, ih);
206 0 : sc->sc_ih = pci_intr_establish(pa->pa_pc,
207 0 : ih, IPL_BIO, piixpm_intr, sc,
208 0 : sc->sc_dev.dv_xname);
209 0 : if (sc->sc_ih != NULL) {
210 0 : printf(": %s", intrstr);
211 0 : sc->sc_poll = 0;
212 0 : }
213 : }
214 : }
215 0 : if (sc->sc_poll)
216 0 : printf(": polling");
217 : }
218 :
219 0 : printf("\n");
220 :
221 : /* Attach I2C bus */
222 0 : rw_init(&sc->sc_i2c_lock, "iiclk");
223 0 : sc->sc_i2c_tag.ic_cookie = sc;
224 0 : sc->sc_i2c_tag.ic_acquire_bus = piixpm_i2c_acquire_bus;
225 0 : sc->sc_i2c_tag.ic_release_bus = piixpm_i2c_release_bus;
226 0 : sc->sc_i2c_tag.ic_exec = piixpm_i2c_exec;
227 :
228 0 : bzero(&iba, sizeof(iba));
229 0 : iba.iba_name = "iic";
230 0 : iba.iba_tag = &sc->sc_i2c_tag;
231 0 : config_found(self, &iba, iicbus_print);
232 :
233 0 : return;
234 0 : }
235 :
236 : int
237 0 : piixpm_i2c_acquire_bus(void *cookie, int flags)
238 : {
239 0 : struct piixpm_softc *sc = cookie;
240 :
241 0 : if (cold || sc->sc_poll || (flags & I2C_F_POLL))
242 0 : return (0);
243 :
244 0 : return (rw_enter(&sc->sc_i2c_lock, RW_WRITE | RW_INTR));
245 0 : }
246 :
247 : void
248 0 : piixpm_i2c_release_bus(void *cookie, int flags)
249 : {
250 0 : struct piixpm_softc *sc = cookie;
251 :
252 0 : if (cold || sc->sc_poll || (flags & I2C_F_POLL))
253 0 : return;
254 :
255 0 : rw_exit(&sc->sc_i2c_lock);
256 0 : }
257 :
258 : int
259 0 : piixpm_i2c_exec(void *cookie, i2c_op_t op, i2c_addr_t addr,
260 : const void *cmdbuf, size_t cmdlen, void *buf, size_t len, int flags)
261 : {
262 0 : struct piixpm_softc *sc = cookie;
263 : u_int8_t *b;
264 : u_int8_t ctl, st;
265 : int retries;
266 :
267 : DPRINTF(("%s: exec: op %d, addr 0x%02x, cmdlen %d, len %d, "
268 : "flags 0x%02x\n", sc->sc_dev.dv_xname, op, addr, cmdlen,
269 : len, flags));
270 :
271 : /* Wait for bus to be idle */
272 0 : for (retries = 100; retries > 0; retries--) {
273 0 : st = bus_space_read_1(sc->sc_iot, sc->sc_ioh, PIIX_SMB_HS);
274 0 : if (!(st & PIIX_SMB_HS_BUSY))
275 : break;
276 0 : DELAY(PIIXPM_DELAY);
277 : }
278 : DPRINTF(("%s: exec: st 0x%b\n", sc->sc_dev.dv_xname, st,
279 : PIIX_SMB_HS_BITS));
280 0 : if (st & PIIX_SMB_HS_BUSY)
281 0 : return (1);
282 :
283 0 : if (cold || sc->sc_poll)
284 0 : flags |= I2C_F_POLL;
285 :
286 0 : if (!I2C_OP_STOP_P(op) || cmdlen > 1 || len > 2)
287 0 : return (1);
288 :
289 : /* Setup transfer */
290 0 : sc->sc_i2c_xfer.op = op;
291 0 : sc->sc_i2c_xfer.buf = buf;
292 0 : sc->sc_i2c_xfer.len = len;
293 0 : sc->sc_i2c_xfer.flags = flags;
294 0 : sc->sc_i2c_xfer.error = 0;
295 :
296 : /* Set slave address and transfer direction */
297 0 : bus_space_write_1(sc->sc_iot, sc->sc_ioh, PIIX_SMB_TXSLVA,
298 : PIIX_SMB_TXSLVA_ADDR(addr) |
299 : (I2C_OP_READ_P(op) ? PIIX_SMB_TXSLVA_READ : 0));
300 :
301 : b = (void *)cmdbuf;
302 0 : if (cmdlen > 0)
303 : /* Set command byte */
304 0 : bus_space_write_1(sc->sc_iot, sc->sc_ioh, PIIX_SMB_HCMD, b[0]);
305 :
306 0 : if (I2C_OP_WRITE_P(op)) {
307 : /* Write data */
308 : b = buf;
309 0 : if (len > 0)
310 0 : bus_space_write_1(sc->sc_iot, sc->sc_ioh,
311 : PIIX_SMB_HD0, b[0]);
312 0 : if (len > 1)
313 0 : bus_space_write_1(sc->sc_iot, sc->sc_ioh,
314 : PIIX_SMB_HD1, b[1]);
315 : }
316 :
317 : /* Set SMBus command */
318 0 : if (len == 0)
319 0 : ctl = PIIX_SMB_HC_CMD_BYTE;
320 0 : else if (len == 1)
321 0 : ctl = PIIX_SMB_HC_CMD_BDATA;
322 0 : else if (len == 2)
323 : ctl = PIIX_SMB_HC_CMD_WDATA;
324 : else
325 0 : panic("%s: unexpected len %zd", __func__, len);
326 :
327 0 : if ((flags & I2C_F_POLL) == 0)
328 0 : ctl |= PIIX_SMB_HC_INTREN;
329 :
330 : /* Start transaction */
331 0 : ctl |= PIIX_SMB_HC_START;
332 0 : bus_space_write_1(sc->sc_iot, sc->sc_ioh, PIIX_SMB_HC, ctl);
333 :
334 0 : if (flags & I2C_F_POLL) {
335 : /* Poll for completion */
336 0 : DELAY(PIIXPM_DELAY);
337 0 : for (retries = 1000; retries > 0; retries--) {
338 0 : st = bus_space_read_1(sc->sc_iot, sc->sc_ioh,
339 : PIIX_SMB_HS);
340 0 : if ((st & PIIX_SMB_HS_BUSY) == 0)
341 : break;
342 0 : DELAY(PIIXPM_DELAY);
343 : }
344 0 : if (st & PIIX_SMB_HS_BUSY)
345 : goto timeout;
346 0 : piixpm_intr(sc);
347 0 : } else {
348 : /* Wait for interrupt */
349 0 : if (tsleep(sc, PRIBIO, "piixpm", PIIXPM_TIMEOUT * hz))
350 : goto timeout;
351 : }
352 :
353 0 : if (sc->sc_i2c_xfer.error)
354 0 : return (1);
355 :
356 0 : return (0);
357 :
358 : timeout:
359 : /*
360 : * Transfer timeout. Kill the transaction and clear status bits.
361 : */
362 0 : printf("%s: exec: op %d, addr 0x%02x, cmdlen %zu, len %zu, "
363 : "flags 0x%02x: timeout, status 0x%b\n",
364 0 : sc->sc_dev.dv_xname, op, addr, cmdlen, len, flags,
365 0 : st, PIIX_SMB_HS_BITS);
366 0 : bus_space_write_1(sc->sc_iot, sc->sc_ioh, PIIX_SMB_HC,
367 : PIIX_SMB_HC_KILL);
368 0 : DELAY(PIIXPM_DELAY);
369 0 : st = bus_space_read_1(sc->sc_iot, sc->sc_ioh, PIIX_SMB_HS);
370 0 : if ((st & PIIX_SMB_HS_FAILED) == 0)
371 0 : printf("%s: abort failed, status 0x%b\n",
372 : sc->sc_dev.dv_xname, st, PIIX_SMB_HS_BITS);
373 0 : bus_space_write_1(sc->sc_iot, sc->sc_ioh, PIIX_SMB_HS, st);
374 0 : return (1);
375 0 : }
376 :
377 : int
378 0 : piixpm_intr(void *arg)
379 : {
380 0 : struct piixpm_softc *sc = arg;
381 : u_int8_t st;
382 : u_int8_t *b;
383 : size_t len;
384 :
385 : /* Read status */
386 0 : st = bus_space_read_1(sc->sc_iot, sc->sc_ioh, PIIX_SMB_HS);
387 0 : if ((st & PIIX_SMB_HS_BUSY) != 0 || (st & (PIIX_SMB_HS_INTR |
388 : PIIX_SMB_HS_DEVERR | PIIX_SMB_HS_BUSERR |
389 0 : PIIX_SMB_HS_FAILED)) == 0)
390 : /* Interrupt was not for us */
391 0 : return (0);
392 :
393 : DPRINTF(("%s: intr st 0x%b\n", sc->sc_dev.dv_xname, st,
394 : PIIX_SMB_HS_BITS));
395 :
396 : /* Clear status bits */
397 0 : bus_space_write_1(sc->sc_iot, sc->sc_ioh, PIIX_SMB_HS, st);
398 :
399 : /* Check for errors */
400 0 : if (st & (PIIX_SMB_HS_DEVERR | PIIX_SMB_HS_BUSERR |
401 : PIIX_SMB_HS_FAILED)) {
402 0 : sc->sc_i2c_xfer.error = 1;
403 0 : goto done;
404 : }
405 :
406 0 : if (st & PIIX_SMB_HS_INTR) {
407 0 : if (I2C_OP_WRITE_P(sc->sc_i2c_xfer.op))
408 : goto done;
409 :
410 : /* Read data */
411 0 : b = sc->sc_i2c_xfer.buf;
412 0 : len = sc->sc_i2c_xfer.len;
413 0 : if (len > 0)
414 0 : b[0] = bus_space_read_1(sc->sc_iot, sc->sc_ioh,
415 : PIIX_SMB_HD0);
416 0 : if (len > 1)
417 0 : b[1] = bus_space_read_1(sc->sc_iot, sc->sc_ioh,
418 : PIIX_SMB_HD1);
419 : }
420 :
421 : done:
422 0 : if ((sc->sc_i2c_xfer.flags & I2C_F_POLL) == 0)
423 0 : wakeup(sc);
424 0 : return (1);
425 0 : }
|