au1000_generic.c
来自「Linux Kernel 2.6.9 for OMAP1710」· C语言 代码 · 共 697 行 · 第 1/2 页
C
697 行
/* * * Alchemy Semi Au1000 pcmcia driver * * Copyright 2001 MontaVista Software Inc. * Author: MontaVista Software, Inc. * ppopov@mvista.com or source@mvista.com * * ######################################################################## * * This program is free software; you can distribute it and/or modify it * under the terms of the GNU General Public License (Version 2) as * published by the Free Software Foundation. * * This program is distributed in the hope it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 59 Temple Place - Suite 330, Boston MA 02111-1307, USA. * * ######################################################################## * * */#include <linux/module.h>#include <linux/moduleparam.h>#include <linux/init.h>#include <linux/config.h>#include <linux/delay.h>#include <linux/ioport.h>#include <linux/kernel.h>#include <linux/tqueue.h>#include <linux/timer.h>#include <linux/mm.h>#include <linux/proc_fs.h>#include <linux/types.h>#include <linux/vmalloc.h>#include <pcmcia/version.h>#include <pcmcia/cs_types.h>#include <pcmcia/cs.h>#include <pcmcia/ss.h>#include <pcmcia/bulkmem.h>#include <pcmcia/cistpl.h>#include <pcmcia/bus_ops.h>#include "cs_internal.h"#include <asm/io.h>#include <asm/irq.h>#include <asm/system.h>#include <asm/au1000.h>#include <asm/au1000_pcmcia.h>#ifdef DEBUGstatic int pc_debug;module_param(pc_debug, int, 0644);#define debug(lvl,fmt) do { \ if (pc_debug > (lvl)) \ printk(KERN_DEBUG fmt); \} while (0)#else#define debug(lvl,fmt) do { } while (0)#endifMODULE_LICENSE("GPL");MODULE_AUTHOR("Pete Popov, MontaVista Software <ppopov@mvista.com>");MODULE_DESCRIPTION("Linux PCMCIA Card Services: Au1x00 Socket Controller");#define MAP_SIZE 0x1000000/* This structure maintains housekeeping state for each socket, such * as the last known values of the card detect pins, or the Card Services * callback value associated with the socket: */static struct au1000_pcmcia_socket *pcmcia_socket;static int socket_count;/* Returned by the low-level PCMCIA interface: */static struct pcmcia_low_level *pcmcia_low_level;/* Event poll timer structure */static struct timer_list poll_timer;/* Prototypes for routines which are used internally: */static int au1000_pcmcia_driver_init(void);static void au1000_pcmcia_driver_shutdown(void);static void au1000_pcmcia_task_handler(void *data);static void au1000_pcmcia_poll_event(unsigned long data);static void au1000_pcmcia_interrupt(int irq, void *dev, struct pt_regs *regs);static struct tq_struct au1000_pcmcia_task;#ifdef CONFIG_PROC_FSstatic int au1000_pcmcia_proc_status(char *buf, char **start, off_t pos, int count, int *eof, void *data);#endif/* Prototypes for operations which are exported to the * new-and-impr^H^H^H^H^H^H^H^H^H^H in-kernel PCMCIA core: */static int au1000_pcmcia_init(u32 sock);static int au1000_pcmcia_suspend(u32 sock);static int au1000_pcmcia_register_callback(u32 sock, void (*handler)(void *, u32), void *info);static int au1000_pcmcia_inquire_socket(u32 sock, socket_cap_t *cap);static int au1000_pcmcia_get_status(u32 sock, u_int *value);static int au1000_pcmcia_get_socket(u32 sock, socket_state_t *state);static int au1000_pcmcia_set_socket(u32 sock, socket_state_t *state);static int au1000_pcmcia_get_io_map(u32 sock, struct pccard_io_map *io);static int au1000_pcmcia_set_io_map(u32 sock, struct pccard_io_map *io);static int au1000_pcmcia_get_mem_map(u32 sock, struct pccard_mem_map *mem);static int au1000_pcmcia_set_mem_map(u32 sock, struct pccard_mem_map *mem);#ifdef CONFIG_PROC_FSstatic void au1000_pcmcia_proc_setup(u32 sock, struct proc_dir_entry *base);#endifstatic struct pccard_operations au1000_pcmcia_operations = { au1000_pcmcia_init, au1000_pcmcia_suspend, au1000_pcmcia_register_callback, au1000_pcmcia_inquire_socket, au1000_pcmcia_get_status, au1000_pcmcia_get_socket, au1000_pcmcia_set_socket, au1000_pcmcia_get_io_map, au1000_pcmcia_set_io_map, au1000_pcmcia_get_mem_map, au1000_pcmcia_set_mem_map,#ifdef CONFIG_PROC_FS au1000_pcmcia_proc_setup#endif};static spinlock_t pcmcia_lock = SPIN_LOCK_UNLOCKED;static int __init au1000_pcmcia_driver_init(void){ struct pcmcia_init pcmcia_init; struct pcmcia_state state; unsigned int i; printk("\nAu1x00 PCMCIA\n");#ifndef CONFIG_64BIT_PHYS_ADDR printk(KERN_ERR "Au1x00 PCMCIA 36 bit IO support not enabled\n"); return -1;#endif#if defined(CONFIG_MIPS_PB1000) || defined(CONFIG_MIPS_PB1100) || defined(CONFIG_MIPS_PB1500) pcmcia_low_level=&pb1x00_pcmcia_ops;#else#error Unsupported AU1000 board.#endif pcmcia_init.handler=au1000_pcmcia_interrupt; if((socket_count=pcmcia_low_level->init(&pcmcia_init))<0) { printk(KERN_ERR "Unable to initialize PCMCIA service.\n"); return -EIO; } /* NOTE: the chip select must already be setup */ pcmcia_socket = kmalloc(sizeof(struct au1000_pcmcia_socket) * socket_count, GFP_KERNEL); if (!pcmcia_socket) { printk(KERN_ERR "Card Services can't get memory \n"); return -1; } memset(pcmcia_socket, 0, sizeof(struct au1000_pcmcia_socket) * socket_count); /* * Assuming max of 2 sockets, which the Au1000 supports. * WARNING: the Pb1000 has two sockets, and both work, but you * can't use them both at the same time due to glue logic conflicts. */ for(i=0; i < socket_count; i++) { if(pcmcia_low_level->socket_state(i, &state)<0){ printk(KERN_ERR "Unable to get PCMCIA status\n"); return -EIO; } pcmcia_socket[i].k_state=state; pcmcia_socket[i].cs_state.csc_mask=SS_DETECT; if (i == 0) { pcmcia_socket[i].virt_io = (u32)ioremap((ioaddr_t)0xF00000000, 0x1000); pcmcia_socket[i].phys_attr = (memaddr_t)0xF40000000; pcmcia_socket[i].phys_mem = (memaddr_t)0xF80000000; } else { pcmcia_socket[i].virt_io = (u32)ioremap((ioaddr_t)0xF08000000, 0x1000); pcmcia_socket[i].phys_attr = (memaddr_t)0xF48000000; pcmcia_socket[i].phys_mem = (memaddr_t)0xF88000000; } } /* Only advertise as many sockets as we can detect: */ if(register_ss_entry(socket_count, &au1000_pcmcia_operations)<0){ printk(KERN_ERR "Unable to register socket service routine\n"); return -ENXIO; } /* Start the event poll timer. * It will reschedule by itself afterwards. */ au1000_pcmcia_poll_event(0); debug(1, "au1000: initialization complete\n"); return 0;} /* au1000_pcmcia_driver_init() */module_init(au1000_pcmcia_driver_init);static void __exit au1000_pcmcia_driver_shutdown(void){ int i; del_timer_sync(&poll_timer); unregister_ss_entry(&au1000_pcmcia_operations); pcmcia_low_level->shutdown(); flush_scheduled_tasks(); for(i=0; i < socket_count; i++) { if (pcmcia_socket[i].virt_io) iounmap((void *)pcmcia_socket[i].virt_io); } debug(1, "au1000: shutdown complete\n");}module_exit(au1000_pcmcia_driver_shutdown);static int au1000_pcmcia_init(unsigned int sock) { return 0; }static int au1000_pcmcia_suspend(unsigned int sock){ return 0;}static inline unsigned au1000_pcmcia_events(struct pcmcia_state *state, struct pcmcia_state *prev_state, unsigned int mask, unsigned int flags){ unsigned int events=0; if(state->detect!=prev_state->detect){ debug(2, "%s(): card detect value %u\n", __FUNCTION__, state->detect); events |= mask&SS_DETECT; } if(state->ready!=prev_state->ready){ debug(2, "%s(): card ready value %u\n", __FUNCTION__, state->ready); events |= mask&((flags&SS_IOCARD)?0:SS_READY); } *prev_state=*state; return events;} /* au1000_pcmcia_events() *//* * Au1000_pcmcia_task_handler() * Processes socket events. */static void au1000_pcmcia_task_handler(void *data) { struct pcmcia_state state; int i, events, irq_status; for(i=0; i<socket_count; i++) { if((irq_status = pcmcia_low_level->socket_state(i, &state))<0) printk(KERN_ERR "low-level PCMCIA error\n"); events = au1000_pcmcia_events(&state, &pcmcia_socket[i].k_state, pcmcia_socket[i].cs_state.csc_mask, pcmcia_socket[i].cs_state.flags); if(pcmcia_socket[i].handler!=NULL) { pcmcia_socket[i].handler(pcmcia_socket[i].handler_info, events); } }} /* au1000_pcmcia_task_handler() */static struct tq_struct au1000_pcmcia_task = { routine: au1000_pcmcia_task_handler};static void au1000_pcmcia_poll_event(unsigned long dummy){ poll_timer.function = au1000_pcmcia_poll_event; poll_timer.expires = jiffies + AU1000_PCMCIA_POLL_PERIOD; add_timer(&poll_timer); schedule_task(&au1000_pcmcia_task);}/* * au1000_pcmcia_interrupt() * The actual interrupt work is performed by au1000_pcmcia_task(), * because the Card Services event handling code performs scheduling * operations which cannot be executed from within an interrupt context. */static void au1000_pcmcia_interrupt(int irq, void *dev, struct pt_regs *regs){ schedule_task(&au1000_pcmcia_task);}static int au1000_pcmcia_register_callback(unsigned int sock, void (*handler)(void *, unsigned int), void *info){ if(handler==NULL){ pcmcia_socket[sock].handler=NULL; MOD_DEC_USE_COUNT; } else { MOD_INC_USE_COUNT; pcmcia_socket[sock].handler=handler; pcmcia_socket[sock].handler_info=info; } return 0;}/* au1000_pcmcia_inquire_socket() *
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?