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