From: James S. <jsi...@us...> - 2001-09-05 18:30:06
|
Update of /cvsroot/linux-mips/linux/drivers/pcmcia In directory usw-pr-cvs1:/tmp/cvs-serv7925 Added Files: Config.in Makefile au1000_generic.c au1000_pb1000.c Log Message: Added PCMICA support for au1000 boards --- NEW FILE: Config.in --- # # PCMCIA bus subsystem configuration # # Right now the non-CardBus choices are not supported # by the integrated kernel driver. # mainmenu_option next_comment comment 'PCMCIA/CardBus support' #dep_tristate 'CardBus support' CONFIG_PCMCIA $CONFIG_PCI #if [ "$CONFIG_PCMCIA" != "n" ]; then # define_bool CONFIG_CARDBUS y #fi tristate 'PCMCIA/CardBus support' CONFIG_PCMCIA if [ "$CONFIG_PCMCIA" != "n" ]; then if [ "$CONFIG_PCI" != "n" ]; then bool ' CardBus support' CONFIG_CARDBUS fi bool ' i82365 compatible bridge support' CONFIG_I82365 bool ' Databook TCIC host bridge support' CONFIG_TCIC if [ "$CONFIG_HD64465" = "y" ]; then dep_tristate ' HD64465 host bridge support' CONFIG_HD64465_PCMCIA $CONFIG_PCMCIA fi if [ "$CONFIG_MIPS_AU1000" = "y" ]; then dep_tristate ' AU1000 support' CONFIG_PCMCIA_AU1000 $CONFIG_PCMCIA fi fi endmenu --- NEW FILE: Makefile --- # # Makefile for the kernel pcmcia subsystem (c/o David Hinds) # # Note! Dependencies are done automagically by 'make dep', which also # removes any old dependencies. DON'T put your own dependencies here # unless it's something special (ie not a .c file). # # Note 2! The CFLAGS definitions are now inherited from the # parent makes.. O_TARGET := pcmcia.o export-objs := ds.o cs.o cb_enabler.o yenta.o pci_socket.o list-multi := pcmcia_core.o yenta_socket.o yenta_socket-objs := pci_socket.o yenta.o pcmcia_core-objs := cistpl.o rsrc_mgr.o bulkmem.o cs.o ifeq ($(CONFIG_CARDBUS),y) pcmcia_core-objs += cardbus.o endif ifeq ($(CONFIG_PCMCIA),y) obj-y := cistpl.o rsrc_mgr.o bulkmem.o ds.o cs.o ifeq ($(CONFIG_CARDBUS),y) obj-y += cardbus.o cb_enabler.o yenta.o pci_socket.o endif ifeq ($(CONFIG_I82365),y) obj-y += i82365.o endif ifeq ($(CONFIG_TCIC),y) obj-y += tcic.o endif ifeq ($(CONFIG_HD64465_PCMCIA),y) obj-y += hd64465_ss.o endif else ifeq ($(CONFIG_PCMCIA),m) obj-m := pcmcia_core.o ds.o ifeq ($(CONFIG_I82365),y) obj-m += i82365.o endif ifeq ($(CONFIG_TCIC),y) obj-m += tcic.o endif ifeq ($(CONFIG_HD64465_PCMCIA),m) obj-m += hd64465_ss.o endif ifeq ($(CONFIG_CARDBUS),y) obj-m += yenta_socket.o cb_enabler.o endif endif endif obj-$(CONFIG_PCMCIA_AU1000) += au1000_ss.o au1000_ss-objs-y := au1000_generic.o au1000_ss-objs-$(CONFIG_MIPS_PB1000) += au1000_pb1000.o include $(TOPDIR)/Rules.make pcmcia_core.o: $(pcmcia_core-objs) $(LD) $(LD_RFLAG) -r -o $@ $(pcmcia_core-objs) au1000_ss.o: $(au1000_ss-objs-y) $(LD) -r -o $@ $(au1000_ss-objs-y) yenta_socket.o: $(yenta_socket-objs) $(LD) $(LD_RFLAG) -r -o $@ $(yenta_socket-objs) --- NEW FILE: au1000_generic.c --- /* * Alchemy Semi Au1000 pcmcia driver * * Copyright 2001 MontaVista Software Inc. * Author: MontaVista Software, Inc. * pp...@mv... or so...@mv... * * ######################################################################## * * 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/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/version.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 PCMCIA_DEBUG static int pc_debug; #endif MODULE_AUTHOR("Pete Popov, MontaVista Software <pp...@mv...>"); MODULE_DESCRIPTION("Linux PCMCIA Card Services: Au1000 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_FS static 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(unsigned int sock); static int au1000_pcmcia_suspend(unsigned int sock); static int au1000_pcmcia_register_callback(unsigned int sock, void (*handler) (void *, unsigned int), void *info); static int au1000_pcmcia_inquire_socket(unsigned int sock, socket_cap_t * cap); static int au1000_pcmcia_get_status(unsigned int sock, u_int * value); static int au1000_pcmcia_get_socket(unsigned int sock, socket_state_t * state); static int au1000_pcmcia_set_socket(unsigned int sock, socket_state_t * state); static int au1000_pcmcia_get_io_map(unsigned int sock, struct pccard_io_map *io); static int au1000_pcmcia_set_io_map(unsigned int sock, struct pccard_io_map *io); static int au1000_pcmcia_get_mem_map(unsigned int sock, struct pccard_mem_map *mem); static int au1000_pcmcia_set_mem_map(unsigned int sock, struct pccard_mem_map *mem); #ifdef CONFIG_PROC_FS static void au1000_pcmcia_proc_setup(unsigned int sock, struct proc_dir_entry *base); #endif static 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 int __init au1000_pcmcia_driver_init(void) { servinfo_t info; struct pcmcia_init pcmcia_init; struct pcmcia_state state; unsigned int i; unsigned long timing3; printk("\nAU1000 PCMCIA (CS release %s)\n", CS_RELEASE); CardServices(GetCardServicesInfo, &info); if (info.Revision != CS_RELEASE_CODE) { printk(KERN_ERR "Card Services release codes do not match\n"); return -1; } #ifdef CONFIG_MIPS_PB1000 pcmcia_low_level = &pb1000_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 kernel PCMCIA service.\n"); return -EIO; } /* setup the static bus controller */ timing3 = 0x100e3a07; writel(0x00000002, STATIC_CONFIG_3); /* type = PCMCIA */ writel(timing3, STATIC_TIMING_3); writel(0x10000000, STATIC_ADDRESS_3); /* any PCMCIA select */ pcmcia_socket = kmalloc(sizeof(struct au1000_pcmcia_socket) * socket_count, GFP_KERNEL); memset(pcmcia_socket, 0, sizeof(struct au1000_pcmcia_socket) * socket_count); if (!pcmcia_socket) { printk(KERN_ERR "Card Services can't get memory \n"); return -1; } for (i = 0; i < socket_count; i++) { if (pcmcia_low_level->socket_state(i, &state) < 0) { printk(KERN_ERR "Unable to get PCMCIA status from kernel.\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(0xC0000000, 0x1000); pcmcia_socket[i].phys_attr = 0xC4000000; pcmcia_socket[i].phys_mem = 0xC8000000; } else { printk(KERN_ERR "au1000: pcmcia socket 1 not supported\n"); return 1; } } /* 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); } if (state->bvd1 != prev_state->bvd1) { DEBUG(2, "%s(): card BVD1 value %u\n", __FUNCTION__, state->bvd1); events |= mask & (flags & SS_IOCARD) ? SS_STSCHG : SS_BATDEAD; } if (state->bvd2 != prev_state->bvd2) { DEBUG(2, "%s(): card BVD2 value %u\n", __FUNCTION__, state->bvd2); events |= mask & (flags & SS_IOCARD) ? 0 : SS_BATWARN; } *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 "Error in kernel low-level PCMCIA service.\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_task_handler(0); } /* * 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() * * From the sa1100 socket driver : * * Implements the inquire_socket() operation for the in-kernel PCMCIA * service (formerly SS_InquireSocket in Card Services). We set * SS_CAP_STATIC_MAP, which disables the memory resource database check. * (Mapped memory is set up within the socket driver itself.) * * In conjunction with the STATIC_MAP capability is a new field, * `io_offset', recommended by David Hinds. Rather than go through * the SetIOMap interface (which is not quite suited for communicating * window locations up from the socket driver), we just pass up * an offset which is applied to client-requested base I/O addresses * in alloc_io_space(). * * Returns: 0 on success, -1 if no pin has been configured for `sock' */ static int au1000_pcmcia_inquire_socket(unsigned int sock, socket_cap_t * cap) { struct pcmcia_irq_info irq_info; if (sock > socket_count) { printk(KERN_ERR "au1000: socket %u not configured\n", sock); return -1; } /* from the sa1100_generic driver: */ /* SS_CAP_PAGE_REGS: used by setup_cis_mem() in cistpl.c to set the * force_low argument to validate_mem() in rsrc_mgr.c -- since in * general, the mapped * addresses of the PCMCIA memory regions * will not be within 0xffff, setting force_low would be * undesirable. * * SS_CAP_STATIC_MAP: don't bother with the (user-configured) memory * resource database; we instead pass up physical address ranges * and allow other parts of Card Services to deal with remapping. * * SS_CAP_PCCARD: we can deal with 16-bit PCMCIA & CF cards, but * not 32-bit CardBus devices. */ cap->features = (SS_CAP_PAGE_REGS | SS_CAP_STATIC_MAP | SS_CAP_PCCARD); irq_info.sock = sock; irq_info.irq = -1; if (pcmcia_low_level->get_irq_info(&irq_info) < 0) { printk(KERN_ERR "Error obtaining IRQ info from kernel for socket %u\n", sock); return -1; } cap->irq_mask = 0; cap->map_size = MAP_SIZE; cap->pci_irq = irq_info.irq; cap->io_offset = pcmcia_socket[sock].virt_io; return 0; } /* au1000_pcmcia_inquire_socket() */ static int au1000_pcmcia_get_status(unsigned int sock, unsigned int *status) { struct pcmcia_state state; if ((pcmcia_low_level->socket_state(sock, &state)) < 0) { printk(KERN_ERR "Unable to get PCMCIA status from kernel.\n"); return -1; } pcmcia_socket[sock].k_state = state; *status = state.detect ? SS_DETECT : 0; *status |= state.ready ? SS_READY : 0; *status |= pcmcia_socket[sock].cs_state.Vcc ? SS_POWERON : 0; if (pcmcia_socket[sock].cs_state.flags & SS_IOCARD) *status |= state.bvd1 ? SS_STSCHG : 0; else { if (state.bvd1 == 0) *status |= SS_BATDEAD; else if (state.bvd2 == 0) *status |= SS_BATWARN; } *status |= state.vs_3v ? SS_3VCARD : 0; *status |= state.vs_Xv ? SS_XVCARD : 0; DEBUG(2, "\tstatus: %s%s%s%s%s%s%s%s\n", (*status & SS_DETECT) ? "DETECT " : "", (*status & SS_READY) ? "READY " : "", (*status & SS_BATDEAD) ? "BATDEAD " : "", (*status & SS_BATWARN) ? "BATWARN " : "", (*status & SS_POWERON) ? "POWERON " : "", (*status & SS_STSCHG) ? "STSCHG " : "", (*status & SS_3VCARD) ? "3VCARD " : "", (*status & SS_XVCARD) ? "XVCARD " : ""); return 0; } /* au1000_pcmcia_get_status() */ static int au1000_pcmcia_get_socket(unsigned int sock, socket_state_t * state) { *state = pcmcia_socket[sock].cs_state; return 0; } static int au1000_pcmcia_set_socket(unsigned int sock, socket_state_t * state) { struct pcmcia_configure configure; DEBUG(2, "\tmask: %s%s%s%s%s%s\n\tflags: %s%s%s%s%s%s\n" "\tVcc %d Vpp %d irq %d\n", (state->csc_mask == 0) ? "<NONE>" : "", (state->csc_mask & SS_DETECT) ? "DETECT " : "", (state->csc_mask & SS_READY) ? "READY " : "", (state->csc_mask & SS_BATDEAD) ? "BATDEAD " : "", (state->csc_mask & SS_BATWARN) ? "BATWARN " : "", (state->csc_mask & SS_STSCHG) ? "STSCHG " : "", (state->flags == 0) ? "<NONE>" : "", (state->flags & SS_PWR_AUTO) ? "PWR_AUTO " : "", (state->flags & SS_IOCARD) ? "IOCARD " : "", (state->flags & SS_RESET) ? "RESET " : "", (state->flags & SS_SPKR_ENA) ? "SPKR_ENA " : "", (state->flags & SS_OUTPUT_ENA) ? "OUTPUT_ENA " : "", state->Vcc, state->Vpp, state->io_irq); configure.sock = sock; configure.vcc = state->Vcc; configure.vpp = state->Vpp; configure.output = (state->flags & SS_OUTPUT_ENA) ? 1 : 0; configure.speaker = (state->flags & SS_SPKR_ENA) ? 1 : 0; configure.reset = (state->flags & SS_RESET) ? 1 : 0; if (pcmcia_low_level->configure_socket(&configure) < 0) { printk(KERN_ERR "Unable to configure socket %u\n", sock); return -1; } pcmcia_socket[sock].cs_state = *state; return 0; } /* au1000_pcmcia_set_socket() */ static int au1000_pcmcia_get_io_map(unsigned int sock, struct pccard_io_map *map) { DEBUG(1, "au1000_pcmcia_get_io_map: sock %d\n", sock); if (map->map >= MAX_IO_WIN) { printk(KERN_ERR "%s(): map (%d) out of range\n", __FUNCTION__, map->map); return -1; } *map = pcmcia_socket[sock].io_map[map->map]; return 0; } int au1000_pcmcia_set_io_map(unsigned int sock, struct pccard_io_map *map) { unsigned int speed; unsigned long start; if (map->map >= MAX_IO_WIN) { printk(KERN_ERR "%s(): map (%d) out of range\n", __FUNCTION__, map->map); return -1; } if (map->flags & MAP_ACTIVE) { speed = (map->speed > 0) ? map->speed : AU1000_PCMCIA_IO_SPEED; pcmcia_socket[sock].speed_io = speed; } start = map->start; if (map->stop == 1) { map->stop = PAGE_SIZE - 1; } map->start = pcmcia_socket[sock].virt_io; map->stop = map->start + (map->stop - start); pcmcia_socket[sock].io_map[map->map] = *map; DEBUG(3, "set_io_map %d start %x stop %x\n", map->map, map->start, map->stop); return 0; } static int au1000_pcmcia_get_mem_map(unsigned int sock, struct pccard_mem_map *map) { if (map->map >= MAX_WIN) { printk(KERN_ERR "%s(): map (%d) out of range\n", __FUNCTION__, map->map); return -1; } *map = pcmcia_socket[sock].mem_map[map->map]; return 0; } static int au1000_pcmcia_set_mem_map(unsigned int sock, struct pccard_mem_map *map) { unsigned int speed; unsigned long start; u_long flags; if (map->map >= MAX_WIN) { printk(KERN_ERR "%s(): map (%d) out of range\n", __FUNCTION__, map->map); return -1; } if (map->flags & MAP_ACTIVE) { speed = (map->speed > 0) ? map->speed : AU1000_PCMCIA_MEM_SPEED; /* TBD */ if (map->flags & MAP_ATTRIB) { pcmcia_socket[sock].speed_attr = speed; } else { pcmcia_socket[sock].speed_mem = speed; } } save_flags(flags); cli(); start = map->sys_start; if (map->sys_stop == 0) map->sys_stop = MAP_SIZE - 1; if (map->flags & MAP_ATTRIB) { map->sys_start = pcmcia_socket[sock].phys_attr + map->card_start; } else { map->sys_start = pcmcia_socket[sock].phys_mem + map->card_start; } map->sys_stop = map->sys_start + (map->sys_stop - start); pcmcia_socket[sock].mem_map[map->map] = *map; restore_flags(flags); DEBUG(3, "set_mem_map %d start %x stop %x card_start %x\n", map->map, map->sys_start, map->sys_stop, map->card_start); return 0; } #if defined(CONFIG_PROC_FS) static void au1000_pcmcia_proc_setup(unsigned int sock, struct proc_dir_entry *base) { struct proc_dir_entry *entry; if ((entry = create_proc_entry("status", 0, base)) == NULL) { printk(KERN_ERR "Unable to install \"status\" procfs entry\n"); return; } entry->read_proc = au1000_pcmcia_proc_status; entry->data = (void *) sock; } /* au1000_pcmcia_proc_status() * Implements the /proc/bus/pccard/??/status file. * * Returns: the number of characters added to the buffer */ static int au1000_pcmcia_proc_status(char *buf, char **start, off_t pos, int count, int *eof, void *data) { char *p = buf; unsigned int sock = (unsigned int) data; p += sprintf(p, "k_flags : %s%s%s%s%s%s%s\n", pcmcia_socket[sock].k_state.detect ? "detect " : "", pcmcia_socket[sock].k_state.ready ? "ready " : "", pcmcia_socket[sock].k_state.bvd1 ? "bvd1 " : "", pcmcia_socket[sock].k_state.bvd2 ? "bvd2 " : "", pcmcia_socket[sock].k_state.wrprot ? "wrprot " : "", pcmcia_socket[sock].k_state.vs_3v ? "vs_3v " : "", pcmcia_socket[sock].k_state.vs_Xv ? "vs_Xv " : ""); p += sprintf(p, "status : %s%s%s%s%s%s%s%s%s\n", pcmcia_socket[sock]. k_state.detect ? "SS_DETECT " : "", pcmcia_socket[sock].k_state.ready ? "SS_READY " : "", pcmcia_socket[sock].cs_state.Vcc ? "SS_POWERON " : "", pcmcia_socket[sock]. cs_state.flags & SS_IOCARD ? \"SS_IOCARD " : "", (pcmcia_socket[sock].cs_state.flags & SS_IOCARD && pcmcia_socket[sock]. k_state.bvd1) ? "SS_STSCHG " : "", ((pcmcia_socket[sock].cs_state.flags & SS_IOCARD) == 0 && (pcmcia_socket[sock].k_state.bvd1 == 0)) ? "SS_BATDEAD " : "", ((pcmcia_socket[sock].cs_state.flags & SS_IOCARD) == 0 && (pcmcia_socket[sock].k_state.bvd2 == 0)) ? "SS_BATWARN " : "", pcmcia_socket[sock].k_state.vs_3v ? "SS_3VCARD " : "", pcmcia_socket[sock]. k_state.vs_Xv ? "SS_XVCARD " : ""); p += sprintf(p, "mask : %s%s%s%s%s\n", pcmcia_socket[sock]. cs_state.csc_mask & SS_DETECT ? \"SS_DETECT " : "", pcmcia_socket[sock]. cs_state.csc_mask & SS_READY ? \"SS_READY " : "", pcmcia_socket[sock]. cs_state.csc_mask & SS_BATDEAD ? \"SS_BATDEAD " : "", pcmcia_socket[sock]. cs_state.csc_mask & SS_BATWARN ? \"SS_BATWARN " : "", pcmcia_socket[sock]. cs_state.csc_mask & SS_STSCHG ? \"SS_STSCHG " : ""); p += sprintf(p, "cs_flags : %s%s%s%s%s\n", pcmcia_socket[sock]. cs_state.flags & SS_PWR_AUTO ? \"SS_PWR_AUTO " : "", pcmcia_socket[sock]. cs_state.flags & SS_IOCARD ? \"SS_IOCARD " : "", pcmcia_socket[sock]. cs_state.flags & SS_RESET ? \"SS_RESET " : "", pcmcia_socket[sock]. cs_state.flags & SS_SPKR_ENA ? \"SS_SPKR_ENA " : "", pcmcia_socket[sock]. cs_state.flags & SS_OUTPUT_ENA ? \"SS_OUTPUT_ENA " : ""); p += sprintf(p, "Vcc : %d\n", pcmcia_socket[sock].cs_state.Vcc); p += sprintf(p, "Vpp : %d\n", pcmcia_socket[sock].cs_state.Vpp); p += sprintf(p, "irq : %d\n", pcmcia_socket[sock].cs_state.io_irq); p += sprintf(p, "I/O : %u\n", pcmcia_socket[sock].speed_io); p += sprintf(p, "attribute: %u\n", pcmcia_socket[sock].speed_attr); p += sprintf(p, "common : %u\n", pcmcia_socket[sock].speed_mem); return p - buf; } #endif /* defined(CONFIG_PROC_FS) */ --- NEW FILE: au1000_pb1000.c --- /* * Alchemy Semi PB1000 board specific pcmcia routines. * * Copyright 2001 MontaVista Software Inc. * Author: MontaVista Software, Inc. * pp...@mv... or so...@mv... * * ######################################################################## * * 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/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/version.h> #include <linux/types.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> #include <asm/pb1000.h> extern struct pcmcia_x_table x_table; static int pb1000_pcmcia_init(struct pcmcia_init *init) { u32 pcr; pcr = PCR_SLOT_0_RST | PCR_SLOT_1_RST; //writel(readl(PIN_FUNCTION) & ~(1<<8), PIN_FUNCTION); /* pin15 is gpio */ writel(0, PIN_FUNCTION); /* pin15 is gpio */ writel(readl(TSTATE_STATE_SET) | (1 << 15), TSTATE_STATE_SET); /* tristate gpio15 */ au_sync(); writel(0x8000, AU1000_MDR); /* clear pcmcia interrupt */ writel(0x4000, AU1000_MDR); /* enable pcmcia interrupt */ au_sync(); /* There's two sockets, but only the first one, 0, is used and tested */ return 1; } static int pb1000_pcmcia_shutdown(void) { u16 pcr; pcr = 0; writew(pcr, AU1000_PCR); mdelay(20); return 0; } static int pb1000_pcmcia_socket_state(unsigned sock, struct pcmcia_state *state) { u16 levels, pcr; unsigned char vs; levels = readw(AU1000_ACR1); pcr = readw(AU1000_PCR); state->ready = 0; state->vs_Xv = 0; state->vs_3v = 0; state->detect = 0; /* * CD1/2 are active low; so are the VSS pins; Ready is active high */ if (sock == 0) { if ((levels & ACR1_SLOT_0_READY)) state->ready = 1; if (!(levels & (ACR1_SLOT_0_CD1 | ACR1_SLOT_0_CD2))) { state->detect = 1; vs = (levels >> 4) & 0x3; switch (vs) { case 0: case 1: DEBUG(1, "%d: vs_3v\n", sock); state->vs_3v = 1; break; case 2: state->vs_Xv = 1; DEBUG(1, "%d: vs_Xv\n", sock); break; case 3: default: break; } } } else if (sock == 1) { if ((levels & ACR1_SLOT_1_READY)) state->ready = 1; if (!(levels & (ACR1_SLOT_1_CD1 | ACR1_SLOT_1_CD2))) { state->detect = 1; vs = (levels >> 12) & 0x3; switch (vs) { case 0: case 1: state->vs_3v = 1; DEBUG(1, "%d: vs_3v\n", sock); break; case 2: state->vs_Xv = 1; DEBUG(1, "%d: vs_Xv\n", sock); break; case 3: default: break; } } } else { printk(KERN_ERR "pb1000 socket_state bad sock %d\n", sock); } state->bvd1 = 1; state->bvd2 = 1; state->wrprot = 0; return 1; } static int pb1000_pcmcia_get_irq_info(struct pcmcia_irq_info *info) { if (info->sock > PCMCIA_MAX_SOCK) return -1; if (info->sock == 0) info->irq = AU1000_GPIO_15; else info->irq = -1; return 0; } static int pb1000_pcmcia_configure_socket(const struct pcmcia_configure *configure) { u16 pcr; if (configure->sock > PCMCIA_MAX_SOCK) return -1; pcr = readw(AU1000_PCR); if (configure->sock == 0) pcr &= ~(PCR_SLOT_0_VCC0 | PCR_SLOT_0_VCC1); else pcr &= ~(PCR_SLOT_1_VCC0 | PCR_SLOT_1_VCC1); pcr &= ~PCR_SLOT_0_RST; writew(pcr, AU1000_PCR); mdelay(20); switch (configure->vcc) { case 0: /* Vcc 0 */ switch (configure->vpp) { case 0: pcr |= SET_VCC_VPP(VCC_HIZ, VPP_GND, configure->sock); DEBUG(3, "Vcc 0V Vpp 0V, pcr %x\n", pcr); break; case 12: pcr |= SET_VCC_VPP(VCC_HIZ, VPP_12V, configure->sock); DEBUG(3, "Vcc 0V Vpp 12V, pcr %x\n", pcr); break; case 50: pcr |= SET_VCC_VPP(VCC_HIZ, VPP_5V, configure->sock); DEBUG(3, "Vcc 0V Vpp 5V, pcr %x\n", pcr); break; case 33: default: pcr |= SET_VCC_VPP(VCC_HIZ, VPP_HIZ, configure->sock); printk(KERN_ERR "%s: bad Vcc/Vpp combo (%d:%d)\n", __FUNCTION__, configure->vcc, configure->vpp); break; } break; case 50: /* Vcc 5V */ switch (configure->vpp) { case 0: pcr |= SET_VCC_VPP(VCC_5V, VPP_GND, configure->sock); DEBUG(3, "Vcc 5V Vpp 0V, pcr %x\n", pcr); break; case 50: pcr |= SET_VCC_VPP(VCC_5V, VPP_5V, configure->sock); DEBUG(3, "Vcc 5V Vpp 5V, pcr %x\n", pcr); break; case 12: pcr |= SET_VCC_VPP(VCC_5V, VPP_12V, configure->sock); DEBUG(3, "Vcc 5V Vpp 12V, pcr %x\n", pcr); break; case 33: default: pcr |= SET_VCC_VPP(VCC_HIZ, VPP_HIZ, configure->sock); printk(KERN_ERR "%s: bad Vcc/Vpp combo (%d:%d)\n", __FUNCTION__, configure->vcc, configure->vpp); break; } break; case 33: /* Vcc 3.3V */ switch (configure->vpp) { case 0: pcr |= SET_VCC_VPP(VCC_3V, VPP_GND, configure->sock); DEBUG(3, "Vcc 3V Vpp 0V, pcr %x\n", pcr); break; case 50: pcr |= SET_VCC_VPP(VCC_3V, VPP_5V, configure->sock); DEBUG(3, "Vcc 3V Vpp 5V, pcr %x\n", pcr); break; case 12: pcr |= SET_VCC_VPP(VCC_3V, VPP_12V, configure->sock); DEBUG(3, "Vcc 3V Vpp 12V, pcr %x\n", pcr); break; case 33: default: pcr |= SET_VCC_VPP(VCC_HIZ, VPP_HIZ, configure->sock); printk(KERN_ERR "%s: bad Vcc/Vpp combo (%d:%d)\n", __FUNCTION__, configure->vcc, configure->vpp); break; } break; default: /* what's this ? */ pcr |= SET_VCC_VPP(VCC_HIZ, VPP_HIZ, configure->sock); printk(KERN_ERR "%s: bad Vcc %d\n", __FUNCTION__, configure->vcc); break; } writew(pcr, AU1000_PCR); mdelay(400); pcr &= ~PCR_SLOT_0_RST; if (configure->reset) { pcr |= PCR_SLOT_0_RST; } writew(pcr, AU1000_PCR); mdelay(200); return 0; } struct pcmcia_low_level pb1000_pcmcia_ops = { pb1000_pcmcia_init, pb1000_pcmcia_shutdown, pb1000_pcmcia_socket_state, pb1000_pcmcia_get_irq_info, pb1000_pcmcia_configure_socket }; |