📄 cirrus.c
字号:
set_irq_type(dev->irq, IRQT_RISING);
/* enable the ethernet controller */
cirrus_set (dev,PP_RxCFG,RxOKiE | BufferCRC | CRCerroriE | RuntiE | ExtradataiE);
cirrus_set (dev,PP_RxCTL,RxOKA | IndividualA | BroadcastA);
cirrus_set (dev,PP_TxCFG,TxOKiE | Out_of_windowiE | JabberiE);
cirrus_set (dev,PP_BufCFG,Rdy4TxiE | RxMissiE | TxUnderruniE | TxColOvfiE | MissOvfloiE);
cirrus_set (dev,PP_LineCTL,SerRxON | SerTxON);
cirrus_set (dev,PP_BusCTL,EnableRQ);
#ifdef FULL_DUPLEX
cirrus_set (dev,PP_TestCTL,FDX);
#endif /* #ifdef FULL_DUPLEX */
/* start the queue */
netif_start_queue (dev);
return (0);
}
static int cirrus_stop (struct net_device *dev)
{
/* disable ethernet controller */
cirrus_write (dev,PP_BusCTL,0);
cirrus_write (dev,PP_TestCTL,0);
cirrus_write (dev,PP_SelfCTL,0);
cirrus_write (dev,PP_LineCTL,0);
cirrus_write (dev,PP_BufCFG,0);
cirrus_write (dev,PP_TxCFG,0);
cirrus_write (dev,PP_RxCTL,0);
cirrus_write (dev,PP_RxCFG,0);
/* uninstall interrupt handler */
free_irq (dev->irq,dev);
/* stop the queue */
netif_stop_queue (dev);
return (0);
}
static int cirrus_set_mac_address (struct net_device *dev, void *p)
{
cirrus_t *priv = netdev_priv(dev);
struct sockaddr *addr = (struct sockaddr *)p;
int i;
if (netif_running(dev))
return -EBUSY;
spin_lock(&priv->lock);
memcpy(dev->dev_addr, addr->sa_data, dev->addr_len);
/* configure MAC address */
for (i = 0; i < ETH_ALEN; i += 2)
cirrus_write (dev,PP_IA + i,dev->dev_addr[i] | (dev->dev_addr[i + 1] << 8));
spin_unlock(&priv->lock);
return 0;
}
static struct net_device_stats *cirrus_get_stats (struct net_device *dev)
{
cirrus_t *priv = netdev_priv(dev);
return (&priv->stats);
}
static void cirrus_set_receive_mode (struct net_device *dev)
{
cirrus_t *priv = netdev_priv(dev);
spin_lock(&priv->lock);
if ((dev->flags & IFF_PROMISC))
cirrus_set (dev,PP_RxCTL,PromiscuousA);
else
cirrus_clear (dev,PP_RxCTL,PromiscuousA);
if ((dev->flags & IFF_ALLMULTI) || dev->mc_list)
cirrus_set (dev,PP_RxCTL,MulticastA);
else
cirrus_clear (dev,PP_RxCTL,MulticastA);
spin_unlock(&priv->lock);
}
static int cirrus_eeprom_wait (struct net_device *dev)
{
int i;
for (i = 0; i < 200; i++) {
if (!(cirrus_read (dev,PP_SelfST) & SIBUSY))
return (0);
udelay (1);
}
return (-1);
}
static int cirrus_eeprom_read (struct net_device *dev,u16 *value,u16 offset)
{
if (cirrus_eeprom_wait (dev) < 0)
return (-1);
cirrus_write (dev,PP_EEPROMCommand,offset | EEReadRegister);
if (cirrus_eeprom_wait (dev) < 0)
return (-1);
*value = cirrus_read (dev,PP_EEPROMData);
return (0);
}
static int cirrus_eeprom (struct net_device *dev,cirrus_eeprom_t *eeprom)
{
u16 offset,buf[16],*word;
u8 checksum = 0,*byte;
if (cirrus_eeprom_read (dev,buf,0) < 0) {
read_timed_out:
printk (KERN_DEBUG "%s: EEPROM read timed out\n",dev->name);
return (-ETIMEDOUT);
}
if ((buf[0] >> 8) != 0xa1) {
printk (KERN_DEBUG "%s: No EEPROM present\n",dev->name);
return (-ENODEV);
}
if ((buf[0] & 0xff) < sizeof (buf)) {
eeprom_too_small:
printk (KERN_DEBUG "%s: EEPROM too small\n",dev->name);
return (-ENODEV);
}
for (offset = 1; offset < ((buf[0] & 0xff) >> 1); offset++) {
if (cirrus_eeprom_read (dev,buf + offset,offset) < 0)
goto read_timed_out;
if (buf[offset] == 0xffff)
goto eeprom_too_small;
}
if (buf[1] != 0x2020) {
printk (KERN_DEBUG "%s: Group Header #1 mismatch\n",dev->name);
return (-EIO);
}
if (buf[5] != 0x502c) {
printk (KERN_DEBUG "%s: Group Header #2 mismatch\n",dev->name);
return (-EIO);
}
if (buf[12] != 0x2158) {
printk (KERN_DEBUG "%s: Group Header #3 mismatch\n",dev->name);
return (-EIO);
}
eeprom->io_base = buf[2];
eeprom->irq = buf[3];
eeprom->dma = buf[4];
eeprom->mem_base = (buf[7] << 16) | buf[6];
eeprom->rom_base = (buf[9] << 16) | buf[8];
eeprom->rom_mask = (buf[11] << 16) | buf[10];
word = (u16 *) eeprom->mac;
for (offset = 0; offset < 3; offset++) word[offset] = buf[13 + offset];
byte = (u8 *) buf;
for (offset = 0; offset < sizeof (buf); offset++) checksum += byte[offset];
if (cirrus_eeprom_read (dev,&offset,0x10) < 0)
goto read_timed_out;
if ((offset >> 8) != (u8) (0x100 - checksum)) {
printk (KERN_DEBUG "%s: Checksum mismatch (expected 0x%.2x, got 0x%.2x instead\n",
dev->name,
(u8) (0x100 - checksum),
offset >> 8);
return (-EIO);
}
return (0);
}
static int cirrus_remove(struct device *dev)
{
struct platform_device *pdev = to_platform_device(dev);
struct resource *res;
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (res == NULL) BUG();
iounmap((void __iomem *)(ndev->base_addr));
release_mem_region(res->start, res->end - res->start);
dev_set_drvdata(dev, NULL);
free_netdev(ndev);
printk("cirrus-cs89x0: removed.\n");
return 0;
}
static int cirrus_suspend(struct device *dev, pm_message_t state)
{
printk("%s: not implemented\n",__FUNCTION__);
return 0;
}
static int cirrus_resume(struct device *dev)
{
printk("%s: not implemented\n",__FUNCTION__);
return 0;
}
/*
* Driver initialization routines
*/
void cirrus_parse_mac (const char *macstr, char *mac)
{
int i;
if (strlen(macstr) != 17)
printk("invalid MAC string format\n");
for (i = 0; i < 6; i++) {
mac[i] = simple_strtoul(macstr + i * 3, NULL, 16);
}
}
int __init cirrus_probe (struct net_device *dev, unsigned long ioaddr)
{
cirrus_t *priv = netdev_priv(dev);
int i;
u16 value;
ether_setup (dev);
dev->open = cirrus_start;
dev->stop = cirrus_stop;
dev->hard_start_xmit = cirrus_send_start;
dev->get_stats = cirrus_get_stats;
dev->set_multicast_list = cirrus_set_receive_mode;
dev->set_mac_address = cirrus_set_mac_address;
dev->tx_timeout = cirrus_transmit_timeout;
dev->watchdog_timeo = HZ;
dev->if_port = IF_PORT_10BASET;
dev->priv = (void *)priv;
spin_lock_init(&priv->lock);
SET_MODULE_OWNER (dev);
dev->base_addr = ioaddr;
/* if an EEPROM is present, use it's MAC address */
if (!cirrus_eeprom(dev,&priv->eeprom))
for (i = 0; i < 6; i++)
dev->dev_addr[i] = priv->eeprom.mac[i];
else
cirrus_parse_mac(cirrus_mac, dev->dev_addr);
/* verify EISA registration number for Cirrus Logic */
if ((value = cirrus_read (dev,PP_ProductID)) != EISA_REG_CODE) {
printk (KERN_ERR "%s: incorrect signature 0x%.4x\n",dev->name,value);
return (-ENXIO);
}
/* verify chip version */
value = cirrus_read (dev,PP_ProductID + 2);
if (VERSION (value) != CS8900A) {
printk (KERN_ERR "%s: unknown chip version 0x%08x\n",dev->name,VERSION (value));
return (-ENXIO);
}
printk (KERN_INFO "%s: CS8900A rev %c detected\n",dev->name,'B' + REVISION (value) - REV_B);
/* setup interrupt number */
cirrus_write (dev,PP_IntNum,0);
/* configure MAC address */
for (i = 0; i < ETH_ALEN; i += 2)
cirrus_write (dev,PP_IA + i,dev->dev_addr[i] | (dev->dev_addr[i + 1] << 8));
return register_netdev(dev);
}
int __init cirrus_drv_probe (struct device *dev)
{
struct platform_device *pdev = to_platform_device(dev);
struct resource *res;
unsigned int *addr;
int ret;
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (!res) {
ret = -ENODEV;
goto out;
}
/* Request the regions. */
if (!request_mem_region(res->start, 16, "cirrus-cs89x0")) {
ret = -EBUSY;
goto out;
}
/* remap it. */
addr = ioremap(res->start, res->end - res->start);
if (!addr) {
ret = -ENOMEM;
goto release_1;
}
ndev = alloc_etherdev(sizeof(cirrus_t));
if (!ndev) {
printk("cirrus-cs89x0: could not allocate device.\n");
ret = -ENOMEM;
goto release_2;
}
SET_NETDEV_DEV(ndev, dev);
ndev->irq = platform_get_irq(pdev, 0);
printk(KERN_DEBUG "cirrus: irq:%d\n",ndev->irq);
dev_set_drvdata(dev, ndev);
ret = cirrus_probe(ndev, (unsigned long)addr);
if (ret != 0)
goto release_3;
return 0;
release_3:
dev_set_drvdata(dev, NULL);
free_netdev(ndev);
release_2:
iounmap(addr);
release_1:
release_mem_region(res->start, res->end - res->start);
out:
printk("cirrus-cs89x0: not found (%d).\n", ret);
return ret;
}
static struct device_driver cirrus_driver = {
.name = "cirrus-cs89x0",
.bus = &platform_bus_type,
.probe = cirrus_drv_probe,
.remove = cirrus_remove,
.suspend = cirrus_suspend,
.resume = cirrus_resume,
};
static int __init cirrus_init(void)
{
return driver_register(&cirrus_driver);
}
static void __exit cirrus_cleanup(void)
{
driver_unregister(&cirrus_driver);
}
MODULE_AUTHOR ("Abraham van der Merwe <abraham at 2d3d.co.za>");
MODULE_DESCRIPTION ("Cirrus Logic CS8900A driver for Linux (V0.02)");
MODULE_LICENSE ("GPL");
module_init (cirrus_init);
module_exit (cirrus_cleanup);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -