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