patch-2.4.25 linux-2.4.25/drivers/pcmcia/sibyte_generic.c
Next file: linux-2.4.25/drivers/pcmcia/vrc4173_cardu.c
Previous file: linux-2.4.25/drivers/pcmcia/sa1100_stork.c
Back to the patch index
Back to the overall index
- Lines: 689
- Date:
2004-02-18 05:36:31.000000000 -0800
- Orig file:
linux-2.4.24/drivers/pcmcia/sibyte_generic.c
- Orig date:
1969-12-31 16:00:00.000000000 -0800
diff -urN linux-2.4.24/drivers/pcmcia/sibyte_generic.c linux-2.4.25/drivers/pcmcia/sibyte_generic.c
@@ -0,0 +1,688 @@
+/*
+ * Copyright (C) 2003 Broadcom Corporation
+ * originally contributed to SiByte, Inc as
+ * "sb1250pc.c 0.10 (Stanley Chen & James Liao)"
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that 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.
+ */
+
+/*
+ * Notes / Apologies:
+ * - only ATA cards tested so far
+ * - requires hack in cs.c to avoid poking the CISCOR register
+ * - ack_intr routine might be improved to avoid error msgs.
+ * - remove and re-insert doesn't work (crash or fail to probe drive)
+ */
+
+#include <linux/config.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/ioport.h>
+#include <linux/sched.h>
+#include <linux/proc_fs.h>
+#include <linux/timer.h>
+#include <linux/ide.h>
+
+#include <pcmcia/version.h>
+#include <pcmcia/cs_types.h>
+#include <pcmcia/ss.h>
+#include <pcmcia/bulkmem.h>
+#include <pcmcia/cistpl.h>
+#include <pcmcia/cs.h>
+#include "cs_internal.h"
+
+#include <asm/io.h>
+
+#include <asm/sibyte/board.h>
+#include <asm/sibyte/sb1250_regs.h>
+#include <asm/sibyte/sb1250_scd.h>
+#include <asm/sibyte/sb1250_int.h>
+#include <asm/sibyte/sb1250_genbus.h>
+#include <asm/sibyte/64bit.h>
+
+#define PFX "sibyte-pcmcia: "
+
+MODULE_AUTHOR("Kip Walker, Stanley Chen & James Liao");
+MODULE_DESCRIPTION("SiByte PCMCIA socket driver");
+
+#undef PCMCIA_DEBUG
+#ifdef PCMCIA_DEBUG
+#define DPRINTK(args...) do { printk(KERN_DEBUG args); } while (0)
+#else
+#define DPRINTK(n, args...) do { } while (0)
+#endif
+
+static unsigned long sb_pcmcia_base = PCMCIA_PHYS;
+static unsigned long sb_pcmcia_size;
+#define SIBYTE_CS_REG(pcaddr) (IO_SPACE_BASE + sb_pcmcia_base - mips_io_port_base + pcaddr)
+#define SB_PC_PORT 0xff00
+extern void sibyte_set_ideops(ide_hwif_t *hwif);
+
+/* The card status change interrupt */
+static int cs_irq = K_INT_PCMCIA;
+
+/* Memory map windows */
+static struct pccard_mem_map sibyte_memmap[MAX_WIN];
+static struct pccard_io_map sibyte_iomap[MAX_IO_WIN];
+
+/*====================================================================*/
+/* Socket structures */
+/*====================================================================*/
+
+
+static void sb1250pc_interrupt(int irq, void *dev, struct pt_regs *regs);
+static struct pccard_operations sb1250pc_operations;
+
+typedef struct socket_handler_t {
+ void (*handler)(void *info, u_int events);
+ void *info;
+} socket_handler_t;
+
+static socket_handler_t socket_handler;
+
+/*
+ * cap features:
+ * full 32-bit addressing for 16-bit PCcard memory windows
+ * 16-bit card memory and IO accesses need bus_ops
+ * only 16-bit PCcards
+ * align memory windows
+ * statically mapped memory windows
+ */
+static socket_cap_t sb1250pc_cap = {
+ features: (SS_CAP_PAGE_REGS |
+// SS_CAP_VIRTUAL_BUS |
+ SS_CAP_PCCARD |
+ SS_CAP_MEM_ALIGN | SS_CAP_STATIC_MAP),
+ irq_mask: 0, /* tell ide layer to take PCI irq */
+ map_size: 0x4000000, /* 64MB minimum window size (What *should* this be?)*/
+ io_offset: SB_PC_PORT, /* This is ide5 -- just a special token for ide-sibyte */
+ pci_irq: K_INT_PC_READY, /* XXXKW This serves as IREQ# for CompactFlash */
+ cb_dev: NULL,
+ bus: NULL
+};
+
+/*====================================================================*/
+/* Useful macros */
+/*====================================================================*/
+
+#define READ_PHYSADDR(addr) (*(volatile u32 *)(KSEG1ADDR(addr)))
+#define WRITE_PHYSADDR(addr, data) (*(volatile u32 *)(KSEG1ADDR(addr))) = (data)
+
+#define READ_CSR32(reg) csr_in32(IO_SPACE_BASE + (reg))
+#define WRITE_CSR32(data, reg) csr_out32(data, IO_SPACE_BASE + (reg))
+
+#define sb1250pc_write_config(data) WRITE_CSR32(data, A_IO_PCMCIA_CFG)
+#define sb1250pc_read_config() READ_CSR32(A_IO_PCMCIA_CFG)
+#define sb1250pc_read_status() READ_CSR32(A_IO_PCMCIA_STATUS)
+
+#define CARDPRESENT(s) (((s) & (M_PCMCIA_STATUS_CD1 | M_PCMCIA_STATUS_CD2)) == 0)
+
+int sb_pcmcia_ack_intr(struct hwif_s *hwif)
+{
+ /*
+ * XXXKW verify interrupt and return appropriate value?
+ * Simple check of the bit in A_GPIO_READ didn't DTRT
+ */
+
+ /* Clear out the GPIO edge detector */
+ WRITE_CSR32(1 << K_GPIO_PC_READY, A_GPIO_CLR_EDGE);
+ return 1;
+}
+
+/*====================================================================*/
+/* Interrupt handling */
+/*====================================================================*/
+
+static unsigned int pending_events;
+static spinlock_t pending_event_lock = SPIN_LOCK_UNLOCKED;
+
+static void sb1250pc_bh(void *dummy)
+{
+ unsigned int events;
+
+ spin_lock_irq(&pending_event_lock);
+ events = pending_events;
+ pending_events = 0;
+ spin_unlock_irq(&pending_event_lock);
+
+ if (socket_handler.handler)
+ socket_handler.handler(socket_handler.info, events);
+}
+
+static struct tq_struct sb1250pc_task =
+{
+ routine: sb1250pc_bh
+};
+
+static void sb1250pc_interrupt(int irq, void *dev, struct pt_regs *regs)
+{
+ unsigned int events = 0;
+ uint32_t status;
+
+ status = sb1250pc_read_status();
+
+ if (status & M_PCMCIA_STATUS_CDCHG) {
+ events = SS_DETECT;
+ if (CARDPRESENT(status)) {
+ events |= SS_INSERTION;
+ } else {
+ events |= SS_EJECTION;
+ }
+ }
+#if 0
+ /* XXXKW ignore everything but CD? */
+ if (status & M_PCMCIA_STATUS_RDYCHG) {
+ if (status & M_PCMCIA_STATUS_RDY) {
+ /* XXXKW if ide, ack the interrupt? */
+ events |= SS_READY;
+ }
+ }
+#endif
+
+ if (events) {
+ DPRINTK(PFX " passing %x to bh\n", events);
+
+ /* Pass the events off to the bottom-half */
+ spin_lock(&pending_event_lock);
+ pending_events |= events;
+ spin_unlock(&pending_event_lock);
+ schedule_task(&sb1250pc_task);
+ }
+}
+
+/*====================================================================*/
+/* PC Card Operations */
+/*====================================================================*/
+
+static int sb1250pc_register_callback(unsigned int lsock,
+ void (*handler)(void *, unsigned int),
+ void * info)
+{
+ DPRINTK(PFX "sb1250pc_register_callback(%d)\n", lsock);
+
+ socket_handler.handler = handler;
+ socket_handler.info = info;
+ if (handler == NULL) {
+ MOD_DEC_USE_COUNT;
+ } else {
+ MOD_INC_USE_COUNT;
+ }
+ return 0;
+}
+
+/*====================================================================*/
+
+static int sb1250pc_get_status(unsigned int lsock, u_int *value)
+{
+ u_int val;
+ uint32_t status;
+#if PCMCIA_DEBUG
+ u32 config;
+#endif
+
+ status = sb1250pc_read_status();
+#if PCMCIA_DEBUG
+ config = sb1250pc_read_config();
+#endif
+
+ val = CARDPRESENT(status) ? SS_DETECT : 0;
+ val |= (status & M_PCMCIA_CFG_RESET) ? SS_RESET : 0;
+ val |= (status & (M_PCMCIA_STATUS_3VEN | M_PCMCIA_STATUS_5VEN)) ?
+ SS_POWERON : 0;
+ val |= (status & M_PCMCIA_STATUS_RDY) ? SS_READY : 0;
+ val |= (status & M_PCMCIA_STATUS_WP) ? SS_WRPROT : 0;
+ val |= ((status & M_PCMCIA_STATUS_VS2) &&
+ (~status & M_PCMCIA_STATUS_VS1)) ? SS_3VCARD : 0;
+ val |= (status & (M_PCMCIA_STATUS_CDCHG | M_PCMCIA_STATUS_WPCHG
+ | M_PCMCIA_STATUS_RDYCHG)) ? SS_STSCHG : 0;
+ /* XXXKW SS_INSERTION on cdchange? */
+
+ DPRINTK(PFX "GetStatus(%d) = %x", lsock, val);
+#if PCMCIA_DEBUG
+ DPRINTK(" [config(0x%4.4x) status(0x%4.4x)]", config, status);
+#endif
+ DPRINTK("\n");
+
+ *value = val;
+ return 0;
+}
+
+/*====================================================================*/
+
+static int sb1250pc_inquire_socket(unsigned int lsock, socket_cap_t *cap)
+{
+ *cap = sb1250pc_cap;
+
+ DPRINTK(PFX "InquireSocket(%d) = features 0x%4.4x, irq_mask "
+ "0x%4.4x, map_size 0x%4.4x\n", lsock, cap->features,
+ cap->irq_mask, cap->map_size);
+
+ return 0;
+}
+
+/*====================================================================*/
+
+// This garbage function never seems to get called...
+static int sb1250pc_get_socket(unsigned int lsock, socket_state_t *state)
+{
+ DPRINTK(PFX "Does GetSocket(%d) ever get called???", lsock);
+ return -1;
+} /* sb1250pc_get_socket */
+
+/*====================================================================*/
+
+static uint32_t sibyte_set_power(uint32_t config, int vcc)
+{
+ config &= ~(M_PCMCIA_CFG_3VEN | M_PCMCIA_CFG_5VEN);
+ if (vcc == 33) {
+ config |= M_PCMCIA_CFG_3VEN;
+ } else if (vcc == 50) {
+ config |= M_PCMCIA_CFG_5VEN;
+ }
+
+ sb1250pc_write_config(config);
+ return config;
+}
+
+static int sb1250pc_set_socket(unsigned int lsock, socket_state_t *state)
+{
+ uint32_t config;
+
+ DPRINTK(PFX "SetSocket(%d, flags %#3.3x, Vcc %d, Vpp %d, "
+ "io_irq %d, csc_mask %#2.2x)\n", lsock, state->flags,
+ state->Vcc, state->Vpp, state->io_irq, state->csc_mask);
+
+ config = sb1250pc_read_config();
+
+ config = sibyte_set_power(config, state->Vcc);
+
+ if (state->flags & SS_DEBOUNCED)
+ state->flags &= ~SS_DEBOUNCED; /* SS_DEBOUNCED is oneshot */
+ /* XXXKW SS_OUTPUT_ENA? */
+ /* XXXKW SS_PWR_AUTO? */
+ /* XXXKW SS_IOCARD? */
+
+ if (state->csc_mask & SS_DETECT)
+ config &= ~M_PCMCIA_CFG_CDMASK;
+ else
+ config |= M_PCMCIA_CFG_CDMASK;
+
+ config &= ~M_PCMCIA_CFG_RESET;
+ if (state->flags & SS_RESET) {
+ DPRINTK(PFX " resetting PCMCIA\n");
+ config |= M_PCMCIA_CFG_RESET;
+ }
+ sb1250pc_write_config(config);
+
+ DPRINTK(PFX " new config: %x\n", sb1250pc_read_config());
+
+ return 0;
+}
+
+/*====================================================================*/
+
+static int sb1250pc_get_io_map(unsigned int lsock, struct pccard_io_map *io)
+{
+ *io = sibyte_iomap[io->map];
+
+ DPRINTK(PFX "GetIOMap(%d, %d) = %#2.2x, %d ns, "
+ "%#4.4x-%#4.4x\n", lsock, io->map, io->flags,
+ io->speed, io->start, io->stop);
+ return 0;
+} /* sb1250pc_get_io_map */
+
+static int sb1250pc_set_io_map(unsigned int lsock, struct pccard_io_map *io)
+{
+ unsigned int speed;
+ unsigned long start;
+ u32 config;
+
+ /* SB1250 uses direct mapping */
+ DPRINTK(PFX "SetIOMap(%d, %d, %#2.2x, %d ns, "
+ "%#4.4x-%#4.4x) called\n", lsock, io->map, io->flags,
+ io->speed, io->start, io->stop);
+
+ if (io->map >= MAX_IO_WIN) {
+ DPRINTK(KERN_ERR PFX "map (%d) out of range\n", io->map);
+ return -1;
+ }
+
+ if (io->flags & MAP_ACTIVE) {
+ speed = (io->speed > 0) ? io->speed : 255;
+ }
+
+ config = sb1250pc_read_config();
+
+ if (io->flags & MAP_ATTRIB) {
+ DPRINTK(PFX " Setting pcmcia_cfg_reg to 1 (Attribute Mode)\n");
+ config |= M_PCMCIA_CFG_ATTRMEM;
+ } else {
+ DPRINTK(PFX " Setting pcmcia_cfg_reg to 0 (Data Mode)\n");
+ config &= ~M_PCMCIA_CFG_ATTRMEM;
+ }
+ sb1250pc_write_config(config);
+
+ start = io->start;
+
+ if (io->stop == 1) {
+ io->stop = PAGE_SIZE - 1;
+ }
+
+ if (io->start == 0)
+ io->start = sb_pcmcia_base;
+
+ io->stop = io->start + (io->stop - start);
+
+ sibyte_iomap[io->map] = *io;
+
+ DPRINTK(PFX "SetIOMap(%d, %d, %#2.2x, %d ns, "
+ "%#4.4x-%#4.4x) returns\n", lsock, io->map, io->flags,
+ io->speed, io->start, io->stop);
+ return 0;
+} /* sb1250pc_set_io_map */
+
+/*====================================================================*/
+
+static int sb1250pc_get_mem_map(unsigned int lsock, struct pccard_mem_map *mem)
+{
+ if(mem->map >= MAX_WIN)
+ return -EINVAL;
+
+ *mem = sibyte_memmap[mem->map];
+
+ DPRINTK(PFX "GetMemMap(%d, mem[%d, %#2.2x, %d ns, "
+ "%#5.5lx-%#5.5lx, %#5.5x) called\n", lsock, mem->map, mem->flags,
+ mem->speed, mem->sys_start, mem->sys_stop, mem->card_start);
+
+ return 0;
+}
+
+static int sb1250pc_set_mem_map(unsigned int lsock, struct pccard_mem_map *mem)
+{
+ u32 old_config, new_config;
+
+ if (mem->map >= MAX_WIN) {
+ DPRINTK(KERN_ERR PFX "map (%d) out of range\n", mem->map);
+ return -1;
+ }
+
+ if (mem->sys_start == 0)
+ mem->sys_start = mem->card_start + sb_pcmcia_base;
+
+ if (mem->sys_stop == 0)
+ mem->sys_stop = mem->sys_start + sb_pcmcia_size - 1;
+
+ old_config = sb1250pc_read_config();
+
+ DPRINTK(PFX " Setting mem_map %p\n", mem);
+ if (mem->flags & MAP_ATTRIB) {
+ DPRINTK(PFX " Setting pcmcia_cfg_reg to 1 (Attribute Mode)\n");
+ new_config = old_config | M_PCMCIA_CFG_ATTRMEM;
+ } else {
+ DPRINTK(PFX " Setting pcmcia_cfg_reg to 0 (Data Mode)\n");
+ new_config = old_config & ~M_PCMCIA_CFG_ATTRMEM;
+ }
+ if (new_config != old_config)
+ sb1250pc_write_config(new_config);
+
+ sibyte_memmap[mem->map] = *mem;
+
+ DPRINTK(PFX "SetMemMap(%d, mem[%d, %#2.2x, %d ns], "
+ "%#5.5lx-%#5.5lx, %#5.5x)\n", lsock, mem->map, mem->flags,
+ mem->speed, mem->sys_start, mem->sys_stop, mem->card_start);
+
+ return 0;
+}
+
+/*====================================================================*/
+
+#ifdef CONFIG_PROC_FS
+/* sb1250pc_proc_status()
+ * Implements the /proc/bus/pccard/??/status file.
+ *
+ * Returns: the number of characters added to the buffer
+ *
+ * Be aware that reading status clears the "change" bits; this seems
+ * unlikely to bite us by making us miss interrupts.
+ */
+static int sb1250pc_proc_status(char *buf, char **start, off_t pos,
+ int count, int *eof, void *data)
+{
+ char *p = buf;
+ u32 addr, temp;
+ u32 status, config;
+ //unsigned int sock = (unsigned int) data;
+
+ config = sb1250pc_read_config();
+ status = sb1250pc_read_status();
+ p += sprintf(p, "config(0x%4.4x) status(0x%4.4x)\n", config, status);
+
+ for (addr = sb_pcmcia_base; addr < sb_pcmcia_base + 8; addr+=4) {
+ temp = READ_PHYSADDR(addr);
+ p += sprintf(p, " Looking up addr 0x%x: 0x%8.8x\n", addr, temp);
+ }
+
+ return p - buf;
+}
+
+static void sb1250pc_proc_setup(unsigned int sock, struct proc_dir_entry *base)
+{
+ struct proc_dir_entry *entry;
+
+ if ((entry = create_proc_entry("sb1250pc", 0, base)) == NULL) {
+ printk(KERN_ERR PFX "Unable to install \"sb1250pc\" procfs entry\n");
+ return;
+ } else
+ printk(KERN_INFO PFX "Setting up \"sb1250pc\" procfs entry\n");
+
+ entry->read_proc = sb1250pc_proc_status;
+ entry->data = (void *) sock;
+}
+
+#endif
+
+/*====================================================================*/
+
+static int sibyte_pcmcia_initted = 0;
+
+static int sb1250pc_init(unsigned int s)
+{
+ u32 config, status;
+
+ DPRINTK(PFX "Initializing SB1250 PCMCIA:\n");
+
+ /* Read status to clear interrupt sources */
+ status = sb1250pc_read_status();
+
+ /*
+ * Before getting setting up the IRQ, set the config:
+ * reset off, auto-power off
+ * interrupt mask: WP, CD, Ready off
+ */
+ config = sb1250pc_read_config();
+ config = M_PCMCIA_CFG_CDMASK | M_PCMCIA_CFG_WPMASK | M_PCMCIA_CFG_RDYMASK;
+ sb1250pc_write_config(config);
+
+ if (!sibyte_pcmcia_initted) {
+ uint32_t gpio_ctrl;
+ /* Set up the GPIO for PC_READY for use in ide-cs */
+ gpio_ctrl = READ_CSR32(A_GPIO_INT_TYPE);
+ gpio_ctrl &= ~M_GPIO_INTR_TYPEX(K_GPIO_PC_READY);
+ gpio_ctrl |= V_GPIO_INTR_TYPEX(K_GPIO_PC_READY, K_GPIO_INTR_EDGE);
+ WRITE_CSR32(gpio_ctrl, A_GPIO_INT_TYPE);
+ WRITE_CSR32(1 << K_GPIO_PC_READY, A_GPIO_CLR_EDGE);
+
+ /* Invert to get busy->ready transition */
+ gpio_ctrl = READ_CSR32(A_GPIO_INPUT_INVERT);
+ gpio_ctrl |= 1 << K_GPIO_PC_READY;
+ WRITE_CSR32(gpio_ctrl, A_GPIO_INPUT_INVERT);
+
+ /* Should not be any pending since we masked all sources */
+ if (request_irq(cs_irq, sb1250pc_interrupt, 0, "pcmcia", NULL))
+ return -ENODEV;
+ DPRINTK(PFX " IRQ %d registered\n", cs_irq);
+
+ sibyte_pcmcia_initted = 1;
+ }
+
+ status = sb1250pc_read_status();
+ DPRINTK(PFX " config(0x%4.4x) status(0x%4.4x)\n", config, status);
+
+ sb1250pc_set_socket(s, &dead_socket);
+
+ return 0;
+}
+
+static int sb1250pc_suspend(unsigned int sock)
+{
+ free_irq(cs_irq, NULL);
+ DPRINTK(KERN_INFO PFX " IRQ %d freed\n", cs_irq);
+ return sb1250pc_set_socket(sock, &dead_socket);
+}
+
+static struct pccard_operations sb1250pc_operations = {
+ sb1250pc_init,
+ sb1250pc_suspend,
+ sb1250pc_register_callback,
+ sb1250pc_inquire_socket,
+ sb1250pc_get_status,
+ sb1250pc_get_socket,
+ sb1250pc_set_socket,
+ sb1250pc_get_io_map,
+ sb1250pc_set_io_map,
+ sb1250pc_get_mem_map,
+ sb1250pc_set_mem_map,
+#ifdef CONFIG_PROC_FS
+ sb1250pc_proc_setup
+#endif
+};
+
+/*
+ * XXXKW This is a hack. The ide-cs stuff seems to leave us in
+ * Attribute mode. Since I know that a SELECT_DRIVE will happen as
+ * the first I/O access, use this opportunity to enter data mode.
+ */
+static void sibyte_pcmcia_selectproc(ide_drive_t *drive)
+{
+ sb1250pc_write_config(sb1250pc_read_config() & ~M_PCMCIA_CFG_ATTRMEM);
+}
+
+static int sibyte_pc_prep_ide(void)
+{
+ int i;
+ ide_hwif_t *hwif = NULL;
+
+ /* Stake early claim on an ide_hwif */
+ for (i = 0; i < MAX_HWIFS; i++) {
+ if (!ide_hwifs[i].io_ports[IDE_DATA_OFFSET]) {
+ hwif = &ide_hwifs[i];
+ break;
+ }
+ }
+ if (hwif == NULL) {
+ printk("No space for SiByte onboard PCMCIA driver in ide_hwifs[]. Not enabled.\n");
+ return 1;
+ }
+
+ /*
+ * Prime the hwif with port values, so when a card is
+ * detected, the 'io_offset' from the capabilities will lead
+ * it here
+ */
+ hwif->hw.io_ports[IDE_DATA_OFFSET] = SIBYTE_CS_REG(0);
+ hwif->hw.io_ports[IDE_ERROR_OFFSET] = SIBYTE_CS_REG(1);
+ hwif->hw.io_ports[IDE_NSECTOR_OFFSET] = SIBYTE_CS_REG(2);
+ hwif->hw.io_ports[IDE_SECTOR_OFFSET] = SIBYTE_CS_REG(3);
+ hwif->hw.io_ports[IDE_LCYL_OFFSET] = SIBYTE_CS_REG(4);
+ hwif->hw.io_ports[IDE_HCYL_OFFSET] = SIBYTE_CS_REG(5);
+ hwif->hw.io_ports[IDE_SELECT_OFFSET] = SIBYTE_CS_REG(6);
+ hwif->hw.io_ports[IDE_STATUS_OFFSET] = SIBYTE_CS_REG(7);
+ hwif->hw.io_ports[IDE_CONTROL_OFFSET] = SIBYTE_CS_REG(6); /* XXXKW ? */
+ hwif->hw.ack_intr = sb_pcmcia_ack_intr;
+ hwif->selectproc = sibyte_pcmcia_selectproc;
+ hwif->hold = 1;
+ hwif->mmio = 2;
+ sibyte_set_ideops(&ide_hwifs[i]);
+
+ printk("SiByte onboard PCMCIA-IDE configured as device %i\n", i);
+
+ return 0;
+}
+
+static int __init sibyte_pcmcia_init(void)
+{
+ servinfo_t info;
+ u32 addr, temp;
+ u64 cfg;
+
+ CardServices(GetCardServicesInfo, &info);
+ if (info.Revision != CS_RELEASE_CODE) {
+ printk(KERN_ERR PFX "Card Services release does not match!\n");
+ return -1;
+ }
+
+ cfg = in64(IO_SPACE_BASE + A_SCD_SYSTEM_CFG);
+ if (!(cfg & M_SYS_PCMCIA_ENABLE)) {
+ printk(KERN_INFO PFX "chip not configured for PCMCIA\n");
+ return -1;
+ }
+
+ /* Find memory base address and size */
+ addr = A_IO_EXT_REG(R_IO_EXT_REG(R_IO_EXT_MULT_SIZE, PCMCIA_CS));
+ temp = G_IO_MULT_SIZE(csr_in32(IO_SPACE_BASE + addr));
+ printk(PFX "Looking up addr 0x%x: 0x%4.4x (IO size)\n", addr, temp);
+ sb_pcmcia_size = (temp+1) << S_IO_REGSIZE;
+
+ addr = A_IO_EXT_REG(R_IO_EXT_REG(R_IO_EXT_START_ADDR, PCMCIA_CS));
+ temp = G_IO_START_ADDR(csr_in32(KSEG1|addr));
+ printk(PFX "Looking up addr 0x%x: 0x%4.4x (IO Base Address)\n", addr, temp);
+ if (temp << S_IO_ADDRBASE != PCMCIA_PHYS)
+ panic(PFX "pcmcia base doesn't match gencs value\n");
+
+ /* check and request memory region */
+ if (check_mem_region(sb_pcmcia_base, sb_pcmcia_size)) {
+ printk(KERN_ERR PFX "Can't request memory region?\n");
+ } else {
+ request_mem_region(sb_pcmcia_base, sb_pcmcia_size, "sibyte-pcmcia");
+ printk(PFX "Memory region 0x%8.8lx of size 0x%8.8lx requested.\n",
+ sb_pcmcia_base, sb_pcmcia_size);
+ }
+
+ /* register with socket services */
+ if (register_ss_entry(1, &sb1250pc_operations)) {
+ printk(KERN_ERR PFX "register_ss_entry() failed\n");
+ release_region(sb_pcmcia_base, sb_pcmcia_size);
+ return -ENODEV;
+ }
+
+ if (!sibyte_pc_prep_ide()) {
+ /* XXXKW hack for ide-cs warning squash */
+ request_region(sb1250pc_cap.io_offset, 16, "ide-cs");
+ request_mem_region(SIBYTE_CS_REG(0), 8, "sibyte-ide-cs");
+ }
+
+ return 0;
+}
+
+static void __exit sibyte_pcmcia_exit(void)
+{
+ /* XXXKW untested as module */
+ unregister_ss_entry(&sb1250pc_operations);
+ release_region(sb_pcmcia_base, sb_pcmcia_size);
+}
+
+module_init(sibyte_pcmcia_init);
+module_exit(sibyte_pcmcia_exit);
FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen (who was at: slshen@lbl.gov)