[PATCH] Altix: ioc3 serial support

Add driver support for a 2 port PCI IOC3-based serial card on Altix boxes:

This is a re-submission.  On the original submission I was asked to
organize the code so that the MIPS ioc3 ethernet and serial parts could be
used with this driver.  Stanislaw Skowronek was kind enough to provide the
shim layer for this - thanks Stanislaw.  This patch includes the shim layer
and the Altix PCI ioc3 serial driver.  The MIPS merged ioc3 ethernet and
serial support is forthcoming.

Signed-off-by: Patrick Gefre <pfg@sgi.com>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
This commit is contained in:
Patrick Gefre 2006-01-14 13:20:40 -08:00 committed by Linus Torvalds
parent 7170be5f58
commit 2d0cfb5279
10 changed files with 3410 additions and 1 deletions

View File

@ -701,6 +701,7 @@ CONFIG_SERIAL_CORE_CONSOLE=y
CONFIG_SERIAL_SGI_L1_CONSOLE=y
# CONFIG_SERIAL_JSM is not set
CONFIG_SERIAL_SGI_IOC4=y
CONFIG_SERIAL_SGI_IOC3=y
CONFIG_UNIX98_PTYS=y
CONFIG_LEGACY_PTYS=y
CONFIG_LEGACY_PTY_COUNT=256
@ -1046,6 +1047,7 @@ CONFIG_INFINIBAND_IPOIB=m
# SN Devices
#
CONFIG_SGI_IOC4=y
CONFIG_SGI_IOC3=y
#
# File systems

View File

@ -659,6 +659,7 @@ CONFIG_SERIAL_CORE_CONSOLE=y
CONFIG_SERIAL_SGI_L1_CONSOLE=y
# CONFIG_SERIAL_JSM is not set
CONFIG_SERIAL_SGI_IOC4=y
CONFIG_SERIAL_SGI_IOC3=y
CONFIG_UNIX98_PTYS=y
CONFIG_LEGACY_PTYS=y
CONFIG_LEGACY_PTY_COUNT=256
@ -899,6 +900,7 @@ CONFIG_INFINIBAND_SRP=m
# SN Devices
#
CONFIG_SGI_IOC4=y
CONFIG_SGI_IOC3=y
#
# File systems

View File

@ -190,7 +190,6 @@ config SERIAL_8250_BOCA
To compile this driver as a module, choose M here: the module
will be called 8250_boca.
config SERIAL_8250_HUB6
tristate "Support Hub6 cards"
depends on SERIAL_8250 != n && ISA && SERIAL_8250_MANY_PORTS
@ -917,4 +916,12 @@ config SERIAL_SGI_IOC4
and wish to use the serial ports on this card, say Y.
Otherwise, say N.
config SERIAL_SGI_IOC3
tristate "SGI Altix IOC3 serial support"
depends on (IA64_GENERIC || IA64_SGI_SN2) && SGI_IOC3
select SERIAL_CORE
help
If you have an SGI Altix with an IOC3 serial card,
say Y or M. Otherwise, say N.
endmenu

View File

@ -56,4 +56,5 @@ obj-$(CONFIG_SERIAL_JSM) += jsm/
obj-$(CONFIG_SERIAL_TXX9) += serial_txx9.o
obj-$(CONFIG_SERIAL_VR41XX) += vr41xx_siu.o
obj-$(CONFIG_SERIAL_SGI_IOC4) += ioc4_serial.o
obj-$(CONFIG_SERIAL_SGI_IOC3) += ioc3_serial.o
obj-$(CONFIG_SERIAL_AT91) += at91_serial.o

2197
drivers/serial/ioc3_serial.c Normal file

File diff suppressed because it is too large Load Diff

View File

@ -17,4 +17,18 @@ config SGI_IOC4
If you have an SGI Altix with an IOC4-based
I/O controller say Y. Otherwise say N.
config SGI_IOC3
tristate "SGI IOC3 Base IO support"
depends on (IA64_GENERIC || IA64_SGI_SN2)
default m
---help---
This option enables basic support for the SGI IOC3-based Base IO
controller card. This option does not enable any specific
functions on such a card, but provides necessary infrastructure
for other drivers to utilize.
If you have an SGI Altix with an IOC3-based
I/O controller or a PCI IOC3 serial card say Y.
Otherwise say N.
endmenu

View File

@ -4,3 +4,4 @@
#
obj-$(CONFIG_SGI_IOC4) += ioc4.o
obj-$(CONFIG_SGI_IOC3) += ioc3.o

851
drivers/sn/ioc3.c Normal file
View File

