/* 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 sa1100_pcmcia_socket *skt = PCMCIA_SOCKET(sock);
  struct pcmcia_configure conf;

  DEBUG(2, "%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",
	(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 ":"");
  DEBUG(3, "\tVcc %d  Vpp %d  irq %d\n",
	state->Vcc, state->Vpp, state->io_irq);

  conf = sa1100_pcmcia_state_to_config(sock, state);

  if (pcmcia_low_level->configure_socket(&conf) < 0) {
    printk(KERN_ERR "sa1100_pcmcia: unable to configure socket %d\n", sock);
    return -1;
  }

  skt->cs_state = *state;
  
  return 0;
}  /* sa1100_pcmcia_set_socket() */
/* 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)
{
  struct sa1100_pcmcia_socket *skt = PCMCIA_SOCKET(sock);

  DEBUG(2, "%s() for sock %u\n", __FUNCTION__, sock);

  DEBUG(3, "\tmap %u  speed %u\n\tstart 0x%08x  stop 0x%08x\n",
	map->map, map->speed, map->start, map->stop);
  DEBUG(3, "\tflags: %s%s%s%s%s%s%s%s\n",
	(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) {
    unsigned int clock, speed = map->speed;
    unsigned long mecr;

    if (speed == 0)
      speed = SA1100_PCMCIA_IO_ACCESS;

    clock = cpufreq_get(0);

    mecr = MECR;

    MECR_BSIO_SET(mecr, sock, sa1100_pcmcia_mecr_bs(speed, clock));

    skt->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;
  }

  if (map->stop == 1)
    map->stop = PAGE_SIZE-1;

  map->stop -= map->start;
  map->stop += (unsigned long)skt->virt_io;
  map->start = (unsigned long)skt->virt_io;

  skt->io_map[map->map] = *map;

  return 0;
}  /* sa1100_pcmcia_set_io_map() */
/* 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 sa1100_pcmcia_socket *skt = PCMCIA_SOCKET(sock);
  struct pcmcia_state state[SA1100_PCMCIA_MAX_SOCK];
  struct pcmcia_state_array state_array;
  unsigned int stat;

  DEBUG(2, "%s() for sock %u\n", __FUNCTION__, sock);

  state_array.size = sa1100_pcmcia_socket_count;
  state_array.state = state;

  memset(state, 0, sizeof(state));

  if ((pcmcia_low_level->socket_state(&state_array)) < 0) {
    printk(KERN_ERR "sa1100_pcmcia: unable to get socket status\n");
    return -1;
  }

  skt->k_state = state[sock];

  stat = state[sock].detect ? SS_DETECT : 0;
  stat |= state[sock].ready ? SS_READY  : 0;
  stat |= state[sock].vs_3v ? SS_3VCARD : 0;
  stat |= state[sock].vs_Xv ? SS_XVCARD : 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:
   */
  stat |= skt->cs_state.Vcc ? SS_POWERON : 0;

  if (skt->cs_state.flags & SS_IOCARD)
    stat |= state[sock].bvd1 ? SS_STSCHG : 0;
  else {
    if (state[sock].bvd1 == 0)
      stat |= SS_BATDEAD;
    else if (state[sock].bvd2 == 0)
      stat |= SS_BATWARN;
  }

  DEBUG(3, "\tstatus: %s%s%s%s%s%s%s%s\n",
	stat & SS_DETECT  ? "DETECT "  : "",
	stat & SS_READY   ? "READY "   : "", 
	stat & SS_BATDEAD ? "BATDEAD " : "",
	stat & SS_BATWARN ? "BATWARN " : "",
	stat & SS_POWERON ? "POWERON " : "",
	stat & SS_STSCHG  ? "STSCHG "  : "",
	stat & SS_3VCARD  ? "3VCARD "  : "",
	stat & SS_XVCARD  ? "XVCARD "  : "");

  *status = stat;

  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)
{
  struct sa1100_pcmcia_socket *skt = PCMCIA_SOCKET(sock);

  DEBUG(2, "%s() for sock %u\n", __FUNCTION__, sock);

  *state = skt->cs_state;

  return 0;
}
/* 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)
{
  struct sa1100_pcmcia_socket *skt = PCMCIA_SOCKET(sock);
  int ret = -1;

  DEBUG(2, "%s() for sock %u\n", __FUNCTION__, sock);

  if (map->map < MAX_WIN) {
    *map = skt->mem_map[map->map];
    ret = 0;
  }

  return ret;
}
/* sa1100_pcmcia_init()
 * ^^^^^^^^^^^^^^^^^^^^
 *
 * (Re-)Initialise the socket, turning on status interrupts
 * and PCMCIA bus.  This must wait for power to stabilise
 * so that the card status signals report correctly.
 *
 * Returns: 0
 */
