📄 i82092.c
字号:
/* * 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 <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"/* 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, class: 0, class_mask:0, }, {} };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: NULL, resume: NULL };/* the pccard structure and its functions */static struct pccard_operations i82092aa_operations = { init: i82092aa_init, suspend: i82092aa_suspend, register_callback: i82092aa_register_callback, inquire_socket: i82092aa_inquire_socket, get_status: i82092aa_get_status, get_socket: i82092aa_get_socket, set_socket: i82092aa_set_socket, get_io_map: i82092aa_get_io_map, set_io_map: i82092aa_set_io_map, get_mem_map: i82092aa_get_mem_map, set_mem_map: i82092aa_set_mem_map, proc_setup: i82092aa_proc_setup,};/* The card can do upto 4 sockets, allocate a structure for each of them */struct socket_info { 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 */ socket_cap_t cap; unsigned int pending_events; /* Pending events on this interface */ void (*handler)(void *info, u_int events); /* callback to the driver of the card */ void *info; /* to be passed to the handler */ 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 __init i82092aa_pci_probe(struct pci_dev *dev, const struct pci_device_id *id){ unsigned char configbyte; int i; enter("i82092aa_pci_probe"); if (pci_enable_device(dev)) return -EIO; pci_read_config_byte(dev, 0x40, &configbyte); /* PCI Configuration Control */ switch(configbyte&6) { case 0: printk(KERN_INFO "i82092aa: configured as a 2 socket device.\n"); socket_count = 2; break; case 2: printk(KERN_INFO "i82092aa: configured as a 1 socket device.\n"); socket_count = 1; break; case 4: case 6: printk(KERN_INFO "i82092aa: configured as a 4 socket device.\n"); socket_count = 4; break; default: printk(KERN_ERR "i82092aa: Oops, you did something we didn't think of.\n"); return -EIO; break; } for (i = 0;i<socket_count;i++) { sockets[i].card_state = 1; /* 1 = present but empty */ sockets[i].io_base = (dev->resource[0].start & ~1); if (sockets[i].io_base > 0) request_region(sockets[i].io_base, 2, "i82092aa"); sockets[i].cap.features |= SS_CAP_PCCARD; sockets[i].cap.map_size = 0x1000; sockets[i].cap.irq_mask = 0; sockets[i].cap.pci_irq = dev->irq; 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 (request_irq(dev->irq, i82092aa_interrupt, SA_SHIRQ, "i82092aa", i82092aa_interrupt)) { printk(KERN_ERR "i82092aa: Failed to register IRQ %d, aborting\n", dev->irq); return -EIO; } if (register_ss_entry(socket_count, &i82092aa_operations) != 0) printk(KERN_NOTICE "i82092aa: register_ss_entry() failed\n"); leave("i82092aa_pci_probe"); return 0;}static void __devexit i82092aa_pci_remove(struct pci_dev *dev){ enter("i82092aa_pci_remove"); free_irq(dev->irq, i82092aa_interrupt); 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;}static 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;}static 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;} static int to_ns(int cycles){ return cycle_time*cycles;}/* Interrupt handler functionality */static void i82092aa_bh(void *dummy){ unsigned int events; int i; for (i=0; i < socket_count; i++) { events = xchg(&(sockets[i].pending_events),0); printk("events = %x \n",events); if (sockets[i].handler) sockets[i].handler(sockets[i].info, events); }} static struct tq_struct i82092aa_task = { routine: i82092aa_bh}; static void i82092aa_interrupt(int irq, void *dev, struct pt_regs *regs){ int i; int loopcount = 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 */ (sockets[i].handler==NULL)) /* no way to handle events */ continue; 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) { sockets[i].pending_events |= events; schedule_task(&i82092aa_task); } active |= events; } if (active==0) /* no more events to handle */ break; } /* 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){ enter("set_bridge_state"); indirect_write(sock, I365_GBLCTL,0x00); indirect_write(sock, I365_GENCTL,0x00); indirect_setbit(sock, I365_INTCTL,0x08); leave("set_bridge_state");} static int i82092aa_init(unsigned int s){ int i; pccard_io_map io = { 0, 0, 0, 0, 1 }; pccard_mem_map mem = { 0, 0, 0, 0, 0, 0 }; enter("i82092aa_init"); mem.sys_stop = 0x0fff; i82092aa_set_socket(s, &dead_socket); for (i = 0; i < 2; i++) { io.map = i; i82092aa_set_io_map(s, &io); } for (i = 0; i < 5; i++) { mem.map = i; i82092aa_set_mem_map(s, &mem); } leave("i82092aa_init"); return 0;} static int i82092aa_suspend(unsigned int sock){ int retval; enter("i82092aa_suspend"); retval = i82092aa_set_socket(sock, &dead_socket); leave("i82092aa_suspend"); return retval;} static int i82092aa_register_callback(unsigned int sock, void (*handler)(void *, unsigned int), void * info){ enter("i82092aa_register_callback"); sockets[sock].handler = handler; sockets[sock].info = info; if (handler == NULL) { MOD_DEC_USE_COUNT; } else { MOD_INC_USE_COUNT; } leave("i82092aa_register_callback"); return 0;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -