xref: /openbsd-src/sys/dev/pci/alipm.c (revision a28daedfc357b214be5c701aa8ba8adb29a7f1c2)
1 /*	$OpenBSD: alipm.c,v 1.14 2009/03/29 21:53:52 sthen Exp $	*/
2 
3 /*
4  * Copyright (c) 2005 Mark Kettenis
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 #include <sys/param.h>
20 #include <sys/device.h>
21 #include <sys/kernel.h>
22 #include <sys/rwlock.h>
23 #include <sys/proc.h>
24 #include <sys/systm.h>
25 
26 #include <dev/i2c/i2cvar.h>
27 
28 #include <dev/pci/pcidevs.h>
29 #include <dev/pci/pcireg.h>
30 #include <dev/pci/pcivar.h>
31 
32 #ifdef __sparc64__
33 #include <arch/sparc64/dev/ofwi2cvar.h>
34 #endif
35 
36 /*
37  * Acer Labs M7101 Power register definitions.
38  */
39 
40 /* PCI configuration registers. */
41 #define ALIPM_CONF	0xd0		/* general configuration */
42 #define ALIPM_CONF_SMBEN	0x0400		/* enable SMBus */
43 #define ALIPM_BASE	0xe0		/* ACPI and SMBus base address */
44 #define ALIPM_SMB_HOSTC	0xf0		/* host configuration */
45 #define ALIPM_SMB_HOSTC_HSTEN	0x00000001	/* enable host controller */
46 #define ALIPM_SMB_HOSTC_CLOCK	0x00e00000	/* clock speed */
47 #define ALIPM_SMB_HOSTC_149K	0x00000000	/* 149 KHz clock */
48 #define ALIPM_SMB_HOSTC_74K	0x00200000	/*  74 KHz clock */
49 #define ALIPM_SMB_HOSTC_37K	0x00400000	/*  37 KHz clock */
50 #define ALIPM_SMB_HOSTC_223K	0x00800000	/* 223 KHz clock */
51 #define ALIPM_SMB_HOSTC_111K	0x00a00000	/* 111 KHz clock */
52 #define ALIPM_SMB_HOSTC_55K	0x00c00000	/*  55 KHz clock */
53 
54 #define ALIPM_SMB_SIZE		32	/* SMBus I/O space size */
55 
56 /* SMBus I/O registers */
57 #define ALIPM_SMB_HS	0x00		/* host status */
58 #define ALIPM_SMB_HS_IDLE	0x04
59 #define ALIPM_SMB_HS_BUSY	0x08	/* running a command */
60 #define ALIPM_SMB_HS_DONE	0x10	/* command completed */
61 #define ALIPM_SMB_HS_DEVERR	0x20	/* command error */
62 #define ALIPM_SMB_HS_BUSERR	0x40	/* transaction collision */
63 #define ALIPM_SMB_HS_FAILED	0x80	/* failed bus transaction */
64 #define ALIPM_SMB_HS_BITS \
65   "\020\003IDLE\004BUSY\005DONE\006DEVERR\007BUSERR\010FAILED"
66 #define ALIPM_SMB_HC	0x01		/* host control */
67 #define ALIPM_SMB_HC_KILL	0x04		/* kill command */
68 #define ALIPM_SMB_HC_RESET	0x08		/* reset bus */
69 #define ALIPM_SMB_HC_CMD_QUICK	0x00		/* QUICK command */
70 #define ALIPM_SMB_HC_CMD_BYTE	0x10		/* BYTE command */
71 #define ALIPM_SMB_HC_CMD_BDATA	0x20		/* BYTE DATA command */
72 #define ALIPM_SMB_HC_CMD_WDATA	0x30		/* WORD DATA command */
73 #define ALIPM_SMB_HC_CMD_BLOCK 0x40		/* BLOCK command */
74 #define ALIPM_SMB_START		0x02	/* start command */
75 #define ALIPM_SMB_TXSLVA	0x03	/* transmit slave address */
76 #define ALIPM_SMB_TXSLVA_READ	(1 << 0)	/* read direction */
77 #define ALIPM_SMB_TXSLVA_ADDR(x) (((x) & 0x7f) << 1) /* 7-bit address */
78 #define ALIPM_SMB_HD0		0x04	/* host data 0 */
79 #define ALIPM_SMB_HD1		0x05	/* host data 1 */
80 #define ALIPM_SMB_HBDB		0x06	/* host block data byte */
81 #define ALIPM_SMB_HCMD		0x07	/* host command */
82 
83 /*
84  * Newer chips have a more standard, but different PCI configuration
85  * register layout.
86  */
87 
88 #define ALIPM_SMB_BASE	0x14		/* SMBus base address */
89 #define ALIPM_SMB_HOSTX	0xe0		/* host configuration */
90 
91 #ifdef ALIPM_DEBUG
92 #define DPRINTF(x) printf x
93 #else
94 #define DPRINTF(x)
95 #endif
96 
97 #define ALIPM_DELAY	100
98 #define ALIPM_TIMEOUT	1
99 
100 struct alipm_softc {
101 	struct device sc_dev;
102 
103 	bus_space_tag_t sc_iot;
104 	bus_space_handle_t sc_ioh;
105 
106 	struct i2c_controller sc_smb_tag;
107 	struct rwlock sc_smb_lock;
108 };
109 
110 int	alipm_match(struct device *, void *, void *);
111 void	alipm_attach(struct device *, struct device *, void *);
112 
113 int	alipm_smb_acquire_bus(void *, int);
114 void	alipm_smb_release_bus(void *, int);
115 int	alipm_smb_exec(void *, i2c_op_t, i2c_addr_t, const void *,
116 	    size_t, void *, size_t, int);
117 
118 struct cfattach alipm_ca = {
119 	sizeof(struct alipm_softc),
120 	alipm_match,
121 	alipm_attach
122 };
123 
124 struct cfdriver alipm_cd = {
125 	NULL, "alipm", DV_DULL
126 };
127 
128 int
129 alipm_match(struct device *parent, void *match, void *aux)
130 {
131 	struct pci_attach_args *pa = aux;
132 
133 	if (PCI_VENDOR(pa->pa_id) == PCI_VENDOR_ALI &&
134 	    (PCI_PRODUCT(pa->pa_id) == PCI_PRODUCT_ALI_M7101))
135 		return (1);
136 	return (0);
137 }
138 
139 void
140 alipm_attach(struct device *parent, struct device *self, void *aux)
141 {
142 	struct alipm_softc *sc = (struct alipm_softc *) self;
143 	struct pci_attach_args *pa = aux;
144 	struct i2cbus_attach_args iba;
145 	pcireg_t iobase, reg;
146 	bus_size_t iosize = ALIPM_SMB_SIZE;
147 
148 	/* Old chips don't have the PCI 2.2 Capabilities List. */
149 	reg = pci_conf_read(pa->pa_pc, pa->pa_tag, PCI_COMMAND_STATUS_REG);
150 	if ((reg & PCI_STATUS_CAPLIST_SUPPORT) == 0) {
151 		/* Map I/O space */
152 		iobase = pci_conf_read(pa->pa_pc, pa->pa_tag, ALIPM_BASE);
153 		sc->sc_iot = pa->pa_iot;
154 		if (iobase == 0 ||
155 		    bus_space_map(sc->sc_iot, iobase >> 16,
156 		    iosize, 0, &sc->sc_ioh)) {
157 			printf(": can't map i/o space\n");
158 			return;
159 		}
160 
161 		reg = pci_conf_read(pa->pa_pc, pa->pa_tag, ALIPM_CONF);
162 		if ((reg & ALIPM_CONF_SMBEN) == 0) {
163 			printf(": SMBus disabled\n");
164 			goto fail;
165 		}
166 
167 		reg = pci_conf_read(pa->pa_pc, pa->pa_tag, ALIPM_SMB_HOSTC);
168 		if ((reg & ALIPM_SMB_HOSTC_HSTEN) == 0) {
169 			printf(": SMBus host disabled\n");
170 			goto fail;
171 		}
172 	} else {
173 		/* Map I/O space */
174 		if (pci_mapreg_map(pa, ALIPM_SMB_BASE, PCI_MAPREG_TYPE_IO, 0,
175 		    &sc->sc_iot, &sc->sc_ioh, NULL, &iosize, ALIPM_SMB_SIZE)) {
176 			printf(": can't map i/o space\n");
177 			return;
178 		}
179 
180 		reg = pci_conf_read(pa->pa_pc, pa->pa_tag, ALIPM_SMB_HOSTX);
181 		if ((reg & ALIPM_SMB_HOSTC_HSTEN) == 0) {
182 			printf(": SMBus host disabled\n");
183 			goto fail;
184 		}
185 	}
186 
187 	switch (reg & ALIPM_SMB_HOSTC_CLOCK) {
188 	case ALIPM_SMB_HOSTC_149K:
189 		printf(": 149KHz clock");
190 		break;
191 	case ALIPM_SMB_HOSTC_74K:
192 		printf(": 74KHz clock");
193 		break;
194 	case ALIPM_SMB_HOSTC_37K:
195 		printf(": 37KHz clock");
196 		break;
197 	case ALIPM_SMB_HOSTC_223K:
198 		printf(": 223KHz clock");
199 		break;
200 	case ALIPM_SMB_HOSTC_111K:
201 		printf(": 111KHz clock");
202 		break;
203 	case ALIPM_SMB_HOSTC_55K:
204 		printf(": 55KHz clock");
205 		break;
206 	default:
207 		printf(" unknown clock speed");
208 		break;
209 	}
210 
211 	printf("\n");
212 
213 	/* Attach I2C bus */
214 	rw_init(&sc->sc_smb_lock, "alipm");
215 	sc->sc_smb_tag.ic_cookie = sc;
216 	sc->sc_smb_tag.ic_acquire_bus = alipm_smb_acquire_bus;
217 	sc->sc_smb_tag.ic_release_bus = alipm_smb_release_bus;
218 	sc->sc_smb_tag.ic_exec = alipm_smb_exec;
219 
220 	bzero(&iba, sizeof iba);
221 	iba.iba_name = "iic";
222 	iba.iba_tag = &sc->sc_smb_tag;
223 #ifdef __sparc64__
224 	iba.iba_bus_scan = ofwiic_pci_scan;
225 	iba.iba_bus_scan_arg = pa;
226 #endif
227 	config_found(&sc->sc_dev, &iba, iicbus_print);
228 
229 	return;
230 
231 fail:
232 	bus_space_unmap(sc->sc_iot, sc->sc_ioh, iosize);
233 }
234 
235 int
236 alipm_smb_acquire_bus(void *cookie, int flags)
237 {
238 	struct alipm_softc *sc = cookie;
239 
240 	if (flags & I2C_F_POLL)
241 		return (0);
242 
243 	return (rw_enter(&sc->sc_smb_lock, RW_WRITE | RW_INTR));
244 }
245 
246 void
247 alipm_smb_release_bus(void *cookie, int flags)
248 {
249 	struct alipm_softc *sc = cookie;
250 
251 	if (flags & I2C_F_POLL)
252 		return;
253 
254 	rw_exit(&sc->sc_smb_lock);
255 }
256 
257 int
258 alipm_smb_exec(void *cookie, i2c_op_t op, i2c_addr_t addr,
259     const void *cmdbuf, size_t cmdlen, void *buf, size_t len, int flags)
260 {
261 	struct alipm_softc *sc = cookie;
262 	u_int8_t *b;
263 	u_int8_t ctl, st;
264 	int retries, error = 0;
265 
266 	DPRINTF(("%s: exec op %d, addr 0x%x, cmdlen %d, len %d, "
267 	    "flags 0x%x\n", sc->sc_dev.dv_xname, op, addr, cmdlen,
268 	    len, flags));
269 
270 	if (!I2C_OP_STOP_P(op) || cmdlen > 1 || len > 2)
271 		return (EOPNOTSUPP);
272 
273 	/* Clear status bits */
274 	bus_space_write_1(sc->sc_iot, sc->sc_ioh, ALIPM_SMB_HS,
275 	    ALIPM_SMB_HS_DONE | ALIPM_SMB_HS_FAILED |
276 	    ALIPM_SMB_HS_BUSERR | ALIPM_SMB_HS_DEVERR);
277 	bus_space_barrier(sc->sc_iot, sc->sc_ioh, ALIPM_SMB_HS, 1,
278 	    BUS_SPACE_BARRIER_READ | BUS_SPACE_BARRIER_WRITE);
279 
280 	/* Wait until bus is idle */
281 	for (retries = 1000; retries > 0; retries--) {
282 		st = bus_space_read_1(sc->sc_iot, sc->sc_ioh, ALIPM_SMB_HS);
283 		bus_space_barrier(sc->sc_iot, sc->sc_ioh, ALIPM_SMB_HS, 1,
284 		    BUS_SPACE_BARRIER_READ);
285 		if (st & (ALIPM_SMB_HS_IDLE | ALIPM_SMB_HS_FAILED |
286 		    ALIPM_SMB_HS_BUSERR | ALIPM_SMB_HS_DEVERR))
287 			break;
288 		DELAY(ALIPM_DELAY);
289 	}
290 	if (retries == 0) {
291 		printf("%s: timeout st 0x%b\n", sc->sc_dev.dv_xname,
292 		    st, ALIPM_SMB_HS_BITS);
293 		return (ETIMEDOUT);
294 	}
295 	if (st & (ALIPM_SMB_HS_FAILED |
296 	    ALIPM_SMB_HS_BUSERR | ALIPM_SMB_HS_DEVERR)) {
297 		printf("%s: error st 0x%b\n", sc->sc_dev.dv_xname,
298 		    st, ALIPM_SMB_HS_BITS);
299 		return (EIO);
300 	}
301 
302 	/* Set slave address and transfer direction. */
303 	bus_space_write_1(sc->sc_iot, sc->sc_ioh, ALIPM_SMB_TXSLVA,
304 	    ALIPM_SMB_TXSLVA_ADDR(addr) |
305 	    (I2C_OP_READ_P(op) ? ALIPM_SMB_TXSLVA_READ : 0));
306 
307 	b = (void *)cmdbuf;
308 	if (cmdlen > 0)
309 		/* Set command byte */
310 		bus_space_write_1(sc->sc_iot, sc->sc_ioh,
311 		     ALIPM_SMB_HCMD, b[0]);
312 
313 	if (I2C_OP_WRITE_P(op)) {
314 		/* Write data. */
315 		b = buf;
316 		if (len > 0)
317 			bus_space_write_1(sc->sc_iot, sc->sc_ioh,
318 			    ALIPM_SMB_HD0, b[0]);
319 		if (len > 1)
320 			bus_space_write_1(sc->sc_iot, sc->sc_ioh,
321 			    ALIPM_SMB_HD1, b[1]);
322 	}
323 
324 	/* Set SMBus command */
325 	if (len == 0)
326 		ctl = ALIPM_SMB_HC_CMD_BYTE;
327 	else if (len == 1)
328 		ctl = ALIPM_SMB_HC_CMD_BDATA;
329 	else if (len == 2)
330 		ctl = ALIPM_SMB_HC_CMD_WDATA;
331 	bus_space_write_1(sc->sc_iot, sc->sc_ioh, ALIPM_SMB_HC, ctl);
332 
333 	/* Start transaction */
334 	bus_space_barrier(sc->sc_iot, sc->sc_ioh, 0, ALIPM_SMB_SIZE,
335 	    BUS_SPACE_BARRIER_WRITE);
336 	bus_space_write_1(sc->sc_iot, sc->sc_ioh, ALIPM_SMB_START, 0xff);
337 	bus_space_barrier(sc->sc_iot, sc->sc_ioh, 0, ALIPM_SMB_SIZE,
338 	    BUS_SPACE_BARRIER_READ | BUS_SPACE_BARRIER_WRITE);
339 
340 	/* Poll for completion */
341 	DELAY(ALIPM_DELAY);
342 	for (retries = 1000; retries > 0; retries--) {
343 		st = bus_space_read_1(sc->sc_iot, sc->sc_ioh, ALIPM_SMB_HS);
344 		bus_space_barrier(sc->sc_iot, sc->sc_ioh, ALIPM_SMB_HS, 1,
345 		    BUS_SPACE_BARRIER_READ);
346 		if (st & (ALIPM_SMB_HS_IDLE | ALIPM_SMB_HS_FAILED |
347 		    ALIPM_SMB_HS_BUSERR | ALIPM_SMB_HS_DEVERR))
348 			break;
349 		DELAY(ALIPM_DELAY);
350 	}
351 	if (retries == 0) {
352 		printf("%s: timeout st 0x%b, resetting\n",
353 		    sc->sc_dev.dv_xname, st, ALIPM_SMB_HS_BITS);
354 		bus_space_write_1(sc->sc_iot, sc->sc_ioh, ALIPM_SMB_HC,
355 		    ALIPM_SMB_HC_RESET);
356 		bus_space_barrier(sc->sc_iot, sc->sc_ioh, 0, ALIPM_SMB_SIZE,
357 		     BUS_SPACE_BARRIER_READ | BUS_SPACE_BARRIER_WRITE);
358 		st = bus_space_read_1(sc->sc_iot, sc->sc_ioh, ALIPM_SMB_HS);
359 		bus_space_barrier(sc->sc_iot, sc->sc_ioh, ALIPM_SMB_HS, 1,
360 		    BUS_SPACE_BARRIER_READ);
361 		error = ETIMEDOUT;
362 		goto done;
363 	}
364 
365 	if ((st & ALIPM_SMB_HS_DONE) == 0) {
366 		bus_space_write_1(sc->sc_iot, sc->sc_ioh, ALIPM_SMB_HC,
367 		     ALIPM_SMB_HC_KILL);
368 		bus_space_barrier(sc->sc_iot, sc->sc_ioh, 0, ALIPM_SMB_SIZE,
369 		     BUS_SPACE_BARRIER_READ | BUS_SPACE_BARRIER_WRITE);
370 		st = bus_space_read_1(sc->sc_iot, sc->sc_ioh, ALIPM_SMB_HS);
371 		bus_space_barrier(sc->sc_iot, sc->sc_ioh, ALIPM_SMB_HS, 1,
372 		    BUS_SPACE_BARRIER_READ);
373 		if ((st & ALIPM_SMB_HS_FAILED) == 0)
374 			printf("%s: error st 0x%b\n", sc->sc_dev.dv_xname,
375 			    st, ALIPM_SMB_HS_BITS);
376 	}
377 
378 	/* Check for errors */
379 	if (st & (ALIPM_SMB_HS_FAILED |
380 	    ALIPM_SMB_HS_BUSERR | ALIPM_SMB_HS_DEVERR)) {
381 		error = EIO;
382 		goto done;
383 	}
384 
385 	if (I2C_OP_READ_P(op)) {
386 		/* Read data */
387 		b = buf;
388 		if (len > 0) {
389 			b[0] = bus_space_read_1(sc->sc_iot, sc->sc_ioh,
390 			    ALIPM_SMB_HD0);
391 			bus_space_barrier(sc->sc_iot, sc->sc_ioh,
392 			    ALIPM_SMB_HD0, 1, BUS_SPACE_BARRIER_READ);
393 		}
394 		if (len > 1) {
395 			b[1] = bus_space_read_1(sc->sc_iot, sc->sc_ioh,
396 			    ALIPM_SMB_HD1);
397 			bus_space_barrier(sc->sc_iot, sc->sc_ioh,
398 			    ALIPM_SMB_HD1, 1, BUS_SPACE_BARRIER_READ);
399 		}
400 	}
401 
402 done:
403 	/* Clear status bits */
404 	bus_space_write_1(sc->sc_iot, sc->sc_ioh, ALIPM_SMB_HS, st);
405 
406 	return (error);
407 }
408