static int sa1100_pcmcia_init(unsigned int sock)
{
  struct sa1100_pcmcia_socket *skt = PCMCIA_SOCKET(sock);
  struct pcmcia_configure conf;

  DEBUG(2, "%s(): initializing socket %u\n", __FUNCTION__, sock);

  skt->cs_state = dead_socket;

  conf = sa1100_pcmcia_state_to_config(sock, &dead_socket);

  pcmcia_low_level->configure_socket(&conf);

  return pcmcia_low_level->socket_init(sock);
}
/* 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)
{
  struct sa1100_pcmcia_socket *skt = PCMCIA_SOCKET(sock);

  if (handler == NULL) {
    skt->handler = NULL;
    MOD_DEC_USE_COUNT;
  } else {
    MOD_INC_USE_COUNT;
    skt->handler_info = info;
    skt->handler = handler;
  }

  return 0;
}
/*
 * sa1100_pcmcia_suspend()
 * ^^^^^^^^^^^^^^^^^^^^^^^
 *
 * Remove power on the socket, disable IRQs from the card.
 * Turn off status interrupts, and disable the PCMCIA bus.
 *
 * Returns: 0
 */
static int sa1100_pcmcia_suspend(unsigned int sock)
{
  struct sa1100_pcmcia_socket *skt = PCMCIA_SOCKET(sock);
  struct pcmcia_configure conf;
  int ret;

  DEBUG(2, "%s(): suspending socket %u\n", __FUNCTION__, sock);

  conf = sa1100_pcmcia_state_to_config(sock, &dead_socket);

  ret = pcmcia_low_level->configure_socket(&conf);

  if (ret == 0) {
    skt->cs_state = dead_socket;
    ret = pcmcia_low_level->socket_suspend(sock);
  }

  return ret;
}
/* 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().
 *
 * 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.
 *
 * Return value is irrelevant; the pcmcia subsystem ignores it.
 */
