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