1/*	$NetBSD: alipm.c,v 1.7 2010/03/11 04:00:36 mrg Exp $ */
2/*	$OpenBSD: alipm.c,v 1.13 2007/05/03 12:19:01 dlg Exp $	*/
3
4/*
5 * Copyright (c) 2005 Mark Kettenis
6 *
7 * Permission to use, copy, modify, and distribute this software for any
8 * purpose with or without fee is hereby granted, provided that the above
9 * copyright notice and this permission notice appear in all copies.
10 *
11 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
12 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
13 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
14 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
15 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
16 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
17 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
18 */
19
20#include <sys/cdefs.h>
21__KERNEL_RCSID(0, "$NetBSD: alipm.c,v 1.7 2010/03/11 04:00:36 mrg Exp $");
22
23#include <sys/param.h>
24#include <sys/device.h>
25#include <sys/kernel.h>
26#include <sys/rwlock.h>
27#include <sys/proc.h>
28#include <sys/systm.h>
29
30#include <dev/i2c/i2cvar.h>
31
32#include <dev/pci/pcidevs.h>
33#include <dev/pci/pcireg.h>
34#include <dev/pci/pcivar.h>
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
100struct 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	krwlock_t sc_smb_lock;
108};
109
110static int	alipm_match(device_t, cfdata_t, void *);
111static void	alipm_attach(device_t, device_t, void *);
112
113int	alipm_smb_acquire_bus(void *, int);
114void	alipm_smb_release_bus(void *, int);
115int	alipm_smb_exec(void *, i2c_op_t, i2c_addr_t, const void *,
116	    size_t, void *, size_t, int);
117
118CFATTACH_DECL(alipm, sizeof(struct alipm_softc),
119	alipm_match, alipm_attach, NULL, NULL);
120
121static int
122alipm_match(device_t parent, cfdata_t match, void *aux)
123{
124	struct pci_attach_args *pa = aux;
125
126	if (PCI_VENDOR(pa->pa_id) == PCI_VENDOR_ALI &&
127	    (PCI_PRODUCT(pa->pa_id) == PCI_PRODUCT_ALI_M7101))
128		return (1);
129	return (0);
130}
131
132static void
133alipm_attach(device_t parent, device_t self, void *aux)
134{
135	struct alipm_softc *sc = device_private(self);
136	struct pci_attach_args *pa = aux;
137	struct i2cbus_attach_args iba;
138	pcireg_t iobase, reg;
139	bus_size_t iosize = ALIPM_SMB_SIZE;
140
141	/* Old chips don't have the PCI 2.2 Capabilities List. */
142	reg = pci_conf_read(pa->pa_pc, pa->pa_tag, PCI_COMMAND_STATUS_REG);
143	if ((reg & PCI_STATUS_CAPLIST_SUPPORT) == 0) {
144		/* Map I/O space */
145		iobase = pci_conf_read(pa->pa_pc, pa->pa_tag, ALIPM_BASE);
146		sc->sc_iot = pa->pa_iot;
147		if (iobase == 0 ||
148		    bus_space_map(sc->sc_iot, iobase >> 16,
149		    iosize, 0, &sc->sc_ioh)) {
150			aprint_error_dev(&sc->sc_dev, "can't map I/O space\n");
151			return;
152		}
153
154		reg = pci_conf_read(pa->pa_pc, pa->pa_tag, ALIPM_CONF);
155		if ((reg & ALIPM_CONF_SMBEN) == 0) {
156			aprint_error_dev(&sc->sc_dev, "SMBus disabled\n");
157			goto fail;
158		}
159
160		reg = pci_conf_read(pa->pa_pc, pa->pa_tag, ALIPM_SMB_HOSTC);
161		if ((reg & ALIPM_SMB_HOSTC_HSTEN) == 0) {
162			aprint_error_dev(&sc->sc_dev, "SMBus host disabled\n");
163			goto fail;
164		}
165	} else {
166		/* Map I/O space */
167		if (pci_mapreg_map(pa, ALIPM_SMB_BASE, PCI_MAPREG_TYPE_IO, 0,
168		    &sc->sc_iot, &sc->sc_ioh, NULL, &iosize)) {
169			aprint_error_dev(&sc->sc_dev, "can't map I/O space\n");
170			return;
171		}
172
173		reg = pci_conf_read(pa->pa_pc, pa->pa_tag, ALIPM_SMB_HOSTX);
174		if ((reg & ALIPM_SMB_HOSTC_HSTEN) == 0) {
175			aprint_error_dev(&sc->sc_dev, "SMBus host disabled\n");
176			goto fail;
177		}
178	}
179
180	switch (reg & ALIPM_SMB_HOSTC_CLOCK) {
181	case ALIPM_SMB_HOSTC_149K:
182		aprint_normal(": 149KHz clock\n");
183		break;
184	case ALIPM_SMB_HOSTC_74K:
185		aprint_normal(": 74KHz clock\n");
186		break;
187	case ALIPM_SMB_HOSTC_37K:
188		aprint_normal(": 37KHz clock\n");
189		break;
190	case ALIPM_SMB_HOSTC_223K:
191		aprint_normal(": 223KHz clock\n");
192		break;
193	case ALIPM_SMB_HOSTC_111K:
194		aprint_normal(": 111KHz clock\n");
195		break;
196	case ALIPM_SMB_HOSTC_55K:
197		aprint_normal(": 55KHz clock\n");
198		break;
199	default:
200		aprint_normal(" unknown clock speed\n");
201		break;
202	}
203	aprint_naive("\n");
204
205	/* Attach I2C bus */
206	rw_init(&sc->sc_smb_lock);
207	sc->sc_smb_tag.ic_cookie = sc;
208	sc->sc_smb_tag.ic_acquire_bus = alipm_smb_acquire_bus;
209	sc->sc_smb_tag.ic_release_bus = alipm_smb_release_bus;
210	sc->sc_smb_tag.ic_exec = alipm_smb_exec;
211
212	memset(&iba, 0, sizeof iba);
213	iba.iba_tag = &sc->sc_smb_tag;
214	(void)config_found_ia(&sc->sc_dev, "i2cbus", &iba, iicbus_print);
215
216	return;
217
218fail:
219	bus_space_unmap(sc->sc_iot, sc->sc_ioh, iosize);
220}
221
222int
223alipm_smb_acquire_bus(void *cookie, int flags)
224{
225	struct alipm_softc *sc = cookie;
226
227	if (flags & I2C_F_POLL)
228		return (0);
229
230	rw_enter(&sc->sc_smb_lock, RW_WRITER);
231	return 0;
232}
233
234void
235alipm_smb_release_bus(void *cookie, int flags)
236{
237	struct alipm_softc *sc = cookie;
238
239	if (flags & I2C_F_POLL)
240		return;
241
242	rw_exit(&sc->sc_smb_lock);
243}
244
245int
246alipm_smb_exec(void *cookie, i2c_op_t op, i2c_addr_t addr,
247    const void *cmdbuf, size_t cmdlen, void *buf, size_t len, int flags)
248{
249	struct alipm_softc *sc = cookie;
250	u_int8_t *b;
251	u_int8_t ctl, st;
252	int retries, error = 0;
253
254	DPRINTF(("%s: exec op %d, addr 0x%x, cmdlen %d, len %d, "
255	    "flags 0x%x\n", device_xname(&sc->sc_dev), op, addr, cmdlen,
256	    len, flags));
257
258	if (!I2C_OP_STOP_P(op) || cmdlen > 1 || len > 2 ||
259	    (cmdlen == 0 && len > 1))
260		return (EOPNOTSUPP);
261
262	/* Clear status bits */
263	bus_space_write_1(sc->sc_iot, sc->sc_ioh, ALIPM_SMB_HS,
264	    ALIPM_SMB_HS_DONE | ALIPM_SMB_HS_FAILED |
265	    ALIPM_SMB_HS_BUSERR | ALIPM_SMB_HS_DEVERR);
266	bus_space_barrier(sc->sc_iot, sc->sc_ioh, ALIPM_SMB_HS, 1,
267	    BUS_SPACE_BARRIER_READ | BUS_SPACE_BARRIER_WRITE);
268
269	/* Wait until bus is idle */
270	for (retries = 1000; retries > 0; retries--) {
271		st = bus_space_read_1(sc->sc_iot, sc->sc_ioh, ALIPM_SMB_HS);
272		bus_space_barrier(sc->sc_iot, sc->sc_ioh, ALIPM_SMB_HS, 1,
273		    BUS_SPACE_BARRIER_READ);
274		if (st & (ALIPM_SMB_HS_IDLE | ALIPM_SMB_HS_FAILED |
275		    ALIPM_SMB_HS_BUSERR | ALIPM_SMB_HS_DEVERR))
276			break;
277		DELAY(ALIPM_DELAY);
278	}
279	if (retries == 0) {
280		aprint_error_dev(&sc->sc_dev, "timeout st 0x%x\n", st);
281		return (ETIMEDOUT);
282	}
283	if (st & (ALIPM_SMB_HS_FAILED |
284	    ALIPM_SMB_HS_BUSERR | ALIPM_SMB_HS_DEVERR)) {
285		aprint_error_dev(&sc->sc_dev, "error st 0x%x\n", st);
286		return (EIO);
287	}
288
289	/* Set slave address and transfer direction. */
290	bus_space_write_1(sc->sc_iot, sc->sc_ioh, ALIPM_SMB_TXSLVA,
291	    ALIPM_SMB_TXSLVA_ADDR(addr) |
292	    (I2C_OP_READ_P(op) ? ALIPM_SMB_TXSLVA_READ : 0));
293
294	if (cmdlen > 0)
295		/* Set command byte */
296		bus_space_write_1(sc->sc_iot, sc->sc_ioh,
297		     ALIPM_SMB_HCMD, ((const u_int8_t *)cmdbuf)[0]);
298
299	if (I2C_OP_WRITE_P(op)) {
300		/* Write data. */
301		b = buf;
302		if (cmdlen == 0 && len == 1)
303			bus_space_write_1(sc->sc_iot, sc->sc_ioh,
304			    ALIPM_SMB_HCMD, b[0]);
305		else if (len > 0)
306			bus_space_write_1(sc->sc_iot, sc->sc_ioh,
307			    ALIPM_SMB_HD0, b[0]);
308		if (len > 1)
309			bus_space_write_1(sc->sc_iot, sc->sc_ioh,
310			    ALIPM_SMB_HD1, b[1]);
311	}
312
313	/* Set SMBus command */
314	if (cmdlen == 0) {
315		if (len == 0)
316			ctl = ALIPM_SMB_HC_CMD_QUICK;
317		else
318			ctl = ALIPM_SMB_HC_CMD_BYTE;
319	} else if (len == 1)
320		ctl = ALIPM_SMB_HC_CMD_BDATA;
321	else
322		ctl = ALIPM_SMB_HC_CMD_WDATA;
323	bus_space_write_1(sc->sc_iot, sc->sc_ioh, ALIPM_SMB_HC, ctl);
324
325	/* Start transaction */
326	bus_space_barrier(sc->sc_iot, sc->sc_ioh, 0, ALIPM_SMB_SIZE,
327	    BUS_SPACE_BARRIER_WRITE);
328	bus_space_write_1(sc->sc_iot, sc->sc_ioh, ALIPM_SMB_START, 0xff);
329	bus_space_barrier(sc->sc_iot, sc->sc_ioh, 0, ALIPM_SMB_SIZE,
330	    BUS_SPACE_BARRIER_READ | BUS_SPACE_BARRIER_WRITE);
331
332	/* Poll for completion */
333	DELAY(ALIPM_DELAY);
334	for (retries = 1000; retries > 0; retries--) {
335		st = bus_space_read_1(sc->sc_iot, sc->sc_ioh, ALIPM_SMB_HS);
336		bus_space_barrier(sc->sc_iot, sc->sc_ioh, ALIPM_SMB_HS, 1,
337		    BUS_SPACE_BARRIER_READ);
338		if (st & (ALIPM_SMB_HS_IDLE | ALIPM_SMB_HS_FAILED |
339		    ALIPM_SMB_HS_BUSERR | ALIPM_SMB_HS_DEVERR))
340			break;
341		DELAY(ALIPM_DELAY);
342	}
343	if (retries == 0) {
344		aprint_error_dev(&sc->sc_dev, "timeout st 0x%x, resetting\n",st);
345		bus_space_write_1(sc->sc_iot, sc->sc_ioh, ALIPM_SMB_HC,
346		    ALIPM_SMB_HC_RESET);
347		bus_space_barrier(sc->sc_iot, sc->sc_ioh, 0, ALIPM_SMB_SIZE,
348		     BUS_SPACE_BARRIER_READ | BUS_SPACE_BARRIER_WRITE);
349		st = bus_space_read_1(sc->sc_iot, sc->sc_ioh, ALIPM_SMB_HS);
350		bus_space_barrier(sc->sc_iot, sc->sc_ioh, ALIPM_SMB_HS, 1,
351		    BUS_SPACE_BARRIER_READ);
352		error = ETIMEDOUT;
353		goto done;
354	}
355
356	if ((st & ALIPM_SMB_HS_DONE) == 0) {
357		bus_space_write_1(sc->sc_iot, sc->sc_ioh, ALIPM_SMB_HC,
358		     ALIPM_SMB_HC_KILL);
359		bus_space_barrier(sc->sc_iot, sc->sc_ioh, 0, ALIPM_SMB_SIZE,
360		     BUS_SPACE_BARRIER_READ | BUS_SPACE_BARRIER_WRITE);
361		st = bus_space_read_1(sc->sc_iot, sc->sc_ioh, ALIPM_SMB_HS);
362		bus_space_barrier(sc->sc_iot, sc->sc_ioh, ALIPM_SMB_HS, 1,
363		    BUS_SPACE_BARRIER_READ);
364		if ((st & ALIPM_SMB_HS_FAILED) == 0)
365			aprint_error_dev(&sc->sc_dev, "error st 0x%x\n", st);
366	}
367
368	/* Check for errors */
369	if (st & (ALIPM_SMB_HS_FAILED |
370	    ALIPM_SMB_HS_BUSERR | ALIPM_SMB_HS_DEVERR)) {
371		error = EIO;
372		goto done;
373	}
374
375	if (I2C_OP_READ_P(op)) {
376		/* Read data */
377		b = buf;
378		if (len > 0) {
379			b[0] = bus_space_read_1(sc->sc_iot, sc->sc_ioh,
380			    ALIPM_SMB_HD0);
381			bus_space_barrier(sc->sc_iot, sc->sc_ioh,
382			    ALIPM_SMB_HD0, 1, BUS_SPACE_BARRIER_READ);
383		}
384		if (len > 1) {
385			b[1] = bus_space_read_1(sc->sc_iot, sc->sc_ioh,
386			    ALIPM_SMB_HD1);
387			bus_space_barrier(sc->sc_iot, sc->sc_ioh,
388			    ALIPM_SMB_HD1, 1, BUS_SPACE_BARRIER_READ);
389		}
390	}
391
392done:
393	/* Clear status bits */
394	bus_space_write_1(sc->sc_iot, sc->sc_ioh, ALIPM_SMB_HS, st);
395
396	return (error);
397}
398