rp_pci.c revision 144090
1227825Stheraven/*-
2227825Stheraven * Copyright (c) Comtrol Corporation <support@comtrol.com>
3227825Stheraven * All rights reserved.
4227825Stheraven *
5227825Stheraven * PCI-specific part separated from:
6227825Stheraven * sys/i386/isa/rp.c,v 1.33 1999/09/28 11:45:27 phk Exp
7227825Stheraven *
8227825Stheraven * Redistribution and use in source and binary forms, with or without
9227825Stheraven * modification, are permitted prodived that the follwoing conditions
10227825Stheraven * are met.
11227825Stheraven * 1. Redistributions of source code must retain the above copyright
12227825Stheraven *    notive, this list of conditions and the following disclainer.
13227825Stheraven * 2. Redistributions in binary form must reproduce the above copyright
14227825Stheraven *    notice, this list of conditions and the following disclaimer in the
15227825Stheraven *    documentation and/or other materials prodided with the distribution.
16227825Stheraven * 3. All advertising materials mentioning features or use of this software
17227825Stheraven *    must display the following acknowledgement:
18227825Stheraven *       This product includes software developed by Comtrol Corporation.
19227825Stheraven * 4. The name of Comtrol Corporation may not be used to endorse or
20227825Stheraven *    promote products derived from this software without specific
21227825Stheraven *    prior written permission.
22227825Stheraven *
23227825Stheraven * THIS SOFTWARE IS PROVIDED BY COMTROL CORPORATION ``AS IS'' AND ANY
24227825Stheraven * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
25227825Stheraven * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
26227825Stheraven * ARE DISCLAIMED.  IN NO EVENT SHALL COMTROL CORPORATION BE LIABLE FOR
27227825Stheraven * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
28227825Stheraven * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
29227825Stheraven * OR SERVICES; LOSS OF USE, DATA, LIFE OR PROFITS; OR BUSINESS INTERRUPTION)
30227825Stheraven * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
31227825Stheraven * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
32227825Stheraven * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
33227825Stheraven * SUCH DAMAGE.
34227825Stheraven */
35227825Stheraven
36227825Stheraven#include <sys/cdefs.h>
37227825Stheraven__FBSDID("$FreeBSD: head/sys/dev/rp/rp_pci.c 144090 2005-03-25 03:10:51Z jhb $");
38227825Stheraven
39227825Stheraven#include <sys/param.h>
40227825Stheraven#include <sys/systm.h>
41227825Stheraven#include <sys/fcntl.h>
42227825Stheraven#include <sys/malloc.h>
43227825Stheraven#include <sys/tty.h>
44227825Stheraven#include <sys/conf.h>
45227825Stheraven#include <sys/kernel.h>
46227825Stheraven#include <sys/module.h>
47227825Stheraven#include <machine/resource.h>
48227825Stheraven#include <machine/bus.h>
49227825Stheraven#include <sys/bus.h>
50227825Stheraven#include <sys/rman.h>
51227825Stheraven
52227825Stheraven#define ROCKET_C
53227825Stheraven#include <dev/rp/rpreg.h>
54227825Stheraven#include <dev/rp/rpvar.h>
55227825Stheraven
56227825Stheraven#include <dev/pci/pcireg.h>
57227825Stheraven#include <dev/pci/pcivar.h>
58227825Stheraven
59227825Stheraven/* PCI IDs  */
60227825Stheraven#define RP_VENDOR_ID		0x11FE
61227825Stheraven#define RP_DEVICE_ID_32I	0x0001
62227825Stheraven#define RP_DEVICE_ID_8I		0x0002
63227825Stheraven#define RP_DEVICE_ID_16I	0x0003
64227825Stheraven#define RP_DEVICE_ID_4Q		0x0004
65227825Stheraven#define RP_DEVICE_ID_8O		0x0005
66227825Stheraven#define RP_DEVICE_ID_8J		0x0006
67227825Stheraven#define RP_DEVICE_ID_4J		0x0007
68227825Stheraven#define RP_DEVICE_ID_6M		0x000C
69261272Sdim#define RP_DEVICE_ID_4M		0x000D
70261272Sdim#define RP_DEVICE_ID_UPCI_8O	0x0805
71261272Sdim
72261272Sdim/**************************************************************************
73261272Sdim  MUDBAC remapped for PCI
74227825Stheraven**************************************************************************/
75227825Stheraven
76227825Stheraven#define _CFG_INT_PCI	0x40
77227825Stheraven#define _PCI_INT_FUNC	0x3A
78227825Stheraven
79227825Stheraven#define PCI_STROB	0x2000
80227825Stheraven#define INTR_EN_PCI	0x0010
81227825Stheraven
82227825Stheraven/***************************************************************************
83227825StheravenFunction: sPCIControllerEOI
84227825StheravenPurpose:  Strobe the MUDBAC's End Of Interrupt bit.
85227825StheravenCall:	  sPCIControllerEOI(CtlP)
86227825Stheraven	  CONTROLLER_T *CtlP; Ptr to controller structure
87227825Stheraven*/
88227825Stheraven#define sPCIControllerEOI(CtlP) rp_writeio2(CtlP, 0, _PCI_INT_FUNC, PCI_STROB)
89227825Stheraven
90227825Stheraven/***************************************************************************
91227825StheravenFunction: sPCIGetControllerIntStatus
92227825StheravenPurpose:  Get the controller interrupt status
93227825StheravenCall:	  sPCIGetControllerIntStatus(CtlP)
94227825Stheraven	  CONTROLLER_T *CtlP; Ptr to controller structure
95227825StheravenReturn:   Byte_t: The controller interrupt status in the lower 4
96227825Stheraven			 bits.	Bits 0 through 3 represent AIOP's 0
97227825Stheraven			 through 3 respectively.  If a bit is set that
98227825Stheraven			 AIOP is interrupting.	Bits 4 through 7 will
99227825Stheraven			 always be cleared.
100227825Stheraven*/
101227825Stheraven#define sPCIGetControllerIntStatus(CTLP) ((rp_readio2(CTLP, 0, _PCI_INT_FUNC) >> 8) & 0x1f)
102227825Stheraven
103227825Stheravenstatic devclass_t rp_devclass;
104227825Stheraven
105227825Stheravenstatic int rp_pciprobe(device_t dev);
106227825Stheravenstatic int rp_pciattach(device_t dev);
107227825Stheraven#if notdef
108227825Stheravenstatic int rp_pcidetach(device_t dev);
109227825Stheravenstatic int rp_pcishutdown(device_t dev);
110227825Stheraven#endif /* notdef */
111227825Stheravenstatic void rp_pcireleaseresource(CONTROLLER_t *ctlp);
112227825Stheravenstatic int sPCIInitController( CONTROLLER_t *CtlP,
113227825Stheraven			       int AiopNum,
114227825Stheraven			       int IRQNum,
115227825Stheraven			       Byte_t Frequency,
116227825Stheraven			       int PeriodicOnly,
117227825Stheraven			       int VendorDevice);
118227825Stheravenstatic rp_aiop2rid_t rp_pci_aiop2rid;
119288943Sdimstatic rp_aiop2off_t rp_pci_aiop2off;
120227825Stheravenstatic rp_ctlmask_t rp_pci_ctlmask;
121227825Stheraven
122227825Stheraven/*
123227825Stheraven * The following functions are the pci-specific part
124227825Stheraven * of rp driver.
125227825Stheraven */
126227825Stheraven
127227825Stheravenstatic int
128227825Stheravenrp_pciprobe(device_t dev)
129227825Stheraven{
130227825Stheraven	char *s;
131227825Stheraven
132227825Stheraven	s = NULL;
133227825Stheraven	if (pci_get_vendor(dev) == RP_VENDOR_ID)
134227825Stheraven		s = "RocketPort PCI";
135227825Stheraven
136227825Stheraven	if (s != NULL) {
137227825Stheraven		device_set_desc(dev, s);
138261272Sdim		return (BUS_PROBE_DEFAULT);
139261272Sdim	}
140261272Sdim
141261272Sdim	return (ENXIO);
142261272Sdim}
143261272Sdim
144261272Sdimstatic int
145227825Stheravenrp_pciattach(device_t dev)
146227825Stheraven{
147227825Stheraven	int	num_ports, num_aiops;
148261272Sdim	int	aiop;
149261272Sdim	CONTROLLER_t	*ctlp;
150261272Sdim	int	unit;
151261272Sdim	int	retval;
152261272Sdim	u_int32_t	stcmd;
153227825Stheraven
154227825Stheraven	ctlp = device_get_softc(dev);
155261272Sdim	bzero(ctlp, sizeof(*ctlp));
156261272Sdim	ctlp->dev = dev;
157261272Sdim	unit = device_get_unit(dev);
158261272Sdim	ctlp->aiop2rid = rp_pci_aiop2rid;
159227825Stheraven	ctlp->aiop2off = rp_pci_aiop2off;
160227825Stheraven	ctlp->ctlmask = rp_pci_ctlmask;
161261272Sdim
162261272Sdim	/* Wake up the device. */
163261272Sdim	stcmd = pci_read_config(dev, PCIR_COMMAND, 4);
164261272Sdim	if ((stcmd & PCIM_CMD_PORTEN) == 0) {
165227825Stheraven		stcmd |= (PCIM_CMD_PORTEN);
166227825Stheraven		pci_write_config(dev, PCIR_COMMAND, 4, stcmd);
167227825Stheraven	}
168227825Stheraven
169227825Stheraven	/* The IO ports of AIOPs for a PCI controller are continuous. */
170227825Stheraven	ctlp->io_num = 1;
171227825Stheraven	ctlp->io_rid = malloc(sizeof(*(ctlp->io_rid)) * ctlp->io_num, M_DEVBUF, M_NOWAIT | M_ZERO);
172227825Stheraven	ctlp->io = malloc(sizeof(*(ctlp->io)) * ctlp->io_num, M_DEVBUF, M_NOWAIT | M_ZERO);
173227825Stheraven	if (ctlp->io_rid == NULL || ctlp->io == NULL) {
174227825Stheraven		device_printf(dev, "rp_pciattach: Out of memory.\n");
175227825Stheraven		retval = ENOMEM;
176227825Stheraven		goto nogo;
177227825Stheraven	}
178227825Stheraven
179227825Stheraven	ctlp->bus_ctlp = NULL;
180227825Stheraven
181227825Stheraven	switch (pci_get_device(dev)) {
182227825Stheraven	case RP_DEVICE_ID_UPCI_8O:
183227825Stheraven		ctlp->io_rid[0] = PCIR_BAR(2);
184227825Stheraven		break;
185227825Stheraven	default:
186227825Stheraven		ctlp->io_rid[0] = PCIR_BAR(0);
187227825Stheraven		break;
188227825Stheraven	}
189227825Stheraven	ctlp->io[0] = bus_alloc_resource_any(dev, SYS_RES_IOPORT,
190227825Stheraven		&ctlp->io_rid[0], RF_ACTIVE);
191227825Stheraven	if(ctlp->io[0] == NULL) {
192227825Stheraven		device_printf(dev, "ioaddr mapping failed for RocketPort(PCI).\n");
193227825Stheraven		retval = ENXIO;
194227825Stheraven		goto nogo;
195227825Stheraven	}
196227825Stheraven
197227825Stheraven	num_aiops = sPCIInitController(ctlp,
198227825Stheraven				       MAX_AIOPS_PER_BOARD, 0,
199227825Stheraven				       FREQ_DIS, 0, pci_get_device(dev));
200227825Stheraven
201227825Stheraven	num_ports = 0;
202227825Stheraven	for(aiop=0; aiop < num_aiops; aiop++) {
203227825Stheraven		sResetAiopByNum(ctlp, aiop);
204227825Stheraven		num_ports += sGetAiopNumChan(ctlp, aiop);
205227825Stheraven	}
206227825Stheraven
207227825Stheraven	retval = rp_attachcommon(ctlp, num_aiops, num_ports);
208227825Stheraven	if (retval != 0)
209227825Stheraven		goto nogo;
210227825Stheraven
211227825Stheraven	return (0);
212227825Stheraven
213227825Stheravennogo:
214227825Stheraven	rp_pcireleaseresource(ctlp);
215227825Stheraven
216227825Stheraven	return (retval);
217227825Stheraven}
218227825Stheraven
219227825Stheravenstatic int
220227825Stheravenrp_pcidetach(device_t dev)
221227825Stheraven{
222227825Stheraven	CONTROLLER_t	*ctlp;
223227825Stheraven
224227825Stheraven	if (device_get_state(dev) == DS_BUSY)
225227825Stheraven		return (EBUSY);
226227825Stheraven
227227825Stheraven	ctlp = device_get_softc(dev);
228227825Stheraven
229227825Stheraven	rp_pcireleaseresource(ctlp);
230227825Stheraven
231227825Stheraven	return (0);
232227825Stheraven}
233227825Stheraven
234227825Stheravenstatic int
235227825Stheravenrp_pcishutdown(device_t dev)
236227825Stheraven{
237227825Stheraven	CONTROLLER_t	*ctlp;
238227825Stheraven
239227825Stheraven	if (device_get_state(dev) == DS_BUSY)
240227825Stheraven		return (EBUSY);
241227825Stheraven
242227825Stheraven	ctlp = device_get_softc(dev);
243227825Stheraven
244227825Stheraven	rp_pcireleaseresource(ctlp);
245227825Stheraven
246227825Stheraven	return (0);
247227825Stheraven}
248227825Stheraven
249227825Stheravenstatic void
250227825Stheravenrp_pcireleaseresource(CONTROLLER_t *ctlp)
251261272Sdim{
252261272Sdim	rp_untimeout();
253261272Sdim	if (ctlp->io != NULL) {
254261272Sdim		if (ctlp->io[0] != NULL)
255261272Sdim			bus_release_resource(ctlp->dev, SYS_RES_IOPORT, ctlp->io_rid[0], ctlp->io[0]);
256227825Stheraven		free(ctlp->io, M_DEVBUF);
257227825Stheraven		ctlp->io = NULL;
258227825Stheraven	}
259227825Stheraven	if (ctlp->io_rid != NULL) {
260227825Stheraven		free(ctlp->io_rid, M_DEVBUF);
261227825Stheraven		ctlp->io = NULL;
262227825Stheraven	}
263227825Stheraven	rp_releaseresource(ctlp);
264227825Stheraven}
265227825Stheraven
266227825Stheravenstatic int
267227825StheravensPCIInitController( CONTROLLER_t *CtlP,
268227825Stheraven		    int AiopNum,
269227825Stheraven		    int IRQNum,
270227825Stheraven		    Byte_t Frequency,
271227825Stheraven		    int PeriodicOnly,
272227825Stheraven		    int VendorDevice)
273227825Stheraven{
274227825Stheraven	int		i;
275227825Stheraven
276227825Stheraven	CtlP->CtlID = CTLID_0001;	/* controller release 1 */
277227825Stheraven
278227825Stheraven	sPCIControllerEOI(CtlP);
279227825Stheraven
280227825Stheraven	/* Init AIOPs */
281227825Stheraven	CtlP->NumAiop = 0;
282227825Stheraven	for(i=0; i < AiopNum; i++)
283227825Stheraven	{
284227825Stheraven		/*device_printf(CtlP->dev, "aiop %d.\n", i);*/
285227825Stheraven		CtlP->AiopID[i] = sReadAiopID(CtlP, i);	/* read AIOP ID */
286227825Stheraven		/*device_printf(CtlP->dev, "ID = %d.\n", CtlP->AiopID[i]);*/
287227825Stheraven		if(CtlP->AiopID[i] == AIOPID_NULL)	/* if AIOP does not exist */
288227825Stheraven		{
289227825Stheraven			break;				/* done looking for AIOPs */
290227825Stheraven		}
291227825Stheraven
292227825Stheraven		switch( VendorDevice ) {
293227825Stheraven		case RP_DEVICE_ID_4Q:
294227825Stheraven		case RP_DEVICE_ID_4J:
295227825Stheraven		case RP_DEVICE_ID_4M:
296227825Stheraven      			CtlP->AiopNumChan[i] = 4;
297227825Stheraven			break;
298227825Stheraven		case RP_DEVICE_ID_6M:
299227825Stheraven      			CtlP->AiopNumChan[i] = 6;
300227825Stheraven			break;
301288943Sdim		case RP_DEVICE_ID_8O:
302227825Stheraven		case RP_DEVICE_ID_8J:
303227825Stheraven		case RP_DEVICE_ID_8I:
304227825Stheraven		case RP_DEVICE_ID_16I:
305227825Stheraven		case RP_DEVICE_ID_32I:
306227825Stheraven      			CtlP->AiopNumChan[i] = 8;
307227825Stheraven			break;
308227825Stheraven		default:
309227825Stheraven#if notdef
310227825Stheraven      			CtlP->AiopNumChan[i] = 8;
311227825Stheraven#else
312227825Stheraven      			CtlP->AiopNumChan[i] = sReadAiopNumChan(CtlP, i);
313227825Stheraven#endif /* notdef */
314227825Stheraven			break;
315227825Stheraven		}
316227825Stheraven		/*device_printf(CtlP->dev, "%d channels.\n", CtlP->AiopNumChan[i]);*/
317227825Stheraven		rp_writeaiop2(CtlP, i, _INDX_ADDR,_CLK_PRE);	/* clock prescaler */
318227825Stheraven		/*device_printf(CtlP->dev, "configuring clock prescaler.\n");*/
319227825Stheraven		rp_writeaiop1(CtlP, i, _INDX_DATA,CLOCK_PRESC);
320261272Sdim		/*device_printf(CtlP->dev, "configured clock prescaler.\n");*/
321261272Sdim		CtlP->NumAiop++;				/* bump count of AIOPs */
322261272Sdim	}
323261272Sdim
324261272Sdim	if(CtlP->NumAiop == 0)
325227825Stheraven		return(-1);
326227825Stheraven	else
327227825Stheraven		return(CtlP->NumAiop);
328261272Sdim}
329261272Sdim
330261272Sdim/*
331261272Sdim * ARGSUSED
332261272Sdim * Maps (aiop, offset) to rid.
333227825Stheraven */
334227825Stheravenstatic int
335261272Sdimrp_pci_aiop2rid(int aiop, int offset)
336261272Sdim{
337261272Sdim	/* Always return zero for a PCI controller. */
338261272Sdim	return 0;
339261272Sdim}
340227825Stheraven
341227825Stheraven/*
342261272Sdim * ARGSUSED
343261272Sdim * Maps (aiop, offset) to the offset of resource.
344261272Sdim */
345261272Sdimstatic int
346227825Stheravenrp_pci_aiop2off(int aiop, int offset)
347227825Stheraven{
348227825Stheraven	/* Each AIOP reserves 0x40 bytes. */
349227825Stheraven	return aiop * 0x40 + offset;
350227825Stheraven}
351227825Stheraven
352227825Stheraven/* Read the int status for a PCI controller. */
353227825Stheravenstatic unsigned char
354227825Stheravenrp_pci_ctlmask(CONTROLLER_t *ctlp)
355227825Stheraven{
356227825Stheraven	return sPCIGetControllerIntStatus(ctlp);
357227825Stheraven}
358227825Stheraven
359227825Stheravenstatic device_method_t rp_pcimethods[] = {
360227825Stheraven	/* Device interface */
361227825Stheraven	DEVMETHOD(device_probe,		rp_pciprobe),
362227825Stheraven	DEVMETHOD(device_attach,	rp_pciattach),
363227825Stheraven	DEVMETHOD(device_detach,	rp_pcidetach),
364227825Stheraven	DEVMETHOD(device_shutdown,	rp_pcishutdown),
365227825Stheraven
366227825Stheraven	{ 0, 0 }
367227825Stheraven};
368227825Stheraven
369227825Stheravenstatic driver_t rp_pcidriver = {
370227825Stheraven	"rp",
371227825Stheraven	rp_pcimethods,
372227825Stheraven	sizeof(CONTROLLER_t),
373227825Stheraven};
374227825Stheraven
375227825Stheraven/*
376227825Stheraven * rp can be attached to a pci bus.
377227825Stheraven */
378227825StheravenDRIVER_MODULE(rp, pci, rp_pcidriver, rp_devclass, 0, 0);
379227825Stheraven