Commit 927f6aaa authored by Chas Williams's avatar Chas Williams Committed by Stephen Hemminger

[ATM]: Update LANAI driver to modern PCI and DMA APIs (from mitch@sfgoth.com)

parent d98e960e
/* lanai.c -- Copyright 1999 by Mitchell Blank Jr <mitch@sfgoth.com>
/* lanai.c -- Copyright 1999-2003 by Mitchell Blank Jr <mitch@sfgoth.com>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
......@@ -55,6 +55,7 @@
*/
/* Version history:
* v.1.00 -- 26-JUL-2003 -- PCI/DMA updates
* v.0.02 -- 11-JAN-2000 -- Endian fixes
* v.0.01 -- 30-NOV-1999 -- Initial release
*/
......@@ -178,7 +179,7 @@
printk(KERN_DEBUG DEV_LABEL ": " format, ##args)
#define APRINTK(truth, format, args...) \
do { \
if (!(truth)) \
if (unlikely(!(truth))) \
printk(KERN_ERR DEV_LABEL ": " format, ##args); \
} while (0)
......@@ -215,7 +216,7 @@ struct lanai_buffer {
u32 *start; /* From get_free_pages */
u32 *end; /* One past last byte */
u32 *ptr; /* Pointer to current host location */
int order; /* log2(size/PAGE_SIZE) */
dma_addr_t dmaaddr;
};
struct lanai_vcc_stats {
......@@ -373,89 +374,76 @@ static void vci_bitfield_iterate(struct lanai_dev *lanai,
/*
* Lanai needs DMA buffers aligned to 256 bytes of at least 1024 bytes -
* we assume that any page allocation will do. I'm sure this is
* never going to be a problem, but it's good to document assumtions
* usually any page allocation will do. Just to be safe in case
* PAGE_SIZE is insanely tiny, though...
*/
#if PAGE_SIZE < 1024
#error PAGE_SIZE too small to support LANAI chipset
#endif
/*
* We also assume that the maximum buffer size will be some number
* of whole pages, although that wouldn't be too hard to fix
*/
#if PAGE_SIZE > (128 * 1024)
#error PAGE_SIZE too large to support LANAI chipset
#endif
/* Convert a size to "order" for __get_free_pages */
static int bytes_to_order(int bytes)
{
int order = 0;
if (bytes > (128 * 1024))
bytes = 128 * 1024; /* Max buffer size for lanai */
while ((PAGE_SIZE << order) < bytes)
order++;
return order;
}
#define LANAI_PAGE_SIZE ((PAGE_SIZE >= 1024) ? PAGE_SIZE : 1024)
/*
* Allocate a buffer in host RAM for service list, RX, or TX
* Returns buf->order<0 if no memory
* Note that the size will be rounded up to an "order" of pages, and
* Returns buf->start==NULL if no memory
* Note that the size will be rounded up 2^n bytes, and
* if we can't allocate that we'll settle for something smaller
* until minbytes
*
* NOTE: buffer must be 32-bit DMA capable - when linux can
* make distinction, this will need tweaking for this
* to work on BIG memory machines.
*/
static void lanai_buf_allocate(struct lanai_buffer *buf,
int bytes, int minbytes)
size_t bytes, size_t minbytes, struct pci_dev *pci)
{
unsigned long address;
int order = bytes_to_order(bytes);
int size;
if (bytes > (128 * 1024)) /* max lanai buffer size */
bytes = 128 * 1024;
for (size = LANAI_PAGE_SIZE; size < bytes; size *= 2)
;
if (minbytes < LANAI_PAGE_SIZE)
minbytes = LANAI_PAGE_SIZE;
do {
address = __get_free_pages(GFP_KERNEL, order);
if (address != 0) { /* Success */
bytes = PAGE_SIZE << order;
buf->start = buf->ptr = (u32 *) address;
buf->end = (u32 *) (address + bytes);
memset((void *) address, 0, bytes);
/*
* Technically we could use non-consistent mappings for
* everything, but the way the lanai uses DMA memory would
* make that a terrific pain. This is much simpler.
*/
buf->start = pci_alloc_consistent(pci, size, &buf->dmaaddr);
if (buf->start != NULL) { /* Success */
/* Lanai requires 256-byte alignment of DMA bufs */
APRINTK((buf->dmaaddr & ~0xFFFFFF00) == 0,
"bad dmaaddr: 0x%lx\n",
(unsigned long) buf->dmaaddr);
buf->ptr = buf->start;
buf->end = (u32 *)
(&((unsigned char *) buf->start)[size]);
memset(buf->start, 0, size);
break;
}
if ((PAGE_SIZE << --order) < minbytes)
order = -1; /* Too small - give up */
} while (order >= 0);
buf->order = order;
}
static inline void lanai_buf_deallocate(struct lanai_buffer *buf)
{
if (buf->order >= 0) {
APRINTK(buf->start != 0, "lanai_buf_deallocate: start==0!\n");
free_pages((unsigned long) buf->start, buf->order);
buf->start = buf->end = buf->ptr = 0;
}
size /= 2;
} while (size >= minbytes);
}
/* size of buffer in bytes */
static inline int lanai_buf_size(const struct lanai_buffer *buf)
static inline size_t lanai_buf_size(const struct lanai_buffer *buf)
{
return ((unsigned long) buf->end) - ((unsigned long) buf->start);
}
/* size of buffer as "card order" (0=1k .. 7=128k) */
static inline int lanai_buf_size_cardorder(const struct lanai_buffer *buf)
static void lanai_buf_deallocate(struct lanai_buffer *buf,
struct pci_dev *pci)
{
return buf->order + PAGE_SHIFT - 10;
if (buf->start != NULL) {
pci_free_consistent(pci, lanai_buf_size(buf),
buf->start, buf->dmaaddr);
buf->start = buf->end = buf->ptr = NULL;
}
}
/* DMA-able address for this buffer */
static unsigned long lanai_buf_dmaaddr(const struct lanai_buffer *buf)
/* size of buffer as "card order" (0=1k .. 7=128k) */
static int lanai_buf_size_cardorder(const struct lanai_buffer *buf)
{
unsigned long r = virt_to_bus(buf->start);
APRINTK((r & ~0xFFFFFF00) == 0, "bad dmaaddr: 0x%lx\n", (long) r);
return r;
int order = get_order(lanai_buf_size(buf)) + (PAGE_SHIFT - 10);
/* This can only happen if PAGE_SIZE is gigantic, but just in case */
if (order > 7)
order = 7;
return order;
}
/* -------------------- HANDLE BACKLOG_VCCS BITFIELD: */
......@@ -492,7 +480,7 @@ enum lanai_register {
Reset_Reg = 0x00, /* Reset; read for chip type; bits: */
#define RESET_GET_BOARD_REV(x) (((x)>> 0)&0x03) /* Board revision */
#define RESET_GET_BOARD_ID(x) (((x)>> 2)&0x03) /* Board ID */
#define BOARD_ID_LANAI256 (0) /* 25.6M adaptor card */
#define BOARD_ID_LANAI256 (0) /* 25.6M adapter card */
Endian_Reg = 0x04, /* Endian setting */
IntStatus_Reg = 0x08, /* Interrupt status */
IntStatusMasked_Reg = 0x0C, /* Interrupt status (masked) */
......@@ -850,7 +838,7 @@ static void host_vcc_start_rx(const struct lanai_vcc *lvcc)
{
u32 addr1;
if (lvcc->rx.atmvcc->qos.aal == ATM_AAL5) {
unsigned long dmaaddr = lanai_buf_dmaaddr(&lvcc->rx.buf);
dma_addr_t dmaaddr = lvcc->rx.buf.dmaaddr;
cardvcc_write(lvcc, 0xFFFF, vcc_rxcrc1);
cardvcc_write(lvcc, 0xFFFF, vcc_rxcrc2);
cardvcc_write(lvcc, 0, vcc_rxwriteptr);
......@@ -872,7 +860,7 @@ static void host_vcc_start_rx(const struct lanai_vcc *lvcc)
static void host_vcc_start_tx(const struct lanai_vcc *lvcc)
{
unsigned long dmaaddr = lanai_buf_dmaaddr(&lvcc->tx.buf);
dma_addr_t dmaaddr = lvcc->tx.buf.dmaaddr;
cardvcc_write(lvcc, 0, vcc_txicg);
cardvcc_write(lvcc, 0xFFFF, vcc_txcrc1);
cardvcc_write(lvcc, 0xFFFF, vcc_txcrc2);
......@@ -971,14 +959,15 @@ static void lanai_shutdown_tx_vci(struct lanai_dev *lanai,
static inline int aal0_buffer_allocate(struct lanai_dev *lanai)
{
DPRINTK("aal0_buffer_allocate: allocating AAL0 RX buffer\n");
lanai_buf_allocate(&lanai->aal0buf, AAL0_RX_BUFFER_SIZE, 80);
return (lanai->aal0buf.order < 0) ? -ENOMEM : 0;
lanai_buf_allocate(&lanai->aal0buf, AAL0_RX_BUFFER_SIZE, 80,
lanai->pci);
return (lanai->aal0buf.start == NULL) ? -ENOMEM : 0;
}
static inline void aal0_buffer_free(struct lanai_dev *lanai)
{
DPRINTK("aal0_buffer_allocate: freeing AAL0 RX buffer\n");
lanai_buf_deallocate(&lanai->aal0buf);
lanai_buf_deallocate(&lanai->aal0buf, lanai->pci);
}
/* -------------------- EEPROM UTILITIES: */
......@@ -1678,36 +1667,37 @@ static inline struct lanai_vcc *new_lanai_vcc(void)
return lvcc;
}
static int lanai_get_sized_buffer(int number, struct lanai_buffer *buf,
int max_sdu, int multiplier, int min, const char *name)
static int lanai_get_sized_buffer(struct lanai_dev *lanai,
struct lanai_buffer *buf, int max_sdu, int multiplier,
int min, const char *name)
{
int size;
if (max_sdu < 1)
max_sdu = 1;
max_sdu = aal5_size(max_sdu);
size = (max_sdu + 16) * multiplier + 16;
lanai_buf_allocate(buf, size, min);
if (buf->order < 0)
lanai_buf_allocate(buf, size, min, lanai->pci);
if (buf->start == NULL)
return -ENOMEM;
if (lanai_buf_size(buf) < size)
printk(KERN_WARNING DEV_LABEL "(itf %d): wanted %d bytes "
"for %s buffer, got only %d\n", number, size, name,
"for %s buffer, got only %d\n", lanai->number, size, name,
lanai_buf_size(buf));
DPRINTK("Allocated %d byte %s buffer\n", lanai_buf_size(buf), name);
return 0;
}
/* Setup a RX buffer for a currently unbound AAL5 vci */
static inline int lanai_setup_rx_vci_aal5(int number, struct lanai_vcc *lvcc,
const struct atm_qos *qos)
static inline int lanai_setup_rx_vci_aal5(struct lanai_dev *lanai,
struct lanai_vcc *lvcc, const struct atm_qos *qos)
{
return lanai_get_sized_buffer(number, &lvcc->rx.buf,
return lanai_get_sized_buffer(lanai, &lvcc->rx.buf,
qos->rxtp.max_sdu, AAL5_RX_MULTIPLIER, qos->rxtp.max_sdu + 32,
"RX");
}
/* Setup a TX buffer for a currently unbound AAL5 vci */
static int lanai_setup_tx_vci(int number, struct lanai_vcc *lvcc,
static int lanai_setup_tx_vci(struct lanai_dev *lanai, struct lanai_vcc *lvcc,
const struct atm_qos *qos)
{
int max_sdu, multiplier;
......@@ -1720,7 +1710,7 @@ static int lanai_setup_tx_vci(int number, struct lanai_vcc *lvcc,
max_sdu = qos->txtp.max_sdu;
multiplier = AAL5_TX_MULTIPLIER;
}
return lanai_get_sized_buffer(number, &lvcc->tx.buf, max_sdu,
return lanai_get_sized_buffer(lanai, &lvcc->tx.buf, max_sdu,
multiplier, 80, "TX");
}
......@@ -1781,8 +1771,9 @@ static void lanai_reset(struct lanai_dev *lanai)
*/
static int __init service_buffer_allocate(struct lanai_dev *lanai)
{
lanai_buf_allocate(&lanai->service, SERVICE_ENTRIES * 4, 0);
if (lanai->service.order < 0)
lanai_buf_allocate(&lanai->service, SERVICE_ENTRIES * 4, 8,
lanai->pci);
if (lanai->service.start == NULL)
return -ENOMEM;
DPRINTK("allocated service buffer at 0x%08lX, size %d(%d)\n",
(unsigned long) lanai->service.start,
......@@ -1793,14 +1784,14 @@ static int __init service_buffer_allocate(struct lanai_dev *lanai)
/* ServiceStuff register contains size and address of buffer */
reg_write(lanai,
SSTUFF_SET_SIZE(lanai_buf_size_cardorder(&lanai->service)) |
SSTUFF_SET_ADDR(lanai_buf_dmaaddr(&lanai->service)),
SSTUFF_SET_ADDR(lanai->service.dmaaddr),
ServiceStuff_Reg);
return 0;
}
static inline void service_buffer_deallocate(struct lanai_dev *lanai)
{
lanai_buf_deallocate(&lanai->service);
lanai_buf_deallocate(&lanai->service, lanai->pci);
}
/* Bitfields in service list */
......@@ -2098,11 +2089,28 @@ static int check_board_id_and_rev(const char *name, u32 val, int *revp)
/* -------------------- PCI INITIALIZATION/SHUTDOWN: */
static inline int __init lanai_pci_start(struct lanai_dev *lanai)
static int __init lanai_pci_start(struct lanai_dev *lanai)
{
struct pci_dev *pci = lanai->pci;
int result;
u16 w;
if (pci_enable_device(pci) != 0) {
printk(KERN_ERR DEV_LABEL "(itf %d): can't enable "
"PCI device", lanai->number);
return -ENXIO;
}
pci_set_master(pci);
if (pci_set_dma_mask(pci, 0xFFFFFFFF) != 0) {
printk(KERN_WARNING DEV_LABEL
"(itf %d): No suitable DMA available.\n", lanai->number);
return -EBUSY;
}
if (pci_set_consistent_dma_mask(pci, 0xFFFFFFFF) != 0) {
printk(KERN_WARNING DEV_LABEL
"(itf %d): No suitable DMA available.\n", lanai->number);
return -EBUSY;
}
/* Get the pci revision byte */
result = pci_read_config_byte(pci, PCI_REVISION_ID,
&lanai->pci_revision);
......@@ -2113,7 +2121,8 @@ static inline int __init lanai_pci_start(struct lanai_dev *lanai)
}
result = pci_read_config_word(pci, PCI_SUBSYSTEM_ID, &w);
if (result != PCIBIOS_SUCCESSFUL) {
printk(KERN_ERR DEV_LABEL "(itf %d): can't read PCI_SUBSYSTEM_ID: %d\n", lanai->number, result);
printk(KERN_ERR DEV_LABEL "(itf %d): can't read "
"PCI_SUBSYSTEM_ID: %d\n", lanai->number, result);
return -EINVAL;
}
if ((result = check_board_id_and_rev("PCI", w, NULL)) != 0)
......@@ -2125,43 +2134,11 @@ static inline int __init lanai_pci_start(struct lanai_dev *lanai)
"PCI_LATENCY_TIMER: %d\n", lanai->number, result);
return -EINVAL;
}
result = pci_read_config_word(pci, PCI_COMMAND, &w);
if (result != PCIBIOS_SUCCESSFUL) {
printk(KERN_ERR DEV_LABEL "(itf %d): can't read "
"PCI_COMMAND: %d\n", lanai->number, result);
return -EINVAL;
}
w |= (PCI_COMMAND_MEMORY | PCI_COMMAND_MASTER | PCI_COMMAND_SERR |
PCI_COMMAND_PARITY);
result = pci_write_config_word(pci, PCI_COMMAND, w);
if (result != PCIBIOS_SUCCESSFUL) {
printk(KERN_ERR DEV_LABEL "(itf %d): can't "
"write PCI_COMMAND: %d\n", lanai->number, result);
return -EINVAL;
}
pcistatus_check(lanai, 1);
pcistatus_check(lanai, 0);
return 0;
}
static void lanai_pci_stop(struct lanai_dev *lanai)
{
struct pci_dev *pci = lanai->pci;
int result;
u16 pci_command;
result = pci_read_config_word(pci, PCI_COMMAND, &pci_command);
if (result != PCIBIOS_SUCCESSFUL) {
printk(KERN_ERR DEV_LABEL "(itf %d): can't "
"read PCI_COMMAND: %d\n", lanai->number, result);
return;
}
pci_command &= ~(PCI_COMMAND_MEMORY | PCI_COMMAND_MASTER);
result = pci_write_config_word(pci, PCI_COMMAND, pci_command);
if (result != PCIBIOS_SUCCESSFUL)
printk(KERN_ERR DEV_LABEL "(itf %d): can't "
"write PCI_COMMAND: %d\n", lanai->number, result);
}
/* -------------------- VPI/VCI ALLOCATION: */
/*
......@@ -2445,7 +2422,7 @@ static int __init lanai_dev_open(struct atm_dev *atmdev)
#endif
iounmap((void *) lanai->base);
error_pci:
lanai_pci_stop(lanai);
pci_disable_device(lanai->pci);
error:
return result;
}
......@@ -2470,7 +2447,7 @@ static void lanai_dev_close(struct atm_dev *atmdev)
lanai->conf1 |= CONFIG1_POWERDOWN;
conf1_write(lanai);
#endif
lanai_pci_stop(lanai);
pci_disable_device(lanai->pci);
vcc_table_deallocate(lanai);
service_buffer_deallocate(lanai);
iounmap((void *) lanai->base);
......@@ -2493,7 +2470,7 @@ static void lanai_close(struct atm_vcc *atmvcc)
if (--lanai->naal0 <= 0)
aal0_buffer_free(lanai);
} else
lanai_buf_deallocate(&lvcc->rx.buf);
lanai_buf_deallocate(&lvcc->rx.buf, lanai->pci);
lvcc->rx.atmvcc = NULL;
}
if (lvcc->tx.atmvcc == atmvcc) {
......@@ -2503,7 +2480,7 @@ static void lanai_close(struct atm_vcc *atmvcc)
lanai->cbrvcc = NULL;
}
lanai_shutdown_tx_vci(lanai, lvcc);
lanai_buf_deallocate(&lvcc->tx.buf);
lanai_buf_deallocate(&lvcc->tx.buf, lanai->pci);
lvcc->tx.atmvcc = NULL;
}
if (--lvcc->nref == 0) {
......@@ -2551,7 +2528,7 @@ static int lanai_open(struct atm_vcc *atmvcc, short vpi, int vci)
result = aal0_buffer_allocate(lanai);
} else
result = lanai_setup_rx_vci_aal5(
lanai->number, lvcc, &atmvcc->qos);
lanai, lvcc, &atmvcc->qos);
if (result != 0)
goto out_free;
lvcc->rx.atmvcc = atmvcc;
......@@ -2566,7 +2543,7 @@ static int lanai_open(struct atm_vcc *atmvcc, short vpi, int vci)
if (atmvcc->qos.txtp.traffic_class != ATM_NONE) {
APRINTK(lvcc->tx.atmvcc == NULL, "tx.atmvcc!=NULL, vci=%d\n",
vci);
result = lanai_setup_tx_vci(lanai->number, lvcc, &atmvcc->qos);
result = lanai_setup_tx_vci(lanai, lvcc, &atmvcc->qos);
if (result != 0)
goto out_free;
lvcc->tx.atmvcc = atmvcc;
......@@ -2849,49 +2826,69 @@ static const struct atmdev_ops ops = {
.proc_read = lanai_proc_read
};
/* detect one type of card LANAI2 or LANAIHB */
static int __init lanai_detect_1(unsigned int vendor, unsigned int device)
/* initialize one probed card */
static int __devinit lanai_init_one(struct pci_dev *pci,
const struct pci_device_id *ident)
{
struct pci_dev *pci = NULL;
struct lanai_dev *lanai;
struct atm_dev *atmdev;
int count = 0, result;
while ((pci = pci_find_device(vendor, device, pci)) != NULL) {
lanai = (struct lanai_dev *)
kmalloc(sizeof *lanai, GFP_KERNEL);
if (lanai == NULL) {
printk(KERN_ERR DEV_LABEL ": couldn't allocate "
"dev_data structure!\n");
break;
}
atmdev = atm_dev_register(DEV_LABEL, &ops, -1, 0);
if (atmdev == NULL) {
printk(KERN_ERR DEV_LABEL ": couldn't register "
"atm device!\n");
kfree(lanai);
break;
}
atmdev->dev_data = lanai;
lanai->pci = pci;
lanai->type = (enum lanai_type) device;
if ((result = lanai_dev_open(atmdev)) != 0) {
DPRINTK("lanai_start() failed, err=%d\n", -result);
atm_dev_deregister(atmdev);
kfree(lanai);
continue;
}
count++;
int result;
lanai = (struct lanai_dev *) kmalloc(sizeof(*lanai), GFP_KERNEL);
if (lanai == NULL) {
printk(KERN_ERR DEV_LABEL
": couldn't allocate dev_data structure!\n");
return -ENOMEM;
}
atmdev = atm_dev_register(DEV_LABEL, &ops, -1, 0);
if (atmdev == NULL) {
printk(KERN_ERR DEV_LABEL
": couldn't register atm device!\n");
kfree(lanai);
return -EBUSY;
}
atmdev->dev_data = lanai;
lanai->pci = pci;
lanai->type = (enum lanai_type) ident->device;
result = lanai_dev_open(atmdev);
if (result != 0) {
DPRINTK("lanai_start() failed, err=%d\n", -result);
atm_dev_deregister(atmdev);
kfree(lanai);
}
return count;
return result;
}
static struct pci_device_id lanai_pci_tbl[] __devinitdata = {
{
PCI_VENDOR_ID_EF, PCI_VENDOR_ID_EF_ATM_LANAI2,
PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0
},
{
PCI_VENDOR_ID_EF, PCI_VENDOR_ID_EF_ATM_LANAIHB,
PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0
},
{ 0, } /* terminal entry */
};
MODULE_DEVICE_TABLE(pci, lanai_pci_tbl);
static struct pci_driver lanai_driver = {
.name = DEV_LABEL,
.id_table = lanai_pci_tbl,
.probe = lanai_init_one,
};
static int __init lanai_module_init(void)
{
if (lanai_detect_1(PCI_VENDOR_ID_EF, PCI_VENDOR_ID_EF_ATM_LANAI2) +
lanai_detect_1(PCI_VENDOR_ID_EF, PCI_VENDOR_ID_EF_ATM_LANAIHB))
return 0;
printk(KERN_ERR DEV_LABEL ": no adaptor found\n");
return -ENODEV;
int x;
x = pci_module_init(&lanai_driver);
if (x != 0)
printk(KERN_ERR DEV_LABEL ": no adapter found\n");
return x;
}
static void __exit lanai_module_exit(void)
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment