📄 plx9052-26.c
字号:
/*====================================================================== Device driver for PLX9052 PCI-to-PCMCIA bridges. Version 0.1.5. (C) 2003 Pavel Roskin <proski@gnu.org> The contents of this file are subject to the Mozilla Public License Version 1.1 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.mozilla.org/MPL/ Software distributed under the License is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for the specific language governing rights and limitations under the License. Alternatively, the contents of this file may be used under the terms of the GNU General Public License version 2 (the "GPL"), in which case the provisions of the GPL are applicable instead of the above. If you wish to allow the use of your version of this file only under the terms of the GPL and not to allow others to use your version of this file under the MPL, indicate your decision by deleting the provisions above and replace them with the notice and other provisions required by the GPL. If you do not delete the provisions above, a recipient may use your version of this file under either the MPL or the GPL.======================================================================*/#include <linux/module.h>#include <linux/init.h>#include <linux/config.h>#include <linux/types.h>#include <linux/fcntl.h>#include <linux/string.h>#include <linux/kernel.h>#include <linux/errno.h>#include <linux/timer.h>#include <linux/sched.h>#include <linux/slab.h>#include <linux/pci.h>#include <linux/interrupt.h>#include <linux/ioport.h>#include <linux/delay.h>#include <linux/spinlock.h>#include <linux/workqueue.h>#include <linux/version.h>#include <asm/irq.h>#include <asm/io.h>#include <asm/bitops.h>#include <asm/system.h>#include <pcmcia/version.h>#include <pcmcia/cs_types.h>#include <pcmcia/ss.h>#include <pcmcia/cs.h>/*====================================================================*//* Module parameters */MODULE_AUTHOR("Pavel Roskin <proski@gnu.org>");MODULE_DESCRIPTION("PLX9052 PCMCIA socket driver");MODULE_LICENSE("Dual MPL/GPL");/*====================================================================*/#define DEVICE_NAME "plx9052"#define PLX_IOWIN_MIN 0x04 /* Minimal I/O window */#define PLX_IOWIN_MAX 0x1000 /* Maximal I/O window */#define PLX_MEMWIN_MIN 0x10 /* Minimal memory window */#define PLX_MEMWIN_MAX 0x80000 /* Maximal memory window */#define PLX_CIS_START 0x03000000 /* Start of CIS in PLX local memory *//* * PLX9052 has 4 local address spaces. * The driver allocates them dynamically. */#define PLX_AREAS 4 /* Number of local address spaces (areas) */#define PLX_AREA_USED 0x20 /* Area is used */#define PLX_AREA_IO 0x10 /* Area is used for I/O window */#define PLX_AREA_MAP_MASK 0x0f /* Map number *//* PCI base address for corresponding local address space */#define PLX_PCI_BASE(x) (PCI_BASE_ADDRESS_2 + (x << 2))/* PLX9052 registers *//* Local address space range (0 <= x < PLX_AREAS) */#define PLX_LASRR(x) (0x00 + (x << 2))#define PLX_LASRR_IO 1 /* Range is used as I/O space */#define PLX_LAS_MEMMASK 0x0ffffff0 /* Mask for memory address lines */#define PLX_LAS_IOMASK 0x0ffffffc /* Mask for I/O address lines *//* Local address space base address (0 <= x < PLX_AREAS) */#define PLX_LASBA(x) (0x14 + (x << 2))#define PLX_LASBA_DISABLE 0 /* Disable address decoding */#define PLX_LASBA_ENABLE 1 /* Enable address decoding *//* Local address space bus region descriptors (0 <= x < PLX_AREAS) */#define PLX_LASBRD(x) (0x28 + (x << 2))#define PLX_LASBRD_WIDTH_MASK 0x00c00000#define PLX_LASBRD_WIDTH_8 0x00000000#define PLX_LASBRD_WIDTH_16 0x00400000#define PLX_LASBRD_WIDTH_32 0x00800000#define PLX_LASBRD_WS_MASK 0x003fffc0#define PLX_LASBRD_WS0 0x00000000#define PLX_LASBRD_WS1 0x0010a840/* Chip select base address (0 <= x < PLX_AREAS) */#define PLX_CSBASE(x) (0x3c + (x << 2))#define PLX_CSBASE_ENABLE 1#define PLX_INTCSR 0x4c /* Interrupt Control and Status Register */#define PLX_INT1_ENABLE 0x40 /* Interrupt 1 Enable bit */#define PLX_INT1_ACTIVE 0x04 /* Interrupt 1 Active bit */#define PLX_CNTRL 0x50 /* Control register */#define PLX_CNTRL_RESET 0x40000000 /* Reset local bus */#ifdef __IN_PCMCIA_PACKAGE__socket_state_t dead_socket = { 0, SS_DETECT, 0, 0, 0};#endifstruct plx9052_socket { void (*handler) (void *info, u_int events); void *info; socket_state_t state; struct pci_dev *pdev; struct pcmcia_socket socket; /* PCI resource 1, PLX9052 registers */ unsigned int plxctl_addr; unsigned int plxctl_len; /* should be 0x80 */ /* PCI resource 2, mapped to PCMCIA memory space */ unsigned long mem_phys; unsigned long mem_len; /* should be 0x1000 */ u8 *mem_virt; char area_used[PLX_AREAS]; /* Used local address spaces */ /* Event processing */ u_int event; spinlock_t event_lock; struct work_struct event_work;};/* Forward declarations */static void plx9052_close(struct pci_dev *pdev);static int plx9052_set_socket(struct pcmcia_socket *sock, socket_state_t * state);/* Check that an area is size-aligned. * Return 0 for good areas, -1 for bad ones. */static int plx9052_align_check(u32 base, u32 len){ int power = 0; int l = len; if (!len) return -1; while (l >>= 1) power++; if (len != (1 << power)) return -1; if (base & ((1 << power) - 1)) return -1; return 0;}static inline void plx9052_outl(struct plx9052_socket *socket, u32 val, ioaddr_t addr){ outl_p(val, socket->plxctl_addr + addr);}static inline u32 plx9052_inl(struct plx9052_socket *socket, ioaddr_t addr){ return inl_p(socket->plxctl_addr + addr);}static inline void plx9052_enable_irq(struct plx9052_socket *socket){ u32 reg; reg = plx9052_inl(socket, PLX_INTCSR); if (!(reg & PLX_INT1_ENABLE)) { reg |= PLX_INT1_ENABLE; plx9052_outl(socket, reg, PLX_INTCSR); }}static inline void plx9052_disable_irq(struct plx9052_socket *socket){ u32 reg; reg = plx9052_inl(socket, PLX_INTCSR); if (reg & PLX_INT1_ENABLE) { reg &= ~PLX_INT1_ENABLE; plx9052_outl(socket, reg, PLX_INTCSR); }}static inline int plx9052_irq_active(struct plx9052_socket *socket){ u32 reg; reg = plx9052_inl(socket, PLX_INTCSR); return (reg & PLX_INT1_ACTIVE);}/* Hack - determine card presence by the first byte of CIS */static inline int plx9052_card_present(struct plx9052_socket *socket){ return (((volatile u8 *) socket->mem_virt)[0] == 1);}static void plx9052_event(struct plx9052_socket *socket){ u_int event; if (!socket->handler) return; spin_lock_irq(&socket->event_lock); event = socket->event; socket->event = 0; spin_unlock_irq(&socket->event_lock); if (!event) return; /* To make sure it'socket an insertion, wait 1 second before CIS is ready. * If the card is currently active, report ejection immediately. */ if (!(socket->state.flags & SS_OUTPUT_ENA)) { current->state = TASK_INTERRUPTIBLE; schedule_timeout(HZ); } socket->handler(socket->info, event); if (!(socket->state.flags & SS_RESET)) plx9052_enable_irq(socket);}static int plx9052_get_area(struct plx9052_socket *socket, int is_io, int map){ int i; /* Area 0 is used internally for CIS */ for (i = 1; i < PLX_AREAS; i++) { if (!socket->area_used[i]) { socket->area_used[i] = PLX_AREA_USED | map | (is_io ? PLX_AREA_IO : 0); return i; } } return -1;}static void plx9052_disable_areas(struct plx9052_socket *socket, int is_io, int map){ int i; int type = PLX_AREA_USED | map | (is_io ? PLX_AREA_IO : 0); /* Note that there may be more than one area for the map */ for (i = 1; i < PLX_AREAS; i++) { if (socket->area_used[i] == type) { socket->area_used[i] = 0; plx9052_outl(socket, PLX_LASBA_DISABLE, PLX_LASBA(i)); } }}/* program a size-aligned area */static int plx9052_program_area(struct plx9052_socket *socket, int is_io, int map, unsigned long sysbase, unsigned long syslen, u32 cardbase, unsigned int flags){ u32 brd; u32 mask; int area; area = plx9052_get_area(socket, is_io, map); if (area < 0) { printk(KERN_ERR "No free areas left\n"); return -EINVAL; } pci_write_config_dword(socket->pdev, PLX_PCI_BASE(area), sysbase | is_io); mask = ~(syslen - 1); if (is_io) mask = (mask & PLX_LAS_IOMASK) | PLX_LASRR_IO; else mask &= PLX_LAS_MEMMASK; brd = plx9052_inl(socket, PLX_LASBRD(area)); brd &= ~PLX_LASBRD_WIDTH_MASK; if (flags & MAP_16BIT) brd |= PLX_LASBRD_WIDTH_16; brd &= ~PLX_LASBRD_WS_MASK; if (!(flags & MAP_0WS)) brd |= PLX_LASBRD_WS1; plx9052_outl(socket, brd, PLX_LASBRD(area)); plx9052_outl(socket, mask, PLX_LASRR(area)); plx9052_outl(socket, cardbase | PLX_LASBA_ENABLE, PLX_LASBA(area)); return 0;}static int plx9052_init(struct pcmcia_socket *sock){ struct plx9052_socket *socket = container_of(sock, struct plx9052_socket, socket);#if 0 /* Dump all registers */ int i; for (i = 0; i < 0x20; i++) { printk("%08x ", plx9052_inl(socket, i << 2)); if ((i & 7) == 7) printk("\n"); }#endif /* Area 0 is used to access CIS from the driver */ plx9052_outl(socket, PLX_LASBA_ENABLE | PLX_CIS_START, PLX_LASBA(0)); socket->area_used[0] = 1; plx9052_outl(socket, PLX_CIS_START | (PLX_MEMWIN_MAX >> 1) | PLX_CSBASE_ENABLE, PLX_CSBASE(0)); plx9052_outl(socket, (PLX_IOWIN_MAX >> 1) | PLX_CSBASE_ENABLE, PLX_CSBASE(1)); plx9052_enable_irq(socket); socket->state.csc_mask |= SS_DETECT; return 0;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -