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