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