xref: /freebsd/sys/dev/nfsmb/nfsmb.c (revision bd53cac13dfdfdcf08d8ff32e55b9dd322992631)
1271b33a6SRui Paulo /*-
2271b33a6SRui Paulo  * Copyright (c) 2005 Ruslan Ermilov
3271b33a6SRui Paulo  * All rights reserved.
4271b33a6SRui Paulo  *
5271b33a6SRui Paulo  * Redistribution and use in source and binary forms, with or without
6271b33a6SRui Paulo  * modification, are permitted provided that the following conditions
7271b33a6SRui Paulo  * are met:
8271b33a6SRui Paulo  * 1. Redistributions of source code must retain the above copyright
9271b33a6SRui Paulo  *    notice, this list of conditions and the following disclaimer.
10271b33a6SRui Paulo  * 2. Redistributions in binary form must reproduce the above copyright
11271b33a6SRui Paulo  *    notice, this list of conditions and the following disclaimer in the
12271b33a6SRui Paulo  *    documentation and/or other materials provided with the distribution.
13271b33a6SRui Paulo  *
14271b33a6SRui Paulo  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15271b33a6SRui Paulo  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16271b33a6SRui Paulo  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17271b33a6SRui Paulo  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18271b33a6SRui Paulo  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19271b33a6SRui Paulo  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20271b33a6SRui Paulo  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21271b33a6SRui Paulo  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22271b33a6SRui Paulo  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23271b33a6SRui Paulo  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24271b33a6SRui Paulo  * SUCH DAMAGE.
25271b33a6SRui Paulo  */
26271b33a6SRui Paulo 
27271b33a6SRui Paulo #include <sys/cdefs.h>
28271b33a6SRui Paulo __FBSDID("$FreeBSD$");
29271b33a6SRui Paulo 
30271b33a6SRui Paulo #include <sys/param.h>
31271b33a6SRui Paulo #include <sys/bus.h>
32271b33a6SRui Paulo #include <sys/kernel.h>
33271b33a6SRui Paulo #include <sys/lock.h>
34271b33a6SRui Paulo #include <sys/module.h>
35271b33a6SRui Paulo #include <sys/mutex.h>
36271b33a6SRui Paulo #include <sys/systm.h>
37271b33a6SRui Paulo 
38271b33a6SRui Paulo #include <machine/bus.h>
39271b33a6SRui Paulo #include <machine/resource.h>
40271b33a6SRui Paulo #include <sys/rman.h>
41271b33a6SRui Paulo 
42271b33a6SRui Paulo #include <dev/pci/pcivar.h>
43271b33a6SRui Paulo #include <dev/pci/pcireg.h>
44271b33a6SRui Paulo 
45271b33a6SRui Paulo #include <dev/smbus/smbconf.h>
46271b33a6SRui Paulo #include "smbus_if.h"
47271b33a6SRui Paulo 
48271b33a6SRui Paulo #define	NFSMB_DEBUG(x)	if (nfsmb_debug) (x)
49271b33a6SRui Paulo 
50271b33a6SRui Paulo #ifdef DEBUG
51271b33a6SRui Paulo static int nfsmb_debug = 1;
52271b33a6SRui Paulo #else
53271b33a6SRui Paulo static int nfsmb_debug = 0;
54271b33a6SRui Paulo #endif
55271b33a6SRui Paulo 
56271b33a6SRui Paulo /* NVIDIA nForce2/3/4 MCP */
57271b33a6SRui Paulo #define	NFSMB_VENDORID_NVIDIA		0x10de
58271b33a6SRui Paulo #define	NFSMB_DEVICEID_NF2_SMB		0x0064
59271b33a6SRui Paulo #define	NFSMB_DEVICEID_NF2_ULTRA_SMB	0x0084
60271b33a6SRui Paulo #define	NFSMB_DEVICEID_NF3_PRO150_SMB	0x00d4
61271b33a6SRui Paulo #define	NFSMB_DEVICEID_NF3_250GB_SMB	0x00e4
62271b33a6SRui Paulo #define	NFSMB_DEVICEID_NF4_SMB		0x0052
63271b33a6SRui Paulo #define	NFSMB_DEVICEID_NF4_04_SMB	0x0034
64271b33a6SRui Paulo #define	NFSMB_DEVICEID_NF4_51_SMB	0x0264
65271b33a6SRui Paulo #define	NFSMB_DEVICEID_NF4_55_SMB	0x0368
66271b33a6SRui Paulo #define	NFSMB_DEVICEID_NF4_61_SMB	0x03eb
67271b33a6SRui Paulo #define	NFSMB_DEVICEID_NF4_65_SMB	0x0446
68271b33a6SRui Paulo #define	NFSMB_DEVICEID_NF4_67_SMB	0x0542
69271b33a6SRui Paulo #define	NFSMB_DEVICEID_NF4_73_SMB	0x07d8
70271b33a6SRui Paulo #define	NFSMB_DEVICEID_NF4_78S_SMB	0x0752
71271b33a6SRui Paulo #define	NFSMB_DEVICEID_NF4_79_SMB	0x0aa2
72271b33a6SRui Paulo 
73271b33a6SRui Paulo /* PCI Configuration space registers */
74271b33a6SRui Paulo #define	NF2PCI_SMBASE_1		PCIR_BAR(4)
75271b33a6SRui Paulo #define	NF2PCI_SMBASE_2		PCIR_BAR(5)
76271b33a6SRui Paulo 
77271b33a6SRui Paulo /*
78271b33a6SRui Paulo  * ACPI 3.0, Chapter 12, SMBus Host Controller Interface.
79271b33a6SRui Paulo  */
80271b33a6SRui Paulo #define	SMB_PRTCL		0x00	/* protocol */
81271b33a6SRui Paulo #define	SMB_STS			0x01	/* status */
82271b33a6SRui Paulo #define	SMB_ADDR		0x02	/* address */
83271b33a6SRui Paulo #define	SMB_CMD			0x03	/* command */
84271b33a6SRui Paulo #define	SMB_DATA		0x04	/* 32 data registers */
85271b33a6SRui Paulo #define	SMB_BCNT		0x24	/* number of data bytes */
86271b33a6SRui Paulo #define	SMB_ALRM_A		0x25	/* alarm address */
87271b33a6SRui Paulo #define	SMB_ALRM_D		0x26	/* 2 bytes alarm data */
88271b33a6SRui Paulo 
89271b33a6SRui Paulo #define	SMB_STS_DONE		0x80
90271b33a6SRui Paulo #define	SMB_STS_ALRM		0x40
91271b33a6SRui Paulo #define	SMB_STS_RES		0x20
92271b33a6SRui Paulo #define	SMB_STS_STATUS		0x1f
93271b33a6SRui Paulo #define	SMB_STS_OK		0x00	/* OK */
94271b33a6SRui Paulo #define	SMB_STS_UF		0x07	/* Unknown Failure */
95271b33a6SRui Paulo #define	SMB_STS_DANA		0x10	/* Device Address Not Acknowledged */
96271b33a6SRui Paulo #define	SMB_STS_DED		0x11	/* Device Error Detected */
97271b33a6SRui Paulo #define	SMB_STS_DCAD		0x12	/* Device Command Access Denied */
98271b33a6SRui Paulo #define	SMB_STS_UE		0x13	/* Unknown Error */
99271b33a6SRui Paulo #define	SMB_STS_DAD		0x17	/* Device Access Denied */
100271b33a6SRui Paulo #define	SMB_STS_T		0x18	/* Timeout */
101271b33a6SRui Paulo #define	SMB_STS_HUP		0x19	/* Host Unsupported Protocol */
102271b33a6SRui Paulo #define	SMB_STS_B		0x1A	/* Busy */
103271b33a6SRui Paulo #define	SMB_STS_PEC		0x1F	/* PEC (CRC-8) Error */
104271b33a6SRui Paulo 
105271b33a6SRui Paulo #define	SMB_PRTCL_WRITE		0x00
106271b33a6SRui Paulo #define	SMB_PRTCL_READ		0x01
107271b33a6SRui Paulo #define	SMB_PRTCL_QUICK		0x02
108271b33a6SRui Paulo #define	SMB_PRTCL_BYTE		0x04
109271b33a6SRui Paulo #define	SMB_PRTCL_BYTE_DATA	0x06
110271b33a6SRui Paulo #define	SMB_PRTCL_WORD_DATA	0x08
111271b33a6SRui Paulo #define	SMB_PRTCL_BLOCK_DATA	0x0a
112271b33a6SRui Paulo #define	SMB_PRTCL_PROC_CALL	0x0c
113271b33a6SRui Paulo #define	SMB_PRTCL_BLOCK_PROC_CALL 0x0d
114271b33a6SRui Paulo #define	SMB_PRTCL_PEC		0x80
115271b33a6SRui Paulo 
116271b33a6SRui Paulo struct nfsmb_softc {
117271b33a6SRui Paulo 	int rid;
118271b33a6SRui Paulo 	struct resource *res;
119271b33a6SRui Paulo 	device_t smbus;
120271b33a6SRui Paulo 	device_t subdev;
121271b33a6SRui Paulo 	struct mtx lock;
122271b33a6SRui Paulo };
123271b33a6SRui Paulo 
124271b33a6SRui Paulo #define	NFSMB_LOCK(nfsmb)		mtx_lock(&(nfsmb)->lock)
125271b33a6SRui Paulo #define	NFSMB_UNLOCK(nfsmb)		mtx_unlock(&(nfsmb)->lock)
126271b33a6SRui Paulo #define	NFSMB_LOCK_ASSERT(nfsmb)	mtx_assert(&(nfsmb)->lock, MA_OWNED)
127271b33a6SRui Paulo 
128271b33a6SRui Paulo #define	NFSMB_SMBINB(nfsmb, register)					\
129271b33a6SRui Paulo 	(bus_read_1(nfsmb->res, register))
130271b33a6SRui Paulo #define	NFSMB_SMBOUTB(nfsmb, register, value) \
131271b33a6SRui Paulo 	(bus_write_1(nfsmb->res, register, value))
132271b33a6SRui Paulo 
133271b33a6SRui Paulo static int	nfsmb_detach(device_t dev);
134271b33a6SRui Paulo static int	nfsmbsub_detach(device_t dev);
135271b33a6SRui Paulo 
136271b33a6SRui Paulo static int
137271b33a6SRui Paulo nfsmbsub_probe(device_t dev)
138271b33a6SRui Paulo {
139271b33a6SRui Paulo 
140271b33a6SRui Paulo 	device_set_desc(dev, "nForce2/3/4 MCP SMBus Controller");
141271b33a6SRui Paulo 	return (BUS_PROBE_DEFAULT);
142271b33a6SRui Paulo }
143271b33a6SRui Paulo 
144271b33a6SRui Paulo static int
145271b33a6SRui Paulo nfsmb_probe(device_t dev)
146271b33a6SRui Paulo {
147271b33a6SRui Paulo 	u_int16_t vid;
148271b33a6SRui Paulo 	u_int16_t did;
149271b33a6SRui Paulo 
150271b33a6SRui Paulo 	vid = pci_get_vendor(dev);
151271b33a6SRui Paulo 	did = pci_get_device(dev);
152271b33a6SRui Paulo 
153271b33a6SRui Paulo 	if (vid == NFSMB_VENDORID_NVIDIA) {
154271b33a6SRui Paulo 		switch(did) {
155271b33a6SRui Paulo 		case NFSMB_DEVICEID_NF2_SMB:
156271b33a6SRui Paulo 		case NFSMB_DEVICEID_NF2_ULTRA_SMB:
157271b33a6SRui Paulo 		case NFSMB_DEVICEID_NF3_PRO150_SMB:
158271b33a6SRui Paulo 		case NFSMB_DEVICEID_NF3_250GB_SMB:
159271b33a6SRui Paulo 		case NFSMB_DEVICEID_NF4_SMB:
160271b33a6SRui Paulo 		case NFSMB_DEVICEID_NF4_04_SMB:
161271b33a6SRui Paulo 		case NFSMB_DEVICEID_NF4_51_SMB:
162271b33a6SRui Paulo 		case NFSMB_DEVICEID_NF4_55_SMB:
163271b33a6SRui Paulo 		case NFSMB_DEVICEID_NF4_61_SMB:
164271b33a6SRui Paulo 		case NFSMB_DEVICEID_NF4_65_SMB:
165271b33a6SRui Paulo 		case NFSMB_DEVICEID_NF4_67_SMB:
166271b33a6SRui Paulo 		case NFSMB_DEVICEID_NF4_73_SMB:
167271b33a6SRui Paulo 		case NFSMB_DEVICEID_NF4_78S_SMB:
168271b33a6SRui Paulo 		case NFSMB_DEVICEID_NF4_79_SMB:
169271b33a6SRui Paulo 			device_set_desc(dev, "nForce2/3/4 MCP SMBus Controller");
170271b33a6SRui Paulo 			return (BUS_PROBE_DEFAULT);
171271b33a6SRui Paulo 		}
172271b33a6SRui Paulo 	}
173271b33a6SRui Paulo 
174271b33a6SRui Paulo 	return (ENXIO);
175271b33a6SRui Paulo }
176271b33a6SRui Paulo 
177271b33a6SRui Paulo static int
178271b33a6SRui Paulo nfsmbsub_attach(device_t dev)
179271b33a6SRui Paulo {
180271b33a6SRui Paulo 	device_t parent;
181271b33a6SRui Paulo 	struct nfsmb_softc *nfsmbsub_sc = device_get_softc(dev);
182271b33a6SRui Paulo 
183271b33a6SRui Paulo 	parent = device_get_parent(dev);
184271b33a6SRui Paulo 
185271b33a6SRui Paulo 	nfsmbsub_sc->rid = NF2PCI_SMBASE_2;
186271b33a6SRui Paulo 
187271b33a6SRui Paulo 	nfsmbsub_sc->res = bus_alloc_resource_any(parent, SYS_RES_IOPORT,
188271b33a6SRui Paulo 	    &nfsmbsub_sc->rid, RF_ACTIVE);
189271b33a6SRui Paulo 	if (nfsmbsub_sc->res == NULL) {
190271b33a6SRui Paulo 		/* Older incarnations of the device used non-standard BARs. */
191271b33a6SRui Paulo 		nfsmbsub_sc->rid = 0x54;
192271b33a6SRui Paulo 		nfsmbsub_sc->res = bus_alloc_resource_any(parent,
193271b33a6SRui Paulo 		    SYS_RES_IOPORT, &nfsmbsub_sc->rid, RF_ACTIVE);
194271b33a6SRui Paulo 		if (nfsmbsub_sc->res == NULL) {
195271b33a6SRui Paulo 			device_printf(dev, "could not map i/o space\n");
196271b33a6SRui Paulo 			return (ENXIO);
197271b33a6SRui Paulo 		}
198271b33a6SRui Paulo 	}
199271b33a6SRui Paulo 	mtx_init(&nfsmbsub_sc->lock, device_get_nameunit(dev), "nfsmb",
200271b33a6SRui Paulo 	    MTX_DEF);
201271b33a6SRui Paulo 
202271b33a6SRui Paulo 	nfsmbsub_sc->smbus = device_add_child(dev, "smbus", -1);
203271b33a6SRui Paulo 	if (nfsmbsub_sc->smbus == NULL) {
204271b33a6SRui Paulo 		nfsmbsub_detach(dev);
205271b33a6SRui Paulo 		return (EINVAL);
206271b33a6SRui Paulo 	}
207271b33a6SRui Paulo 
208271b33a6SRui Paulo 	bus_generic_attach(dev);
209271b33a6SRui Paulo 
210271b33a6SRui Paulo 	return (0);
211271b33a6SRui Paulo }
212271b33a6SRui Paulo 
213271b33a6SRui Paulo static int
214271b33a6SRui Paulo nfsmb_attach(device_t dev)
215271b33a6SRui Paulo {
216271b33a6SRui Paulo 	struct nfsmb_softc *nfsmb_sc = device_get_softc(dev);
217271b33a6SRui Paulo 
218271b33a6SRui Paulo 	/* Allocate I/O space */
219271b33a6SRui Paulo 	nfsmb_sc->rid = NF2PCI_SMBASE_1;
220271b33a6SRui Paulo 
221271b33a6SRui Paulo 	nfsmb_sc->res = bus_alloc_resource_any(dev, SYS_RES_IOPORT,
222271b33a6SRui Paulo 		&nfsmb_sc->rid, RF_ACTIVE);
223271b33a6SRui Paulo 
224271b33a6SRui Paulo 	if (nfsmb_sc->res == NULL) {
225271b33a6SRui Paulo 		/* Older incarnations of the device used non-standard BARs. */
226271b33a6SRui Paulo 		nfsmb_sc->rid = 0x50;
227271b33a6SRui Paulo 		nfsmb_sc->res = bus_alloc_resource_any(dev,
228271b33a6SRui Paulo 		    SYS_RES_IOPORT, &nfsmb_sc->rid, RF_ACTIVE);
229271b33a6SRui Paulo 		if (nfsmb_sc->res == NULL) {
230271b33a6SRui Paulo 			device_printf(dev, "could not map i/o space\n");
231271b33a6SRui Paulo 			return (ENXIO);
232271b33a6SRui Paulo 		}
233271b33a6SRui Paulo 	}
234271b33a6SRui Paulo 
235271b33a6SRui Paulo 	mtx_init(&nfsmb_sc->lock, device_get_nameunit(dev), "nfsmb", MTX_DEF);
236271b33a6SRui Paulo 
237271b33a6SRui Paulo 	/* Allocate a new smbus device */
238271b33a6SRui Paulo 	nfsmb_sc->smbus = device_add_child(dev, "smbus", -1);
239271b33a6SRui Paulo 	if (!nfsmb_sc->smbus) {
240271b33a6SRui Paulo 		nfsmb_detach(dev);
241271b33a6SRui Paulo 		return (EINVAL);
242271b33a6SRui Paulo 	}
243271b33a6SRui Paulo 
244271b33a6SRui Paulo 	nfsmb_sc->subdev = NULL;
245271b33a6SRui Paulo 	switch (pci_get_device(dev)) {
246271b33a6SRui Paulo 	case NFSMB_DEVICEID_NF2_SMB:
247271b33a6SRui Paulo 	case NFSMB_DEVICEID_NF2_ULTRA_SMB:
248271b33a6SRui Paulo 	case NFSMB_DEVICEID_NF3_PRO150_SMB:
249271b33a6SRui Paulo 	case NFSMB_DEVICEID_NF3_250GB_SMB:
250271b33a6SRui Paulo 	case NFSMB_DEVICEID_NF4_SMB:
251271b33a6SRui Paulo 	case NFSMB_DEVICEID_NF4_04_SMB:
252271b33a6SRui Paulo 	case NFSMB_DEVICEID_NF4_51_SMB:
253271b33a6SRui Paulo 	case NFSMB_DEVICEID_NF4_55_SMB:
254271b33a6SRui Paulo 	case NFSMB_DEVICEID_NF4_61_SMB:
255271b33a6SRui Paulo 	case NFSMB_DEVICEID_NF4_65_SMB:
256271b33a6SRui Paulo 	case NFSMB_DEVICEID_NF4_67_SMB:
257271b33a6SRui Paulo 	case NFSMB_DEVICEID_NF4_73_SMB:
258271b33a6SRui Paulo 	case NFSMB_DEVICEID_NF4_78S_SMB:
259271b33a6SRui Paulo 	case NFSMB_DEVICEID_NF4_79_SMB:
260271b33a6SRui Paulo 		/* Trying to add secondary device as slave */
261271b33a6SRui Paulo 		nfsmb_sc->subdev = device_add_child(dev, "nfsmb", -1);
262271b33a6SRui Paulo 		if (!nfsmb_sc->subdev) {
263271b33a6SRui Paulo 			nfsmb_detach(dev);
264271b33a6SRui Paulo 			return (EINVAL);
265271b33a6SRui Paulo 		}
266271b33a6SRui Paulo 		break;
267271b33a6SRui Paulo 	default:
268271b33a6SRui Paulo 		break;
269271b33a6SRui Paulo 	}
270271b33a6SRui Paulo 
271271b33a6SRui Paulo 	bus_generic_attach(dev);
272271b33a6SRui Paulo 
273271b33a6SRui Paulo 	return (0);
274271b33a6SRui Paulo }
275271b33a6SRui Paulo 
276271b33a6SRui Paulo static int
277271b33a6SRui Paulo nfsmbsub_detach(device_t dev)
278271b33a6SRui Paulo {
279271b33a6SRui Paulo 	device_t parent;
280271b33a6SRui Paulo 	struct nfsmb_softc *nfsmbsub_sc = device_get_softc(dev);
281271b33a6SRui Paulo 
282271b33a6SRui Paulo 	parent = device_get_parent(dev);
283271b33a6SRui Paulo 
284271b33a6SRui Paulo 	if (nfsmbsub_sc->smbus) {
285271b33a6SRui Paulo 		device_delete_child(dev, nfsmbsub_sc->smbus);
286271b33a6SRui Paulo 		nfsmbsub_sc->smbus = NULL;
287271b33a6SRui Paulo 	}
288271b33a6SRui Paulo 	mtx_destroy(&nfsmbsub_sc->lock);
289271b33a6SRui Paulo 	if (nfsmbsub_sc->res) {
290271b33a6SRui Paulo 		bus_release_resource(parent, SYS_RES_IOPORT, nfsmbsub_sc->rid,
291271b33a6SRui Paulo 		    nfsmbsub_sc->res);
292271b33a6SRui Paulo 		nfsmbsub_sc->res = NULL;
293271b33a6SRui Paulo 	}
294271b33a6SRui Paulo 	return (0);
295271b33a6SRui Paulo }
296271b33a6SRui Paulo 
297271b33a6SRui Paulo static int
298271b33a6SRui Paulo nfsmb_detach(device_t dev)
299271b33a6SRui Paulo {
300271b33a6SRui Paulo 	struct nfsmb_softc *nfsmb_sc = device_get_softc(dev);
301271b33a6SRui Paulo 
302271b33a6SRui Paulo 	if (nfsmb_sc->subdev) {
303271b33a6SRui Paulo 		device_delete_child(dev, nfsmb_sc->subdev);
304271b33a6SRui Paulo 		nfsmb_sc->subdev = NULL;
305271b33a6SRui Paulo 	}
306271b33a6SRui Paulo 
307271b33a6SRui Paulo 	if (nfsmb_sc->smbus) {
308271b33a6SRui Paulo 		device_delete_child(dev, nfsmb_sc->smbus);
309271b33a6SRui Paulo 		nfsmb_sc->smbus = NULL;
310271b33a6SRui Paulo 	}
311271b33a6SRui Paulo 
312271b33a6SRui Paulo 	mtx_destroy(&nfsmb_sc->lock);
313271b33a6SRui Paulo 	if (nfsmb_sc->res) {
314271b33a6SRui Paulo 		bus_release_resource(dev, SYS_RES_IOPORT, nfsmb_sc->rid,
315271b33a6SRui Paulo 		    nfsmb_sc->res);
316271b33a6SRui Paulo 		nfsmb_sc->res = NULL;
317271b33a6SRui Paulo 	}
318271b33a6SRui Paulo 
319271b33a6SRui Paulo 	return (0);
320271b33a6SRui Paulo }
321271b33a6SRui Paulo 
322271b33a6SRui Paulo static int
323271b33a6SRui Paulo nfsmb_callback(device_t dev, int index, void *data)
324271b33a6SRui Paulo {
325271b33a6SRui Paulo 	int error = 0;
326271b33a6SRui Paulo 
327271b33a6SRui Paulo 	switch (index) {
328271b33a6SRui Paulo 	case SMB_REQUEST_BUS:
329271b33a6SRui Paulo 	case SMB_RELEASE_BUS:
330271b33a6SRui Paulo 		break;
331271b33a6SRui Paulo 	default:
332271b33a6SRui Paulo 		error = EINVAL;
333271b33a6SRui Paulo 	}
334271b33a6SRui Paulo 
335271b33a6SRui Paulo 	return (error);
336271b33a6SRui Paulo }
337271b33a6SRui Paulo 
338271b33a6SRui Paulo static int
339271b33a6SRui Paulo nfsmb_wait(struct nfsmb_softc *sc)
340271b33a6SRui Paulo {
341271b33a6SRui Paulo 	u_char sts;
342271b33a6SRui Paulo 	int error, count;
343271b33a6SRui Paulo 
344271b33a6SRui Paulo 	NFSMB_LOCK_ASSERT(sc);
345271b33a6SRui Paulo 	if (NFSMB_SMBINB(sc, SMB_PRTCL) != 0)
346271b33a6SRui Paulo 	{
347271b33a6SRui Paulo 		count = 10000;
348271b33a6SRui Paulo 		do {
349271b33a6SRui Paulo 			DELAY(500);
350271b33a6SRui Paulo 		} while (NFSMB_SMBINB(sc, SMB_PRTCL) != 0 && count--);
351271b33a6SRui Paulo 		if (count == 0)
352271b33a6SRui Paulo 			return (SMB_ETIMEOUT);
353271b33a6SRui Paulo 	}
354271b33a6SRui Paulo 
355271b33a6SRui Paulo 	sts = NFSMB_SMBINB(sc, SMB_STS) & SMB_STS_STATUS;
356271b33a6SRui Paulo 	NFSMB_DEBUG(printf("nfsmb: STS=0x%x\n", sts));
357271b33a6SRui Paulo 
358271b33a6SRui Paulo 	switch (sts) {
359271b33a6SRui Paulo 	case SMB_STS_OK:
360271b33a6SRui Paulo 		error = SMB_ENOERR;
361271b33a6SRui Paulo 		break;
362271b33a6SRui Paulo 	case SMB_STS_DANA:
363271b33a6SRui Paulo 		error = SMB_ENOACK;
364271b33a6SRui Paulo 		break;
365271b33a6SRui Paulo 	case SMB_STS_B:
366271b33a6SRui Paulo 		error = SMB_EBUSY;
367271b33a6SRui Paulo 		break;
368271b33a6SRui Paulo 	case SMB_STS_T:
369271b33a6SRui Paulo 		error = SMB_ETIMEOUT;
370271b33a6SRui Paulo 		break;
371271b33a6SRui Paulo 	case SMB_STS_DCAD:
372271b33a6SRui Paulo 	case SMB_STS_DAD:
373271b33a6SRui Paulo 	case SMB_STS_HUP:
374271b33a6SRui Paulo 		error = SMB_ENOTSUPP;
375271b33a6SRui Paulo 		break;
376271b33a6SRui Paulo 	default:
377271b33a6SRui Paulo 		error = SMB_EBUSERR;
378271b33a6SRui Paulo 		break;
379271b33a6SRui Paulo 	}
380271b33a6SRui Paulo 
381271b33a6SRui Paulo 	return (error);
382271b33a6SRui Paulo }
383271b33a6SRui Paulo 
384271b33a6SRui Paulo static int
385271b33a6SRui Paulo nfsmb_quick(device_t dev, u_char slave, int how)
386271b33a6SRui Paulo {
387271b33a6SRui Paulo 	struct nfsmb_softc *sc = (struct nfsmb_softc *)device_get_softc(dev);
388271b33a6SRui Paulo 	u_char protocol;
389271b33a6SRui Paulo 	int error;
390271b33a6SRui Paulo 
391271b33a6SRui Paulo 	protocol = SMB_PRTCL_QUICK;
392271b33a6SRui Paulo 
393271b33a6SRui Paulo 	switch (how) {
394271b33a6SRui Paulo 	case SMB_QWRITE:
395271b33a6SRui Paulo 		protocol |= SMB_PRTCL_WRITE;
396271b33a6SRui Paulo 		NFSMB_DEBUG(printf("nfsmb: QWRITE to 0x%x", slave));
397271b33a6SRui Paulo 		break;
398271b33a6SRui Paulo 	case SMB_QREAD:
399271b33a6SRui Paulo 		protocol |= SMB_PRTCL_READ;
400271b33a6SRui Paulo 		NFSMB_DEBUG(printf("nfsmb: QREAD to 0x%x", slave));
401271b33a6SRui Paulo 		break;
402271b33a6SRui Paulo 	default:
403271b33a6SRui Paulo 		panic("%s: unknown QUICK command (%x)!", __func__, how);
404271b33a6SRui Paulo 	}
405271b33a6SRui Paulo 
406271b33a6SRui Paulo 	NFSMB_LOCK(sc);
407271b33a6SRui Paulo 	NFSMB_SMBOUTB(sc, SMB_ADDR, slave);
408271b33a6SRui Paulo 	NFSMB_SMBOUTB(sc, SMB_PRTCL, protocol);
409271b33a6SRui Paulo 
410271b33a6SRui Paulo 	error = nfsmb_wait(sc);
411271b33a6SRui Paulo 
412271b33a6SRui Paulo 	NFSMB_DEBUG(printf(", error=0x%x\n", error));
413271b33a6SRui Paulo 	NFSMB_UNLOCK(sc);
414271b33a6SRui Paulo 
415271b33a6SRui Paulo 	return (error);
416271b33a6SRui Paulo }
417271b33a6SRui Paulo 
418271b33a6SRui Paulo static int
419271b33a6SRui Paulo nfsmb_sendb(device_t dev, u_char slave, char byte)
420271b33a6SRui Paulo {
421271b33a6SRui Paulo 	struct nfsmb_softc *sc = (struct nfsmb_softc *)device_get_softc(dev);
422271b33a6SRui Paulo 	int error;
423271b33a6SRui Paulo 
424271b33a6SRui Paulo 	NFSMB_LOCK(sc);
425271b33a6SRui Paulo 	NFSMB_SMBOUTB(sc, SMB_CMD, byte);
426271b33a6SRui Paulo 	NFSMB_SMBOUTB(sc, SMB_ADDR, slave);
427271b33a6SRui Paulo 	NFSMB_SMBOUTB(sc, SMB_PRTCL, SMB_PRTCL_WRITE | SMB_PRTCL_BYTE);
428271b33a6SRui Paulo 
429271b33a6SRui Paulo 	error = nfsmb_wait(sc);
430271b33a6SRui Paulo 
431271b33a6SRui Paulo 	NFSMB_DEBUG(printf("nfsmb: SENDB to 0x%x, byte=0x%x, error=0x%x\n", slave, byte, error));
432271b33a6SRui Paulo 	NFSMB_UNLOCK(sc);
433271b33a6SRui Paulo 
434271b33a6SRui Paulo 	return (error);
435271b33a6SRui Paulo }
436271b33a6SRui Paulo 
437271b33a6SRui Paulo static int
438271b33a6SRui Paulo nfsmb_recvb(device_t dev, u_char slave, char *byte)
439271b33a6SRui Paulo {
440271b33a6SRui Paulo 	struct nfsmb_softc *sc = (struct nfsmb_softc *)device_get_softc(dev);
441271b33a6SRui Paulo 	int error;
442271b33a6SRui Paulo 
443271b33a6SRui Paulo 	NFSMB_LOCK(sc);
444271b33a6SRui Paulo 	NFSMB_SMBOUTB(sc, SMB_ADDR, slave);
445271b33a6SRui Paulo 	NFSMB_SMBOUTB(sc, SMB_PRTCL, SMB_PRTCL_READ | SMB_PRTCL_BYTE);
446271b33a6SRui Paulo 
447271b33a6SRui Paulo 	if ((error = nfsmb_wait(sc)) == SMB_ENOERR)
448271b33a6SRui Paulo 		*byte = NFSMB_SMBINB(sc, SMB_DATA);
449271b33a6SRui Paulo 
450271b33a6SRui Paulo 	NFSMB_DEBUG(printf("nfsmb: RECVB from 0x%x, byte=0x%x, error=0x%x\n", slave, *byte, error));
451271b33a6SRui Paulo 	NFSMB_UNLOCK(sc);
452271b33a6SRui Paulo 
453271b33a6SRui Paulo 	return (error);
454271b33a6SRui Paulo }
455271b33a6SRui Paulo 
456271b33a6SRui Paulo static int
457271b33a6SRui Paulo nfsmb_writeb(device_t dev, u_char slave, char cmd, char byte)
458271b33a6SRui Paulo {
459271b33a6SRui Paulo 	struct nfsmb_softc *sc = (struct nfsmb_softc *)device_get_softc(dev);
460271b33a6SRui Paulo 	int error;
461271b33a6SRui Paulo 
462271b33a6SRui Paulo 	NFSMB_LOCK(sc);
463271b33a6SRui Paulo 	NFSMB_SMBOUTB(sc, SMB_CMD, cmd);
464271b33a6SRui Paulo 	NFSMB_SMBOUTB(sc, SMB_DATA, byte);
465271b33a6SRui Paulo 	NFSMB_SMBOUTB(sc, SMB_ADDR, slave);
466271b33a6SRui Paulo 	NFSMB_SMBOUTB(sc, SMB_PRTCL, SMB_PRTCL_WRITE | SMB_PRTCL_BYTE_DATA);
467271b33a6SRui Paulo 
468271b33a6SRui Paulo 	error = nfsmb_wait(sc);
469271b33a6SRui Paulo 
470271b33a6SRui Paulo 	NFSMB_DEBUG(printf("nfsmb: WRITEB to 0x%x, cmd=0x%x, byte=0x%x, error=0x%x\n", slave, cmd, byte, error));
471271b33a6SRui Paulo 	NFSMB_UNLOCK(sc);
472271b33a6SRui Paulo 
473271b33a6SRui Paulo 	return (error);
474271b33a6SRui Paulo }
475271b33a6SRui Paulo 
476271b33a6SRui Paulo static int
477271b33a6SRui Paulo nfsmb_readb(device_t dev, u_char slave, char cmd, char *byte)
478271b33a6SRui Paulo {
479271b33a6SRui Paulo 	struct nfsmb_softc *sc = (struct nfsmb_softc *)device_get_softc(dev);
480271b33a6SRui Paulo 	int error;
481271b33a6SRui Paulo 
482271b33a6SRui Paulo 	NFSMB_LOCK(sc);
483271b33a6SRui Paulo 	NFSMB_SMBOUTB(sc, SMB_CMD, cmd);
484271b33a6SRui Paulo 	NFSMB_SMBOUTB(sc, SMB_ADDR, slave);
485271b33a6SRui Paulo 	NFSMB_SMBOUTB(sc, SMB_PRTCL, SMB_PRTCL_READ | SMB_PRTCL_BYTE_DATA);
486271b33a6SRui Paulo 
487271b33a6SRui Paulo 	if ((error = nfsmb_wait(sc)) == SMB_ENOERR)
488271b33a6SRui Paulo 		*byte = NFSMB_SMBINB(sc, SMB_DATA);
489271b33a6SRui Paulo 
490271b33a6SRui Paulo 	NFSMB_DEBUG(printf("nfsmb: READB from 0x%x, cmd=0x%x, byte=0x%x, error=0x%x\n", slave, cmd, (unsigned char)*byte, error));
491271b33a6SRui Paulo 	NFSMB_UNLOCK(sc);
492271b33a6SRui Paulo 
493271b33a6SRui Paulo 	return (error);
494271b33a6SRui Paulo }
495271b33a6SRui Paulo 
496271b33a6SRui Paulo static int
497271b33a6SRui Paulo nfsmb_writew(device_t dev, u_char slave, char cmd, short word)
498271b33a6SRui Paulo {
499271b33a6SRui Paulo 	struct nfsmb_softc *sc = (struct nfsmb_softc *)device_get_softc(dev);
500271b33a6SRui Paulo 	int error;
501271b33a6SRui Paulo 
502271b33a6SRui Paulo 	NFSMB_LOCK(sc);
503271b33a6SRui Paulo 	NFSMB_SMBOUTB(sc, SMB_CMD, cmd);
504271b33a6SRui Paulo 	NFSMB_SMBOUTB(sc, SMB_DATA, word);
505271b33a6SRui Paulo 	NFSMB_SMBOUTB(sc, SMB_DATA + 1, word >> 8);
506271b33a6SRui Paulo 	NFSMB_SMBOUTB(sc, SMB_ADDR, slave);
507271b33a6SRui Paulo 	NFSMB_SMBOUTB(sc, SMB_PRTCL, SMB_PRTCL_WRITE | SMB_PRTCL_WORD_DATA);
508271b33a6SRui Paulo 
509271b33a6SRui Paulo 	error = nfsmb_wait(sc);
510271b33a6SRui Paulo 
511271b33a6SRui Paulo 	NFSMB_DEBUG(printf("nfsmb: WRITEW to 0x%x, cmd=0x%x, word=0x%x, error=0x%x\n", slave, cmd, word, error));
512271b33a6SRui Paulo 	NFSMB_UNLOCK(sc);
513271b33a6SRui Paulo 
514271b33a6SRui Paulo 	return (error);
515271b33a6SRui Paulo }
516271b33a6SRui Paulo 
517271b33a6SRui Paulo static int
518271b33a6SRui Paulo nfsmb_readw(device_t dev, u_char slave, char cmd, short *word)
519271b33a6SRui Paulo {
520271b33a6SRui Paulo 	struct nfsmb_softc *sc = (struct nfsmb_softc *)device_get_softc(dev);
521271b33a6SRui Paulo 	int error;
522271b33a6SRui Paulo 
523271b33a6SRui Paulo 	NFSMB_LOCK(sc);
524271b33a6SRui Paulo 	NFSMB_SMBOUTB(sc, SMB_CMD, cmd);
525271b33a6SRui Paulo 	NFSMB_SMBOUTB(sc, SMB_ADDR, slave);
526271b33a6SRui Paulo 	NFSMB_SMBOUTB(sc, SMB_PRTCL, SMB_PRTCL_READ | SMB_PRTCL_WORD_DATA);
527271b33a6SRui Paulo 
528271b33a6SRui Paulo 	if ((error = nfsmb_wait(sc)) == SMB_ENOERR)
529271b33a6SRui Paulo 		*word = NFSMB_SMBINB(sc, SMB_DATA) |
530271b33a6SRui Paulo 		    (NFSMB_SMBINB(sc, SMB_DATA + 1) << 8);
531271b33a6SRui Paulo 
532271b33a6SRui Paulo 	NFSMB_DEBUG(printf("nfsmb: READW from 0x%x, cmd=0x%x, word=0x%x, error=0x%x\n", slave, cmd, (unsigned short)*word, error));
533271b33a6SRui Paulo 	NFSMB_UNLOCK(sc);
534271b33a6SRui Paulo 
535271b33a6SRui Paulo 	return (error);
536271b33a6SRui Paulo }
537271b33a6SRui Paulo 
538271b33a6SRui Paulo static int
539271b33a6SRui Paulo nfsmb_bwrite(device_t dev, u_char slave, char cmd, u_char count, char *buf)
540271b33a6SRui Paulo {
541271b33a6SRui Paulo 	struct nfsmb_softc *sc = (struct nfsmb_softc *)device_get_softc(dev);
542271b33a6SRui Paulo 	u_char i;
543271b33a6SRui Paulo 	int error;
544271b33a6SRui Paulo 
545271b33a6SRui Paulo 	if (count < 1 || count > 32)
546271b33a6SRui Paulo 		return (SMB_EINVAL);
547271b33a6SRui Paulo 
548271b33a6SRui Paulo 	NFSMB_LOCK(sc);
549271b33a6SRui Paulo 	NFSMB_SMBOUTB(sc, SMB_CMD, cmd);
550271b33a6SRui Paulo 	NFSMB_SMBOUTB(sc, SMB_BCNT, count);
551271b33a6SRui Paulo 	for (i = 0; i < count; i++)
552271b33a6SRui Paulo 		NFSMB_SMBOUTB(sc, SMB_DATA + i, buf[i]);
553271b33a6SRui Paulo 	NFSMB_SMBOUTB(sc, SMB_ADDR, slave);
554271b33a6SRui Paulo 	NFSMB_SMBOUTB(sc, SMB_PRTCL, SMB_PRTCL_WRITE | SMB_PRTCL_BLOCK_DATA);
555271b33a6SRui Paulo 
556271b33a6SRui Paulo 	error = nfsmb_wait(sc);
557271b33a6SRui Paulo 
558271b33a6SRui Paulo 	NFSMB_DEBUG(printf("nfsmb: WRITEBLK to 0x%x, count=0x%x, cmd=0x%x, error=0x%x", slave, count, cmd, error));
559271b33a6SRui Paulo 	NFSMB_UNLOCK(sc);
560271b33a6SRui Paulo 
561271b33a6SRui Paulo 	return (error);
562271b33a6SRui Paulo }
563271b33a6SRui Paulo 
564271b33a6SRui Paulo static int
565271b33a6SRui Paulo nfsmb_bread(device_t dev, u_char slave, char cmd, u_char *count, char *buf)
566271b33a6SRui Paulo {
567271b33a6SRui Paulo 	struct nfsmb_softc *sc = (struct nfsmb_softc *)device_get_softc(dev);
568271b33a6SRui Paulo 	u_char data, len, i;
569271b33a6SRui Paulo 	int error;
570271b33a6SRui Paulo 
571271b33a6SRui Paulo 	if (*count < 1 || *count > 32)
572271b33a6SRui Paulo 		return (SMB_EINVAL);
573271b33a6SRui Paulo 
574271b33a6SRui Paulo 	NFSMB_LOCK(sc);
575271b33a6SRui Paulo 	NFSMB_SMBOUTB(sc, SMB_CMD, cmd);
576271b33a6SRui Paulo 	NFSMB_SMBOUTB(sc, SMB_ADDR, slave);
577271b33a6SRui Paulo 	NFSMB_SMBOUTB(sc, SMB_PRTCL, SMB_PRTCL_READ | SMB_PRTCL_BLOCK_DATA);
578271b33a6SRui Paulo 
579271b33a6SRui Paulo 	if ((error = nfsmb_wait(sc)) == SMB_ENOERR) {
580271b33a6SRui Paulo 		len = NFSMB_SMBINB(sc, SMB_BCNT);
581271b33a6SRui Paulo 		for (i = 0; i < len; i++) {
582271b33a6SRui Paulo 			data = NFSMB_SMBINB(sc, SMB_DATA + i);
583271b33a6SRui Paulo 			if (i < *count)
584271b33a6SRui Paulo 				buf[i] = data;
585271b33a6SRui Paulo 		}
586271b33a6SRui Paulo 		*count = len;
587271b33a6SRui Paulo 	}
588271b33a6SRui Paulo 
589271b33a6SRui Paulo 	NFSMB_DEBUG(printf("nfsmb: READBLK to 0x%x, count=0x%x, cmd=0x%x, error=0x%x", slave, *count, cmd, error));
590271b33a6SRui Paulo 	NFSMB_UNLOCK(sc);
591271b33a6SRui Paulo 
592271b33a6SRui Paulo 	return (error);
593271b33a6SRui Paulo }
594271b33a6SRui Paulo 
595271b33a6SRui Paulo static device_method_t nfsmb_methods[] = {
596271b33a6SRui Paulo 	/* Device interface */
597271b33a6SRui Paulo 	DEVMETHOD(device_probe,		nfsmb_probe),
598271b33a6SRui Paulo 	DEVMETHOD(device_attach,	nfsmb_attach),
599271b33a6SRui Paulo 	DEVMETHOD(device_detach,	nfsmb_detach),
600271b33a6SRui Paulo 
601271b33a6SRui Paulo 	/* SMBus interface */
602271b33a6SRui Paulo 	DEVMETHOD(smbus_callback,	nfsmb_callback),
603271b33a6SRui Paulo 	DEVMETHOD(smbus_quick,		nfsmb_quick),
604271b33a6SRui Paulo 	DEVMETHOD(smbus_sendb,		nfsmb_sendb),
605271b33a6SRui Paulo 	DEVMETHOD(smbus_recvb,		nfsmb_recvb),
606271b33a6SRui Paulo 	DEVMETHOD(smbus_writeb,		nfsmb_writeb),
607271b33a6SRui Paulo 	DEVMETHOD(smbus_readb,		nfsmb_readb),
608271b33a6SRui Paulo 	DEVMETHOD(smbus_writew,		nfsmb_writew),
609271b33a6SRui Paulo 	DEVMETHOD(smbus_readw,		nfsmb_readw),
610271b33a6SRui Paulo 	DEVMETHOD(smbus_bwrite,		nfsmb_bwrite),
611271b33a6SRui Paulo 	DEVMETHOD(smbus_bread,		nfsmb_bread),
612271b33a6SRui Paulo 	{ 0, 0 }
613271b33a6SRui Paulo };
614271b33a6SRui Paulo 
615271b33a6SRui Paulo static device_method_t nfsmbsub_methods[] = {
616271b33a6SRui Paulo 	/* Device interface */
617271b33a6SRui Paulo 	DEVMETHOD(device_probe,		nfsmbsub_probe),
618271b33a6SRui Paulo 	DEVMETHOD(device_attach,	nfsmbsub_attach),
619271b33a6SRui Paulo 	DEVMETHOD(device_detach,	nfsmbsub_detach),
620271b33a6SRui Paulo 
621271b33a6SRui Paulo 	/* SMBus interface */
622271b33a6SRui Paulo 	DEVMETHOD(smbus_callback,	nfsmb_callback),
623271b33a6SRui Paulo 	DEVMETHOD(smbus_quick,		nfsmb_quick),
624271b33a6SRui Paulo 	DEVMETHOD(smbus_sendb,		nfsmb_sendb),
625271b33a6SRui Paulo 	DEVMETHOD(smbus_recvb,		nfsmb_recvb),
626271b33a6SRui Paulo 	DEVMETHOD(smbus_writeb,		nfsmb_writeb),
627271b33a6SRui Paulo 	DEVMETHOD(smbus_readb,		nfsmb_readb),
628271b33a6SRui Paulo 	DEVMETHOD(smbus_writew,		nfsmb_writew),
629271b33a6SRui Paulo 	DEVMETHOD(smbus_readw,		nfsmb_readw),
630271b33a6SRui Paulo 	DEVMETHOD(smbus_bwrite,		nfsmb_bwrite),
631271b33a6SRui Paulo 	DEVMETHOD(smbus_bread,		nfsmb_bread),
632271b33a6SRui Paulo 	{ 0, 0 }
633271b33a6SRui Paulo };
634271b33a6SRui Paulo 
635271b33a6SRui Paulo static driver_t nfsmb_driver = {
636271b33a6SRui Paulo 	"nfsmb",
637271b33a6SRui Paulo 	nfsmb_methods,
638271b33a6SRui Paulo 	sizeof(struct nfsmb_softc),
639271b33a6SRui Paulo };
640271b33a6SRui Paulo 
641271b33a6SRui Paulo static driver_t nfsmbsub_driver = {
642271b33a6SRui Paulo 	"nfsmb",
643271b33a6SRui Paulo 	nfsmbsub_methods,
644271b33a6SRui Paulo 	sizeof(struct nfsmb_softc),
645271b33a6SRui Paulo };
646271b33a6SRui Paulo 
647*bd53cac1SJohn Baldwin DRIVER_MODULE(nfsmb, pci, nfsmb_driver, 0, 0);
648*bd53cac1SJohn Baldwin DRIVER_MODULE(nfsmb, nfsmb, nfsmbsub_driver, 0, 0);
649c6d39765SJohn Baldwin DRIVER_MODULE(smbus, nfsmb, smbus_driver, 0, 0);
650271b33a6SRui Paulo 
651271b33a6SRui Paulo MODULE_DEPEND(nfsmb, pci, 1, 1, 1);
652271b33a6SRui Paulo MODULE_DEPEND(nfsmb, smbus, SMBUS_MINVER, SMBUS_PREFVER, SMBUS_MAXVER);
653271b33a6SRui Paulo MODULE_VERSION(nfsmb, 1);
654