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