module.c
/*
* SBE 2T3E3 synchronous serial card driver for Linux
*
* Copyright (C) 2009-2010 Krzysztof Halasa <khc@pm.waw.pl>
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of version 2 of the GNU General Public License
* as published by the Free Software Foundation.
*
* This code is based on a driver written by SBE Inc.
*/
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/delay.h>
#include <linux/netdevice.h>
#include <linux/pci.h>
#include <linux/hdlc.h>
#include <linux/if_arp.h>
#include <linux/interrupt.h>
#include "2t3e3.h"
static void check_leds(unsigned long arg)
{
struct card *card = (struct card *)arg;
struct channel *channel0 = &card->channels[0];
static int blinker;
update_led(channel0, ++blinker);
if (has_two_ports(channel0->pdev))
update_led(&card->channels[1], blinker);
card->timer.expires = jiffies + HZ / 10;
add_timer(&card->timer);
}
static void t3e3_remove_channel(struct channel *channel)
{
struct pci_dev *pdev = channel->pdev;
struct net_device *dev = channel->dev;
/* system hangs if board asserts irq while module is unloaded */
cpld_stop_intr(channel);
free_irq(dev->irq, dev);
dc_drop_descriptor_list(channel);
unregister_hdlc_device(dev);
free_netdev(dev);
pci_release_regions(pdev);
pci_disable_device(pdev);
pci_set_drvdata(pdev, NULL);
}
static int __devinit t3e3_init_channel(struct channel *channel, struct pci_dev *pdev, struct card *card)
{
struct net_device *dev;
unsigned int val;
int err;
err = pci_enable_device(pdev);
if (err)
return err;
err = pci_request_regions(pdev, "SBE 2T3E3");
if (err)
goto disable;
dev = alloc_hdlcdev(channel);
if (!dev) {
printk(KERN_ERR "SBE 2T3E3" ": Out of memory\n");
goto free_regions;
}
t3e3_sc_init(channel);
dev_to_priv(dev) = channel;
channel->pdev = pdev;
channel->dev = dev;
channel->card = card;
channel->addr = pci_resource_start(pdev, 0);
if (pdev->subsystem_device == PCI_SUBDEVICE_ID_SBE_2T3E3_P1)
channel->h.slot = 1;
else
channel->h.slot = 0;
if (setup_device(dev, channel))
goto free_regions;
pci_read_config_dword(channel->pdev, 0x40, &val); /* mask sleep mode */
pci_write_config_dword(channel->pdev, 0x40, val & 0x3FFFFFFF);
pci_read_config_byte(channel->pdev, PCI_CACHE_LINE_SIZE, &channel->h.cache_size);
pci_read_config_dword(channel->pdev, PCI_COMMAND, &channel->h.command);
t3e3_init(channel);
if (request_irq(dev->irq, &t3e3_intr, IRQF_SHARED, dev->name, dev)) {
printk(KERN_WARNING "%s: could not get irq: %d\n", dev->name, dev->irq);
goto free_regions;
}
pci_set_drvdata(pdev, channel);
return 0;
free_regions:
pci_release_regions(pdev);
disable:
pci_disable_device(pdev);
return err;
}
static void __devexit t3e3_remove_card(struct pci_dev *pdev)
{
struct channel *channel0 = pci_get_drvdata(pdev);
struct card *card = channel0->card;
del_timer(&card->timer);
if (has_two_ports(channel0->pdev)) {
t3e3_remove_channel(&card->channels[1]);
pci_dev_put(card->channels[1].pdev);
}
t3e3_remove_channel(channel0);
kfree(card);
}
static int __devinit t3e3_init_card(struct pci_dev *pdev, const struct pci_device_id *ent)
{
/* pdev points to channel #0 */
struct pci_dev *pdev1 = NULL;
struct card *card;
int channels = 1, err;
if (has_two_ports(pdev)) {
while ((pdev1 = pci_get_subsys(PCI_VENDOR_ID_DEC, PCI_DEVICE_ID_DEC_21142,
PCI_VENDOR_ID_SBE, PCI_SUBDEVICE_ID_SBE_2T3E3_P1,
pdev1)))
if (pdev1->bus == pdev->bus &&
pdev1->devfn == pdev->devfn + 8 /* next device on the same bus */)
break; /* found the second channel */
if (!pdev1) {
printk(KERN_ERR "SBE 2T3E3" ": Can't find the second channel\n");
return -EFAULT;
}
channels = 2;
/* holds the reference for pdev1 */
}
card = kzalloc(sizeof(struct card) + channels * sizeof(struct channel), GFP_KERNEL);
if (!card) {
printk(KERN_ERR "SBE 2T3E3" ": Out of memory\n");
return -ENOBUFS;
}
spin_lock_init(&card->bootrom_lock);
card->bootrom_addr = pci_resource_start(pdev, 0);
err = t3e3_init_channel(&card->channels[0], pdev, card);
if (err)
goto free_card;
if (channels == 2) {
err = t3e3_init_channel(&card->channels[1], pdev1, card);
if (err) {
t3e3_remove_channel(&card->channels[0]);
goto free_card;
}
}
/* start LED timer */
init_timer(&card->timer);
card->timer.function = check_leds;
card->timer.expires = jiffies + HZ / 10;
card->timer.data = (unsigned long)card;
add_timer(&card->timer);
return 0;
free_card:
kfree(card);
return err;
}
static struct pci_device_id t3e3_pci_tbl[] __devinitdata = {
{ PCI_VENDOR_ID_DEC, PCI_DEVICE_ID_DEC_21142,
PCI_VENDOR_ID_SBE, PCI_SUBDEVICE_ID_SBE_T3E3, 0, 0, 0 },
{ PCI_VENDOR_ID_DEC, PCI_DEVICE_ID_DEC_21142,
PCI_VENDOR_ID_SBE, PCI_SUBDEVICE_ID_SBE_2T3E3_P0, 0, 0, 0 },
/* channel 1 will be initialized after channel 0 */
{ 0, }
};
static struct pci_driver t3e3_pci_driver = {
.name = "SBE T3E3",
.id_table = t3e3_pci_tbl,
.probe = t3e3_init_card,
.remove = t3e3_remove_card,
};
static int __init t3e3_init_module(void)
{
return pci_register_driver(&t3e3_pci_driver);
}
static void __exit t3e3_cleanup_module(void)
{
pci_unregister_driver(&t3e3_pci_driver);
}
module_init(t3e3_init_module);
module_exit(t3e3_cleanup_module);
MODULE_LICENSE("GPL");
MODULE_DEVICE_TABLE(pci, t3e3_pci_tbl);