static int
sa1100_pcmcia_inquire_socket(unsigned int sock, socket_cap_t *cap)
{
  struct sa1100_pcmcia_socket *skt = PCMCIA_SOCKET(sock);
  int ret = -1;

  DEBUG(2, "%s() for sock %u\n", __FUNCTION__, sock);

  if (sock < sa1100_pcmcia_socket_count) {
    cap->features  = SS_CAP_PAGE_REGS | SS_CAP_STATIC_MAP | SS_CAP_PCCARD;
    cap->irq_mask  = 0;
    cap->map_size  = PAGE_SIZE;
    cap->pci_irq   = skt->irq;
    cap->io_offset = (unsigned long)skt->virt_io;

    ret = 0;
  }

  return ret;
}
/* 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;
  unsigned int all_events;

  DEBUG(4, "%s(): entering PCMCIA monitoring thread\n", __FUNCTION__);

  state_array.size = sa1100_pcmcia_socket_count;
  state_array.state = state;

  do {
    unsigned int events;
    int ret, i;

    memset(state, 0, sizeof(state));

    DEBUG(4, "%s(): interrogating low-level PCMCIA service\n", __FUNCTION__);

    ret = pcmcia_low_level->socket_state(&state_array);
    if (ret < 0) {
      printk(KERN_ERR "sa1100_pcmcia: unable to read socket status\n");
      break;
    }

    all_events = 0;

    for (i = 0; i < state_array.size; i++, all_events |= events) {
      struct sa1100_pcmcia_socket *skt = PCMCIA_SOCKET(i);

      events = sa1100_pcmcia_events(&state[i], &skt->k_state,
				    skt->cs_state.csc_mask,
				    skt->cs_state.flags);

      if (events && sa1100_pcmcia_socket[i].handler != NULL)
	skt->handler(skt->handler_info, events);
    }
  } while(all_events);
}  /* sa1100_pcmcia_task_handler() */
Пример #11
0
int au1x00_drv_pcmcia_remove(struct platform_device *dev)
{
	struct skt_dev_info *sinfo = platform_get_drvdata(dev);
	int i;

	mutex_lock(&pcmcia_sockets_lock);
	platform_set_drvdata(dev, NULL);

	for (i = 0; i < sinfo->nskt; i++) {
		struct au1000_pcmcia_socket *skt = PCMCIA_SOCKET(i);

		del_timer_sync(&skt->poll_timer);
		pcmcia_unregister_socket(&skt->socket);
		skt->ops->hw_shutdown(skt);
		au1x00_pcmcia_config_skt(skt, &dead_socket);
		iounmap(skt->virt_io + (u32)mips_io_port_base);
		skt->virt_io = NULL;
	}

	kfree(sinfo);
	mutex_unlock(&pcmcia_sockets_lock);
	return 0;
}
Пример #12
0
int au1x00_drv_pcmcia_remove(struct device *dev)
{
	struct skt_dev_info *sinfo = dev_get_drvdata(dev);
	int i;

	down(&pcmcia_sockets_lock);
	dev_set_drvdata(dev, NULL);

	for (i = 0; i < sinfo->nskt; i++) {
		struct au1000_pcmcia_socket *skt = PCMCIA_SOCKET(i);

		del_timer_sync(&skt->poll_timer);
		pcmcia_unregister_socket(&skt->socket);
		flush_scheduled_work();
		skt->ops->hw_shutdown(skt);
		au1x00_pcmcia_config_skt(skt, &dead_socket);
		iounmap(skt->virt_io);
		skt->virt_io = NULL;
	}

	kfree(sinfo);
	up(&pcmcia_sockets_lock);
	return 0;
}
Пример #13
0
int au1x00_pcmcia_socket_probe(struct device *dev, struct pcmcia_low_level *ops, int first, int nr)
{
	struct skt_dev_info *sinfo;
	struct au1000_pcmcia_socket *skt;
	int ret, i;

	sinfo = kzalloc(sizeof(struct skt_dev_info), GFP_KERNEL);
	if (!sinfo) {
		ret = -ENOMEM;
		goto out;
	}

	sinfo->nskt = nr;

	/*
	 * Initialise the per-socket structure.
	 */
	for (i = 0; i < nr; i++) {
		skt = PCMCIA_SOCKET(i);
		memset(skt, 0, sizeof(*skt));

		skt->socket.resource_ops = &pccard_static_ops;
		skt->socket.ops = &au1x00_pcmcia_operations;
		skt->socket.owner = ops->owner;
		skt->socket.dev.parent = dev;

		init_timer(&skt->poll_timer);
		skt->poll_timer.function = au1x00_pcmcia_poll_event;
		skt->poll_timer.data = (unsigned long)skt;
		skt->poll_timer.expires = jiffies + AU1000_PCMCIA_POLL_PERIOD;

		skt->nr		= first + i;
		skt->irq	= 255;
		skt->dev	= dev;
		skt->ops	= ops;

		skt->res_skt.name	= skt_names[skt->nr];
		skt->res_io.name	= "io";
		skt->res_io.flags	= IORESOURCE_MEM | IORESOURCE_BUSY;
		skt->res_mem.name	= "memory";
		skt->res_mem.flags	= IORESOURCE_MEM;
		skt->res_attr.name	= "attribute";
		skt->res_attr.flags	= IORESOURCE_MEM;

		/*
		 * PCMCIA client drivers use the inb/outb macros to access the
		 * IO registers. Since mips_io_port_base is added to the
		 * access address of the mips implementation of inb/outb,
		 * we need to subtract it here because we want to access the
		 * I/O or MEM address directly, without going through this
		 * "mips_io_port_base" mechanism.
		 */
		if (i == 0) {
			skt->virt_io = (void *)
				(ioremap((phys_t)AU1X_SOCK0_IO, 0x1000) -
				(u32)mips_io_port_base);
			skt->phys_attr = AU1X_SOCK0_PHYS_ATTR;
			skt->phys_mem = AU1X_SOCK0_PHYS_MEM;
		}
		else  {
			skt->virt_io = (void *)
				(ioremap((phys_t)AU1X_SOCK1_IO, 0x1000) -
				(u32)mips_io_port_base);
			skt->phys_attr = AU1X_SOCK1_PHYS_ATTR;
			skt->phys_mem = AU1X_SOCK1_PHYS_MEM;
		}
		pcmcia_base_vaddrs[i] = (u32 *)skt->virt_io;
		ret = ops->hw_init(skt);

		skt->socket.features = SS_CAP_STATIC_MAP|SS_CAP_PCCARD;
		skt->socket.irq_mask = 0;
		skt->socket.map_size = MAP_SIZE;
		skt->socket.pci_irq = skt->irq;
		skt->socket.io_offset = (unsigned long)skt->virt_io;

		skt->status = au1x00_pcmcia_skt_state(skt);

		ret = pcmcia_register_socket(&skt->socket);
		if (ret)
			goto out_err;

		WARN_ON(skt->socket.sock != i);

		add_timer(&skt->poll_timer);
	}

	dev_set_drvdata(dev, sinfo);
	return 0;


out_err:
	flush_scheduled_work();
	ops->hw_shutdown(skt);
	while (i-- > 0) {
		skt = PCMCIA_SOCKET(i);

		del_timer_sync(&skt->poll_timer);
		pcmcia_unregister_socket(&skt->socket);
		flush_scheduled_work();
		if (i == 0) {
			iounmap(skt->virt_io + (u32)mips_io_port_base);
			skt->virt_io = NULL;
		}
#ifndef CONFIG_MIPS_XXS1500
		else {
			iounmap(skt->virt_io + (u32)mips_io_port_base);
			skt->virt_io = NULL;
		}
#endif
		ops->hw_shutdown(skt);

	}
	kfree(sinfo);
out:
	return ret;
}
/* 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)
{
  unsigned int sock = (unsigned int)data;
  unsigned int clock = cpufreq_get(0);
  struct sa1100_pcmcia_socket *skt = PCMCIA_SOCKET(sock);
  unsigned long mecr = MECR;
  char *p = buf;

  p+=sprintf(p, "k_state  : %s%s%s%s%s%s%s\n", 
	     skt->k_state.detect ? "detect " : "",
	     skt->k_state.ready  ? "ready "  : "",
	     skt->k_state.bvd1   ? "bvd1 "   : "",
	     skt->k_state.bvd2   ? "bvd2 "   : "",
	     skt->k_state.wrprot ? "wrprot " : "",
	     skt->k_state.vs_3v  ? "vs_3v "  : "",
	     skt->k_state.vs_Xv  ? "vs_Xv "  : "");

  p+=sprintf(p, "status   : %s%s%s%s%s%s%s%s%s\n",
	     skt->k_state.detect ? "SS_DETECT " : "",
	     skt->k_state.ready  ? "SS_READY " : "",
	     skt->cs_state.Vcc   ? "SS_POWERON " : "",
	     skt->cs_state.flags & SS_IOCARD ? "SS_IOCARD " : "",
	     (skt->cs_state.flags & SS_IOCARD &&
	      skt->k_state.bvd1) ? "SS_STSCHG " : "",
	     ((skt->cs_state.flags & SS_IOCARD)==0 &&
	      (skt->k_state.bvd1==0)) ? "SS_BATDEAD " : "",
	     ((skt->cs_state.flags & SS_IOCARD)==0 &&
	      (skt->k_state.bvd2==0)) ? "SS_BATWARN " : "",
	     skt->k_state.vs_3v  ? "SS_3VCARD " : "",
	     skt->k_state.vs_Xv  ? "SS_XVCARD " : "");

  p+=sprintf(p, "mask     : %s%s%s%s%s\n",
	     skt->cs_state.csc_mask & SS_DETECT  ? "SS_DETECT "  : "",
	     skt->cs_state.csc_mask & SS_READY   ? "SS_READY "   : "",
	     skt->cs_state.csc_mask & SS_BATDEAD ? "SS_BATDEAD " : "",
	     skt->cs_state.csc_mask & SS_BATWARN ? "SS_BATWARN " : "",
	     skt->cs_state.csc_mask & SS_STSCHG  ? "SS_STSCHG "  : "");

  p+=sprintf(p, "cs_flags : %s%s%s%s%s\n",
	     skt->cs_state.flags & SS_PWR_AUTO   ? "SS_PWR_AUTO "   : "",
	     skt->cs_state.flags & SS_IOCARD     ? "SS_IOCARD "     : "",
	     skt->cs_state.flags & SS_RESET      ? "SS_RESET "      : "",
	     skt->cs_state.flags & SS_SPKR_ENA   ? "SS_SPKR_ENA "   : "",
	     skt->cs_state.flags & SS_OUTPUT_ENA ? "SS_OUTPUT_ENA " : "");

  p+=sprintf(p, "Vcc      : %d\n", skt->cs_state.Vcc);
  p+=sprintf(p, "Vpp      : %d\n", skt->cs_state.Vpp);
  p+=sprintf(p, "IRQ      : %d\n", skt->cs_state.io_irq);

  p+=sprintf(p, "I/O      : %u (%u)\n", skt->speed_io,
	     sa1100_pcmcia_cmd_time(clock, MECR_BSIO_GET(mecr, sock)));

  p+=sprintf(p, "attribute: %u (%u)\n", skt->speed_attr,
	     sa1100_pcmcia_cmd_time(clock, MECR_BSA_GET(mecr, sock)));

  p+=sprintf(p, "common   : %u (%u)\n", skt->speed_mem,
	     sa1100_pcmcia_cmd_time(clock, MECR_BSM_GET(mecr, sock)));

  return p-buf;
}
/* 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)
{
  struct sa1100_pcmcia_socket *skt = PCMCIA_SOCKET(sock);
  unsigned long start;

  DEBUG(2, "%s() for sock %u\n", __FUNCTION__, sock);

  DEBUG(3, "\tmap %u speed %u sys_start %08lx sys_stop %08lx card_start %08x\n",
	map->map, map->speed, map->sys_start, map->sys_stop, map->card_start);
  DEBUG(3, "\tflags: %s%s%s%s%s%s%s%s\n",
	(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) {
    unsigned int clock, speed = map->speed;
    unsigned long mecr;

    /*
     * When clients issue RequestMap, the access speed is not always
     * properly configured.  Choose some sensible defaults.
     */
    if (speed == 0) {
      if (skt->cs_state.Vcc == 33)
	speed = SA1100_PCMCIA_3V_MEM_ACCESS;
      else
	speed = SA1100_PCMCIA_5V_MEM_ACCESS;
    }

    clock = cpufreq_get(0);

    /* Fixme: MECR is not pre-empt safe. */
    mecr = MECR;

    if (map->flags & MAP_ATTRIB) {
      MECR_BSA_SET(mecr, sock, sa1100_pcmcia_mecr_bs(speed, clock));
      skt->speed_attr = speed;
    } else {
      MECR_BSM_SET(mecr, sock, sa1100_pcmcia_mecr_bs(speed, clock));
      skt->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->flags & MAP_ATTRIB) ? skt->phys_attr : skt->phys_mem;

  if (map->sys_stop == 0)
    map->sys_stop = PAGE_SIZE-1;

  map->sys_stop -= map->sys_start;
  map->sys_stop += start;
  map->sys_start = start;

  skt->mem_map[map->map] = *map;

  return 0;
}  /* sa1100_pcmcia_set_mem_map() */
/* 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.
 *
 * 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;
  int ret;

  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 -EINVAL;
  }

  ret = sa1100_pcmcia_machine_init();
  if (ret)
    return ret;

  pcmcia_init.handler = sa1100_pcmcia_interrupt;

  ret = pcmcia_low_level->init(&pcmcia_init);
  if (ret < 0) {
    printk(KERN_ERR "Unable to initialize kernel PCMCIA service (%d).\n", ret);
    return ret == -1 ? -EIO : ret;
  }

  sa1100_pcmcia_socket_count = ret;
  state_array.size  = sa1100_pcmcia_socket_count;
  state_array.state = state;

  memset(state, 0, sizeof(state));

  if (pcmcia_low_level->socket_state(&state_array) < 0) {
    pcmcia_low_level->shutdown();
    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 = MECR;

  clock = cpufreq_get(0);

  for (i = 0; i < sa1100_pcmcia_socket_count; i++) {
    struct sa1100_pcmcia_socket *skt = PCMCIA_SOCKET(i);
    struct pcmcia_irq_info irq_info;

    if (!request_mem_region(_PCMCIA(i), PCMCIASp, "PCMCIA")) {
      ret = -EBUSY;
      goto out_err;
    }

    irq_info.sock = i;
    irq_info.irq  = -1;
    ret = pcmcia_low_level->get_irq_info(&irq_info);
    if (ret < 0)
      printk(KERN_ERR "Unable to get IRQ for socket %u (%d)\n", i, ret);

    skt->irq        = irq_info.irq;
    skt->k_state    = state[i];
    skt->speed_io   = SA1100_PCMCIA_IO_ACCESS;
    skt->speed_attr = SA1100_PCMCIA_5V_MEM_ACCESS;
    skt->speed_mem  = SA1100_PCMCIA_5V_MEM_ACCESS;
    skt->phys_attr  = _PCMCIAAttr(i);
    skt->phys_mem   = _PCMCIAMem(i);
    skt->virt_io    = ioremap(_PCMCIAIO(i), 0x10000);

    if (skt->virt_io == NULL) {
      ret = -ENOMEM;
      goto out_err;
    }

    MECR_FAST_SET(mecr, i, 0);
    MECR_BSIO_SET(mecr, i, sa1100_pcmcia_mecr_bs(skt->speed_io, clock));
    MECR_BSA_SET(mecr, i, sa1100_pcmcia_mecr_bs(skt->speed_attr, clock));
    MECR_BSM_SET(mecr, i, sa1100_pcmcia_mecr_bs(skt->speed_mem, clock));
  }

  MECR = mecr;

#ifdef CONFIG_CPU_FREQ
  ret = cpufreq_register_notifier(&sa1100_pcmcia_notifier_block);
  if (ret < 0) {
    printk(KERN_ERR "Unable to register CPU frequency change notifier (%d)\n", ret);
    goto out_err;
  }
#endif

  /* Only advertise as many sockets as we can detect */
  ret = register_ss_entry(sa1100_pcmcia_socket_count,
			  &sa1100_pcmcia_operations);
  if (ret < 0) {
    printk(KERN_ERR "Unable to register sockets\n");
    goto out_err;
  }

  /*
   * Start the event poll timer.  It will reschedule by itself afterwards.
   */
  sa1100_pcmcia_poll_event(0);

  return 0;

 out_err:
  for (i = 0; i < sa1100_pcmcia_socket_count; i++) {
    iounmap(sa1100_pcmcia_socket[i].virt_io);
    release_mem_region(_PCMCIA(i), PCMCIASp);
  }

  pcmcia_low_level->shutdown();

  return ret;
}  /* sa1100_pcmcia_driver_init() */