@ -0,0 +1,851 @@
/*
* SGI IOC3 master driver and IRQ demuxer
*
* Copyright (c) 2005 Stanislaw Skowronek <skylark@linux-mips.org>
* Heavily based on similar work by:
* Brent Casavant <bcasavan@sgi.com> - IOC4 master driver
* Pat Gefre <pfg@sgi.com> - IOC3 serial port IRQ demuxer
*/
#include <linux/config.h>
#include <linux/errno.h>
#include <linux/module.h>
#include <linux/pci.h>
#include <linux/interrupt.h>
#include <linux/spinlock.h>
#include <linux/delay.h>
#include <linux/ioc3.h>
#include <linux/rwsem.h>
#define IOC3_PCI_SIZE 0x100000
static LIST_HEAD(ioc3_devices);
static int ioc3_counter;
static DECLARE_RWSEM(ioc3_devices_rwsem);
static struct ioc3_submodule *ioc3_submodules[IOC3_MAX_SUBMODULES];
static struct ioc3_submodule *ioc3_ethernet;
static rwlock_t ioc3_submodules_lock = RW_LOCK_UNLOCKED;
/* NIC probing code */
#define GPCR_MLAN_EN 0x00200000 /* enable MCR to pin 8 */
static inline unsigned mcr_pack(unsigned pulse, unsigned sample)
{
return (pulse << 10) | (sample << 2);
}
static int nic_wait(struct ioc3_driver_data *idd)
{
volatile unsigned mcr;
do {
mcr = (volatile unsigned)idd->vma->mcr;
} while (!(mcr & 2));
return mcr & 1;
}
static int nic_reset(struct ioc3_driver_data *idd)
{
int presence;
unsigned long flags;
local_irq_save(flags);
idd->vma->mcr = mcr_pack(500, 65);
presence = nic_wait(idd);
local_irq_restore(flags);
udelay(500);
return presence;
}
static inline int nic_read_bit(struct ioc3_driver_data *idd)
{
int result;
unsigned long flags;
local_irq_save(flags);
idd->vma->mcr = mcr_pack(6, 13);
result = nic_wait(idd);
local_irq_restore(flags);
udelay(500);
return result;
}
static inline void nic_write_bit(struct ioc3_driver_data *idd, int bit)
{
if (bit)
idd->vma->mcr = mcr_pack(6, 110);
else
idd->vma->mcr = mcr_pack(80, 30);
nic_wait(idd);
}
static unsigned nic_read_byte(struct ioc3_driver_data *idd)
{
unsigned result = 0;
int i;
for (i = 0; i < 8; i++)
result = (result >> 1) | (nic_read_bit(idd) << 7);
return result;
}
static void nic_write_byte(struct ioc3_driver_data *idd, int byte)
{
int i, bit;
for (i = 8; i; i--) {
bit = byte & 1;
byte >>= 1;
nic_write_bit(idd, bit);
}
}
static unsigned long
nic_find(struct ioc3_driver_data *idd, int *last, unsigned long addr)
{
int a, b, index, disc;
nic_reset(idd);
/* Search ROM. */
nic_write_byte(idd, 0xF0);
/* Algorithm from ``Book of iButton Standards''. */
for (index = 0, disc = 0; index < 64; index++) {
a = nic_read_bit(idd);
b = nic_read_bit(idd);
if (a && b) {
printk(KERN_WARNING "IOC3 NIC search failed.\n");
*last = 0;
return 0;
}
if (!a && !b) {
if (index == *last) {
addr |= 1UL << index;
} else if (index > *last) {
addr &= ~(1UL << index);
disc = index;
} else if ((addr & (1UL << index)) == 0)
disc = index;
nic_write_bit(idd, (addr>>index)&1);
continue;
} else {
if (a)
addr |= 1UL << index;
else
addr &= ~(1UL << index);
nic_write_bit(idd, a);
continue;
}
}
*last = disc;
return addr;
}
static void nic_addr(struct ioc3_driver_data *idd, unsigned long addr)
{
int index;
nic_reset(idd);
nic_write_byte(idd, 0xF0);
for (index = 0; index < 64; index++) {
nic_read_bit(idd);
nic_read_bit(idd);
nic_write_bit(idd, (addr>>index)&1);
}
}
static void crc16_byte(unsigned int *crc, unsigned char db)
{
int i;
for(i=0;i<8;i++) {
*crc <<= 1;
if((db^(*crc>>16)) & 1)
*crc ^= 0x8005;
db >>= 1;
}
*crc &= 0xFFFF;
}
static unsigned int crc16_area(unsigned char *dbs, int size, unsigned int crc)
{
while(size--)
crc16_byte(&crc, *(dbs++));
return crc;
}
static void crc8_byte(unsigned int *crc, unsigned char db)
{
int i,f;
for(i=0;i<8;i++) {
f = (*crc ^ db) & 1;
*crc >>= 1;
db >>= 1;
if(f)
*crc ^= 0x8c;
}
*crc &= 0xff;
}
static unsigned int crc8_addr(unsigned long addr)
{
int i;
unsigned int crc = 0x00;
for(i=0;i<8;i++)
crc8_byte(&crc, addr>>(i<<3));
return crc;
}
static void
read_redir_page(struct ioc3_driver_data *idd, unsigned long addr, int page,
unsigned char *redir, unsigned char *data)
{
int loops = 16, i;
while(redir[page] != 0xFF) {
page = redir[page]^0xFF;
loops--;
if(loops<0) {
printk(KERN_ERR "IOC3: NIC circular redirection\n");
return;
}
}
loops = 3;
while(loops>0) {
nic_addr(idd, addr);
nic_write_byte(idd, 0xF0);
nic_write_byte(idd, (page << 5) & 0xE0);
nic_write_byte(idd, (page >> 3) & 0x1F);
for(i=0;i<0x20;i++)
data[i] = nic_read_byte(idd);
if(crc16_area(data, 0x20, 0x0000) == 0x800d)
return;
loops--;
}
printk(KERN_ERR "IOC3: CRC error in data page\n");
for(i=0;i<0x20;i++)
data[i] = 0x00;
}
static void
read_redir_map(struct ioc3_driver_data *idd, unsigned long addr,
unsigned char *redir)
{
int i,j,loops = 3,crc_ok;
unsigned int crc;
while(loops>0) {
crc_ok = 1;
nic_addr(idd, addr);
nic_write_byte(idd, 0xAA);
nic_write_byte(idd, 0x00);
nic_write_byte(idd, 0x01);
for(i=0;i<64;i+=8) {
for(j=0;j<8;j++)
redir[i+j] = nic_read_byte(idd);
crc = crc16_area(redir+i, 8, (i==0)?0x8707:0x0000);
crc16_byte(&crc, nic_read_byte(idd));
crc16_byte(&crc, nic_read_byte(idd));
if(crc != 0x800d)
crc_ok = 0;
}
if(crc_ok)
return;
loops--;
}
printk(KERN_ERR "IOC3: CRC error in redirection page\n");
for(i=0;i<64;i++)
redir[i] = 0xFF;
}
static void read_nic(struct ioc3_driver_data *idd, unsigned long addr)
{
unsigned char redir[64];
unsigned char data[64],part[32];
int i,j;
/* read redirections */
read_redir_map(idd, addr, redir);
/* read data pages */
read_redir_page(idd, addr, 0, redir, data);
read_redir_page(idd, addr, 1, redir, data+32);
/* assemble the part # */
j=0;
for(i=0;i<19;i++)
if(data[i+11] != ' ')
part[j++] = data[i+11];
for(i=0;i<6;i++)
if(data[i+32] != ' ')
part[j++] = data[i+32];
part[j] = 0;
/* skip Octane power supplies */
if(!strncmp(part, "060-0035-", 9))
return;
if(!strncmp(part, "060-0038-", 9))
return;
strcpy(idd->nic_part, part);
/* assemble the serial # */
j=0;
for(i=0;i<10;i++)
if(data[i+1] != ' ')
idd->nic_serial[j++] = data[i+1];
idd->nic_serial[j] = 0;
}
static void read_mac(struct ioc3_driver_data *idd, unsigned long addr)
{
int i, loops = 3;
unsigned char data[13];
while(loops>0) {
nic_addr(idd, addr);
nic_write_byte(idd, 0xF0);
nic_write_byte(idd, 0x00);
nic_write_byte(idd, 0x00);
nic_read_byte(idd);
for(i=0;i<13;i++)
data[i] = nic_read_byte(idd);
if(crc16_area(data, 13, 0x0000) == 0x800d) {
for(i=10;i>4;i--)
idd->nic_mac[10-i] = data[i];
return;
}
loops--;
}
printk(KERN_ERR "IOC3: CRC error in MAC address\n");
for(i=0;i<6;i++)
idd->nic_mac[i] = 0x00;
}
static void probe_nic(struct ioc3_driver_data *idd)
{
int save = 0, loops = 3;
unsigned long first, addr;
idd->vma->gpcr_s = GPCR_MLAN_EN;
while(loops>0) {
idd->nic_part[0] = 0;
idd->nic_serial[0] = 0;
addr = first = nic_find(idd, &save, 0);
if(!first)
return;
while(1) {
if(crc8_addr(addr))
break;
else {
switch(addr & 0xFF) {
case 0x0B:
read_nic(idd, addr);
break;
case 0x09:
case 0x89:
case 0x91:
read_mac(idd, addr);
break;
}
}
addr = nic_find(idd, &save, addr);
if(addr == first)
return;
}
loops--;
}
printk(KERN_ERR "IOC3: CRC error in NIC address\n");
}
/* Interrupts */
static inline void
write_ireg(struct ioc3_driver_data *idd, uint32_t val, int which)
{
unsigned long flags;
spin_lock_irqsave(&idd->ir_lock, flags);
switch (which) {
case IOC3_W_IES:
writel(val, &idd->vma->sio_ies);
break;
case IOC3_W_IEC:
writel(val, &idd->vma->sio_iec);
break;
}
spin_unlock_irqrestore(&idd->ir_lock, flags);
}
static inline uint32_t get_pending_intrs(struct ioc3_driver_data *idd)
{
unsigned long flag;
uint32_t intrs = 0;
spin_lock_irqsave(&idd->ir_lock, flag);
intrs = readl(&idd->vma->sio_ir);
intrs &= readl(&idd->vma->sio_ies);
spin_unlock_irqrestore(&idd->ir_lock, flag);
return intrs;
}
static irqreturn_t ioc3_intr_io(int irq, void *arg, struct pt_regs *regs)
{
unsigned long flags;
struct ioc3_driver_data *idd = (struct ioc3_driver_data *)arg;
int handled = 1, id;
unsigned int pending;
read_lock_irqsave(&ioc3_submodules_lock, flags);
if(idd->dual_irq && idd->vma->eisr) {
/* send Ethernet IRQ to the driver */
if(ioc3_ethernet && idd->active[ioc3_ethernet->id] &&
ioc3_ethernet->intr) {
handled = handled && !ioc3_ethernet->intr(ioc3_ethernet,
idd, 0, regs);
}
}
pending = get_pending_intrs(idd); /* look at the IO IRQs */
for(id=0;id<IOC3_MAX_SUBMODULES;id++) {
if(idd->active[id] && ioc3_submodules[id]
&& (pending & ioc3_submodules[id]->irq_mask)
&& ioc3_submodules[id]->intr) {
write_ireg(idd, ioc3_submodules[id]->irq_mask,
IOC3_W_IEC);
if(!ioc3_submodules[id]->intr(ioc3_submodules[id],
idd, pending & ioc3_submodules[id]->irq_mask,
regs))
pending &= ~ioc3_submodules[id]->irq_mask;
if (ioc3_submodules[id]->reset_mask)
write_ireg(idd, ioc3_submodules[id]->irq_mask,
IOC3_W_IES);
}
}
read_unlock_irqrestore(&ioc3_submodules_lock, flags);
if(pending) {
printk(KERN_WARNING
"IOC3: Pending IRQs 0x%08x discarded and disabled\n",pending);
write_ireg(idd, pending, IOC3_W_IEC);
handled = 1;
}
return handled?IRQ_HANDLED:IRQ_NONE;
}
static irqreturn_t ioc3_intr_eth(int irq, void *arg, struct pt_regs *regs)
{
unsigned long flags;
struct ioc3_driver_data *idd = (struct ioc3_driver_data *)arg;
int handled = 1;
if(!idd->dual_irq)
return IRQ_NONE;
read_lock_irqsave(&ioc3_submodules_lock, flags);
if(ioc3_ethernet && idd->active[ioc3_ethernet->id]
&& ioc3_ethernet->intr)
handled = handled && !ioc3_ethernet->intr(ioc3_ethernet, idd, 0,
regs);
read_unlock_irqrestore(&ioc3_submodules_lock, flags);
return handled?IRQ_HANDLED:IRQ_NONE;
}
void ioc3_enable(struct ioc3_submodule *is,
struct ioc3_driver_data *idd, unsigned int irqs)
{
write_ireg(idd, irqs & is->irq_mask, IOC3_W_IES);
}
void ioc3_ack(struct ioc3_submodule *is, struct ioc3_driver_data *idd,
unsigned int irqs)
{
writel(irqs & is->irq_mask, &idd->vma->sio_ir);
}
void ioc3_disable(struct ioc3_submodule *is,
struct ioc3_driver_data *idd, unsigned int irqs)
{
write_ireg(idd, irqs & is->irq_mask, IOC3_W_IEC);
}
void ioc3_gpcr_set(struct ioc3_driver_data *idd, unsigned int val)
{
unsigned long flags;
spin_lock_irqsave(&idd->gpio_lock, flags);
writel(val, &idd->vma->gpcr_s);
spin_unlock_irqrestore(&idd->gpio_lock, flags);
}
/* Keep it simple, stupid! */
static int find_slot(void **tab, int max)
{
int i;
for(i=0;i<max;i++)
if(!(tab[i]))
return i;
return -1;
}
/* Register an IOC3 submodule */
int ioc3_register_submodule(struct ioc3_submodule *is)
{
struct ioc3_driver_data *idd;
int alloc_id;
unsigned long flags;
write_lock_irqsave(&ioc3_submodules_lock, flags);
alloc_id = find_slot((void **)ioc3_submodules, IOC3_MAX_SUBMODULES);
if(alloc_id != -1) {
ioc3_submodules[alloc_id] = is;
if(is->ethernet) {
if(ioc3_ethernet==NULL)
ioc3_ethernet=is;
else
printk(KERN_WARNING
"IOC3 Ethernet module already registered!\n");
}
}
write_unlock_irqrestore(&ioc3_submodules_lock, flags);
if(alloc_id == -1) {
printk(KERN_WARNING "Increase IOC3_MAX_SUBMODULES!\n");
return -ENOMEM;
}
is->id=alloc_id;
/* Initialize submodule for each IOC3 */
if (!is->probe)
return 0;
down_read(&ioc3_devices_rwsem);
list_for_each_entry(idd, &ioc3_devices, list) {
/* set to 1 for IRQs in probe */
idd->active[alloc_id] = 1;
idd->active[alloc_id] = !is->probe(is, idd);
}
up_read(&ioc3_devices_rwsem);
return 0;
}
/* Unregister an IOC3 submodule */
void ioc3_unregister_submodule(struct ioc3_submodule *is)
{
struct ioc3_driver_data *idd;
unsigned long flags;
write_lock_irqsave(&ioc3_submodules_lock, flags);
if(ioc3_submodules[is->id]==is)
ioc3_submodules[is->id]=NULL;
else
printk(KERN_WARNING
"IOC3 submodule %s has wrong ID.\n",is->name);
if(ioc3_ethernet==is)
ioc3_ethernet = NULL;
write_unlock_irqrestore(&ioc3_submodules_lock, flags);
/* Remove submodule for each IOC3 */
down_read(&ioc3_devices_rwsem);
list_for_each_entry(idd, &ioc3_devices, list)
if(idd->active[is->id]) {
if(is->remove)
if(is->remove(is, idd))
printk(KERN_WARNING
"%s: IOC3 submodule %s remove failed "
"for pci_dev %s.\n",
__FUNCTION__, module_name(is->owner),
pci_name(idd->pdev));
idd->active[is->id] = 0;
if(is->irq_mask)
write_ireg(idd, is->irq_mask, IOC3_W_IEC);
}
up_read(&ioc3_devices_rwsem);
}
/*********************
* Device management *
*********************/
static char *
ioc3_class_names[]={"unknown", "IP27 BaseIO", "IP30 system", "MENET 1/2/3",
"MENET 4", "CADduo", "Altix Serial"};
static int ioc3_class(struct ioc3_driver_data *idd)
{
int res = IOC3_CLASS_NONE;
/* NIC-based logic */
if(!strncmp(idd->nic_part, "030-0891-", 9))
res = IOC3_CLASS_BASE_IP30;
if(!strncmp(idd->nic_part, "030-1155-", 9))
res = IOC3_CLASS_CADDUO;
if(!strncmp(idd->nic_part, "030-1657-", 9))
res = IOC3_CLASS_SERIAL;
if(!strncmp(idd->nic_part, "030-1664-", 9))
res = IOC3_CLASS_SERIAL;
/* total random heuristics */
#ifdef CONFIG_SGI_IP27
if(!idd->nic_part[0])
res = IOC3_CLASS_BASE_IP27;
#endif
/* print educational message */
printk(KERN_INFO "IOC3 part: [%s], serial: [%s] => class %s\n",
idd->nic_part, idd->nic_serial, ioc3_class_names[res]);
return res;
}
/* Adds a new instance of an IOC3 card */
static int ioc3_probe(struct pci_dev *pdev, const struct pci_device_id *pci_id)
{
struct ioc3_driver_data *idd;
uint32_t pcmd;
int ret, id;
/* Enable IOC3 and take ownership of it */
if ((ret = pci_enable_device(pdev))) {
printk(KERN_WARNING
"%s: Failed to enable IOC3 device for pci_dev %s.\n",
__FUNCTION__, pci_name(pdev));
goto out;
}
pci_set_master(pdev);
#ifdef USE_64BIT_DMA
ret = pci_set_dma_mask(pdev, 0xffffffffffffffffULL);
if (!ret) {
ret = pci_set_consistent_dma_mask(pdev, 0xffffffffffffffffULL);
if (ret < 0) {
printk(KERN_WARNING "%s: Unable to obtain 64 bit DMA "
"for consistent allocations\n",
__FUNCTION__);
}
}
#endif
/* Set up per-IOC3 data */
idd = kmalloc(sizeof(struct ioc3_driver_data), GFP_KERNEL);
if (!idd) {
printk(KERN_WARNING
"%s: Failed to allocate IOC3 data for pci_dev %s.\n",
__FUNCTION__, pci_name(pdev));
ret = -ENODEV;
goto out_idd;
}
memset(idd, 0, sizeof(struct ioc3_driver_data));
spin_lock_init(&idd->ir_lock);
spin_lock_init(&idd->gpio_lock);
idd->pdev = pdev;
/* Map all IOC3 registers. These are shared between subdevices
* so the main IOC3 module manages them.
*/
idd->pma = pci_resource_start(pdev, 0);
if (!idd->pma) {
printk(KERN_WARNING
"%s: Unable to find IOC3 resource "
"for pci_dev %s.\n",
__FUNCTION__, pci_name(pdev));
ret = -ENODEV;
goto out_pci;
}
if (!request_region(idd->pma, IOC3_PCI_SIZE, "ioc3")) {
printk(KERN_WARNING
"%s: Unable to request IOC3 region "
"for pci_dev %s.\n",
__FUNCTION__, pci_name(pdev));
ret = -ENODEV;
goto out_pci;
}
idd->vma = ioremap(idd->pma, IOC3_PCI_SIZE);
if (!idd->vma) {
printk(KERN_WARNING
"%s: Unable to remap IOC3 region "
"for pci_dev %s.\n",
__FUNCTION__, pci_name(pdev));
ret = -ENODEV;
goto out_misc_region;
}
/* Track PCI-device specific data */
pci_set_drvdata(pdev, idd);
down_write(&ioc3_devices_rwsem);
list_add(&idd->list, &ioc3_devices);
idd->id = ioc3_counter++;
up_write(&ioc3_devices_rwsem);
idd->gpdr_shadow = idd->vma->gpdr;
/* Read IOC3 NIC contents */
probe_nic(idd);
/* Detect IOC3 class */
idd->class = ioc3_class(idd);
/* Initialize IOC3 */
pci_read_config_dword(pdev, PCI_COMMAND, &pcmd);
pci_write_config_dword(pdev, PCI_COMMAND,
pcmd | PCI_COMMAND_MEMORY |
PCI_COMMAND_PARITY | PCI_COMMAND_SERR |
PCI_SCR_DROP_MODE_EN);
write_ireg(idd, ~0, IOC3_W_IEC);
writel(~0, &idd->vma->sio_ir);
/* Set up IRQs */
if(idd->class == IOC3_CLASS_BASE_IP30
|| idd->class == IOC3_CLASS_BASE_IP27) {
writel(0, &idd->vma->eier);
writel(~0, &idd->vma->eisr);
idd->dual_irq = 1;
if (!request_irq(pdev->irq, ioc3_intr_eth, SA_SHIRQ,
"ioc3-eth", (void *)idd)) {
idd->irq_eth = pdev->irq;
} else {
printk(KERN_WARNING
"%s : request_irq fails for IRQ 0x%x\n ",
__FUNCTION__, pdev->irq);
}
if (!request_irq(pdev->irq+2, ioc3_intr_io, SA_SHIRQ,
"ioc3-io", (void *)idd)) {
idd->irq_io = pdev->irq+2;
} else {
printk(KERN_WARNING
"%s : request_irq fails for IRQ 0x%x\n ",
__FUNCTION__, pdev->irq+2);
}
} else {
if (!request_irq(pdev->irq, ioc3_intr_io, SA_SHIRQ,
"ioc3", (void *)idd)) {
idd->irq_io = pdev->irq;
} else {
printk(KERN_WARNING
"%s : request_irq fails for IRQ 0x%x\n ",
__FUNCTION__, pdev->irq);
}
}
/* Add this IOC3 to all submodules */
read_lock(&ioc3_submodules_lock);
for(id=0;id<IOC3_MAX_SUBMODULES;id++)
if(ioc3_submodules[id] && ioc3_submodules[id]->probe) {
idd->active[id] = 1;
idd->active[id] = !ioc3_submodules[id]->probe
(ioc3_submodules[id], idd);
}
read_unlock(&ioc3_submodules_lock);
printk(KERN_INFO "IOC3 Master Driver loaded for %s\n", pci_name(pdev));
return 0;
out_misc_region:
release_region(idd->pma, IOC3_PCI_SIZE);
out_pci:
kfree(idd);
out_idd:
pci_disable_device(pdev);
out:
return ret;
}
/* Removes a particular instance of an IOC3 card. */
static void ioc3_remove(struct pci_dev *pdev)
{
int id;
struct ioc3_driver_data *idd;
idd = pci_get_drvdata(pdev);
/* Remove this IOC3 from all submodules */
read_lock(&ioc3_submodules_lock);
for(id=0;id<IOC3_MAX_SUBMODULES;id++)
if(idd->active[id]) {
if(ioc3_submodules[id] && ioc3_submodules[id]->remove)
if(ioc3_submodules[id]->remove(ioc3_submodules[id],
idd))
printk(KERN_WARNING
"%s: IOC3 submodule 0x%s remove failed "
"for pci_dev %s.\n",
__FUNCTION__,
module_name(ioc3_submodules[id]->owner),
pci_name(pdev));
idd->active[id] = 0;
}
read_unlock(&ioc3_submodules_lock);
/* Clear and disable all IRQs */
write_ireg(idd, ~0, IOC3_W_IEC);
writel(~0, &idd->vma->sio_ir);
/* Release resources */
free_irq(idd->irq_io, (void *)idd);
if(idd->dual_irq)
free_irq(idd->irq_eth, (void *)idd);
iounmap(idd->vma);
release_region(idd->pma, IOC3_PCI_SIZE);
/* Disable IOC3 and relinquish */
pci_disable_device(pdev);
/* Remove and free driver data */
down_write(&ioc3_devices_rwsem);
list_del(&idd->list);
up_write(&ioc3_devices_rwsem);
kfree(idd);
}
static struct pci_device_id ioc3_id_table[] = {
{PCI_VENDOR_ID_SGI, PCI_DEVICE_ID_SGI_IOC3, PCI_ANY_ID, PCI_ANY_ID},
{0}
};
static struct pci_driver ioc3_driver = {
.name = "IOC3",
.id_table = ioc3_id_table,
.probe = ioc3_probe,
.remove = ioc3_remove,
};
MODULE_DEVICE_TABLE(pci, ioc3_id_table);
/*********************
* Module management *
*********************/
/* Module load */
static int __devinit ioc3_init(void)
{
if (ia64_platform_is("sn2"))
return pci_register_driver(&ioc3_driver);
return 0;
}
/* Module unload */
static void __devexit ioc3_exit(void)
{
pci_unregister_driver(&ioc3_driver);
}
module_init(ioc3_init);
module_exit(ioc3_exit);
MODULE_AUTHOR("Stanislaw Skowronek <skylark@linux-mips.org>");
MODULE_DESCRIPTION("PCI driver for SGI IOC3");
MODULE_LICENSE("GPL");
EXPORT_SYMBOL(ioc3_register_submodule);
EXPORT_SYMBOL(ioc3_unregister_submodule);
EXPORT_SYMBOL(ioc3_ack);
EXPORT_SYMBOL(ioc3_gpcr_set);
EXPORT_SYMBOL(ioc3_disable);
EXPORT_SYMBOL(ioc3_enable);

