patch-2.4.13 linux/drivers/pcmcia/sa1100_generic.c
Next file: linux/drivers/pcmcia/sa1100_graphicsclient.c
Previous file: linux/drivers/pcmcia/sa1100_freebird.c
Back to the patch index
Back to the overall index
- Lines: 1169
- Date:
Thu Oct 11 09:43:29 2001
- Orig file:
v2.4.12/linux/drivers/pcmcia/sa1100_generic.c
- Orig date:
Wed Dec 31 16:00:00 1969
diff -u --recursive --new-file v2.4.12/linux/drivers/pcmcia/sa1100_generic.c linux/drivers/pcmcia/sa1100_generic.c
@@ -0,0 +1,1168 @@
+/*======================================================================
+
+ Device driver for the PCMCIA control functionality of StrongARM
+ SA-1100 microprocessors.
+
+ 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.
+
+ The initial developer of the original code is John G. Dorsey
+ <john+@cs.cmu.edu>. Portions created by John G. Dorsey are
+ Copyright (C) 1999 John G. Dorsey. All Rights Reserved.
+
+ Alternatively, the contents of this file may be used under the
+ terms of the GNU 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/cpufreq.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/notifier.h>
+#include <linux/proc_fs.h>
+#include <linux/version.h>
+
+#include <pcmcia/version.h>
+#include <pcmcia/cs_types.h>
+#include <pcmcia/cs.h>
+#include <pcmcia/ss.h>
+#include <pcmcia/bus_ops.h>
+
+#include <asm/hardware.h>
+#include <asm/io.h>
+#include <asm/irq.h>
+#include <asm/system.h>
+
+#include "sa1100.h"
+
+#ifdef PCMCIA_DEBUG
+static int pc_debug;
+#endif
+
+MODULE_AUTHOR("John Dorsey <john+@cs.cmu.edu>");
+MODULE_DESCRIPTION("Linux PCMCIA Card Services: SA-1100 Socket Controller");
+
+/* 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 sa1100_pcmcia_socket
+sa1100_pcmcia_socket[SA1100_PCMCIA_MAX_SOCK];
+
+static int sa1100_pcmcia_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 sa1100_pcmcia_driver_init(void);
+static void sa1100_pcmcia_driver_shutdown(void);
+static void sa1100_pcmcia_task_handler(void *data);
+static void sa1100_pcmcia_poll_event(unsigned long data);
+static void sa1100_pcmcia_interrupt(int irq, void *dev,
+ struct pt_regs *regs);
+static struct tq_struct sa1100_pcmcia_task;
+
+#ifdef CONFIG_PROC_FS
+static int sa1100_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 sa1100_pcmcia_init(unsigned int sock);
+static int sa1100_pcmcia_suspend(unsigned int sock);
+static int sa1100_pcmcia_register_callback(unsigned int sock,
+ void (*handler)(void *,
+ unsigned int),
+ void *info);
+static int sa1100_pcmcia_inquire_socket(unsigned int sock,
+ socket_cap_t *cap);
+static int sa1100_pcmcia_get_status(unsigned int sock, u_int *value);
+static int sa1100_pcmcia_get_socket(unsigned int sock,
+ socket_state_t *state);
+static int sa1100_pcmcia_set_socket(unsigned int sock,
+ socket_state_t *state);
+static int sa1100_pcmcia_get_io_map(unsigned int sock,
+ struct pccard_io_map *io);
+static int sa1100_pcmcia_set_io_map(unsigned int sock,
+ struct pccard_io_map *io);
+static int sa1100_pcmcia_get_mem_map(unsigned int sock,
+ struct pccard_mem_map *mem);
+static int sa1100_pcmcia_set_mem_map(unsigned int sock,
+ struct pccard_mem_map *mem);
+#ifdef CONFIG_PROC_FS
+static void sa1100_pcmcia_proc_setup(unsigned int sock,
+ struct proc_dir_entry *base);
+#endif
+
+static struct pccard_operations sa1100_pcmcia_operations = {
+ sa1100_pcmcia_init,
+ sa1100_pcmcia_suspend,
+ sa1100_pcmcia_register_callback,
+ sa1100_pcmcia_inquire_socket,
+ sa1100_pcmcia_get_status,
+ sa1100_pcmcia_get_socket,
+ sa1100_pcmcia_set_socket,
+ sa1100_pcmcia_get_io_map,
+ sa1100_pcmcia_set_io_map,
+ sa1100_pcmcia_get_mem_map,
+ sa1100_pcmcia_set_mem_map,
+#ifdef CONFIG_PROC_FS
+ sa1100_pcmcia_proc_setup
+#endif
+};
+
+#ifdef CONFIG_CPU_FREQ
+/* forward declaration */
+static struct notifier_block sa1100_pcmcia_notifier_block;
+#endif
+
+
+/* sa1100_pcmcia_driver_init()
+ * ^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ *
+ * This routine performs a basic sanity check to ensure that this
+ * kernel has been built with the appropriate board-specific low-level
+ * PCMCIA support, performs low-level PCMCIA initialization, registers
+ * this socket driver with Card Services, and then spawns the daemon
+ * thread which is the real workhorse of the socket driver.
+ *
+ * Please see linux/Documentation/arm/SA1100/PCMCIA for more information
+ * on the low-level kernel interface.
+ *
+ * Returns: 0 on success, -1 on error
+ */
+static int __init sa1100_pcmcia_driver_init(void){
+ servinfo_t info;
+ struct pcmcia_init pcmcia_init;
+ struct pcmcia_state state[SA1100_PCMCIA_MAX_SOCK];
+ struct pcmcia_state_array state_array;
+ unsigned int i, clock;
+ unsigned long mecr;
+
+ printk(KERN_INFO "SA-1100 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;
+ }
+
+ if(machine_is_assabet()){
+#ifdef CONFIG_SA1100_ASSABET
+ if(machine_has_neponset()){
+#ifdef CONFIG_ASSABET_NEPONSET
+ pcmcia_low_level=&neponset_pcmcia_ops;
+#else
+ printk(KERN_ERR "Card Services disabled: missing Neponset support\n");
+ return -1;
+#endif
+ }else{
+ pcmcia_low_level=&assabet_pcmcia_ops;
+ }
+#endif
+ } else if (machine_is_freebird()) {
+#ifdef CONFIG_SA1100_FREEBIRD
+ pcmcia_low_level = &freebird_pcmcia_ops;
+#endif
+ } else if (machine_is_h3600()) {
+#ifdef CONFIG_SA1100_H3600
+ pcmcia_low_level = &h3600_pcmcia_ops;
+#endif
+ } else if (machine_is_cerf()) {
+#ifdef CONFIG_SA1100_CERF
+ pcmcia_low_level = &cerf_pcmcia_ops;
+#endif
+ } else if (machine_is_graphicsclient()) {
+#ifdef CONFIG_SA1100_GRAPHICSCLIENT
+ pcmcia_low_level = &gcplus_pcmcia_ops;
+#endif
+ } else if (machine_is_xp860()) {
+#ifdef CONFIG_SA1100_XP860
+ pcmcia_low_level = &xp860_pcmcia_ops;
+#endif
+ } else if (machine_is_yopy()) {
+#ifdef CONFIG_SA1100_YOPY
+ pcmcia_low_level = &yopy_pcmcia_ops;
+#endif
+ } else if (machine_is_pangolin()) {
+#ifdef CONFIG_SA1100_PANGOLIN
+ pcmcia_low_level = &pangolin_pcmcia_ops;
+#endif
+ } else if (machine_is_jornada720()) {
+#ifdef CONFIG_SA1100_JORNADA720
+ pcmcia_low_level = &jornada720_pcmcia_ops;
+#endif
+ } else if(machine_is_pfs168()){
+#ifdef CONFIG_SA1100_PFS168
+ pcmcia_low_level=&pfs168_pcmcia_ops;
+#endif
+ } else if(machine_is_flexanet()){
+#ifdef CONFIG_SA1100_FLEXANET
+ pcmcia_low_level=&flexanet_pcmcia_ops;
+#endif
+ } else if(machine_is_simpad()){
+#ifdef CONFIG_SA1100_SIMPAD
+ pcmcia_low_level=&simpad_pcmcia_ops;
+#endif
+ } else if(machine_is_graphicsmaster()) {
+#ifdef CONFIG_SA1100_GRAPHICSMASTER
+ pcmcia_low_level=&graphicsmaster_pcmcia_ops;
+#endif
+ } else if(machine_is_adsbitsy()) {
+#ifdef CONFIG_SA1100_ADSBITSY
+ pcmcia_low_level=&adsbitsy_pcmcia_ops;
+#endif
+ } else if(machine_is_stork()) {
+#ifdef CONFIG_SA1100_STORK
+ pcmcia_low_level=&stork_pcmcia_ops;
+#endif
+ }
+
+ if (!pcmcia_low_level) {
+ printk(KERN_ERR "This hardware is not supported by the SA1100 Card Service driver\n");
+ return -ENODEV;
+ }
+
+ pcmcia_init.handler=sa1100_pcmcia_interrupt;
+
+ if((sa1100_pcmcia_socket_count=pcmcia_low_level->init(&pcmcia_init))<0){
+ printk(KERN_ERR "Unable to initialize kernel PCMCIA service.\n");
+ return -EIO;
+ }
+
+ state_array.size=sa1100_pcmcia_socket_count;
+ state_array.state=state;
+
+ if(pcmcia_low_level->socket_state(&state_array)<0){
+ printk(KERN_ERR "Unable to get PCMCIA status from kernel.\n");
+ return -EIO;
+ }
+
+ /* We initialize the MECR to default values here, because we are
+ * not guaranteed to see a SetIOMap operation at runtime.
+ */
+ mecr=0;
+
+ clock = get_cclk_frequency() * 100;
+
+ for(i=0; i<sa1100_pcmcia_socket_count; ++i){
+ sa1100_pcmcia_socket[i].k_state=state[i];
+
+ /* This is an interim fix. Apparently, SetSocket is no longer
+ * called to initialize each socket (prior to the first detect
+ * event). For now, we'll just manually set up the mask.
+ */
+ sa1100_pcmcia_socket[i].cs_state.csc_mask=SS_DETECT;
+
+ sa1100_pcmcia_socket[i].virt_io=(i==0)?PCMCIA_IO_0_BASE:PCMCIA_IO_1_BASE;
+ sa1100_pcmcia_socket[i].phys_attr=_PCMCIAAttr(i);
+ sa1100_pcmcia_socket[i].phys_mem=_PCMCIAMem(i);
+
+ MECR_FAST_SET(mecr, i, 0);
+ MECR_BSIO_SET(mecr, i,
+ sa1100_pcmcia_mecr_bs(SA1100_PCMCIA_IO_ACCESS, clock));
+ MECR_BSA_SET(mecr, i,
+ sa1100_pcmcia_mecr_bs(SA1100_PCMCIA_5V_MEM_ACCESS, clock));
+ MECR_BSM_SET(mecr, i,
+ sa1100_pcmcia_mecr_bs(SA1100_PCMCIA_5V_MEM_ACCESS, clock));
+
+ sa1100_pcmcia_socket[i].speed_io=SA1100_PCMCIA_IO_ACCESS;
+ sa1100_pcmcia_socket[i].speed_attr=SA1100_PCMCIA_5V_MEM_ACCESS;
+ sa1100_pcmcia_socket[i].speed_mem=SA1100_PCMCIA_5V_MEM_ACCESS;
+ }
+
+ MECR=mecr;
+
+#ifdef CONFIG_CPU_FREQ
+ if(cpufreq_register_notifier(&sa1100_pcmcia_notifier_block) < 0){
+ printk(KERN_ERR "Unable to register CPU frequency change notifier\n");
+ return -ENXIO;
+ }
+#endif
+
+ /* Only advertise as many sockets as we can detect: */
+ if(register_ss_entry(sa1100_pcmcia_socket_count,
+ &sa1100_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. */
+ sa1100_pcmcia_poll_event(0);
+
+ DEBUG(1, "sa1100: initialization complete\n");
+
+ return 0;
+
+} /* sa1100_pcmcia_driver_init() */
+
+module_init(sa1100_pcmcia_driver_init);
+
+
+/* sa1100_pcmcia_driver_shutdown()
+ * ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ * Invokes the low-level kernel service to free IRQs associated with this
+ * socket controller and reset GPIO edge detection.
+ */
+static void __exit sa1100_pcmcia_driver_shutdown(void){
+
+ del_timer_sync(&poll_timer);
+ unregister_ss_entry(&sa1100_pcmcia_operations);
+#ifdef CONFIG_CPU_FREQ
+ cpufreq_unregister_notifier(&sa1100_pcmcia_notifier_block);
+#endif
+ pcmcia_low_level->shutdown();
+ flush_scheduled_tasks();
+
+ DEBUG(1, "sa1100: shutdown complete\n");
+}
+
+module_exit(sa1100_pcmcia_driver_shutdown);
+
+
+/* sa1100_pcmcia_init()
+ * ^^^^^^^^^^^^^^^^^^^^
+ * We perform all of the interesting initialization tasks in
+ * sa1100_pcmcia_driver_init().
+ *
+ * Returns: 0
+ */
+static int sa1100_pcmcia_init(unsigned int sock){
+
+ DEBUG(2, "%s(): initializing socket %u\n", __FUNCTION__, sock);
+
+ return 0;
+}
+
+
+/* sa1100_pcmcia_suspend()
+ * ^^^^^^^^^^^^^^^^^^^^^^^
+ * We don't currently perform any actions on a suspend.
+ *
+ * Returns: 0
+ */
+static int sa1100_pcmcia_suspend(unsigned int sock){
+
+ DEBUG(2, "%s(): suspending socket %u\n", __FUNCTION__, sock);
+
+ return 0;
+}
+
+
+/* sa1100_pcmcia_events()
+ * ^^^^^^^^^^^^^^^^^^^^^^
+ * Helper routine to generate a Card Services event mask based on
+ * state information obtained from the kernel low-level PCMCIA layer
+ * in a recent (and previous) sampling. Updates `prev_state'.
+ *
+ * Returns: an event mask for the given socket state.
+ */
+static inline unsigned sa1100_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;
+ }
+
+ DEBUG(2, "events: %s%s%s%s%s%s\n",
+ (events==0)?"<NONE>":"",
+ (events&SS_DETECT)?"DETECT ":"",
+ (events&SS_READY)?"READY ":"",
+ (events&SS_BATDEAD)?"BATDEAD ":"",
+ (events&SS_BATWARN)?"BATWARN ":"",
+ (events&SS_STSCHG)?"STSCHG ":"");
+
+ *prev_state=*state;
+
+ return events;
+
+} /* sa1100_pcmcia_events() */
+
+
+/* sa1100_pcmcia_task_handler()
+ * ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ * Processes serviceable socket events using the "eventd" thread context.
+ *
+ * Event processing (specifically, the invocation of the Card Services event
+ * callback) occurs in this thread rather than in the actual interrupt
+ * handler due to the use of scheduling operations in the PCMCIA core.
+ */
+static void sa1100_pcmcia_task_handler(void *data) {
+ struct pcmcia_state state[SA1100_PCMCIA_MAX_SOCK];
+ struct pcmcia_state_array state_array;
+ int i, events, all_events, irq_status;
+
+ DEBUG(2, "%s(): entering PCMCIA monitoring thread\n", __FUNCTION__);
+
+ state_array.size=sa1100_pcmcia_socket_count;
+ state_array.state=state;
+
+ do {
+
+ DEBUG(3, "%s(): interrogating low-level PCMCIA service\n", __FUNCTION__);
+
+ if((irq_status=pcmcia_low_level->socket_state(&state_array))<0)
+ printk(KERN_ERR "Error in kernel low-level PCMCIA service.\n");
+
+ all_events=0;
+
+ if(irq_status>0){
+
+ for(i=0; i<state_array.size; ++i, all_events|=events)
+ if((events=
+ sa1100_pcmcia_events(&state[i],
+ &sa1100_pcmcia_socket[i].k_state,
+ sa1100_pcmcia_socket[i].cs_state.csc_mask,
+ sa1100_pcmcia_socket[i].cs_state.flags)))
+ if(sa1100_pcmcia_socket[i].handler!=NULL)
+ sa1100_pcmcia_socket[i].handler(sa1100_pcmcia_socket[i].handler_info,
+ events);
+ }
+
+ } while(all_events);
+} /* sa1100_pcmcia_task_handler() */
+
+static struct tq_struct sa1100_pcmcia_task = {
+ routine: sa1100_pcmcia_task_handler
+};
+
+
+/* sa1100_pcmcia_poll_event()
+ * ^^^^^^^^^^^^^^^^^^^^^^^^^^
+ * Let's poll for events in addition to IRQs since IRQ only is unreliable...
+ */
+static void sa1100_pcmcia_poll_event(unsigned long dummy)
+{
+ DEBUG(3, "%s(): polling for events\n", __FUNCTION__);
+ poll_timer.function = sa1100_pcmcia_poll_event;
+ poll_timer.expires = jiffies + SA1100_PCMCIA_POLL_PERIOD;
+ add_timer(&poll_timer);
+ schedule_task(&sa1100_pcmcia_task);
+}
+
+
+/* sa1100_pcmcia_interrupt()
+ * ^^^^^^^^^^^^^^^^^^^^^^^^^
+ * Service routine for socket driver interrupts (requested by the
+ * low-level PCMCIA init() operation via sa1100_pcmcia_thread()).
+ * The actual interrupt-servicing work is performed by
+ * sa1100_pcmcia_thread(), largely because the Card Services event-
+ * handling code performs scheduling operations which cannot be
+ * executed from within an interrupt context.
+ */
+static void sa1100_pcmcia_interrupt(int irq, void *dev, struct pt_regs *regs){
+ DEBUG(3, "%s(): servicing IRQ %d\n", __FUNCTION__, irq);
+ schedule_task(&sa1100_pcmcia_task);
+}
+
+
+/* sa1100_pcmcia_register_callback()
+ * ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ * Implements the register_callback() operation for the in-kernel
+ * PCMCIA service (formerly SS_RegisterCallback in Card Services). If
+ * the function pointer `handler' is not NULL, remember the callback
+ * location in the state for `sock', and increment the usage counter
+ * for the driver module. (The callback is invoked from the interrupt
+ * service routine, sa1100_pcmcia_interrupt(), to notify Card Services
+ * of interesting events.) Otherwise, clear the callback pointer in the
+ * socket state and decrement the module usage count.
+ *
+ * Returns: 0
+ */
+static int sa1100_pcmcia_register_callback(unsigned int sock,
+ void (*handler)(void *,
+ unsigned int),
+ void *info){
+ if(handler==NULL){
+ sa1100_pcmcia_socket[sock].handler=NULL;
+ MOD_DEC_USE_COUNT;
+ } else {
+ MOD_INC_USE_COUNT;
+ sa1100_pcmcia_socket[sock].handler=handler;
+ sa1100_pcmcia_socket[sock].handler_info=info;
+ }
+
+ return 0;
+}
+
+
+/* sa1100_pcmcia_inquire_socket()
+ * ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ * Implements the inquire_socket() operation for the in-kernel PCMCIA
+ * service (formerly SS_InquireSocket in Card Services). Of note is
+ * the setting of the SS_CAP_PAGE_REGS bit in the `features' field of
+ * `cap' to "trick" Card Services into tolerating large "I/O memory"
+ * addresses. Also set is 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 sa1100_pcmcia_inquire_socket(unsigned int sock,
+ socket_cap_t *cap){
+ struct pcmcia_irq_info irq_info;
+
+ DEBUG(3, "%s() for sock %u\n", __FUNCTION__, sock);
+
+ if(sock>=sa1100_pcmcia_socket_count){
+ printk(KERN_ERR "sa1100: socket %u not configured\n", sock);
+ return -1;
+ }
+
+ /* 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=PAGE_SIZE;
+ cap->pci_irq=irq_info.irq;
+ cap->io_offset=sa1100_pcmcia_socket[sock].virt_io;
+
+ return 0;
+
+} /* sa1100_pcmcia_inquire_socket() */
+
+
+/* sa1100_pcmcia_get_status()
+ * ^^^^^^^^^^^^^^^^^^^^^^^^^^
+ * Implements the get_status() operation for the in-kernel PCMCIA
+ * service (formerly SS_GetStatus in Card Services). Essentially just
+ * fills in bits in `status' according to internal driver state or
+ * the value of the voltage detect chipselect register.
+ *
+ * As a debugging note, during card startup, the PCMCIA core issues
+ * three set_socket() commands in a row the first with RESET deasserted,
+ * the second with RESET asserted, and the last with RESET deasserted
+ * again. Following the third set_socket(), a get_status() command will
+ * be issued. The kernel is looking for the SS_READY flag (see
+ * setup_socket(), reset_socket(), and unreset_socket() in cs.c).
+ *
+ * Returns: 0
+ */
+static int sa1100_pcmcia_get_status(unsigned int sock,
+ unsigned int *status){
+ struct pcmcia_state state[SA1100_PCMCIA_MAX_SOCK];
+ struct pcmcia_state_array state_array;
+
+ DEBUG(3, "%s() for sock %u\n", __FUNCTION__, sock);
+
+ state_array.size=sa1100_pcmcia_socket_count;
+ state_array.state=state;
+
+ if((pcmcia_low_level->socket_state(&state_array))<0){
+ printk(KERN_ERR "Unable to get PCMCIA status from kernel.\n");
+ return -1;
+ }
+
+ sa1100_pcmcia_socket[sock].k_state=state[sock];
+
+ *status=state[sock].detect?SS_DETECT:0;
+
+ *status|=state[sock].ready?SS_READY:0;
+
+ /* The power status of individual sockets is not available
+ * explicitly from the hardware, so we just remember the state
+ * and regurgitate it upon request:
+ */
+ *status|=sa1100_pcmcia_socket[sock].cs_state.Vcc?SS_POWERON:0;
+
+ if(sa1100_pcmcia_socket[sock].cs_state.flags&SS_IOCARD)
+ *status|=state[sock].bvd1?SS_STSCHG:0;
+ else {
+ if(state[sock].bvd1==0)
+ *status|=SS_BATDEAD;
+ else if(state[sock].bvd2==0)
+ *status|=SS_BATWARN;
+ }
+
+ *status|=state[sock].vs_3v?SS_3VCARD:0;
+
+ *status|=state[sock].vs_Xv?SS_XVCARD:0;
+
+ DEBUG(3, "\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;
+
+} /* sa1100_pcmcia_get_status() */
+
+
+/* sa1100_pcmcia_get_socket()
+ * ^^^^^^^^^^^^^^^^^^^^^^^^^^
+ * Implements the get_socket() operation for the in-kernel PCMCIA
+ * service (formerly SS_GetSocket in Card Services). Not a very
+ * exciting routine.
+ *
+ * Returns: 0
+ */
+static int sa1100_pcmcia_get_socket(unsigned int sock,
+ socket_state_t *state){
+
+ DEBUG(3, "%s() for sock %u\n", __FUNCTION__, sock);
+
+ /* This information was given to us in an earlier call to set_socket(),
+ * so we're just regurgitating it here:
+ */
+ *state=sa1100_pcmcia_socket[sock].cs_state;
+
+ return 0;
+}
+
+
+/* sa1100_pcmcia_set_socket()
+ * ^^^^^^^^^^^^^^^^^^^^^^^^^^
+ * Implements the set_socket() operation for the in-kernel PCMCIA
+ * service (formerly SS_SetSocket in Card Services). We more or
+ * less punt all of this work and let the kernel handle the details
+ * of power configuration, reset, &c. We also record the value of
+ * `state' in order to regurgitate it to the PCMCIA core later.
+ *
+ * Returns: 0
+ */
+static int sa1100_pcmcia_set_socket(unsigned int sock,
+ socket_state_t *state){
+ struct pcmcia_configure configure;
+
+ DEBUG(3, "%s() for sock %u\n", __FUNCTION__, sock);
+
+ DEBUG(3, "\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;
+ }
+
+ sa1100_pcmcia_socket[sock].cs_state=*state;
+
+ return 0;
+
+} /* sa1100_pcmcia_set_socket() */
+
+
+/* sa1100_pcmcia_get_io_map()
+ * ^^^^^^^^^^^^^^^^^^^^^^^^^^
+ * Implements the get_io_map() operation for the in-kernel PCMCIA
+ * service (formerly SS_GetIOMap in Card Services). Just returns an
+ * I/O map descriptor which was assigned earlier by a set_io_map().
+ *
+ * Returns: 0 on success, -1 if the map index was out of range
+ */
+static int sa1100_pcmcia_get_io_map(unsigned int sock,
+ struct pccard_io_map *map){
+
+ DEBUG(4, "%s() for sock %u\n", __FUNCTION__, sock);
+
+ if(map->map>=MAX_IO_WIN){
+ printk(KERN_ERR "%s(): map (%d) out of range\n", __FUNCTION__,
+ map->map);
+ return -1;
+ }
+
+ *map=sa1100_pcmcia_socket[sock].io_map[map->map];
+
+ return 0;
+}
+
+
+/* sa1100_pcmcia_set_io_map()
+ * ^^^^^^^^^^^^^^^^^^^^^^^^^^
+ * Implements the set_io_map() operation for the in-kernel PCMCIA
+ * service (formerly SS_SetIOMap in Card Services). We configure
+ * the map speed as requested, but override the address ranges
+ * supplied by Card Services.
+ *
+ * Returns: 0 on success, -1 on error
+ */
+static int sa1100_pcmcia_set_io_map(unsigned int sock,
+ struct pccard_io_map *map){
+ unsigned int clock, speed;
+ unsigned long mecr, start;
+
+ DEBUG(4, "%s() for sock %u\n", __FUNCTION__, sock);
+
+ DEBUG(4, "\tmap %u speed %u\n\tstart 0x%08lx stop 0x%08lx\n"
+ "\tflags: %s%s%s%s%s%s%s%s\n",
+ map->map, map->speed, map->start, map->stop,
+ (map->flags==0)?"<NONE>":"",
+ (map->flags&MAP_ACTIVE)?"ACTIVE ":"",
+ (map->flags&MAP_16BIT)?"16BIT ":"",
+ (map->flags&MAP_AUTOSZ)?"AUTOSZ ":"",
+ (map->flags&MAP_0WS)?"0WS ":"",
+ (map->flags&MAP_WRPROT)?"WRPROT ":"",
+ (map->flags&MAP_USE_WAIT)?"USE_WAIT ":"",
+ (map->flags&MAP_PREFETCH)?"PREFETCH ":"");
+
+ 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:SA1100_PCMCIA_IO_ACCESS;
+
+ clock = get_cclk_frequency() * 100;
+
+ mecr=MECR;
+
+ MECR_BSIO_SET(mecr, sock, sa1100_pcmcia_mecr_bs(speed, clock));
+
+ sa1100_pcmcia_socket[sock].speed_io=speed;
+
+ DEBUG(4, "%s(): FAST%u %lx BSM%u %lx BSA%u %lx BSIO%u %lx\n",
+ __FUNCTION__, sock, MECR_FAST_GET(mecr, sock), sock,
+ MECR_BSM_GET(mecr, sock), sock, MECR_BSA_GET(mecr, sock),
+ sock, MECR_BSIO_GET(mecr, sock));
+
+ MECR=mecr;
+
+ }
+
+ start=map->start;
+
+ if(map->stop==1)
+ map->stop=PAGE_SIZE-1;
+
+ map->start=sa1100_pcmcia_socket[sock].virt_io;
+ map->stop=map->start+(map->stop-start);
+
+ sa1100_pcmcia_socket[sock].io_map[map->map]=*map;
+
+ return 0;
+
+} /* sa1100_pcmcia_set_io_map() */
+
+
+/* sa1100_pcmcia_get_mem_map()
+ * ^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ * Implements the get_mem_map() operation for the in-kernel PCMCIA
+ * service (formerly SS_GetMemMap in Card Services). Just returns a
+ * memory map descriptor which was assigned earlier by a
+ * set_mem_map() request.
+ *
+ * Returns: 0 on success, -1 if the map index was out of range
+ */
+static int sa1100_pcmcia_get_mem_map(unsigned int sock,
+ struct pccard_mem_map *map){
+
+ DEBUG(4, "%s() for sock %u\n", __FUNCTION__, sock);
+
+ if(map->map>=MAX_WIN){
+ printk(KERN_ERR "%s(): map (%d) out of range\n", __FUNCTION__,
+ map->map);
+ return -1;
+ }
+
+ *map=sa1100_pcmcia_socket[sock].mem_map[map->map];
+
+ return 0;
+}
+
+
+/* sa1100_pcmcia_set_mem_map()
+ * ^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ * Implements the set_mem_map() operation for the in-kernel PCMCIA
+ * service (formerly SS_SetMemMap in Card Services). We configure
+ * the map speed as requested, but override the address ranges
+ * supplied by Card Services.
+ *
+ * Returns: 0 on success, -1 on error
+ */
+static int sa1100_pcmcia_set_mem_map(unsigned int sock,
+ struct pccard_mem_map *map){
+ unsigned int clock, speed;
+ unsigned long mecr, start;
+
+ DEBUG(4, "%s() for sock %u\n", __FUNCTION__, sock);
+
+ DEBUG(4, "\tmap %u speed %u\n\tsys_start %#lx\n"
+ "\tsys_stop %#lx\n\tcard_start %#x\n"
+ "\tflags: %s%s%s%s%s%s%s%s\n",
+ map->map, map->speed, map->sys_start, map->sys_stop,
+ map->card_start, (map->flags==0)?"<NONE>":"",
+ (map->flags&MAP_ACTIVE)?"ACTIVE ":"",
+ (map->flags&MAP_16BIT)?"16BIT ":"",
+ (map->flags&MAP_AUTOSZ)?"AUTOSZ ":"",
+ (map->flags&MAP_0WS)?"0WS ":"",
+ (map->flags&MAP_WRPROT)?"WRPROT ":"",
+ (map->flags&MAP_ATTRIB)?"ATTRIB ":"",
+ (map->flags&MAP_USE_WAIT)?"USE_WAIT ":"");
+
+ 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){
+
+ /* When clients issue RequestMap, the access speed is not always
+ * properly configured:
+ */
+ if(map->speed > 0)
+ speed = map->speed;
+ else
+ switch(sa1100_pcmcia_socket[sock].cs_state.Vcc){
+ case 33:
+ speed = SA1100_PCMCIA_3V_MEM_ACCESS;
+ break;
+ default:
+ speed = SA1100_PCMCIA_5V_MEM_ACCESS;
+ }
+
+ clock = get_cclk_frequency() * 100;
+
+ mecr=MECR;
+
+ if(map->flags&MAP_ATTRIB){
+
+ MECR_BSA_SET(mecr, sock, sa1100_pcmcia_mecr_bs(speed, clock));
+ sa1100_pcmcia_socket[sock].speed_attr=speed;
+
+ } else {
+
+ MECR_BSM_SET(mecr, sock, sa1100_pcmcia_mecr_bs(speed, clock));
+ sa1100_pcmcia_socket[sock].speed_mem=speed;
+
+ }
+
+ DEBUG(4, "%s(): FAST%u %lx BSM%u %lx BSA%u %lx BSIO%u %lx\n",
+ __FUNCTION__, sock, MECR_FAST_GET(mecr, sock), sock,
+ MECR_BSM_GET(mecr, sock), sock, MECR_BSA_GET(mecr, sock),
+ sock, MECR_BSIO_GET(mecr, sock));
+
+ MECR=mecr;
+
+ }
+
+ start=map->sys_start;
+
+ if(map->sys_stop==0)
+ map->sys_stop=PAGE_SIZE-1;
+
+ map->sys_start=(map->flags & MAP_ATTRIB)?\
+ sa1100_pcmcia_socket[sock].phys_attr:\
+ sa1100_pcmcia_socket[sock].phys_mem;
+
+ map->sys_stop=map->sys_start+(map->sys_stop-start);
+
+ sa1100_pcmcia_socket[sock].mem_map[map->map]=*map;
+
+ return 0;
+
+} /* sa1100_pcmcia_set_mem_map() */
+
+
+#if defined(CONFIG_PROC_FS)
+
+/* sa1100_pcmcia_proc_setup()
+ * ^^^^^^^^^^^^^^^^^^^^^^^^^^
+ * Implements the proc_setup() operation for the in-kernel PCMCIA
+ * service (formerly SS_ProcSetup in Card Services).
+ *
+ * Returns: 0 on success, -1 on error
+ */
+static void sa1100_pcmcia_proc_setup(unsigned int sock,
+ struct proc_dir_entry *base){
+ struct proc_dir_entry *entry;
+
+ DEBUG(4, "%s() for sock %u\n", __FUNCTION__, sock);
+
+ if((entry=create_proc_entry("status", 0, base))==NULL){
+ printk(KERN_ERR "Unable to install \"status\" procfs entry\n");
+ return;
+ }
+
+ entry->read_proc=sa1100_pcmcia_proc_status;
+ entry->data=(void *)sock;
+}
+
+
+/* sa1100_pcmcia_proc_status()
+ * ^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ * Implements the /proc/bus/pccard/??/status file.
+ *
+ * Returns: the number of characters added to the buffer
+ */
+static int sa1100_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;
+ unsigned int clock = get_cclk_frequency() * 100;
+ unsigned long mecr = MECR;
+
+ p+=sprintf(p, "k_flags : %s%s%s%s%s%s%s\n",
+ sa1100_pcmcia_socket[sock].k_state.detect?"detect ":"",
+ sa1100_pcmcia_socket[sock].k_state.ready?"ready ":"",
+ sa1100_pcmcia_socket[sock].k_state.bvd1?"bvd1 ":"",
+ sa1100_pcmcia_socket[sock].k_state.bvd2?"bvd2 ":"",
+ sa1100_pcmcia_socket[sock].k_state.wrprot?"wrprot ":"",
+ sa1100_pcmcia_socket[sock].k_state.vs_3v?"vs_3v ":"",
+ sa1100_pcmcia_socket[sock].k_state.vs_Xv?"vs_Xv ":"");
+
+ p+=sprintf(p, "status : %s%s%s%s%s%s%s%s%s\n",
+ sa1100_pcmcia_socket[sock].k_state.detect?"SS_DETECT ":"",
+ sa1100_pcmcia_socket[sock].k_state.ready?"SS_READY ":"",
+ sa1100_pcmcia_socket[sock].cs_state.Vcc?"SS_POWERON ":"",
+ sa1100_pcmcia_socket[sock].cs_state.flags&SS_IOCARD?\
+ "SS_IOCARD ":"",
+ (sa1100_pcmcia_socket[sock].cs_state.flags&SS_IOCARD &&
+ sa1100_pcmcia_socket[sock].k_state.bvd1)?"SS_STSCHG ":"",
+ ((sa1100_pcmcia_socket[sock].cs_state.flags&SS_IOCARD)==0 &&
+ (sa1100_pcmcia_socket[sock].k_state.bvd1==0))?"SS_BATDEAD ":"",
+ ((sa1100_pcmcia_socket[sock].cs_state.flags&SS_IOCARD)==0 &&
+ (sa1100_pcmcia_socket[sock].k_state.bvd2==0))?"SS_BATWARN ":"",
+ sa1100_pcmcia_socket[sock].k_state.vs_3v?"SS_3VCARD ":"",
+ sa1100_pcmcia_socket[sock].k_state.vs_Xv?"SS_XVCARD ":"");
+
+ p+=sprintf(p, "mask : %s%s%s%s%s\n",
+ sa1100_pcmcia_socket[sock].cs_state.csc_mask&SS_DETECT?\
+ "SS_DETECT ":"",
+ sa1100_pcmcia_socket[sock].cs_state.csc_mask&SS_READY?\
+ "SS_READY ":"",
+ sa1100_pcmcia_socket[sock].cs_state.csc_mask&SS_BATDEAD?\
+ "SS_BATDEAD ":"",
+ sa1100_pcmcia_socket[sock].cs_state.csc_mask&SS_BATWARN?\
+ "SS_BATWARN ":"",
+ sa1100_pcmcia_socket[sock].cs_state.csc_mask&SS_STSCHG?\
+ "SS_STSCHG ":"");
+
+ p+=sprintf(p, "cs_flags : %s%s%s%s%s\n",
+ sa1100_pcmcia_socket[sock].cs_state.flags&SS_PWR_AUTO?\
+ "SS_PWR_AUTO ":"",
+ sa1100_pcmcia_socket[sock].cs_state.flags&SS_IOCARD?\
+ "SS_IOCARD ":"",
+ sa1100_pcmcia_socket[sock].cs_state.flags&SS_RESET?\
+ "SS_RESET ":"",
+ sa1100_pcmcia_socket[sock].cs_state.flags&SS_SPKR_ENA?\
+ "SS_SPKR_ENA ":"",
+ sa1100_pcmcia_socket[sock].cs_state.flags&SS_OUTPUT_ENA?\
+ "SS_OUTPUT_ENA ":"");
+
+ p+=sprintf(p, "Vcc : %d\n", sa1100_pcmcia_socket[sock].cs_state.Vcc);
+
+ p+=sprintf(p, "Vpp : %d\n", sa1100_pcmcia_socket[sock].cs_state.Vpp);
+
+ p+=sprintf(p, "irq : %d\n", sa1100_pcmcia_socket[sock].cs_state.io_irq);
+
+ p+=sprintf(p, "I/O : %u (%u)\n", sa1100_pcmcia_socket[sock].speed_io,
+ sa1100_pcmcia_cmd_time(clock, MECR_BSIO_GET(mecr, sock)));
+
+ p+=sprintf(p, "attribute: %u (%u)\n", sa1100_pcmcia_socket[sock].speed_attr,
+ sa1100_pcmcia_cmd_time(clock, MECR_BSA_GET(mecr, sock)));
+
+ p+=sprintf(p, "common : %u (%u)\n", sa1100_pcmcia_socket[sock].speed_mem,
+ sa1100_pcmcia_cmd_time(clock, MECR_BSM_GET(mecr, sock)));
+
+ return p-buf;
+}
+
+#endif /* defined(CONFIG_PROC_FS) */
+
+
+#ifdef CONFIG_CPU_FREQ
+
+/* sa1100_pcmcia_update_mecr()
+ * ^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ * When sa1100_pcmcia_notifier() decides that a MECR adjustment (due
+ * to a core clock frequency change) is needed, this routine establishes
+ * new BS_xx values consistent with the clock speed `clock'.
+ */
+static void sa1100_pcmcia_update_mecr(unsigned int clock){
+ unsigned int sock;
+ unsigned long mecr = MECR;
+
+ for(sock = 0; sock < SA1100_PCMCIA_MAX_SOCK; ++sock){
+
+ MECR_BSIO_SET(mecr, sock,
+ sa1100_pcmcia_mecr_bs(sa1100_pcmcia_socket[sock].speed_io,
+ clock));
+ MECR_BSA_SET(mecr, sock,
+ sa1100_pcmcia_mecr_bs(sa1100_pcmcia_socket[sock].speed_attr,
+ clock));
+ MECR_BSM_SET(mecr, sock,
+ sa1100_pcmcia_mecr_bs(sa1100_pcmcia_socket[sock].speed_mem,
+ clock));
+ }
+
+ MECR = mecr;
+
+}
+
+/* sa1100_pcmcia_notifier()
+ * ^^^^^^^^^^^^^^^^^^^^^^^^
+ * When changing the processor core clock frequency, it is necessary
+ * to adjust the MECR timings accordingly. We've recorded the timings
+ * requested by Card Services, so this is just a matter of finding
+ * out what our current speed is, and then recomputing the new MECR
+ * values.
+ *
+ * Returns: 0 on success, -1 on error
+ */
+static int sa1100_pcmcia_notifier(struct notifier_block *nb,
+ unsigned long val, void *data){
+ struct cpufreq_info *ci = data;
+
+ switch(val){
+ case CPUFREQ_MINMAX:
+
+ break;
+
+ case CPUFREQ_PRECHANGE:
+
+ if(ci->new_freq > ci->old_freq){
+ DEBUG(2, "%s(): new frequency %u.%uMHz > %u.%uMHz, pre-updating\n",
+ __FUNCTION__,
+ ci->new_freq / 1000, (ci->new_freq / 100) % 10,
+ ci->old_freq / 1000, (ci->old_freq / 100) % 10);
+ sa1100_pcmcia_update_mecr(ci->new_freq);
+ }
+
+ break;
+
+ case CPUFREQ_POSTCHANGE:
+
+ if(ci->new_freq < ci->old_freq){
+ DEBUG(2, "%s(): new frequency %u.%uMHz < %u.%uMHz, post-updating\n",
+ __FUNCTION__,
+ ci->new_freq / 1000, (ci->new_freq / 100) % 10,
+ ci->old_freq / 1000, (ci->old_freq / 100) % 10);
+ sa1100_pcmcia_update_mecr(ci->new_freq);
+ }
+
+ break;
+
+ default:
+ printk(KERN_ERR "%s(): unknown CPU frequency event %lx\n", __FUNCTION__,
+ val);
+ return -1;
+
+ }
+
+ return 0;
+
+}
+
+static struct notifier_block sa1100_pcmcia_notifier_block = {
+ notifier_call: sa1100_pcmcia_notifier
+};
+
+#endif
+
FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen (who was at: slshen@lbl.gov)