xref: /dflybsd-src/sys/dev/powermng/alpm/alpm.c (revision 2267fd784e8a7d1ca13e6d3541caa91f36c9e9fb)
1a9656fbcSSascha Wildner /*-
2a9656fbcSSascha Wildner  * Copyright (c) 1998, 1999, 2001 Nicolas Souchu
3a9656fbcSSascha Wildner  * All rights reserved.
4a9656fbcSSascha Wildner  *
5a9656fbcSSascha Wildner  * Redistribution and use in source and binary forms, with or without
6a9656fbcSSascha Wildner  * modification, are permitted provided that the following conditions
7a9656fbcSSascha Wildner  * are met:
8a9656fbcSSascha Wildner  * 1. Redistributions of source code must retain the above copyright
9a9656fbcSSascha Wildner  *    notice, this list of conditions and the following disclaimer.
10a9656fbcSSascha Wildner  * 2. Redistributions in binary form must reproduce the above copyright
11a9656fbcSSascha Wildner  *    notice, this list of conditions and the following disclaimer in the
12a9656fbcSSascha Wildner  *    documentation and/or other materials provided with the distribution.
13a9656fbcSSascha Wildner  *
14a9656fbcSSascha Wildner  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15a9656fbcSSascha Wildner  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16a9656fbcSSascha Wildner  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17a9656fbcSSascha Wildner  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18a9656fbcSSascha Wildner  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19a9656fbcSSascha Wildner  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20a9656fbcSSascha Wildner  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21a9656fbcSSascha Wildner  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22a9656fbcSSascha Wildner  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23a9656fbcSSascha Wildner  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24a9656fbcSSascha Wildner  * SUCH DAMAGE.
25a9656fbcSSascha Wildner  *
26ba88eb23SSascha Wildner  * $FreeBSD: src/sys/pci/alpm.c,v 1.26 2007/01/11 19:56:24 jhb Exp $
27a9656fbcSSascha Wildner  *
28a9656fbcSSascha Wildner  */
29a9656fbcSSascha Wildner 
30a9656fbcSSascha Wildner /*
31a9656fbcSSascha Wildner  * Power Management support for the Acer M15x3 chipsets
32a9656fbcSSascha Wildner  */
33a9656fbcSSascha Wildner #include <sys/param.h>
34a9656fbcSSascha Wildner #include <sys/bus.h>
35ba88eb23SSascha Wildner #include <sys/kernel.h>
36ba88eb23SSascha Wildner #include <sys/lock.h>
37ba88eb23SSascha Wildner #include <sys/module.h>
38a9656fbcSSascha Wildner #include <sys/rman.h>
39ba88eb23SSascha Wildner #include <sys/systm.h>
40a9656fbcSSascha Wildner 
41a9656fbcSSascha Wildner #include <bus/pci/pcivar.h>
42a9656fbcSSascha Wildner #include <bus/pci/pcireg.h>
43a9656fbcSSascha Wildner 
44a9656fbcSSascha Wildner #include <bus/smbus/smbconf.h>
45a9656fbcSSascha Wildner #include "smbus_if.h"
46a9656fbcSSascha Wildner 
47a9656fbcSSascha Wildner #define ALPM_DEBUG(x)	if (alpm_debug) (x)
48a9656fbcSSascha Wildner 
49a9656fbcSSascha Wildner #ifdef DEBUG
50a9656fbcSSascha Wildner static int alpm_debug = 1;
51a9656fbcSSascha Wildner #else
52a9656fbcSSascha Wildner static int alpm_debug = 0;
53a9656fbcSSascha Wildner #endif
54a9656fbcSSascha Wildner 
55a9656fbcSSascha Wildner #define ACER_M1543_PMU_ID	0x710110b9
56a9656fbcSSascha Wildner 
57ba88eb23SSascha Wildner /*
58ba88eb23SSascha Wildner  * I/O registers offsets - the base address is programmed via the
59a9656fbcSSascha Wildner  * SMBBA PCI configuration register
60a9656fbcSSascha Wildner  */
61a9656fbcSSascha Wildner #define SMBSTS		0x0	/* SMBus host/slave status register */
62a9656fbcSSascha Wildner #define SMBCMD		0x1	/* SMBus host/slave command register */
63a9656fbcSSascha Wildner #define SMBSTART	0x2	/* start to generate programmed cycle */
64a9656fbcSSascha Wildner #define SMBHADDR	0x3	/* host address register */
65a9656fbcSSascha Wildner #define SMBHDATA	0x4	/* data A register for host controller */
66a9656fbcSSascha Wildner #define SMBHDATB	0x5	/* data B register for host controller */
67a9656fbcSSascha Wildner #define SMBHBLOCK	0x6	/* block register for host controller */
68a9656fbcSSascha Wildner #define SMBHCMD		0x7	/* command register for host controller */
69a9656fbcSSascha Wildner 
70ba88eb23SSascha Wildner /* SMBHADDR mask. */
71ba88eb23SSascha Wildner #define	LSB		0x1	/* XXX: Better name: Read/Write? */
72ba88eb23SSascha Wildner 
73a9656fbcSSascha Wildner /* SMBSTS masks */
74a9656fbcSSascha Wildner #define TERMINATE	0x80
75a9656fbcSSascha Wildner #define BUS_COLLI	0x40
76a9656fbcSSascha Wildner #define DEVICE_ERR	0x20
77a9656fbcSSascha Wildner #define SMI_I_STS	0x10
78a9656fbcSSascha Wildner #define HST_BSY		0x08
79a9656fbcSSascha Wildner #define IDL_STS		0x04
80a9656fbcSSascha Wildner #define HSTSLV_STS	0x02
81a9656fbcSSascha Wildner #define HSTSLV_BSY	0x01
82a9656fbcSSascha Wildner 
83a9656fbcSSascha Wildner /* SMBCMD masks */
84a9656fbcSSascha Wildner #define SMB_BLK_CLR	0x80
85a9656fbcSSascha Wildner #define T_OUT_CMD	0x08
86a9656fbcSSascha Wildner #define ABORT_HOST	0x04
87a9656fbcSSascha Wildner 
88a9656fbcSSascha Wildner /* SMBus commands */
89a9656fbcSSascha Wildner #define SMBQUICK	0x00
90a9656fbcSSascha Wildner #define SMBSRBYTE	0x10		/* send/receive byte */
91a9656fbcSSascha Wildner #define SMBWRBYTE	0x20		/* write/read byte */
92a9656fbcSSascha Wildner #define SMBWRWORD	0x30		/* write/read word */
93a9656fbcSSascha Wildner #define SMBWRBLOCK	0x40		/* write/read block */
94a9656fbcSSascha Wildner 
95a9656fbcSSascha Wildner /* PCI configuration registers and masks
96a9656fbcSSascha Wildner  */
97a9656fbcSSascha Wildner #define COM		0x4
98a9656fbcSSascha Wildner #define COM_ENABLE_IO	0x1
99a9656fbcSSascha Wildner 
100ba88eb23SSascha Wildner #define SMBBA		PCIR_BAR(1)
101a9656fbcSSascha Wildner 
102a9656fbcSSascha Wildner #define ATPC		0x5b
103a9656fbcSSascha Wildner #define ATPC_SMBCTRL	0x04		/* XX linux has this as 0x6 */
104a9656fbcSSascha Wildner 
105a9656fbcSSascha Wildner #define SMBHSI		0xe0
106a9656fbcSSascha Wildner #define SMBHSI_SLAVE	0x2
107a9656fbcSSascha Wildner #define SMBHSI_HOST	0x1
108a9656fbcSSascha Wildner 
109a9656fbcSSascha Wildner #define SMBHCBC		0xe2
110a9656fbcSSascha Wildner #define SMBHCBC_CLOCK	0x70
111a9656fbcSSascha Wildner 
112a9656fbcSSascha Wildner #define SMBCLOCK_149K	0x0
113a9656fbcSSascha Wildner #define SMBCLOCK_74K	0x20
114a9656fbcSSascha Wildner #define SMBCLOCK_37K	0x40
115a9656fbcSSascha Wildner #define SMBCLOCK_223K	0x80
116a9656fbcSSascha Wildner #define SMBCLOCK_111K	0xa0
117a9656fbcSSascha Wildner #define SMBCLOCK_55K	0xc0
118a9656fbcSSascha Wildner 
119a9656fbcSSascha Wildner struct alpm_softc {
120a9656fbcSSascha Wildner 	int base;
121a9656fbcSSascha Wildner 	struct resource *res;
122a9656fbcSSascha Wildner         bus_space_tag_t smbst;
123a9656fbcSSascha Wildner         bus_space_handle_t smbsh;
124a9656fbcSSascha Wildner 	device_t smbus;
125ba88eb23SSascha Wildner 	struct lock lock;
126a9656fbcSSascha Wildner };
127a9656fbcSSascha Wildner 
128ba88eb23SSascha Wildner #define	ALPM_LOCK(alpm)		lockmgr(&(alpm)->lock, LK_EXCLUSIVE)
129ba88eb23SSascha Wildner #define	ALPM_UNLOCK(alpm)	lockmgr(&(alpm)->lock, LK_RELEASE)
130ba88eb23SSascha Wildner #define	ALPM_LOCK_ASSERT(alpm)	KKASSERT(lockstatus(&(alpm)->lock, curthread) != 0)
131ba88eb23SSascha Wildner 
132a9656fbcSSascha Wildner #define ALPM_SMBINB(alpm,register) \
133a9656fbcSSascha Wildner 	(bus_space_read_1(alpm->smbst, alpm->smbsh, register))
134a9656fbcSSascha Wildner #define ALPM_SMBOUTB(alpm,register,value) \
135a9656fbcSSascha Wildner 	(bus_space_write_1(alpm->smbst, alpm->smbsh, register, value))
136a9656fbcSSascha Wildner 
137ba88eb23SSascha Wildner static int	alpm_detach(device_t dev);
138ba88eb23SSascha Wildner 
139a9656fbcSSascha Wildner static int
alpm_probe(device_t dev)140a9656fbcSSascha Wildner alpm_probe(device_t dev)
141a9656fbcSSascha Wildner {
142a9656fbcSSascha Wildner 
143a9656fbcSSascha Wildner 	if (pci_get_devid(dev) == ACER_M1543_PMU_ID) {
144a9656fbcSSascha Wildner 		device_set_desc(dev, "AcerLabs M15x3 Power Management Unit");
145a9656fbcSSascha Wildner 
146a9656fbcSSascha Wildner 		return (BUS_PROBE_DEFAULT);
147a9656fbcSSascha Wildner 	}
148a9656fbcSSascha Wildner 
149a9656fbcSSascha Wildner 	return (ENXIO);
150a9656fbcSSascha Wildner }
151a9656fbcSSascha Wildner 
152a9656fbcSSascha Wildner static int
alpm_attach(device_t dev)153a9656fbcSSascha Wildner alpm_attach(device_t dev)
154a9656fbcSSascha Wildner {
155a9656fbcSSascha Wildner 	int rid;
156a9656fbcSSascha Wildner 	u_int32_t l;
157a9656fbcSSascha Wildner 	struct alpm_softc *alpm;
158a9656fbcSSascha Wildner 
159a9656fbcSSascha Wildner 	alpm = device_get_softc(dev);
160a9656fbcSSascha Wildner 
161a9656fbcSSascha Wildner 	/* Unlock SMBIO base register access */
162a9656fbcSSascha Wildner 	l = pci_read_config(dev, ATPC, 1);
163a9656fbcSSascha Wildner 	pci_write_config(dev, ATPC, l & ~ATPC_SMBCTRL, 1);
164a9656fbcSSascha Wildner 
165a9656fbcSSascha Wildner 	/*
166a9656fbcSSascha Wildner 	 * XX linux sets clock to 74k, should we?
167a9656fbcSSascha Wildner 	l = pci_read_config(dev, SMBHCBC, 1);
168a9656fbcSSascha Wildner 	l &= 0x1f;
169a9656fbcSSascha Wildner 	l |= SMBCLOCK_74K;
170ba88eb23SSascha Wildner 	pci_write_config(dev, SMBHCBC, l, 1);
171a9656fbcSSascha Wildner 	 */
172a9656fbcSSascha Wildner 
173a9656fbcSSascha Wildner 	if (bootverbose || alpm_debug) {
174a9656fbcSSascha Wildner 		l = pci_read_config(dev, SMBHSI, 1);
175a9656fbcSSascha Wildner 		device_printf(dev, "%s/%s",
176a9656fbcSSascha Wildner 			(l & SMBHSI_HOST) ? "host":"nohost",
177a9656fbcSSascha Wildner 			(l & SMBHSI_SLAVE) ? "slave":"noslave");
178a9656fbcSSascha Wildner 
179a9656fbcSSascha Wildner 		l = pci_read_config(dev, SMBHCBC, 1);
180a9656fbcSSascha Wildner 		switch (l & SMBHCBC_CLOCK) {
181a9656fbcSSascha Wildner 		case SMBCLOCK_149K:
182a9656fbcSSascha Wildner 			kprintf(" 149K");
183a9656fbcSSascha Wildner 			break;
184a9656fbcSSascha Wildner 		case SMBCLOCK_74K:
185a9656fbcSSascha Wildner 			kprintf(" 74K");
186a9656fbcSSascha Wildner 			break;
187a9656fbcSSascha Wildner 		case SMBCLOCK_37K:
188a9656fbcSSascha Wildner 			kprintf(" 37K");
189a9656fbcSSascha Wildner 			break;
190a9656fbcSSascha Wildner 		case SMBCLOCK_223K:
191a9656fbcSSascha Wildner 			kprintf(" 223K");
192a9656fbcSSascha Wildner 			break;
193a9656fbcSSascha Wildner 		case SMBCLOCK_111K:
194a9656fbcSSascha Wildner 			kprintf(" 111K");
195a9656fbcSSascha Wildner 			break;
196a9656fbcSSascha Wildner 		case SMBCLOCK_55K:
197a9656fbcSSascha Wildner 			kprintf(" 55K");
198a9656fbcSSascha Wildner 			break;
199a9656fbcSSascha Wildner 		default:
20030e3ae03SSascha Wildner 			kprintf("unknown");
201a9656fbcSSascha Wildner 			break;
202a9656fbcSSascha Wildner 		}
203a9656fbcSSascha Wildner 		kprintf("\n");
204a9656fbcSSascha Wildner 	}
205a9656fbcSSascha Wildner 
206a9656fbcSSascha Wildner 	rid = SMBBA;
207a9656fbcSSascha Wildner 	alpm->res = bus_alloc_resource_any(dev, SYS_RES_IOPORT, &rid,
208a9656fbcSSascha Wildner 	    RF_ACTIVE);
209a9656fbcSSascha Wildner 
210a9656fbcSSascha Wildner 	if (alpm->res == NULL) {
211a9656fbcSSascha Wildner 		device_printf(dev, "Could not allocate Bus space\n");
212a9656fbcSSascha Wildner 		return (ENXIO);
213a9656fbcSSascha Wildner 	}
214a9656fbcSSascha Wildner 	alpm->smbst = rman_get_bustag(alpm->res);
215a9656fbcSSascha Wildner 	alpm->smbsh = rman_get_bushandle(alpm->res);
216ba88eb23SSascha Wildner 	lockinit(&alpm->lock, "alpm", 0, LK_CANRECURSE);
217a9656fbcSSascha Wildner 
218a9656fbcSSascha Wildner 	/* attach the smbus */
219a9656fbcSSascha Wildner 	alpm->smbus = device_add_child(dev, "smbus", -1);
220ba88eb23SSascha Wildner 	if (alpm->smbus == NULL) {
221ba88eb23SSascha Wildner 		alpm_detach(dev);
222ba88eb23SSascha Wildner 		return (EINVAL);
223ba88eb23SSascha Wildner 	}
224a9656fbcSSascha Wildner 	bus_generic_attach(dev);
225a9656fbcSSascha Wildner 
226a9656fbcSSascha Wildner 	return (0);
227a9656fbcSSascha Wildner }
228a9656fbcSSascha Wildner 
229a9656fbcSSascha Wildner static int
alpm_detach(device_t dev)230a9656fbcSSascha Wildner alpm_detach(device_t dev)
231a9656fbcSSascha Wildner {
232a9656fbcSSascha Wildner 	struct alpm_softc *alpm = device_get_softc(dev);
233a9656fbcSSascha Wildner 
234a9656fbcSSascha Wildner 	if (alpm->smbus) {
235a9656fbcSSascha Wildner 		device_delete_child(dev, alpm->smbus);
236a9656fbcSSascha Wildner 		alpm->smbus = NULL;
237a9656fbcSSascha Wildner 	}
238ba88eb23SSascha Wildner 	lockuninit(&alpm->lock);
239a9656fbcSSascha Wildner 
240a9656fbcSSascha Wildner 	if (alpm->res)
241a9656fbcSSascha Wildner 		bus_release_resource(dev, SYS_RES_IOPORT, SMBBA, alpm->res);
242a9656fbcSSascha Wildner 
243a9656fbcSSascha Wildner 	return (0);
244a9656fbcSSascha Wildner }
245a9656fbcSSascha Wildner 
246a9656fbcSSascha Wildner static int
alpm_callback(device_t dev,int index,void * data)247ba88eb23SSascha Wildner alpm_callback(device_t dev, int index, void *data)
248a9656fbcSSascha Wildner {
249a9656fbcSSascha Wildner 	int error = 0;
250a9656fbcSSascha Wildner 
251a9656fbcSSascha Wildner 	switch (index) {
252a9656fbcSSascha Wildner 	case SMB_REQUEST_BUS:
253a9656fbcSSascha Wildner 	case SMB_RELEASE_BUS:
254a9656fbcSSascha Wildner 		/* ok, bus allocation accepted */
255a9656fbcSSascha Wildner 		break;
256a9656fbcSSascha Wildner 	default:
257a9656fbcSSascha Wildner 		error = EINVAL;
258a9656fbcSSascha Wildner 	}
259a9656fbcSSascha Wildner 
260a9656fbcSSascha Wildner 	return (error);
261a9656fbcSSascha Wildner }
262a9656fbcSSascha Wildner 
263a9656fbcSSascha Wildner static int
alpm_clear(struct alpm_softc * sc)264a9656fbcSSascha Wildner alpm_clear(struct alpm_softc *sc)
265a9656fbcSSascha Wildner {
266a9656fbcSSascha Wildner 	ALPM_SMBOUTB(sc, SMBSTS, 0xff);
267a9656fbcSSascha Wildner 	DELAY(10);
268a9656fbcSSascha Wildner 
269a9656fbcSSascha Wildner 	return (0);
270a9656fbcSSascha Wildner }
271a9656fbcSSascha Wildner 
272a9656fbcSSascha Wildner #if 0
273a9656fbcSSascha Wildner static int
274a9656fbcSSascha Wildner alpm_abort(struct alpm_softc *sc)
275a9656fbcSSascha Wildner {
276a9656fbcSSascha Wildner 	ALPM_SMBOUTB(sc, SMBCMD, T_OUT_CMD | ABORT_HOST);
277a9656fbcSSascha Wildner 
278a9656fbcSSascha Wildner 	return (0);
279a9656fbcSSascha Wildner }
280a9656fbcSSascha Wildner #endif
281a9656fbcSSascha Wildner 
282a9656fbcSSascha Wildner static int
alpm_idle(struct alpm_softc * sc)283a9656fbcSSascha Wildner alpm_idle(struct alpm_softc *sc)
284a9656fbcSSascha Wildner {
285a9656fbcSSascha Wildner 	u_char sts;
286a9656fbcSSascha Wildner 
287a9656fbcSSascha Wildner 	sts = ALPM_SMBINB(sc, SMBSTS);
288a9656fbcSSascha Wildner 
289a9656fbcSSascha Wildner 	ALPM_DEBUG(kprintf("alpm: idle? STS=0x%x\n", sts));
290a9656fbcSSascha Wildner 
291a9656fbcSSascha Wildner 	return (sts & IDL_STS);
292a9656fbcSSascha Wildner }
293a9656fbcSSascha Wildner 
294a9656fbcSSascha Wildner /*
295a9656fbcSSascha Wildner  * Poll the SMBus controller
296a9656fbcSSascha Wildner  */
297a9656fbcSSascha Wildner static int
alpm_wait(struct alpm_softc * sc)298a9656fbcSSascha Wildner alpm_wait(struct alpm_softc *sc)
299a9656fbcSSascha Wildner {
300a9656fbcSSascha Wildner 	int count = 10000;
301a9656fbcSSascha Wildner 	u_char sts = 0;
302a9656fbcSSascha Wildner 	int error;
303a9656fbcSSascha Wildner 
304a9656fbcSSascha Wildner 	/* wait for command to complete and SMBus controller is idle */
305a9656fbcSSascha Wildner 	while (count--) {
306a9656fbcSSascha Wildner 		DELAY(10);
307a9656fbcSSascha Wildner 		sts = ALPM_SMBINB(sc, SMBSTS);
308a9656fbcSSascha Wildner 		if (sts & SMI_I_STS)
309a9656fbcSSascha Wildner 			break;
310a9656fbcSSascha Wildner 	}
311a9656fbcSSascha Wildner 
312a9656fbcSSascha Wildner 	ALPM_DEBUG(kprintf("alpm: STS=0x%x\n", sts));
313a9656fbcSSascha Wildner 
314a9656fbcSSascha Wildner 	error = SMB_ENOERR;
315a9656fbcSSascha Wildner 
316a9656fbcSSascha Wildner 	if (!count)
317a9656fbcSSascha Wildner 		error |= SMB_ETIMEOUT;
318a9656fbcSSascha Wildner 
319a9656fbcSSascha Wildner 	if (sts & TERMINATE)
320a9656fbcSSascha Wildner 		error |= SMB_EABORT;
321a9656fbcSSascha Wildner 
322a9656fbcSSascha Wildner 	if (sts & BUS_COLLI)
323a9656fbcSSascha Wildner 		error |= SMB_ENOACK;
324a9656fbcSSascha Wildner 
325a9656fbcSSascha Wildner 	if (sts & DEVICE_ERR)
326a9656fbcSSascha Wildner 		error |= SMB_EBUSERR;
327a9656fbcSSascha Wildner 
328a9656fbcSSascha Wildner 	if (error != SMB_ENOERR)
329a9656fbcSSascha Wildner 		alpm_clear(sc);
330a9656fbcSSascha Wildner 
331a9656fbcSSascha Wildner 	return (error);
332a9656fbcSSascha Wildner }
333a9656fbcSSascha Wildner 
334a9656fbcSSascha Wildner static int
alpm_quick(device_t dev,u_char slave,int how)335a9656fbcSSascha Wildner alpm_quick(device_t dev, u_char slave, int how)
336a9656fbcSSascha Wildner {
337a9656fbcSSascha Wildner 	struct alpm_softc *sc = (struct alpm_softc *)device_get_softc(dev);
338a9656fbcSSascha Wildner 	int error;
339a9656fbcSSascha Wildner 
340ba88eb23SSascha Wildner 	ALPM_LOCK(sc);
341a9656fbcSSascha Wildner 	alpm_clear(sc);
342ba88eb23SSascha Wildner 	if (!alpm_idle(sc)) {
343ba88eb23SSascha Wildner 		ALPM_UNLOCK(sc);
344a9656fbcSSascha Wildner 		return (EBUSY);
345ba88eb23SSascha Wildner 	}
346a9656fbcSSascha Wildner 
347a9656fbcSSascha Wildner 	switch (how) {
348a9656fbcSSascha Wildner 	case SMB_QWRITE:
349a9656fbcSSascha Wildner 		ALPM_DEBUG(kprintf("alpm: QWRITE to 0x%x", slave));
350a9656fbcSSascha Wildner 		ALPM_SMBOUTB(sc, SMBHADDR, slave & ~LSB);
351a9656fbcSSascha Wildner 		break;
352a9656fbcSSascha Wildner 	case SMB_QREAD:
353a9656fbcSSascha Wildner 		ALPM_DEBUG(kprintf("alpm: QREAD to 0x%x", slave));
354a9656fbcSSascha Wildner 		ALPM_SMBOUTB(sc, SMBHADDR, slave | LSB);
355a9656fbcSSascha Wildner 		break;
356a9656fbcSSascha Wildner 	default:
357a9656fbcSSascha Wildner 		panic("%s: unknown QUICK command (%x)!", __func__, how);
358a9656fbcSSascha Wildner 	}
359a9656fbcSSascha Wildner 	ALPM_SMBOUTB(sc, SMBCMD, SMBQUICK);
360a9656fbcSSascha Wildner 	ALPM_SMBOUTB(sc, SMBSTART, 0xff);
361a9656fbcSSascha Wildner 
362a9656fbcSSascha Wildner 	error = alpm_wait(sc);
363a9656fbcSSascha Wildner 
364a9656fbcSSascha Wildner 	ALPM_DEBUG(kprintf(", error=0x%x\n", error));
365ba88eb23SSascha Wildner 	ALPM_UNLOCK(sc);
366a9656fbcSSascha Wildner 
367a9656fbcSSascha Wildner 	return (error);
368a9656fbcSSascha Wildner }
369a9656fbcSSascha Wildner 
370a9656fbcSSascha Wildner static int
alpm_sendb(device_t dev,u_char slave,char byte)371a9656fbcSSascha Wildner alpm_sendb(device_t dev, u_char slave, char byte)
372a9656fbcSSascha Wildner {
373a9656fbcSSascha Wildner 	struct alpm_softc *sc = (struct alpm_softc *)device_get_softc(dev);
374a9656fbcSSascha Wildner 	int error;
375a9656fbcSSascha Wildner 
376ba88eb23SSascha Wildner 	ALPM_LOCK(sc);
377a9656fbcSSascha Wildner 	alpm_clear(sc);
378ba88eb23SSascha Wildner 	if (!alpm_idle(sc)) {
379ba88eb23SSascha Wildner 		ALPM_UNLOCK(sc);
380a9656fbcSSascha Wildner 		return (SMB_EBUSY);
381ba88eb23SSascha Wildner 	}
382a9656fbcSSascha Wildner 
383a9656fbcSSascha Wildner 	ALPM_SMBOUTB(sc, SMBHADDR, slave & ~LSB);
384a9656fbcSSascha Wildner 	ALPM_SMBOUTB(sc, SMBCMD, SMBSRBYTE);
385a9656fbcSSascha Wildner 	ALPM_SMBOUTB(sc, SMBHDATA, byte);
386a9656fbcSSascha Wildner 	ALPM_SMBOUTB(sc, SMBSTART, 0xff);
387a9656fbcSSascha Wildner 
388a9656fbcSSascha Wildner 	error = alpm_wait(sc);
389a9656fbcSSascha Wildner 
390a9656fbcSSascha Wildner 	ALPM_DEBUG(kprintf("alpm: SENDB to 0x%x, byte=0x%x, error=0x%x\n", slave, byte, error));
391ba88eb23SSascha Wildner 	ALPM_UNLOCK(sc);
392a9656fbcSSascha Wildner 
393a9656fbcSSascha Wildner 	return (error);
394a9656fbcSSascha Wildner }
395a9656fbcSSascha Wildner 
396a9656fbcSSascha Wildner static int
alpm_recvb(device_t dev,u_char slave,char * byte)397a9656fbcSSascha Wildner alpm_recvb(device_t dev, u_char slave, char *byte)
398a9656fbcSSascha Wildner {
399a9656fbcSSascha Wildner 	struct alpm_softc *sc = (struct alpm_softc *)device_get_softc(dev);
400a9656fbcSSascha Wildner 	int error;
401a9656fbcSSascha Wildner 
402ba88eb23SSascha Wildner 	ALPM_LOCK(sc);
403a9656fbcSSascha Wildner 	alpm_clear(sc);
404ba88eb23SSascha Wildner 	if (!alpm_idle(sc)) {
405ba88eb23SSascha Wildner 		ALPM_UNLOCK(sc);
406a9656fbcSSascha Wildner 		return (SMB_EBUSY);
407ba88eb23SSascha Wildner 	}
408a9656fbcSSascha Wildner 
409a9656fbcSSascha Wildner 	ALPM_SMBOUTB(sc, SMBHADDR, slave | LSB);
410a9656fbcSSascha Wildner 	ALPM_SMBOUTB(sc, SMBCMD, SMBSRBYTE);
411a9656fbcSSascha Wildner 	ALPM_SMBOUTB(sc, SMBSTART, 0xff);
412a9656fbcSSascha Wildner 
413a9656fbcSSascha Wildner 	if ((error = alpm_wait(sc)) == SMB_ENOERR)
414a9656fbcSSascha Wildner 		*byte = ALPM_SMBINB(sc, SMBHDATA);
415a9656fbcSSascha Wildner 
416a9656fbcSSascha Wildner 	ALPM_DEBUG(kprintf("alpm: RECVB from 0x%x, byte=0x%x, error=0x%x\n", slave, *byte, error));
417ba88eb23SSascha Wildner 	ALPM_UNLOCK(sc);
418a9656fbcSSascha Wildner 
419a9656fbcSSascha Wildner 	return (error);
420a9656fbcSSascha Wildner }
421a9656fbcSSascha Wildner 
422a9656fbcSSascha Wildner static int
alpm_writeb(device_t dev,u_char slave,char cmd,char byte)423a9656fbcSSascha Wildner alpm_writeb(device_t dev, u_char slave, char cmd, char byte)
424a9656fbcSSascha Wildner {
425a9656fbcSSascha Wildner 	struct alpm_softc *sc = (struct alpm_softc *)device_get_softc(dev);
426a9656fbcSSascha Wildner 	int error;
427a9656fbcSSascha Wildner 
428ba88eb23SSascha Wildner 	ALPM_LOCK(sc);
429a9656fbcSSascha Wildner 	alpm_clear(sc);
430ba88eb23SSascha Wildner 	if (!alpm_idle(sc)) {
431ba88eb23SSascha Wildner 		ALPM_UNLOCK(sc);
432a9656fbcSSascha Wildner 		return (SMB_EBUSY);
433ba88eb23SSascha Wildner 	}
434a9656fbcSSascha Wildner 
435a9656fbcSSascha Wildner 	ALPM_SMBOUTB(sc, SMBHADDR, slave & ~LSB);
436a9656fbcSSascha Wildner 	ALPM_SMBOUTB(sc, SMBCMD, SMBWRBYTE);
437a9656fbcSSascha Wildner 	ALPM_SMBOUTB(sc, SMBHDATA, byte);
438a9656fbcSSascha Wildner 	ALPM_SMBOUTB(sc, SMBHCMD, cmd);
439a9656fbcSSascha Wildner 	ALPM_SMBOUTB(sc, SMBSTART, 0xff);
440a9656fbcSSascha Wildner 
441a9656fbcSSascha Wildner 	error = alpm_wait(sc);
442a9656fbcSSascha Wildner 
443a9656fbcSSascha Wildner 	ALPM_DEBUG(kprintf("alpm: WRITEB to 0x%x, cmd=0x%x, byte=0x%x, error=0x%x\n", slave, cmd, byte, error));
444ba88eb23SSascha Wildner 	ALPM_UNLOCK(sc);
445a9656fbcSSascha Wildner 
446a9656fbcSSascha Wildner 	return (error);
447a9656fbcSSascha Wildner }
448a9656fbcSSascha Wildner 
449a9656fbcSSascha Wildner static int
alpm_readb(device_t dev,u_char slave,char cmd,char * byte)450a9656fbcSSascha Wildner alpm_readb(device_t dev, u_char slave, char cmd, char *byte)
451a9656fbcSSascha Wildner {
452a9656fbcSSascha Wildner 	struct alpm_softc *sc = (struct alpm_softc *)device_get_softc(dev);
453a9656fbcSSascha Wildner 	int error;
454a9656fbcSSascha Wildner 
455ba88eb23SSascha Wildner 	ALPM_LOCK(sc);
456a9656fbcSSascha Wildner 	alpm_clear(sc);
457ba88eb23SSascha Wildner 	if (!alpm_idle(sc)) {
458ba88eb23SSascha Wildner 		ALPM_UNLOCK(sc);
459a9656fbcSSascha Wildner 		return (SMB_EBUSY);
460ba88eb23SSascha Wildner 	}
461a9656fbcSSascha Wildner 
462a9656fbcSSascha Wildner 	ALPM_SMBOUTB(sc, SMBHADDR, slave | LSB);
463a9656fbcSSascha Wildner 	ALPM_SMBOUTB(sc, SMBCMD, SMBWRBYTE);
464a9656fbcSSascha Wildner 	ALPM_SMBOUTB(sc, SMBHCMD, cmd);
465a9656fbcSSascha Wildner 	ALPM_SMBOUTB(sc, SMBSTART, 0xff);
466a9656fbcSSascha Wildner 
467a9656fbcSSascha Wildner 	if ((error = alpm_wait(sc)) == SMB_ENOERR)
468a9656fbcSSascha Wildner 		*byte = ALPM_SMBINB(sc, SMBHDATA);
469a9656fbcSSascha Wildner 
470a9656fbcSSascha Wildner 	ALPM_DEBUG(kprintf("alpm: READB from 0x%x, cmd=0x%x, byte=0x%x, error=0x%x\n", slave, cmd, *byte, error));
471ba88eb23SSascha Wildner 	ALPM_UNLOCK(sc);
472a9656fbcSSascha Wildner 
473a9656fbcSSascha Wildner 	return (error);
474a9656fbcSSascha Wildner }
475a9656fbcSSascha Wildner 
476a9656fbcSSascha Wildner static int
alpm_writew(device_t dev,u_char slave,char cmd,short word)477a9656fbcSSascha Wildner alpm_writew(device_t dev, u_char slave, char cmd, short word)
478a9656fbcSSascha Wildner {
479a9656fbcSSascha Wildner 	struct alpm_softc *sc = (struct alpm_softc *)device_get_softc(dev);
480a9656fbcSSascha Wildner 	int error;
481a9656fbcSSascha Wildner 
482ba88eb23SSascha Wildner 	ALPM_LOCK(sc);
483a9656fbcSSascha Wildner 	alpm_clear(sc);
484ba88eb23SSascha Wildner 	if (!alpm_idle(sc)) {
485ba88eb23SSascha Wildner 		ALPM_UNLOCK(sc);
486a9656fbcSSascha Wildner 		return (SMB_EBUSY);
487ba88eb23SSascha Wildner 	}
488a9656fbcSSascha Wildner 
489a9656fbcSSascha Wildner 	ALPM_SMBOUTB(sc, SMBHADDR, slave & ~LSB);
490a9656fbcSSascha Wildner 	ALPM_SMBOUTB(sc, SMBCMD, SMBWRWORD);
491a9656fbcSSascha Wildner 	ALPM_SMBOUTB(sc, SMBHDATA, word & 0x00ff);
492a9656fbcSSascha Wildner 	ALPM_SMBOUTB(sc, SMBHDATB, (word & 0xff00) >> 8);
493a9656fbcSSascha Wildner 	ALPM_SMBOUTB(sc, SMBHCMD, cmd);
494a9656fbcSSascha Wildner 	ALPM_SMBOUTB(sc, SMBSTART, 0xff);
495a9656fbcSSascha Wildner 
496a9656fbcSSascha Wildner 	error = alpm_wait(sc);
497a9656fbcSSascha Wildner 
498a9656fbcSSascha Wildner 	ALPM_DEBUG(kprintf("alpm: WRITEW to 0x%x, cmd=0x%x, word=0x%x, error=0x%x\n", slave, cmd, word, error));
499ba88eb23SSascha Wildner 	ALPM_UNLOCK(sc);
500a9656fbcSSascha Wildner 
501a9656fbcSSascha Wildner 	return (error);
502a9656fbcSSascha Wildner }
503a9656fbcSSascha Wildner 
504a9656fbcSSascha Wildner static int
alpm_readw(device_t dev,u_char slave,char cmd,short * word)505a9656fbcSSascha Wildner alpm_readw(device_t dev, u_char slave, char cmd, short *word)
506a9656fbcSSascha Wildner {
507a9656fbcSSascha Wildner 	struct alpm_softc *sc = (struct alpm_softc *)device_get_softc(dev);
508a9656fbcSSascha Wildner 	int error;
509a9656fbcSSascha Wildner 	u_char high, low;
510a9656fbcSSascha Wildner 
511ba88eb23SSascha Wildner 	ALPM_LOCK(sc);
512a9656fbcSSascha Wildner 	alpm_clear(sc);
513ba88eb23SSascha Wildner 	if (!alpm_idle(sc)) {
514ba88eb23SSascha Wildner 		ALPM_UNLOCK(sc);
515a9656fbcSSascha Wildner 		return (SMB_EBUSY);
516ba88eb23SSascha Wildner 	}
517a9656fbcSSascha Wildner 
518a9656fbcSSascha Wildner 	ALPM_SMBOUTB(sc, SMBHADDR, slave | LSB);
519a9656fbcSSascha Wildner 	ALPM_SMBOUTB(sc, SMBCMD, SMBWRWORD);
520a9656fbcSSascha Wildner 	ALPM_SMBOUTB(sc, SMBHCMD, cmd);
521a9656fbcSSascha Wildner 	ALPM_SMBOUTB(sc, SMBSTART, 0xff);
522a9656fbcSSascha Wildner 
523a9656fbcSSascha Wildner 	if ((error = alpm_wait(sc)) == SMB_ENOERR) {
524a9656fbcSSascha Wildner 		low = ALPM_SMBINB(sc, SMBHDATA);
525a9656fbcSSascha Wildner 		high = ALPM_SMBINB(sc, SMBHDATB);
526a9656fbcSSascha Wildner 
527a9656fbcSSascha Wildner 		*word = ((high & 0xff) << 8) | (low & 0xff);
528a9656fbcSSascha Wildner 	}
529a9656fbcSSascha Wildner 
530a9656fbcSSascha Wildner 	ALPM_DEBUG(kprintf("alpm: READW from 0x%x, cmd=0x%x, word=0x%x, error=0x%x\n", slave, cmd, *word, error));
531ba88eb23SSascha Wildner 	ALPM_UNLOCK(sc);
532a9656fbcSSascha Wildner 
533a9656fbcSSascha Wildner 	return (error);
534a9656fbcSSascha Wildner }
535a9656fbcSSascha Wildner 
536a9656fbcSSascha Wildner static int
alpm_bwrite(device_t dev,u_char slave,char cmd,u_char count,char * buf)537a9656fbcSSascha Wildner alpm_bwrite(device_t dev, u_char slave, char cmd, u_char count, char *buf)
538a9656fbcSSascha Wildner {
539a9656fbcSSascha Wildner 	struct alpm_softc *sc = (struct alpm_softc *)device_get_softc(dev);
540ba88eb23SSascha Wildner 	u_char i;
541ba88eb23SSascha Wildner 	int error;
542a9656fbcSSascha Wildner 
543ba88eb23SSascha Wildner 	if (count < 1 || count > 32)
544ba88eb23SSascha Wildner 		return (SMB_EINVAL);
545ba88eb23SSascha Wildner 
546ba88eb23SSascha Wildner 	ALPM_LOCK(sc);
547a9656fbcSSascha Wildner 	alpm_clear(sc);
548ba88eb23SSascha Wildner 	if(!alpm_idle(sc)) {
549ba88eb23SSascha Wildner 		ALPM_UNLOCK(sc);
550a9656fbcSSascha Wildner 		return (SMB_EBUSY);
551ba88eb23SSascha Wildner 	}
552a9656fbcSSascha Wildner 
553a9656fbcSSascha Wildner 	ALPM_SMBOUTB(sc, SMBHADDR, slave & ~LSB);
554a9656fbcSSascha Wildner 
555a9656fbcSSascha Wildner 	/* set the cmd and reset the
556a9656fbcSSascha Wildner 	 * 32-byte long internal buffer */
557a9656fbcSSascha Wildner 	ALPM_SMBOUTB(sc, SMBCMD, SMBWRBLOCK | SMB_BLK_CLR);
558a9656fbcSSascha Wildner 
559ba88eb23SSascha Wildner 	ALPM_SMBOUTB(sc, SMBHDATA, count);
560a9656fbcSSascha Wildner 
561a9656fbcSSascha Wildner 	/* fill the 32-byte internal buffer */
562ba88eb23SSascha Wildner 	for (i = 0; i < count; i++) {
563ba88eb23SSascha Wildner 		ALPM_SMBOUTB(sc, SMBHBLOCK, buf[i]);
564a9656fbcSSascha Wildner 		DELAY(2);
565a9656fbcSSascha Wildner 	}
566a9656fbcSSascha Wildner 	ALPM_SMBOUTB(sc, SMBHCMD, cmd);
567a9656fbcSSascha Wildner 	ALPM_SMBOUTB(sc, SMBSTART, 0xff);
568a9656fbcSSascha Wildner 
569ba88eb23SSascha Wildner 	error = alpm_wait(sc);
570a9656fbcSSascha Wildner 
571a9656fbcSSascha Wildner 	ALPM_DEBUG(kprintf("alpm: WRITEBLK to 0x%x, count=0x%x, cmd=0x%x, error=0x%x", slave, count, cmd, error));
572ba88eb23SSascha Wildner 	ALPM_UNLOCK(sc);
573a9656fbcSSascha Wildner 
574a9656fbcSSascha Wildner 	return (error);
575a9656fbcSSascha Wildner }
576a9656fbcSSascha Wildner 
577a9656fbcSSascha Wildner static int
alpm_bread(device_t dev,u_char slave,char cmd,u_char * count,char * buf)578ba88eb23SSascha Wildner alpm_bread(device_t dev, u_char slave, char cmd, u_char *count, char *buf)
579a9656fbcSSascha Wildner {
580a9656fbcSSascha Wildner 	struct alpm_softc *sc = (struct alpm_softc *)device_get_softc(dev);
581ba88eb23SSascha Wildner 	u_char data, len, i;
582ba88eb23SSascha Wildner 	int error;
583a9656fbcSSascha Wildner 
584ba88eb23SSascha Wildner 	if (*count < 1 || *count > 32)
585ba88eb23SSascha Wildner 		return (SMB_EINVAL);
586ba88eb23SSascha Wildner 
587ba88eb23SSascha Wildner 	ALPM_LOCK(sc);
588a9656fbcSSascha Wildner 	alpm_clear(sc);
589ba88eb23SSascha Wildner 	if (!alpm_idle(sc)) {
590ba88eb23SSascha Wildner 		ALPM_UNLOCK(sc);
591a9656fbcSSascha Wildner 		return (SMB_EBUSY);
592ba88eb23SSascha Wildner 	}
593a9656fbcSSascha Wildner 
594a9656fbcSSascha Wildner 	ALPM_SMBOUTB(sc, SMBHADDR, slave | LSB);
595a9656fbcSSascha Wildner 
596a9656fbcSSascha Wildner 	/* set the cmd and reset the
597a9656fbcSSascha Wildner 	 * 32-byte long internal buffer */
598a9656fbcSSascha Wildner 	ALPM_SMBOUTB(sc, SMBCMD, SMBWRBLOCK | SMB_BLK_CLR);
599a9656fbcSSascha Wildner 
600a9656fbcSSascha Wildner 	ALPM_SMBOUTB(sc, SMBHCMD, cmd);
601a9656fbcSSascha Wildner 	ALPM_SMBOUTB(sc, SMBSTART, 0xff);
602a9656fbcSSascha Wildner 
603a9656fbcSSascha Wildner 	if ((error = alpm_wait(sc)) != SMB_ENOERR)
604a9656fbcSSascha Wildner 		goto error;
605a9656fbcSSascha Wildner 
606a9656fbcSSascha Wildner 	len = ALPM_SMBINB(sc, SMBHDATA);
607a9656fbcSSascha Wildner 
608a9656fbcSSascha Wildner 	/* read the 32-byte internal buffer */
609a9656fbcSSascha Wildner 	for (i = 0; i < len; i++) {
610ba88eb23SSascha Wildner 		data = ALPM_SMBINB(sc, SMBHBLOCK);
611ba88eb23SSascha Wildner 		if (i < *count)
612ba88eb23SSascha Wildner 			buf[i] = data;
613a9656fbcSSascha Wildner 		DELAY(2);
614a9656fbcSSascha Wildner 	}
615ba88eb23SSascha Wildner 	*count = len;
616a9656fbcSSascha Wildner 
617a9656fbcSSascha Wildner error:
618ba88eb23SSascha Wildner 	ALPM_DEBUG(kprintf("alpm: READBLK to 0x%x, count=0x%x, cmd=0x%x, error=0x%x", slave, *count, cmd, error));
619ba88eb23SSascha Wildner 	ALPM_UNLOCK(sc);
620a9656fbcSSascha Wildner 
621a9656fbcSSascha Wildner 	return (error);
622a9656fbcSSascha Wildner }
623a9656fbcSSascha Wildner 
624a9656fbcSSascha Wildner static devclass_t alpm_devclass;
625a9656fbcSSascha Wildner 
626a9656fbcSSascha Wildner static device_method_t alpm_methods[] = {
627a9656fbcSSascha Wildner 	/* device interface */
628a9656fbcSSascha Wildner 	DEVMETHOD(device_probe,		alpm_probe),
629a9656fbcSSascha Wildner 	DEVMETHOD(device_attach,	alpm_attach),
630a9656fbcSSascha Wildner 	DEVMETHOD(device_detach,	alpm_detach),
631a9656fbcSSascha Wildner 
632a9656fbcSSascha Wildner 	/* smbus interface */
633a9656fbcSSascha Wildner 	DEVMETHOD(smbus_callback,	alpm_callback),
634a9656fbcSSascha Wildner 	DEVMETHOD(smbus_quick,		alpm_quick),
635a9656fbcSSascha Wildner 	DEVMETHOD(smbus_sendb,		alpm_sendb),
636a9656fbcSSascha Wildner 	DEVMETHOD(smbus_recvb,		alpm_recvb),
637a9656fbcSSascha Wildner 	DEVMETHOD(smbus_writeb,		alpm_writeb),
638a9656fbcSSascha Wildner 	DEVMETHOD(smbus_readb,		alpm_readb),
639a9656fbcSSascha Wildner 	DEVMETHOD(smbus_writew,		alpm_writew),
640a9656fbcSSascha Wildner 	DEVMETHOD(smbus_readw,		alpm_readw),
641a9656fbcSSascha Wildner 	DEVMETHOD(smbus_bwrite,		alpm_bwrite),
642a9656fbcSSascha Wildner 	DEVMETHOD(smbus_bread,		alpm_bread),
643a9656fbcSSascha Wildner 
644*d3c9c58eSSascha Wildner 	DEVMETHOD_END
645a9656fbcSSascha Wildner };
646a9656fbcSSascha Wildner 
647a9656fbcSSascha Wildner static driver_t alpm_driver = {
648a9656fbcSSascha Wildner 	"alpm",
649a9656fbcSSascha Wildner 	alpm_methods,
650a9656fbcSSascha Wildner 	sizeof(struct alpm_softc)
651a9656fbcSSascha Wildner };
652a9656fbcSSascha Wildner 
653aa2b9d05SSascha Wildner DRIVER_MODULE(alpm, pci, alpm_driver, alpm_devclass, NULL, NULL);
654aa2b9d05SSascha Wildner DRIVER_MODULE(smbus, alpm, smbus_driver, smbus_devclass, NULL, NULL);
655a9656fbcSSascha Wildner MODULE_DEPEND(alpm, pci, 1, 1, 1);
656a9656fbcSSascha Wildner MODULE_DEPEND(alpm, smbus, SMBUS_MINVER, SMBUS_PREFVER, SMBUS_MAXVER);
657a9656fbcSSascha Wildner MODULE_VERSION(alpm, 1);
658