241
include/asm-ia64/sn/ioc3.h Normal file
View File

@ -0,0 +1,241 @@
/*
* Copyright (C) 2005 Silicon Graphics, Inc.
*/
#ifndef IA64_SN_IOC3_H
#define IA64_SN_IOC3_H
/* serial port register map */
struct ioc3_serialregs {
uint32_t sscr;
uint32_t stpir;
uint32_t stcir;
uint32_t srpir;
uint32_t srcir;
uint32_t srtr;
uint32_t shadow;
};
/* SUPERIO uart register map */
struct ioc3_uartregs {
char iu_lcr;
union {
char iir; /* read only */
char fcr; /* write only */
} u3;
union {
char ier; /* DLAB == 0 */
char dlm; /* DLAB == 1 */
} u2;
union {
char rbr; /* read only, DLAB == 0 */
char thr; /* write only, DLAB == 0 */
char dll; /* DLAB == 1 */
} u1;
char iu_scr;
char iu_msr;
char iu_lsr;
char iu_mcr;
};
#define iu_rbr u1.rbr
#define iu_thr u1.thr
#define iu_dll u1.dll
#define iu_ier u2.ier
#define iu_dlm u2.dlm
#define iu_iir u3.iir
#define iu_fcr u3.fcr
struct ioc3_sioregs {
char fill[0x170];
struct ioc3_uartregs uartb;
struct ioc3_uartregs uarta;
};
/* PCI IO/mem space register map */
struct ioc3 {
uint32_t pci_id;
uint32_t pci_scr;
uint32_t pci_rev;
uint32_t pci_lat;
uint32_t pci_addr;
uint32_t pci_err_addr_l;
uint32_t pci_err_addr_h;
uint32_t sio_ir;
/* these registers are read-only for general kernel code. To
* modify them use the functions in ioc3.c
*/
uint32_t sio_ies;
uint32_t sio_iec;
uint32_t sio_cr;
uint32_t int_out;
uint32_t mcr;
uint32_t gpcr_s;
uint32_t gpcr_c;
uint32_t gpdr;
uint32_t gppr[9];
char fill[0x4c];
/* serial port registers */
uint32_t sbbr_h;
uint32_t sbbr_l;
struct ioc3_serialregs port_a;
struct ioc3_serialregs port_b;
char fill1[0x1ff10];
/* superio registers */
struct ioc3_sioregs sregs;
};
/* These don't exist on the ioc3 serial card... */
#define eier fill1[8]
#define eisr fill1[4]
#define PCI_LAT 0xc /* Latency Timer */
#define PCI_SCR_DROP_MODE_EN 0x00008000 /* drop pios on parity err */
#define UARTA_BASE 0x178
#define UARTB_BASE 0x170
/* bitmasks for serial RX status byte */
#define RXSB_OVERRUN 0x01 /* char(s) lost */
#define RXSB_PAR_ERR 0x02 /* parity error */
#define RXSB_FRAME_ERR 0x04 /* framing error */
#define RXSB_BREAK 0x08 /* break character */
#define RXSB_CTS 0x10 /* state of CTS */
#define RXSB_DCD 0x20 /* state of DCD */
#define RXSB_MODEM_VALID 0x40 /* DCD, CTS and OVERRUN are valid */
#define RXSB_DATA_VALID 0x80 /* FRAME_ERR PAR_ERR & BREAK valid */
/* bitmasks for serial TX control byte */
#define TXCB_INT_WHEN_DONE 0x20 /* interrupt after this byte is sent */
#define TXCB_INVALID 0x00 /* byte is invalid */
#define TXCB_VALID 0x40 /* byte is valid */
#define TXCB_MCR 0x80 /* data<7:0> to modem cntrl register */
#define TXCB_DELAY 0xc0 /* delay data<7:0> mSec */
/* bitmasks for SBBR_L */
#define SBBR_L_SIZE 0x00000001 /* 0 1KB rings, 1 4KB rings */
/* bitmasks for SSCR_<A:B> */
#define SSCR_RX_THRESHOLD 0x000001ff /* hiwater mark */
#define SSCR_TX_TIMER_BUSY 0x00010000 /* TX timer in progress */
#define SSCR_HFC_EN 0x00020000 /* h/w flow cntrl enabled */
#define SSCR_RX_RING_DCD 0x00040000 /* postRX record on delta-DCD */
#define SSCR_RX_RING_CTS 0x00080000 /* postRX record on delta-CTS */
#define SSCR_HIGH_SPD 0x00100000 /* 4X speed */
#define SSCR_DIAG 0x00200000 /* bypass clock divider */
#define SSCR_RX_DRAIN 0x08000000 /* drain RX buffer to memory */
#define SSCR_DMA_EN 0x10000000 /* enable ring buffer DMA */
#define SSCR_DMA_PAUSE 0x20000000 /* pause DMA */
#define SSCR_PAUSE_STATE 0x40000000 /* set when PAUSE takes effect*/
#define SSCR_RESET 0x80000000 /* reset DMA channels */
/* all producer/comsumer pointers are the same bitfield */
#define PROD_CONS_PTR_4K 0x00000ff8 /* for 4K buffers */
#define PROD_CONS_PTR_1K 0x000003f8 /* for 1K buffers */
#define PROD_CONS_PTR_OFF 3
/* bitmasks for SRCIR_<A:B> */
#define SRCIR_ARM 0x80000000 /* arm RX timer */
/* bitmasks for SHADOW_<A:B> */
#define SHADOW_DR 0x00000001 /* data ready */
#define SHADOW_OE 0x00000002 /* overrun error */
#define SHADOW_PE 0x00000004 /* parity error */
#define SHADOW_FE 0x00000008 /* framing error */
#define SHADOW_BI 0x00000010 /* break interrupt */
#define SHADOW_THRE 0x00000020 /* transmit holding reg empty */
#define SHADOW_TEMT 0x00000040 /* transmit shift reg empty */
#define SHADOW_RFCE 0x00000080 /* char in RX fifo has error */
#define SHADOW_DCTS 0x00010000 /* delta clear to send */
#define SHADOW_DDCD 0x00080000 /* delta data carrier detect */
#define SHADOW_CTS 0x00100000 /* clear to send */
#define SHADOW_DCD 0x00800000 /* data carrier detect */
#define SHADOW_DTR 0x01000000 /* data terminal ready */
#define SHADOW_RTS 0x02000000 /* request to send */
#define SHADOW_OUT1 0x04000000 /* 16550 OUT1 bit */
#define SHADOW_OUT2 0x08000000 /* 16550 OUT2 bit */
#define SHADOW_LOOP 0x10000000 /* loopback enabled */
/* bitmasks for SRTR_<A:B> */
#define SRTR_CNT 0x00000fff /* reload value for RX timer */
#define SRTR_CNT_VAL 0x0fff0000 /* current value of RX timer */
#define SRTR_CNT_VAL_SHIFT 16
#define SRTR_HZ 16000 /* SRTR clock frequency */
/* bitmasks for SIO_IR, SIO_IEC and SIO_IES */
#define SIO_IR_SA_TX_MT 0x00000001 /* Serial port A TX empty */
#define SIO_IR_SA_RX_FULL 0x00000002 /* port A RX buf full */
#define SIO_IR_SA_RX_HIGH 0x00000004 /* port A RX hiwat */
#define SIO_IR_SA_RX_TIMER 0x00000008 /* port A RX timeout */
#define SIO_IR_SA_DELTA_DCD 0x00000010 /* port A delta DCD */
#define SIO_IR_SA_DELTA_CTS 0x00000020 /* port A delta CTS */
#define SIO_IR_SA_INT 0x00000040 /* port A pass-thru intr */
#define SIO_IR_SA_TX_EXPLICIT 0x00000080 /* port A explicit TX thru */
#define SIO_IR_SA_MEMERR 0x00000100 /* port A PCI error */
#define SIO_IR_SB_TX_MT 0x00000200
#define SIO_IR_SB_RX_FULL 0x00000400
#define SIO_IR_SB_RX_HIGH 0x00000800
#define SIO_IR_SB_RX_TIMER 0x00001000
#define SIO_IR_SB_DELTA_DCD 0x00002000
#define SIO_IR_SB_DELTA_CTS 0x00004000
#define SIO_IR_SB_INT 0x00008000
#define SIO_IR_SB_TX_EXPLICIT 0x00010000
#define SIO_IR_SB_MEMERR 0x00020000
#define SIO_IR_PP_INT 0x00040000 /* P port pass-thru intr */
#define SIO_IR_PP_INTA 0x00080000 /* PP context A thru */
#define SIO_IR_PP_INTB 0x00100000 /* PP context B thru */
#define SIO_IR_PP_MEMERR 0x00200000 /* PP PCI error */
#define SIO_IR_KBD_INT 0x00400000 /* kbd/mouse intr */
#define SIO_IR_RT_INT 0x08000000 /* RT output pulse */
#define SIO_IR_GEN_INT1 0x10000000 /* RT input pulse */
#define SIO_IR_GEN_INT_SHIFT 28
/* per device interrupt masks */
#define SIO_IR_SA (SIO_IR_SA_TX_MT | \
SIO_IR_SA_RX_FULL | \
SIO_IR_SA_RX_HIGH | \
SIO_IR_SA_RX_TIMER | \
SIO_IR_SA_DELTA_DCD | \
SIO_IR_SA_DELTA_CTS | \
SIO_IR_SA_INT | \
SIO_IR_SA_TX_EXPLICIT | \
SIO_IR_SA_MEMERR)
#define SIO_IR_SB (SIO_IR_SB_TX_MT | \
SIO_IR_SB_RX_FULL | \
SIO_IR_SB_RX_HIGH | \
SIO_IR_SB_RX_TIMER | \
SIO_IR_SB_DELTA_DCD | \
SIO_IR_SB_DELTA_CTS | \
SIO_IR_SB_INT | \
SIO_IR_SB_TX_EXPLICIT | \
SIO_IR_SB_MEMERR)
#define SIO_IR_PP (SIO_IR_PP_INT | SIO_IR_PP_INTA | \
SIO_IR_PP_INTB | SIO_IR_PP_MEMERR)
#define SIO_IR_RT (SIO_IR_RT_INT | SIO_IR_GEN_INT1)
/* bitmasks for SIO_CR */
#define SIO_CR_CMD_PULSE_SHIFT 15
#define SIO_CR_SER_A_BASE_SHIFT 1
#define SIO_CR_SER_B_BASE_SHIFT 8
#define SIO_CR_ARB_DIAG 0x00380000 /* cur !enet PCI requet (ro) */
#define SIO_CR_ARB_DIAG_TXA 0x00000000
#define SIO_CR_ARB_DIAG_RXA 0x00080000
#define SIO_CR_ARB_DIAG_TXB 0x00100000
#define SIO_CR_ARB_DIAG_RXB 0x00180000
#define SIO_CR_ARB_DIAG_PP 0x00200000
#define SIO_CR_ARB_DIAG_IDLE 0x00400000 /* 0 -> active request (ro) */
/* defs for some of the generic I/O pins */
#define GPCR_PHY_RESET 0x20 /* pin is output to PHY reset */
#define GPCR_UARTB_MODESEL 0x40 /* pin is output to port B mode sel */
#define GPCR_UARTA_MODESEL 0x80 /* pin is output to port A mode sel */
#define GPPR_PHY_RESET_PIN 5 /* GIO pin controlling phy reset */
#define GPPR_UARTB_MODESEL_PIN 6 /* GIO pin cntrling uartb modeselect */
#define GPPR_UARTA_MODESEL_PIN 7 /* GIO pin cntrling uarta modeselect */
#endif /* IA64_SN_IOC3_H */

