i82092.c
来自「Linux Kernel 2.6.9 for OMAP1710」· C语言 代码 · 共 809 行 · 第 1/2 页
C
809 行
/* * Driver for Intel I82092AA PCI-PCMCIA bridge. * * (C) 2001 Red Hat, Inc. * * Author: Arjan Van De Ven <arjanv@redhat.com> * Loosly based on i82365.c from the pcmcia-cs package * * $Id: i82092aa.c,v 1.2 2001/10/23 14:43:34 arjanv Exp $ */#include <linux/kernel.h>#include <linux/config.h>#include <linux/module.h>#include <linux/pci.h>#include <linux/init.h>#include <linux/workqueue.h>#include <linux/interrupt.h>#include <linux/device.h>#include <pcmcia/cs_types.h>#include <pcmcia/ss.h>#include <pcmcia/cs.h>#include <asm/system.h>#include <asm/io.h>#include "i82092aa.h"#include "i82365.h"MODULE_LICENSE("GPL");/* PCI core routines */static struct pci_device_id i82092aa_pci_ids[] = { { .vendor = PCI_VENDOR_ID_INTEL, .device = PCI_DEVICE_ID_INTEL_82092AA_0, .subvendor = PCI_ANY_ID, .subdevice = PCI_ANY_ID, }, {} };MODULE_DEVICE_TABLE(pci, i82092aa_pci_ids);static int i82092aa_socket_suspend (struct pci_dev *dev, u32 state){ return pcmcia_socket_dev_suspend(&dev->dev, state);}static int i82092aa_socket_resume (struct pci_dev *dev){ return pcmcia_socket_dev_resume(&dev->dev);}static struct pci_driver i82092aa_pci_drv = { .name = "i82092aa", .id_table = i82092aa_pci_ids, .probe = i82092aa_pci_probe, .remove = __devexit_p(i82092aa_pci_remove), .suspend = i82092aa_socket_suspend, .resume = i82092aa_socket_resume,};/* the pccard structure and its functions */static struct pccard_operations i82092aa_operations = { .init = i82092aa_init, .suspend = i82092aa_suspend, .get_status = i82092aa_get_status, .get_socket = i82092aa_get_socket, .set_socket = i82092aa_set_socket, .set_io_map = i82092aa_set_io_map, .set_mem_map = i82092aa_set_mem_map,};/* The card can do upto 4 sockets, allocate a structure for each of them */struct socket_info { int number; int card_state; /* 0 = no socket, 1 = empty socket, 2 = card but not initialized, 3 = operational card */ int io_base; /* base io address of the socket */ struct pcmcia_socket socket; struct pci_dev *dev; /* The PCI device for the socket */};#define MAX_SOCKETS 4static struct socket_info sockets[MAX_SOCKETS];static int socket_count; /* shortcut */ static int __devinit i82092aa_pci_probe(struct pci_dev *dev, const struct pci_device_id *id){ unsigned char configbyte; int i, ret; enter("i82092aa_pci_probe"); if ((ret = pci_enable_device(dev))) return ret; pci_read_config_byte(dev, 0x40, &configbyte); /* PCI Configuration Control */ switch(configbyte&6) { case 0: socket_count = 2; break; case 2: socket_count = 1; break; case 4: case 6: socket_count = 4; break; default: printk(KERN_ERR "i82092aa: Oops, you did something we didn't think of.\n"); ret = -EIO; goto err_out_disable; } printk(KERN_INFO "i82092aa: configured as a %d socket device.\n", socket_count); if (request_region(pci_resource_start(dev, 0), 2, "i82092aa")) { ret = -EBUSY; goto err_out_disable; } for (i = 0;i<socket_count;i++) { sockets[i].card_state = 1; /* 1 = present but empty */ sockets[i].io_base = pci_resource_start(dev, 0); sockets[i].socket.features |= SS_CAP_PCCARD; sockets[i].socket.map_size = 0x1000; sockets[i].socket.irq_mask = 0; sockets[i].socket.pci_irq = dev->irq; sockets[i].socket.owner = THIS_MODULE; sockets[i].number = i; if (card_present(i)) { sockets[i].card_state = 3; dprintk(KERN_DEBUG "i82092aa: slot %i is occupied\n",i); } else { dprintk(KERN_DEBUG "i82092aa: slot %i is vacant\n",i); } } /* Now, specifiy that all interrupts are to be done as PCI interrupts */ configbyte = 0xFF; /* bitmask, one bit per event, 1 = PCI interrupt, 0 = ISA interrupt */ pci_write_config_byte(dev, 0x50, configbyte); /* PCI Interrupt Routing Register */ /* Register the interrupt handler */ dprintk(KERN_DEBUG "Requesting interrupt %i \n",dev->irq); if ((ret = request_irq(dev->irq, i82092aa_interrupt, SA_SHIRQ, "i82092aa", i82092aa_interrupt))) { printk(KERN_ERR "i82092aa: Failed to register IRQ %d, aborting\n", dev->irq); goto err_out_free_res; } pci_set_drvdata(dev, &sockets[i].socket); for (i = 0; i<socket_count; i++) { sockets[i].socket.dev.dev = &dev->dev; sockets[i].socket.ops = &i82092aa_operations; ret = pcmcia_register_socket(&sockets[i].socket); if (ret) { goto err_out_free_sockets; } } leave("i82092aa_pci_probe"); return 0;err_out_free_sockets: if (i) { for (i--;i>=0;i--) { pcmcia_unregister_socket(&sockets[i].socket); } } free_irq(dev->irq, i82092aa_interrupt);err_out_free_res: release_region(pci_resource_start(dev, 0), 2);err_out_disable: pci_disable_device(dev); return ret; }static void __devexit i82092aa_pci_remove(struct pci_dev *dev){ struct pcmcia_socket *socket = pci_get_drvdata(dev); enter("i82092aa_pci_remove"); free_irq(dev->irq, i82092aa_interrupt); if (socket) pcmcia_unregister_socket(socket); leave("i82092aa_pci_remove");}static spinlock_t port_lock = SPIN_LOCK_UNLOCKED;/* basic value read/write functions */static unsigned char indirect_read(int socket, unsigned short reg){ unsigned short int port; unsigned char val; unsigned long flags; spin_lock_irqsave(&port_lock,flags); reg += socket * 0x40; port = sockets[socket].io_base; outb(reg,port); val = inb(port+1); spin_unlock_irqrestore(&port_lock,flags); return val;}#if 0static unsigned short indirect_read16(int socket, unsigned short reg){ unsigned short int port; unsigned short tmp; unsigned long flags; spin_lock_irqsave(&port_lock,flags); reg = reg + socket * 0x40; port = sockets[socket].io_base; outb(reg,port); tmp = inb(port+1); reg++; outb(reg,port); tmp = tmp | (inb(port+1)<<8); spin_unlock_irqrestore(&port_lock,flags); return tmp;}#endifstatic void indirect_write(int socket, unsigned short reg, unsigned char value){ unsigned short int port; unsigned long flags; spin_lock_irqsave(&port_lock,flags); reg = reg + socket * 0x40; port = sockets[socket].io_base; outb(reg,port); outb(value,port+1); spin_unlock_irqrestore(&port_lock,flags);}static void indirect_setbit(int socket, unsigned short reg, unsigned char mask){ unsigned short int port; unsigned char val; unsigned long flags; spin_lock_irqsave(&port_lock,flags); reg = reg + socket * 0x40; port = sockets[socket].io_base; outb(reg,port); val = inb(port+1); val |= mask; outb(reg,port); outb(val,port+1); spin_unlock_irqrestore(&port_lock,flags);}static void indirect_resetbit(int socket, unsigned short reg, unsigned char mask){ unsigned short int port; unsigned char val; unsigned long flags; spin_lock_irqsave(&port_lock,flags); reg = reg + socket * 0x40; port = sockets[socket].io_base; outb(reg,port); val = inb(port+1); val &= ~mask; outb(reg,port); outb(val,port+1); spin_unlock_irqrestore(&port_lock,flags);}static void indirect_write16(int socket, unsigned short reg, unsigned short value){ unsigned short int port; unsigned char val; unsigned long flags; spin_lock_irqsave(&port_lock,flags); reg = reg + socket * 0x40; port = sockets[socket].io_base; outb(reg,port); val = value & 255; outb(val,port+1); reg++; outb(reg,port); val = value>>8; outb(val,port+1); spin_unlock_irqrestore(&port_lock,flags);}/* simple helper functions *//* External clock time, in nanoseconds. 120 ns = 8.33 MHz */static int cycle_time = 120;static int to_cycles(int ns){ if (cycle_time!=0) return ns/cycle_time; else return 0;} /* Interrupt handler functionality */static irqreturn_t i82092aa_interrupt(int irq, void *dev, struct pt_regs *regs){ int i; int loopcount = 0; int handled = 0; unsigned int events, active=0; /* enter("i82092aa_interrupt");*/ while (1) { loopcount++; if (loopcount>20) { printk(KERN_ERR "i82092aa: infinite eventloop in interrupt \n"); break; } active = 0; for (i=0;i<socket_count;i++) { int csc; if (sockets[i].card_state==0) /* Inactive socket, should not happen */ continue; csc = indirect_read(i,I365_CSC); /* card status change register */ if (csc==0) /* no events on this socket */ continue; handled = 1; events = 0; if (csc & I365_CSC_DETECT) { events |= SS_DETECT; printk("Card detected in socket %i!\n",i); } if (indirect_read(i,I365_INTCTL) & I365_PC_IOCARD) { /* For IO/CARDS, bit 0 means "read the card" */ events |= (csc & I365_CSC_STSCHG) ? SS_STSCHG : 0; } else { /* Check for battery/ready events */ events |= (csc & I365_CSC_BVD1) ? SS_BATDEAD : 0; events |= (csc & I365_CSC_BVD2) ? SS_BATWARN : 0; events |= (csc & I365_CSC_READY) ? SS_READY : 0; } if (events) { pcmcia_parse_events(&sockets[i].socket, events); } active |= events; } if (active==0) /* no more events to handle */ break; } return IRQ_RETVAL(handled);/* leave("i82092aa_interrupt");*/}/* socket functions */static int card_present(int socketno){ unsigned int val; enter("card_present"); if ((socketno<0) || (socketno >= MAX_SOCKETS)) return 0; if (sockets[socketno].io_base == 0) return 0; val = indirect_read(socketno, 1); /* Interface status register */ if ((val&12)==12) { leave("card_present 1"); return 1; } leave("card_present 0"); return 0;}static void set_bridge_state(int sock)
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?