93
include/linux/ioc3.h Normal file
View File

@ -0,0 +1,93 @@
/*
* This file is subject to the terms and conditions of the GNU General Public
* License. See the file "COPYING" in the main directory of this archive
* for more details.
*
* Copyright (c) 2005 Stanislaw Skowronek <skylark@linux-mips.org>
*/
#ifndef _LINUX_IOC3_H
#define _LINUX_IOC3_H
#include <asm/sn/ioc3.h>
#define IOC3_MAX_SUBMODULES 32
#define IOC3_CLASS_NONE 0
#define IOC3_CLASS_BASE_IP27 1
#define IOC3_CLASS_BASE_IP30 2
#define IOC3_CLASS_MENET_123 3
#define IOC3_CLASS_MENET_4 4
#define IOC3_CLASS_CADDUO 5
#define IOC3_CLASS_SERIAL 6
/* One of these per IOC3 */
struct ioc3_driver_data {
struct list_head list;
int id; /* IOC3 sequence number */
/* PCI mapping */
unsigned long pma; /* physical address */
struct __iomem ioc3 *vma; /* pointer to registers */
struct pci_dev *pdev; /* PCI device */
/* IRQ stuff */
int dual_irq; /* set if separate IRQs are used */
int irq_io, irq_eth; /* IRQ numbers */
/* GPIO magic */
spinlock_t gpio_lock;
unsigned int gpdr_shadow;
/* NIC identifiers */
char nic_part[32];
char nic_serial[16];
char nic_mac[6];
/* submodule set */
int class;
void *data[IOC3_MAX_SUBMODULES]; /* for submodule use */
int active[IOC3_MAX_SUBMODULES]; /* set if probe succeeds */
/* is_ir_lock must be held while
* modifying sio_ie values, so
* we can be sure that sio_ie is
* not changing when we read it
* along with sio_ir.
*/
spinlock_t ir_lock; /* SIO_IE[SC] mod lock */
};
/* One per submodule */
struct ioc3_submodule {
char *name; /* descriptive submodule name */
struct module *owner; /* owning kernel module */
int ethernet; /* set for ethernet drivers */
int (*probe) (struct ioc3_submodule *, struct ioc3_driver_data *);
int (*remove) (struct ioc3_submodule *, struct ioc3_driver_data *);
int id; /* assigned by IOC3, index for the "data" array */
/* IRQ stuff */
unsigned int irq_mask; /* IOC3 IRQ mask, leave clear for Ethernet */
int reset_mask; /* non-zero if you want the ioc3.c module to reset interrupts */
int (*intr) (struct ioc3_submodule *, struct ioc3_driver_data *, unsigned int, struct pt_regs *);
/* private submodule data */
void *data; /* assigned by submodule */
};
/**********************************
* Functions needed by submodules *
**********************************/
#define IOC3_W_IES 0
#define IOC3_W_IEC 1
/* registers a submodule for all existing and future IOC3 chips */
extern int ioc3_register_submodule(struct ioc3_submodule *);
/* unregisters a submodule */
extern void ioc3_unregister_submodule(struct ioc3_submodule *);
/* enables IRQs indicated by irq_mask for a specified IOC3 chip */
extern void ioc3_enable(struct ioc3_submodule *, struct ioc3_driver_data *, unsigned int);
/* ackowledges specified IRQs */
extern void ioc3_ack(struct ioc3_submodule *, struct ioc3_driver_data *, unsigned int);
/* disables IRQs indicated by irq_mask for a specified IOC3 chip */
extern void ioc3_disable(struct ioc3_submodule *, struct ioc3_driver_data *, unsigned int);
/* atomically sets GPCR bits */
extern void ioc3_gpcr_set(struct ioc3_driver_data *, unsigned int);
/* general ireg writer */
extern void ioc3_write_ireg(struct ioc3_driver_data *idd, uint32_t value, int reg);
#endif