summaryrefslogtreecommitdiffstats
path: root/drivers/pcmcia
diff options
context:
space:
mode:
authorRalf Baechle <ralf@linux-mips.org>1999-10-09 00:00:47 +0000
committerRalf Baechle <ralf@linux-mips.org>1999-10-09 00:00:47 +0000
commitd6434e1042f3b0a6dfe1b1f615af369486f9b1fa (patch)
treee2be02f33984c48ec019c654051d27964e42c441 /drivers/pcmcia
parent609d1e803baf519487233b765eb487f9ec227a18 (diff)
Merge with 2.3.19.
Diffstat (limited to 'drivers/pcmcia')
-rw-r--r--drivers/pcmcia/.cvsignore2
-rw-r--r--drivers/pcmcia/Config.in14
-rw-r--r--drivers/pcmcia/Makefile39
-rw-r--r--drivers/pcmcia/bulkmem.c625
-rw-r--r--drivers/pcmcia/cardbus.c641
-rw-r--r--drivers/pcmcia/cb_enabler.c397
-rw-r--r--drivers/pcmcia/cirrus.h157
-rw-r--r--drivers/pcmcia/cistpl.c1394
-rw-r--r--drivers/pcmcia/cs.c2243
-rw-r--r--drivers/pcmcia/cs_internal.h280
-rw-r--r--drivers/pcmcia/ds.c899
-rw-r--r--drivers/pcmcia/i82365.c2804
-rw-r--r--drivers/pcmcia/i82365.h135
-rw-r--r--drivers/pcmcia/o2micro.h98
-rw-r--r--drivers/pcmcia/ricoh.h132
-rw-r--r--drivers/pcmcia/rsrc_mgr.c790
-rw-r--r--drivers/pcmcia/rsrc_mgr.h39
-rw-r--r--drivers/pcmcia/smc34c90.h52
-rw-r--r--drivers/pcmcia/tcic.c966
-rw-r--r--drivers/pcmcia/tcic.h266
-rw-r--r--drivers/pcmcia/ti113x.h180
-rw-r--r--drivers/pcmcia/topic.h96
-rw-r--r--drivers/pcmcia/vg468.h106
-rw-r--r--drivers/pcmcia/yenta.h153
24 files changed, 12508 insertions, 0 deletions
diff --git a/drivers/pcmcia/.cvsignore b/drivers/pcmcia/.cvsignore
new file mode 100644
index 000000000..857dd22e9
--- /dev/null
+++ b/drivers/pcmcia/.cvsignore
@@ -0,0 +1,2 @@
+.depend
+.*.flags
diff --git a/drivers/pcmcia/Config.in b/drivers/pcmcia/Config.in
new file mode 100644
index 000000000..2883046c1
--- /dev/null
+++ b/drivers/pcmcia/Config.in
@@ -0,0 +1,14 @@
+#
+# PCMCIA bus subsystem configuration
+#
+mainmenu_option next_comment
+comment 'PCMCIA/Cardbus support'
+
+tristate 'PCMCIA/Cardbus support' CONFIG_PCMCIA
+if [ "$CONFIG_PCMCIA" != "n" ]; then
+ if [ "$CONFIG_PCI" != "n" ]; then
+ bool ' CardBus support' CONFIG_CARDBUS
+ fi
+fi
+
+endmenu
diff --git a/drivers/pcmcia/Makefile b/drivers/pcmcia/Makefile
new file mode 100644
index 000000000..71750544d
--- /dev/null
+++ b/drivers/pcmcia/Makefile
@@ -0,0 +1,39 @@
+#
+# 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..
+
+SUB_DIRS :=
+MOD_SUB_DIRS := $(SUB_DIRS)
+ALL_SUB_DIRS := $(SUB_DIRS)
+
+MOD_LIST_NAME := PCMCIA_MODULES
+
+ifeq ($(CONFIG_PCMCIA),y)
+ O_OBJS := i82365.o tcic.o cistpl.o rsrc_mgr.o bulkmem.o
+ OX_OBJS := ds.o cs.o
+ O_TARGET := pcmcia.o
+ ifeq ($(CONFIG_CARDBUS),y)
+ O_OBJS += cardbus.o
+ endif
+else
+ ifeq ($(CONFIG_PCMCIA),m)
+ M_OBJS := i82365.o tcic.o pcmcia_core.o
+ MX_OBJS := ds.o
+ MIX_OBJS := cs.o
+ CORE_OBJS := cistpl.o rsrc_mgr.o bulkmem.o cs.o
+ ifeq ($(CONFIG_CARDBUS),y)
+ CORE_OBJS += cardbus.o
+ endif
+ endif
+endif
+
+include $(TOPDIR)/Rules.make
+
+pcmcia_core.o: $(CORE_OBJS)
+ $(LD) $(LD_RFLAG) -r -o $@ $(CORE_OBJS)
diff --git a/drivers/pcmcia/bulkmem.c b/drivers/pcmcia/bulkmem.c
new file mode 100644
index 000000000..7eaad9626
--- /dev/null
+++ b/drivers/pcmcia/bulkmem.c
@@ -0,0 +1,625 @@
+/*======================================================================
+
+ PCMCIA Bulk Memory Services
+
+ bulkmem.c 1.32 1999/09/15 15:32:19
+
+ 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 David A. Hinds
+ <dhinds@hyper.stanford.edu>. Portions created by David A. Hinds
+ are Copyright (C) 1999 David A. Hinds. 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.
+
+======================================================================*/
+
+#define __NO_VERSION__
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/string.h>
+#include <linux/errno.h>
+#include <linux/malloc.h>
+#include <linux/mm.h>
+#include <linux/sched.h>
+#include <linux/timer.h>
+
+#define IN_CARD_SERVICES
+#include <pcmcia/cs_types.h>
+#include <pcmcia/ss.h>
+#include <pcmcia/cs.h>
+#include <pcmcia/bulkmem.h>
+#include <pcmcia/cistpl.h>
+#include "cs_internal.h"
+
+/*======================================================================
+
+ This function handles submitting an MTD request, and retrying
+ requests when an MTD is busy.
+
+ An MTD request should never block.
+
+======================================================================*/
+
+static int do_mtd_request(memory_handle_t handle, mtd_request_t *req,
+ caddr_t buf)
+{
+ int ret, tries;
+ client_t *mtd;
+ socket_info_t *s;
+
+ mtd = handle->mtd;
+ if (mtd == NULL)
+ return CS_GENERAL_FAILURE;
+ s = SOCKET(mtd);
+ for (tries = 0; tries < 100; tries++) {
+ mtd->event_callback_args.mtdrequest = req;
+ mtd->event_callback_args.buffer = buf;
+ ret = EVENT(mtd, CS_EVENT_MTD_REQUEST, CS_EVENT_PRI_LOW);
+ if (ret != CS_BUSY)
+ break;
+ switch (req->Status) {
+ case MTD_WAITREQ:
+ /* Not that we should ever need this... */
+ interruptible_sleep_on_timeout(&mtd->mtd_req, HZ);
+ break;
+ case MTD_WAITTIMER:
+ case MTD_WAITRDY:
+ interruptible_sleep_on_timeout(&mtd->mtd_req, req->Timeout*HZ/1000);
+ req->Function |= MTD_REQ_TIMEOUT;
+ break;
+ case MTD_WAITPOWER:
+ interruptible_sleep_on(&mtd->mtd_req);
+ break;
+ }
+ if (signal_pending(current))
+ printk(KERN_NOTICE "cs: do_mtd_request interrupted!\n");
+ }
+ if (tries == 20) {
+ printk(KERN_NOTICE "cs: MTD request timed out!\n");
+ ret = CS_GENERAL_FAILURE;
+ }
+ wake_up_interruptible(&mtd->mtd_req);
+ retry_erase_list(&mtd->erase_busy, 0);
+ return ret;
+} /* do_mtd_request */
+
+/*======================================================================
+
+ This stuff is all for handling asynchronous erase requests. It
+ is complicated because all the retry stuff has to be dealt with
+ in timer interrupts or in the card status event handler.
+
+======================================================================*/
+
+static void insert_queue(erase_busy_t *head, erase_busy_t *entry)
+{
+ DEBUG(2, "cs: adding 0x%p to queue 0x%p\n", entry, head);
+ entry->next = head;
+ entry->prev = head->prev;
+ head->prev->next = entry;
+ head->prev = entry;
+}
+
+static void remove_queue(erase_busy_t *entry)
+{
+ DEBUG(2, "cs: unqueueing 0x%p\n", entry);
+ entry->next->prev = entry->prev;
+ entry->prev->next = entry->next;
+}
+
+static void retry_erase(erase_busy_t *busy, u_int cause)
+{
+ eraseq_entry_t *erase = busy->erase;
+ mtd_request_t req;
+ client_t *mtd;
+ socket_info_t *s;
+ int ret;
+
+ DEBUG(2, "cs: trying erase request 0x%p...\n", busy);
+ if (busy->next)
+ remove_queue(busy);
+ req.Function = MTD_REQ_ERASE | cause;
+ req.TransferLength = erase->Size;
+ req.DestCardOffset = erase->Offset + erase->Handle->info.CardOffset;
+ req.MediaID = erase->Handle->MediaID;
+ mtd = erase->Handle->mtd;
+ s = SOCKET(mtd);
+ mtd->event_callback_args.mtdrequest = &req;
+ ret = EVENT(mtd, CS_EVENT_MTD_REQUEST, CS_EVENT_PRI_LOW);
+ if (ret == CS_BUSY) {
+ DEBUG(2, " Status = %d, requeueing.\n", req.Status);
+ switch (req.Status) {
+ case MTD_WAITREQ:
+ case MTD_WAITPOWER:
+ insert_queue(&mtd->erase_busy, busy);
+ break;
+ case MTD_WAITTIMER:
+ case MTD_WAITRDY:
+ if (req.Status == MTD_WAITRDY)
+ insert_queue(&s->erase_busy, busy);
+ busy->timeout.expires = jiffies + req.Timeout*HZ/1000;
+ add_timer(&busy->timeout);
+ break;
+ }
+ } else {
+ /* update erase queue status */
+ DEBUG(2, " Ret = %d\n", ret);
+ switch (ret) {
+ case CS_SUCCESS:
+ erase->State = ERASE_PASSED; break;
+ case CS_WRITE_PROTECTED:
+ erase->State = ERASE_MEDIA_WRPROT; break;
+ case CS_BAD_OFFSET:
+ erase->State = ERASE_BAD_OFFSET; break;
+ case CS_BAD_SIZE:
+ erase->State = ERASE_BAD_SIZE; break;
+ case CS_NO_CARD:
+ erase->State = ERASE_BAD_SOCKET; break;
+ default:
+ erase->State = ERASE_FAILED; break;
+ }
+ busy->client->event_callback_args.info = erase;
+ EVENT(busy->client, CS_EVENT_ERASE_COMPLETE, CS_EVENT_PRI_LOW);
+ kfree_s(busy, sizeof(*busy));
+ /* Resubmit anything waiting for a request to finish */
+ wake_up_interruptible(&mtd->mtd_req);
+ retry_erase_list(&mtd->erase_busy, 0);
+ }
+} /* retry_erase */
+
+void retry_erase_list(erase_busy_t *list, u_int cause)
+{
+ erase_busy_t tmp = *list;
+
+ DEBUG(2, "cs: rescanning erase queue list 0x%p\n", list);
+ if (list->next == list)
+ return;
+ /* First, truncate the original list */
+ list->prev->next = &tmp;
+ list->next->prev = &tmp;
+ list->prev = list->next = list;
+ tmp.prev->next = &tmp;
+ tmp.next->prev = &tmp;
+
+ /* Now, retry each request, in order. */
+ while (tmp.next != &tmp)
+ retry_erase(tmp.next, cause);
+} /* retry_erase_list */
+
+static void handle_erase_timeout(u_long arg)
+{
+ DEBUG(0, "cs: erase timeout for entry 0x%lx\n", arg);
+ retry_erase((erase_busy_t *)arg, MTD_REQ_TIMEOUT);
+}
+
+static void setup_erase_request(client_handle_t handle, eraseq_entry_t *erase)
+{
+ erase_busy_t *busy;
+ region_info_t *info;
+
+ if (CHECK_REGION(erase->Handle))
+ erase->State = ERASE_BAD_SOCKET;
+ else {
+ info = &erase->Handle->info;
+ if ((erase->Offset >= info->RegionSize) ||
+ (erase->Offset & (info->BlockSize-1)))
+ erase->State = ERASE_BAD_OFFSET;
+ else if ((erase->Offset+erase->Size > info->RegionSize) ||
+ (erase->Size & (info->BlockSize-1)))
+ erase->State = ERASE_BAD_SIZE;
+ else {
+ erase->State = 1;
+ busy = kmalloc(sizeof(erase_busy_t), GFP_KERNEL);
+ busy->erase = erase;
+ busy->client = handle;
+ busy->timeout.prev = busy->timeout.next = NULL;
+ busy->timeout.data = (u_long)busy;
+ busy->timeout.function = &handle_erase_timeout;
+ busy->prev = busy->next = NULL;
+ retry_erase(busy, 0);
+ }
+ }
+} /* setup_erase_request */
+
+/*======================================================================
+
+ MTD helper functions
+
+======================================================================*/
+
+static int mtd_modify_window(window_handle_t win, mtd_mod_win_t *req)
+{
+ if ((win == NULL) || (win->magic != WINDOW_MAGIC))
+ return CS_BAD_HANDLE;
+ win->ctl.flags = MAP_16BIT | MAP_ACTIVE;
+ if (req->Attributes & WIN_USE_WAIT)
+ win->ctl.flags |= MAP_USE_WAIT;
+ if (req->Attributes & WIN_MEMORY_TYPE)
+ win->ctl.flags |= MAP_ATTRIB;
+ win->ctl.speed = req->AccessSpeed;
+ win->ctl.card_start = req->CardOffset;
+ win->sock->ss_entry(win->sock->sock, SS_SetMemMap, &win->ctl);
+ return CS_SUCCESS;
+}
+
+static int mtd_set_vpp(client_handle_t handle, mtd_vpp_req_t *req)
+{
+ socket_info_t *s;
+ if (CHECK_HANDLE(handle))
+ return CS_BAD_HANDLE;
+ if (req->Vpp1 != req->Vpp2)
+ return CS_BAD_VPP;
+ s = SOCKET(handle);
+ s->socket.Vpp = req->Vpp1;
+ if (s->ss_entry(s->sock, SS_SetSocket, &s->socket))
+ return CS_BAD_VPP;
+ return CS_SUCCESS;
+}
+
+static int mtd_rdy_mask(client_handle_t handle, mtd_rdy_req_t *req)
+{
+ socket_info_t *s;
+ if (CHECK_HANDLE(handle))
+ return CS_BAD_HANDLE;
+ s = SOCKET(handle);
+ if (req->Mask & CS_EVENT_READY_CHANGE)
+ s->socket.csc_mask |= SS_READY;
+ else
+ s->socket.csc_mask &= ~SS_READY;
+ if (s->ss_entry(s->sock, SS_SetSocket, &s->socket))
+ return CS_GENERAL_FAILURE;
+ return CS_SUCCESS;
+}
+
+int MTDHelperEntry(int func, void *a1, void *a2)
+{
+ switch (func) {
+ case MTDRequestWindow:
+ return CardServices(RequestWindow, a1, a2, NULL);
+ case MTDReleaseWindow:
+ return CardServices(ReleaseWindow, a1, NULL, NULL);
+ case MTDModifyWindow:
+ return mtd_modify_window(a1, a2); break;
+ case MTDSetVpp:
+ return mtd_set_vpp(a1, a2); break;
+ case MTDRDYMask:
+ return mtd_rdy_mask(a1, a2); break;
+ default:
+ return CS_UNSUPPORTED_FUNCTION; break;
+ }
+} /* MTDHelperEntry */
+
+/*======================================================================
+
+ This stuff is used by Card Services to initialize the table of
+ region info used for subsequent calls to GetFirstRegion and
+ GetNextRegion.
+
+======================================================================*/
+
+static void setup_regions(client_handle_t handle, int attr,
+ memory_handle_t *list)
+{
+ int i, code, has_jedec, has_geo;
+ u_int offset;
+ cistpl_device_t device;
+ cistpl_jedec_t jedec;
+ cistpl_device_geo_t geo;
+ memory_handle_t r;
+
+ DEBUG(1, "cs: setup_regions(0x%p, %d, 0x%p)\n",
+ handle, attr, list);
+
+ code = (attr) ? CISTPL_DEVICE_A : CISTPL_DEVICE;
+ if (read_tuple(handle, code, &device) != CS_SUCCESS)
+ return;
+ code = (attr) ? CISTPL_JEDEC_A : CISTPL_JEDEC_C;
+ has_jedec = (read_tuple(handle, code, &jedec) == CS_SUCCESS);
+ if (has_jedec && (device.ndev != jedec.nid)) {
+#ifdef PCMCIA_DEBUG
+ printk(KERN_DEBUG "cs: Device info does not match JEDEC info.\n");
+#endif
+ has_jedec = 0;
+ }
+ code = (attr) ? CISTPL_DEVICE_GEO_A : CISTPL_DEVICE_GEO;
+ has_geo = (read_tuple(handle, code, &geo) == CS_SUCCESS);
+ if (has_geo && (device.ndev != geo.ngeo)) {
+#ifdef PCMCIA_DEBUG
+ printk(KERN_DEBUG "cs: Device info does not match geometry tuple.\n");
+#endif
+ has_geo = 0;
+ }
+
+ offset = 0;
+ for (i = 0; i < device.ndev; i++) {
+ if ((device.dev[i].type != CISTPL_DTYPE_NULL) &&
+ (device.dev[i].size != 0)) {
+ r = kmalloc(sizeof(*r), GFP_KERNEL);
+ r->region_magic = REGION_MAGIC;
+ r->state = 0;
+ r->dev_info[0] = '\0';
+ r->mtd = NULL;
+ r->info.Attributes = (attr) ? REGION_TYPE_AM : 0;
+ r->info.CardOffset = offset;
+ r->info.RegionSize = device.dev[i].size;
+ r->info.AccessSpeed = device.dev[i].speed;
+ if (has_jedec) {
+ r->info.JedecMfr = jedec.id[i].mfr;
+ r->info.JedecInfo = jedec.id[i].info;
+ } else
+ r->info.JedecMfr = r->info.JedecInfo = 0;
+ if (has_geo) {
+ r->info.BlockSize = geo.geo[i].buswidth *
+ geo.geo[i].erase_block * geo.geo[i].interleave;
+ r->info.PartMultiple =
+ r->info.BlockSize * geo.geo[i].partition;
+ } else
+ r->info.BlockSize = r->info.PartMultiple = 1;
+ r->info.next = *list; *list = r;
+ }
+ offset += device.dev[i].size;
+ }
+} /* setup_regions */
+
+/*======================================================================
+
+ This is tricky. When get_first_region() is called by Driver
+ Services, we initialize the region info table in the socket
+ structure. When it is called by an MTD, we can just scan the
+ table for matching entries.
+
+======================================================================*/
+
+static int match_region(client_handle_t handle, memory_handle_t list,
+ region_info_t *match)
+{
+ while (list != NULL) {
+ if (!(handle->Attributes & INFO_MTD_CLIENT) ||
+ (strcmp(handle->dev_info, list->dev_info) == 0)) {
+ *match = list->info;
+ return CS_SUCCESS;
+ }
+ list = list->info.next;
+ }
+ return CS_NO_MORE_ITEMS;
+} /* match_region */
+
+int get_first_region(client_handle_t handle, region_info_t *rgn)
+{
+ socket_info_t *s = SOCKET(handle);
+ if (CHECK_HANDLE(handle))
+ return CS_BAD_HANDLE;
+
+ if ((handle->Attributes & INFO_MASTER_CLIENT) &&
+ (!(s->state & SOCKET_REGION_INFO))) {
+ setup_regions(handle, 0, &s->c_region);
+ setup_regions(handle, 1, &s->a_region);
+ s->state |= SOCKET_REGION_INFO;
+ }
+
+ if (rgn->Attributes & REGION_TYPE_AM)
+ return match_region(handle, s->a_region, rgn);
+ else
+ return match_region(handle, s->c_region, rgn);
+} /* get_first_region */
+
+int get_next_region(client_handle_t handle, region_info_t *rgn)
+{
+ if (CHECK_HANDLE(handle))
+ return CS_BAD_HANDLE;
+ return match_region(handle, rgn->next, rgn);
+} /* get_next_region */
+
+/*======================================================================
+
+ Connect an MTD with a memory region.
+
+======================================================================*/
+
+int register_mtd(client_handle_t handle, mtd_reg_t *reg)
+{
+ memory_handle_t list;
+ socket_info_t *s;
+
+ if (CHECK_HANDLE(handle))
+ return CS_BAD_HANDLE;
+ s = SOCKET(handle);
+ if (reg->Attributes & REGION_TYPE_AM)
+ list = s->a_region;
+ else
+ list = s->c_region;
+ DEBUG(1, "cs: register_mtd(0x%p, '%s', 0x%x)\n",
+ handle, handle->dev_info, reg->Offset);
+ while (list) {
+ if (list->info.CardOffset == reg->Offset) break;
+ list = list->info.next;
+ }
+ if (list && (list->mtd == NULL) &&
+ (strcmp(handle->dev_info, list->dev_info) == 0)) {
+ list->info.Attributes = reg->Attributes;
+ list->MediaID = reg->MediaID;
+ list->mtd = handle;
+ handle->mtd_count++;
+ return CS_SUCCESS;
+ } else
+ return CS_BAD_OFFSET;
+} /* register_mtd */
+
+/*======================================================================
+
+ Erase queue management functions
+
+======================================================================*/
+
+int register_erase_queue(client_handle_t *handle, eraseq_hdr_t *header)
+{
+ eraseq_t *queue;
+
+ if ((handle == NULL) || CHECK_HANDLE(*handle))
+ return CS_BAD_HANDLE;
+ queue = kmalloc(sizeof(*queue), GFP_KERNEL);
+ queue->eraseq_magic = ERASEQ_MAGIC;
+ queue->handle = *handle;
+ queue->count = header->QueueEntryCnt;
+ queue->entry = header->QueueEntryArray;
+ *handle = (client_handle_t)queue;
+ return CS_SUCCESS;
+} /* register_erase_queue */
+
+int deregister_erase_queue(eraseq_handle_t eraseq)
+{
+ int i;
+ if (CHECK_ERASEQ(eraseq))
+ return CS_BAD_HANDLE;
+ for (i = 0; i < eraseq->count; i++)
+ if (ERASE_IN_PROGRESS(eraseq->entry[i].State)) break;
+ if (i < eraseq->count)
+ return CS_BUSY;
+ eraseq->eraseq_magic = 0;
+ kfree_s(eraseq, sizeof(*eraseq));
+ return CS_SUCCESS;
+} /* deregister_erase_queue */
+
+int check_erase_queue(eraseq_handle_t eraseq)
+{
+ int i;
+ if (CHECK_ERASEQ(eraseq))
+ return CS_BAD_HANDLE;
+ for (i = 0; i < eraseq->count; i++)
+ if (eraseq->entry[i].State == ERASE_QUEUED)
+ setup_erase_request(eraseq->handle, &eraseq->entry[i]);
+ return CS_SUCCESS;
+} /* check_erase_queue */
+
+/*======================================================================
+
+ Look up the memory region matching the request, and return a
+ memory handle.
+
+======================================================================*/
+
+int open_memory(client_handle_t *handle, open_mem_t *open)
+{
+ socket_info_t *s;
+ memory_handle_t region;
+
+ if ((handle == NULL) || CHECK_HANDLE(*handle))
+ return CS_BAD_HANDLE;
+ s = SOCKET(*handle);
+ if (open->Attributes & MEMORY_TYPE_AM)
+ region = s->a_region;
+ else
+ region = s->c_region;
+ while (region) {
+ if (region->info.CardOffset == open->Offset) break;
+ region = region->info.next;
+ }
+ if (region && region->mtd) {
+ *handle = (client_handle_t)region;
+ DEBUG(1, "cs: open_memory(0x%p, 0x%x) = 0x%p\n",
+ handle, open->Offset, region);
+ return CS_SUCCESS;
+ } else
+ return CS_BAD_OFFSET;
+} /* open_memory */
+
+/*======================================================================
+
+ Close a memory handle from an earlier call to OpenMemory.
+
+ For the moment, I don't think this needs to do anything.
+
+======================================================================*/
+
+int close_memory(memory_handle_t handle)
+{
+ DEBUG(1, "cs: close_memory(0x%p)\n", handle);
+ if (CHECK_REGION(handle))
+ return CS_BAD_HANDLE;
+ return CS_SUCCESS;
+} /* close_memory */
+
+/*======================================================================
+
+ Read from a memory device, using a handle previously returned
+ by a call to OpenMemory.
+
+======================================================================*/
+
+int read_memory(memory_handle_t handle, mem_op_t *req, caddr_t buf)
+{
+ mtd_request_t mtd;
+ if (CHECK_REGION(handle))
+ return CS_BAD_HANDLE;
+ if (req->Offset >= handle->info.RegionSize)
+ return CS_BAD_OFFSET;
+ if (req->Offset+req->Count > handle->info.RegionSize)
+ return CS_BAD_SIZE;
+
+ mtd.SrcCardOffset = req->Offset + handle->info.CardOffset;
+ mtd.TransferLength = req->Count;
+ mtd.MediaID = handle->MediaID;
+ mtd.Function = MTD_REQ_READ;
+ if (req->Attributes & MEM_OP_BUFFER_KERNEL)
+ mtd.Function |= MTD_REQ_KERNEL;
+ return do_mtd_request(handle, &mtd, buf);
+} /* read_memory */
+
+/*======================================================================
+
+ Write to a memory device, using a handle previously returned by
+ a call to OpenMemory.
+
+======================================================================*/
+
+int write_memory(memory_handle_t handle, mem_op_t *req, caddr_t buf)
+{
+ mtd_request_t mtd;
+ if (CHECK_REGION(handle))
+ return CS_BAD_HANDLE;
+ if (req->Offset >= handle->info.RegionSize)
+ return CS_BAD_OFFSET;
+ if (req->Offset+req->Count > handle->info.RegionSize)
+ return CS_BAD_SIZE;
+
+ mtd.DestCardOffset = req->Offset + handle->info.CardOffset;
+ mtd.TransferLength = req->Count;
+ mtd.MediaID = handle->MediaID;
+ mtd.Function = MTD_REQ_WRITE;
+ if (req->Attributes & MEM_OP_BUFFER_KERNEL)
+ mtd.Function |= MTD_REQ_KERNEL;
+ return do_mtd_request(handle, &mtd, buf);
+} /* write_memory */
+
+/*======================================================================
+
+ This isn't needed for anything I could think of.
+
+======================================================================*/
+
+int copy_memory(memory_handle_t handle, copy_op_t *req)
+{
+ if (CHECK_REGION(handle))
+ return CS_BAD_HANDLE;
+ return CS_UNSUPPORTED_FUNCTION;
+}
+
diff --git a/drivers/pcmcia/cardbus.c b/drivers/pcmcia/cardbus.c
new file mode 100644
index 000000000..de8b76da3
--- /dev/null
+++ b/drivers/pcmcia/cardbus.c
@@ -0,0 +1,641 @@
+/*======================================================================
+
+ Cardbus device configuration
+
+ cardbus.c 1.59 1999/09/15 15:32:19
+
+ 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 David A. Hinds
+ <dhinds@hyper.stanford.edu>. Portions created by David A. Hinds
+ are Copyright (C) 1999 David A. Hinds. 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.
+
+ These routines handle allocating resources for Cardbus cards, as
+ well as setting up and shutting down Cardbus sockets. They are
+ called from cs.c in response to Request/ReleaseConfiguration and
+ Request/ReleaseIO calls.
+
+======================================================================*/
+
+#define __NO_VERSION__
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/config.h>
+#include <linux/string.h>
+#include <linux/malloc.h>
+#include <linux/mm.h>
+#include <linux/pci.h>
+#include <linux/ioport.h>
+#include <asm/irq.h>
+#include <asm/io.h>
+
+#ifndef PCMCIA_DEBUG
+#define PCMCIA_DEBUG 1
+#endif
+static int pc_debug = PCMCIA_DEBUG;
+
+#define IN_CARD_SERVICES
+#include <pcmcia/version.h>
+#include <pcmcia/cs_types.h>
+#include <pcmcia/ss.h>
+#include <pcmcia/cs.h>
+#include <pcmcia/bulkmem.h>
+#include <pcmcia/cistpl.h>
+#include "cs_internal.h"
+#include "rsrc_mgr.h"
+
+/*====================================================================*/
+
+#define FIND_FIRST_BIT(n) ((n) - ((n) & ((n)-1)))
+
+#define pci_readb pcibios_read_config_byte
+#define pci_writeb pcibios_write_config_byte
+#define pci_readw pcibios_read_config_word
+#define pci_writew pcibios_write_config_word
+#define pci_readl pcibios_read_config_dword
+#define pci_writel pcibios_write_config_dword
+
+#define CB_BAR(n) (PCI_BASE_ADDRESS_0+(4*(n)))
+#define CB_ROM_BASE 0x0030
+
+/* Offsets in the Expansion ROM Image Header */
+#define ROM_SIGNATURE 0x0000 /* 2 bytes */
+#define ROM_DATA_PTR 0x0018 /* 2 bytes */
+
+/* Offsets in the CardBus PC Card Data Structure */
+#define PCDATA_SIGNATURE 0x0000 /* 4 bytes */
+#define PCDATA_VPD_PTR 0x0008 /* 2 bytes */
+#define PCDATA_LENGTH 0x000a /* 2 bytes */
+#define PCDATA_REVISION 0x000c
+#define PCDATA_IMAGE_SZ 0x0010 /* 2 bytes */
+#define PCDATA_ROM_LEVEL 0x0012 /* 2 bytes */
+#define PCDATA_CODE_TYPE 0x0014
+#define PCDATA_INDICATOR 0x0015
+
+typedef struct cb_config_t {
+ u_int size[7];
+ struct pci_dev dev;
+} cb_config_t;
+
+#define BASE(dev,n) ((dev).resource[n].start)
+#define ROM(dev) ((dev).resource[6].start)
+
+/* There are three classes of bridge maps: IO ports,
+ non-prefetchable memory, and prefetchable memory */
+typedef enum { B_IO, B_M1, B_M2 } bridge_type;
+
+/*=====================================================================
+
+ Expansion ROM's have a special layout, and pointers specify an
+ image number and an offset within that image. check_rom()
+ verifies that the expansion ROM exists and has the standard
+ layout. xlate_rom_addr() converts an image/offset address to an
+ absolute offset from the ROM's base address.
+
+=====================================================================*/
+
+static int check_rom(u_char *b, u_long len)
+{
+ u_int img = 0, ofs = 0, sz;
+ u_short data;
+ DEBUG(0, "ROM image dump:\n");
+ while ((readb(b) == 0x55) && (readb(b+1) == 0xaa)) {
+ data = readb(b+ROM_DATA_PTR) +
+ (readb(b+ROM_DATA_PTR+1) << 8);
+ sz = 512 * (readb(b+data+PCDATA_IMAGE_SZ) +
+ (readb(b+data+PCDATA_IMAGE_SZ+1) << 8));
+ DEBUG(0, " image %d: 0x%06x-0x%06x, signature %c%c%c%c\n",
+ img, ofs, ofs+sz-1,
+ readb(b+data+PCDATA_SIGNATURE),
+ readb(b+data+PCDATA_SIGNATURE+1),
+ readb(b+data+PCDATA_SIGNATURE+2),
+ readb(b+data+PCDATA_SIGNATURE+3));
+ ofs += sz; img++;
+ if ((readb(b+data+PCDATA_INDICATOR) & 0x80) ||
+ (sz == 0) || (ofs >= len)) break;
+ b += sz;
+ }
+ return img;
+}
+
+static u_int xlate_rom_addr(u_char *b, u_int addr)
+{
+ u_int img = 0, ofs = 0, sz;
+ u_short data;
+ while ((readb(b) == 0x55) && (readb(b+1) == 0xaa)) {
+ if (img == (addr >> 28))
+ return (addr & 0x0fffffff) + ofs;
+ data = readb(b+ROM_DATA_PTR) + (readb(b+ROM_DATA_PTR+1) << 8);
+ sz = 512 * (readb(b+data+PCDATA_IMAGE_SZ) +
+ (readb(b+data+PCDATA_IMAGE_SZ+1) << 8));
+ if ((sz == 0) || (readb(b+data+PCDATA_INDICATOR) & 0x80))
+ break;
+ b += sz; ofs += sz; img++;
+ }
+ return 0;
+}
+
+/*=====================================================================
+
+ These are similar to setup_cis_mem and release_cis_mem for 16-bit
+ cards. The "result" that is used externally is the cb_cis_virt
+ pointer in the socket_info_t structure.
+
+=====================================================================*/
+
+int cb_setup_cis_mem(socket_info_t *s, int space)
+{
+ cb_bridge_map *m = &s->cb_cis_map;
+ u_long base = 0;
+ u_int sz, br;
+
+ if (space == s->cb_cis_space)
+ return CS_SUCCESS;
+ else if (s->cb_cis_space != 0)
+ cb_release_cis_mem(s);
+ DEBUG(1, "cs: cb_setup_cis_mem(space %d)\n", space);
+ /* If socket is configured, then use existing memory mapping */
+ if (s->lock_count) {
+ s->cb_cis_virt =
+ ioremap(BASE(s->cb_config[0].dev, space-1),
+ s->cb_config[0].size[space-1] & ~3);
+ s->cb_cis_space = space;
+ return CS_SUCCESS;
+ }
+
+ /* Not configured? Then set up temporary map */
+ br = (space == 7) ? CB_ROM_BASE : CB_BAR(space-1);
+ pci_writel(s->cap.cardbus, 0, br, 0xffffffff);
+ pci_readl(s->cap.cardbus, 0, br, &sz);
+ sz &= PCI_BASE_ADDRESS_MEM_MASK;
+ sz = FIND_FIRST_BIT(sz);
+ if (sz < PAGE_SIZE) sz = PAGE_SIZE;
+ if (find_mem_region(&base, sz, "cb_enabler", sz, 0) != 0) {
+ printk(KERN_NOTICE "cs: could not allocate %dK memory for"
+ " CardBus socket %d\n", sz/1024, s->sock);
+ return CS_OUT_OF_RESOURCE;
+ }
+ s->cb_cis_space = space;
+ s->cb_cis_virt = ioremap(base, sz);
+ DEBUG(1, " phys 0x%08lx-0x%08lx, virt 0x%08lx\n",
+ base, base+sz-1, (u_long)s->cb_cis_virt);
+ pci_writel(s->cap.cardbus, 0, br, base | 1);
+ pci_writeb(s->cap.cardbus, 0, PCI_COMMAND, PCI_COMMAND_MEMORY);
+ m->map = 0; m->flags = MAP_ACTIVE;
+ m->start = base; m->stop = base+sz-1;
+ s->ss_entry(s->sock, SS_SetBridge, m);
+ if ((space == 7) && (check_rom(s->cb_cis_virt, sz) == 0)) {
+ printk(KERN_NOTICE "cs: no valid ROM images found!\n");
+ return CS_READ_FAILURE;
+ }
+ return CS_SUCCESS;
+}
+
+void cb_release_cis_mem(socket_info_t *s)
+{
+ cb_bridge_map *m = &s->cb_cis_map;
+ u_int br;
+ if (s->cb_cis_virt) {
+ DEBUG(1, "cs: cb_release_cis_mem()\n");
+ iounmap(s->cb_cis_virt);
+ s->cb_cis_virt = NULL;
+ s->cb_cis_space = 0;
+ }
+ if (m->start) {
+ /* This is overkill: we probably only need to release the
+ memory region, but the rest should be safe */
+ br = (s->cb_cis_space == 7) ?
+ CB_ROM_BASE : CB_BAR(s->cb_cis_space-1);
+ m->map = 0; m->flags = 0;
+ s->ss_entry(s->sock, SS_SetBridge, m);
+ pci_writeb(s->cap.cardbus, 0, PCI_COMMAND, 0);
+ pci_writel(s->cap.cardbus, 0, br, 0);
+ release_mem_region(m->start, m->stop - m->start + 1);
+ m->start = 0;
+ }
+}
+
+/*=====================================================================
+
+ This is used by the CIS processing code to read CIS information
+ from a CardBus device.
+
+=====================================================================*/
+
+void read_cb_mem(socket_info_t *s, u_char fn, int space,
+ u_int addr, u_int len, void *ptr)
+{
+ DEBUG(3, "cs: read_cb_mem(%d, %#x, %u)\n", space, addr, len);
+ if (space == 0) {
+ if (addr+len > 0x100) goto fail;
+ for (; len; addr++, ptr++, len--)
+ pci_readb(s->cap.cardbus, fn, addr, (u_char *)ptr);
+ } else {
+ if (cb_setup_cis_mem(s, space) != 0) goto fail;
+ if (space == 7) {
+ addr = xlate_rom_addr(s->cb_cis_virt, addr);
+ if (addr == 0) goto fail;
+ }
+ if (addr+len > s->cb_cis_map.stop - s->cb_cis_map.start)
+ goto fail;
+ if (s->cb_cis_virt != NULL)
+ for (; len; addr++, ptr++, len--)
+ *(u_char *)ptr = readb(s->cb_cis_virt+addr);
+ }
+ return;
+ fail:
+ memset(ptr, 0xff, len);
+ return;
+}
+
+/*=====================================================================
+
+ cb_alloc() and cb_free() allocate and free the kernel data
+ structures for a Cardbus device, and handle the lowest level PCI
+ device setup issues.
+
+=====================================================================*/
+
+int cb_alloc(socket_info_t *s)
+{
+ struct pci_dev tmp;
+ u_short vend, v, dev;
+ u_char i, hdr, fn, bus = s->cap.cardbus;
+ cb_config_t *c;
+
+ tmp.bus = s->cap.cb_bus; tmp.devfn = 0;
+ tmp.next = pci_devices; pci_devices = &tmp;
+
+ pci_readw(bus, 0, PCI_VENDOR_ID, &vend);
+ pci_readw(bus, 0, PCI_DEVICE_ID, &dev);
+ printk(KERN_INFO "cs: cb_alloc(bus %d): vendor 0x%04x, "
+ "device 0x%04x\n", bus, vend, dev);
+
+ pci_readb(bus, 0, PCI_HEADER_TYPE, &hdr);
+ if (hdr & 0x80) {
+ /* Count functions */
+ for (fn = 0; fn < 8; fn++) {
+ tmp.devfn = fn;
+ pci_readw(bus, fn, PCI_VENDOR_ID, &v);
+ if (v != vend) break;
+ }
+ } else fn = 1;
+ s->functions = fn;
+
+ c = kmalloc(fn * sizeof(struct cb_config_t), GFP_ATOMIC);
+ if (!c) return CS_OUT_OF_RESOURCE;
+ memset(c, 0, fn * sizeof(struct cb_config_t));
+ s->cb_config = c;
+
+ pci_devices = tmp.next;
+ for (i = 0; i < fn; i++) {
+ c[i].dev.bus = s->cap.cb_bus;
+ c[i].dev.devfn = i;
+ if (i < fn-1) {
+ c[i].dev.sibling = c[i].dev.next = &c[i+1].dev;
+ }
+ }
+ s->cap.cb_bus->devices = &c[0].dev;
+ /* Link into PCI device chain */
+ c[s->functions-1].dev.next = pci_devices;
+ pci_devices = &c[0].dev;
+ for (i = 0; i < fn; i++) {
+ c[i].dev.vendor = vend;
+ pci_readw(bus, i, PCI_DEVICE_ID, &c[i].dev.device);
+ pci_readl(bus, i, PCI_CLASS_REVISION, &c[i].dev.class);
+ c[i].dev.class >>= 8;
+ c[i].dev.hdr_type = hdr;
+#ifdef CONFIG_PROC_FS
+ pci_proc_attach_device(&c[i].dev);
+#endif
+ }
+
+ return CS_SUCCESS;
+}
+
+void cb_free(socket_info_t *s)
+{
+ cb_config_t *c = s->cb_config;
+
+ if (c) {
+ struct pci_dev **p, *q;
+ /* Unlink from PCI device chain */
+ for (p = &pci_devices; *p; p = &((*p)->next))
+ if (*p == &c[0].dev) break;
+ for (q = *p; q; q = q->next) {
+ if (q->bus != (*p)->bus) break;
+#ifdef CONFIG_PROC_FS
+ pci_proc_detach_device(q);
+#endif
+ }
+ if (*p) *p = q;
+ s->cap.cb_bus->devices = NULL;
+ kfree(s->cb_config);
+ s->cb_config = NULL;
+ printk(KERN_INFO "cs: cb_free(bus %d)\n", s->cap.cardbus);
+ }
+}
+
+/*=====================================================================
+
+ cb_config() has the job of allocating all system resources that
+ a Cardbus card requires. Rather than using the CIS (which seems
+ to not always be present), it treats the card as an ordinary PCI
+ device, and probes the base address registers to determine each
+ function's IO and memory space needs.
+
+ It is called from the RequestIO card service.
+
+======================================================================*/
+
+int cb_config(socket_info_t *s)
+{
+ cb_config_t *c = s->cb_config;
+ u_char fn = s->functions;
+ u_char i, j, bus = s->cap.cardbus, *name;
+ u_int sz, align, m, mask[3], num[3], base[3];
+ int irq, try, ret;
+
+ printk(KERN_INFO "cs: cb_config(bus %d)\n", s->cap.cardbus);
+
+ /* Determine IO and memory space needs */
+ num[B_IO] = num[B_M1] = num[B_M2] = 0;
+ mask[B_IO] = mask[B_M1] = mask[B_M2] = 0;
+ for (i = 0; i < fn; i++) {
+ for (j = 0; j < 6; j++) {
+ pci_writel(bus, i, CB_BAR(j), 0xffffffff);
+ pci_readl(bus, i, CB_BAR(j), &sz);
+ if (sz == 0) continue;
+ if (sz & PCI_BASE_ADDRESS_SPACE) {
+ m = B_IO;
+ sz &= PCI_BASE_ADDRESS_IO_MASK;
+ } else {
+ m = (sz & PCI_BASE_ADDRESS_MEM_PREFETCH) ? B_M2 : B_M1;
+ sz &= PCI_BASE_ADDRESS_MEM_MASK;
+ }
+ sz = FIND_FIRST_BIT(sz);
+ c[i].size[j] = sz | m;
+ if (m && (sz < PAGE_SIZE)) sz = PAGE_SIZE;
+ num[m] += sz; mask[m] |= sz;
+ }
+ pci_writel(bus, i, CB_ROM_BASE, 0xffffffff);
+ pci_readl(bus, i, CB_ROM_BASE, &sz);
+ if (sz != 0) {
+ sz = FIND_FIRST_BIT(sz & ~0x00000001);
+ c[i].size[6] = sz | B_M1;
+ if (sz < PAGE_SIZE) sz = PAGE_SIZE;
+ num[B_M1] += sz; mask[B_M1] |= sz;
+ }
+ }
+
+ /* Allocate system resources */
+ name = "cb_enabler";
+ s->io[0].NumPorts = num[B_IO];
+ s->io[0].BasePort = 0;
+ if (num[B_IO]) {
+ if (find_io_region(&s->io[0].BasePort, num[B_IO], name) != 0) {
+ printk(KERN_NOTICE "cs: could not allocate %d IO ports for"
+ " CardBus socket %d\n", num[B_IO], s->sock);
+ goto failed;
+ }
+ base[B_IO] = s->io[0].BasePort + num[B_IO];
+ }
+ s->win[0].size = num[B_M1];
+ s->win[0].base = 0;
+ if (num[B_M1]) {
+ if (find_mem_region(&s->win[0].base, num[B_M1],
+ name, num[B_M1], 0) != 0) {
+ printk(KERN_NOTICE "cs: could not allocate %dK memory for"
+ " CardBus socket %d\n", num[B_M1]/1024, s->sock);
+ goto failed;
+ }
+ base[B_M1] = s->win[0].base + num[B_M1];
+ }
+ s->win[1].size = num[B_M2];
+ s->win[1].base = 0;
+ if (num[B_M2]) {
+ if (find_mem_region(&s->win[1].base, num[B_M2],
+ name, num[B_M2], 0) != 0) {
+ printk(KERN_NOTICE "cs: could not allocate %dK memory for"
+ " CardBus socket %d\n", num[B_M2]/1024, s->sock);
+ goto failed;
+ }
+ base[B_M2] = s->win[1].base + num[B_M2];
+ }
+
+ /* Set up base address registers */
+ while (mask[B_IO] | mask[B_M1] | mask[B_M2]) {
+ num[B_IO] = FIND_FIRST_BIT(mask[B_IO]); mask[B_IO] -= num[B_IO];
+ num[B_M1] = FIND_FIRST_BIT(mask[B_M1]); mask[B_M1] -= num[B_M1];
+ num[B_M2] = FIND_FIRST_BIT(mask[B_M2]); mask[B_M2] -= num[B_M2];
+ for (i = 0; i < fn; i++) {
+ for (j = 0; j < 7; j++) {
+ sz = c[i].size[j];
+ m = sz & 3; sz &= ~3;
+ align = (m && (sz < PAGE_SIZE)) ? PAGE_SIZE : sz;
+ if (sz && (align == num[m])) {
+ base[m] -= align;
+ if (j < 6)
+ printk(KERN_INFO " fn %d bar %d: ", i, j+1);
+ else
+ printk(KERN_INFO " fn %d rom: ", i);
+ printk("%s 0x%x-0x%x\n", (m) ? "mem" : "io",
+ base[m], base[m]+sz-1);
+ BASE(c[i].dev, j) = base[m];
+ }
+ }
+ }
+ }
+
+ /* Allocate interrupt if needed */
+ s->irq.AssignedIRQ = irq = 0; ret = -1;
+ for (i = 0; i < fn; i++) {
+ pci_readb(bus, i, PCI_INTERRUPT_PIN, &j);
+ if (j == 0) continue;
+ if (irq == 0) {
+ if (s->cap.irq_mask & (1 << s->cap.pci_irq)) {
+ irq = s->cap.pci_irq;
+ ret = 0;
+ }
+#ifdef CONFIG_ISA
+ else
+ for (try = 0; try < 2; try++) {
+ for (irq = 0; irq < 32; irq++)
+ if ((s->cap.irq_mask >> irq) & 1) {
+ ret = try_irq(IRQ_TYPE_EXCLUSIVE, irq, try);
+ if (ret == 0) break;
+ }
+ if (ret == 0) break;
+ }
+ if (ret != 0) {
+ printk(KERN_NOTICE "cs: could not allocate interrupt"
+ " for CardBus socket %d\n", s->sock);
+ goto failed;
+ }
+#endif
+ s->irq.AssignedIRQ = irq;
+ }
+ }
+ c[0].dev.irq = irq;
+
+ return CS_SUCCESS;
+
+failed:
+ cb_release(s);
+ return CS_OUT_OF_RESOURCE;
+}
+
+/*======================================================================
+
+ cb_release() releases all the system resources (IO and memory
+ space, and interrupt) committed for a Cardbus card by a prior call
+ to cb_config().
+
+ It is called from the ReleaseIO() service.
+
+======================================================================*/
+
+void cb_release(socket_info_t *s)
+{
+ cb_config_t *c = s->cb_config;
+
+ DEBUG(0, "cs: cb_release(bus %d)\n", s->cap.cardbus);
+
+ if (s->win[0].size > 0)
+ release_mem_region(s->win[0].base, s->win[0].size);
+ if (s->win[1].size > 0)
+ release_mem_region(s->win[1].base, s->win[1].size);
+ if (s->io[0].NumPorts > 0)
+ release_region(s->io[0].BasePort, s->io[0].NumPorts);
+ s->io[0].NumPorts = 0;
+#ifdef CONFIG_ISA
+ if ((c[0].dev.irq != 0) && (c[0].dev.irq != s->cap.pci_irq))
+ undo_irq(IRQ_TYPE_EXCLUSIVE, c[0].dev.irq);
+#endif
+}
+
+/*=====================================================================
+
+ cb_enable() has the job of configuring a socket for a Cardbus
+ card, and initializing the card's PCI configuration registers.
+
+ It first sets up the Cardbus bridge windows, for IO and memory
+ accesses. Then, it initializes each card function's base address
+ registers, interrupt line register, and command register.
+
+ It is called as part of the RequestConfiguration card service.
+ It should be called after a previous call to cb_config() (via the
+ RequestIO service).
+
+======================================================================*/
+
+void cb_enable(socket_info_t *s)
+{
+ u_char i, j, bus = s->cap.cardbus;
+ cb_config_t *c = s->cb_config;
+
+ DEBUG(0, "cs: cb_enable(bus %d)\n", bus);
+
+ /* Configure bridge */
+ if (s->cb_cis_map.start)
+ cb_release_cis_mem(s);
+ for (i = 0; i < 3; i++) {
+ cb_bridge_map m;
+ switch (i) {
+ case B_IO:
+ m.map = 0; m.flags = MAP_IOSPACE | MAP_ACTIVE;
+ m.start = s->io[0].BasePort;
+ m.stop = m.start + s->io[0].NumPorts - 1;
+ break;
+ case B_M1:
+ m.map = 0; m.flags = MAP_ACTIVE;
+ m.start = s->win[0].base;
+ m.stop = m.start + s->win[0].size - 1;
+ break;
+ case B_M2:
+ m.map = 1; m.flags = MAP_PREFETCH | MAP_ACTIVE;
+ m.start = s->win[1].base;
+ m.stop = m.start + s->win[1].size - 1;
+ break;
+ }
+ if (m.start == 0) continue;
+ DEBUG(0, " bridge %s map %d (flags 0x%x): 0x%x-0x%x\n",
+ (m.flags & MAP_IOSPACE) ? "io" : "mem",
+ m.map, m.flags, m.start, m.stop);
+ s->ss_entry(s->sock, SS_SetBridge, &m);
+ }
+
+ /* Set up base address registers */
+ for (i = 0; i < s->functions; i++) {
+ for (j = 0; j < 6; j++) {
+ if (BASE(c[i].dev, j) != 0)
+ pci_writel(bus, i, CB_BAR(j), BASE(c[i].dev, j));
+ }
+ if (ROM(c[i].dev) != 0)
+ pci_writel(bus, i, CB_ROM_BASE, ROM(c[i].dev) | 1);
+ }
+
+ /* Set up PCI interrupt and command registers */
+ for (i = 0; i < s->functions; i++) {
+ pci_writeb(bus, i, PCI_COMMAND, PCI_COMMAND_MASTER |
+ PCI_COMMAND_IO | PCI_COMMAND_MEMORY);
+ pci_writeb(bus, i, PCI_CACHE_LINE_SIZE, 8);
+ }
+
+ if (s->irq.AssignedIRQ) {
+ for (i = 0; i < s->functions; i++)
+ pci_writeb(bus, i, PCI_INTERRUPT_LINE,
+ s->irq.AssignedIRQ);
+ s->socket.io_irq = s->irq.AssignedIRQ;
+ s->ss_entry(s->sock, SS_SetSocket, &s->socket);
+ }
+}
+
+/*======================================================================
+
+ cb_disable() unconfigures a Cardbus card previously set up by
+ cb_enable().
+
+ It is called from the ReleaseConfiguration service.
+
+======================================================================*/
+
+void cb_disable(socket_info_t *s)
+{
+ u_char i;
+ cb_bridge_map m = { 0, 0, 0, 0xffff };
+
+ DEBUG(0, "cs: cb_disable(bus %d)\n", s->cap.cardbus);
+
+ /* Turn off bridge windows */
+ if (s->cb_cis_map.start)
+ cb_release_cis_mem(s);
+ for (i = 0; i < 3; i++) {
+ switch (i) {
+ case B_IO: m.map = 0; m.flags = MAP_IOSPACE; break;
+ case B_M1: m.map = m.flags = 0; break;
+ case B_M2: m.map = 1; m.flags = 0; break;
+ }
+ s->ss_entry(s->sock, SS_SetBridge, &m);
+ }
+}
diff --git a/drivers/pcmcia/cb_enabler.c b/drivers/pcmcia/cb_enabler.c
new file mode 100644
index 000000000..b0e75ceec
--- /dev/null
+++ b/drivers/pcmcia/cb_enabler.c
@@ -0,0 +1,397 @@
+/*======================================================================
+
+ Cardbus device enabler
+
+ cb_enabler.c 1.23 1999/09/15 15:32:19
+
+ 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 David A. Hinds
+ <dhinds@hyper.stanford.edu>. Portions created by David A. Hinds
+ are Copyright (C) 1999 David A. Hinds. 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.
+
+ The general idea:
+
+ A client driver registers using register_driver(). This module
+ then creates a Card Services pseudo-client and registers it, and
+ configures the socket if this is the first client. It then
+ invokes the appropriate PCI client routines in response to Card
+ Services events.
+
+======================================================================*/
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/malloc.h>
+#include <linux/string.h>
+#include <linux/timer.h>
+
+#include <pcmcia/version.h>
+#include <pcmcia/cs_types.h>
+#include <pcmcia/cs.h>
+#include <pcmcia/cistpl.h>
+#include <pcmcia/ds.h>
+
+#ifdef PCMCIA_DEBUG
+static int pc_debug = PCMCIA_DEBUG;
+MODULE_PARM(pc_debug, "i");
+#define DEBUG(n, args...) if (pc_debug>(n)) printk(KERN_DEBUG args)
+static char *version =
+"cb_enabler.c 1.23 1999/09/15 15:32:19 (David Hinds)";
+#else
+#define DEBUG(n, args...) do { } while (0)
+#endif
+
+/*====================================================================*/
+
+/* Parameters that can be set with 'insmod' */
+
+/*====================================================================*/
+
+typedef struct driver_info_t {
+ dev_link_t *(*attach)(void);
+ dev_info_t dev_info;
+ driver_operations *ops;
+ dev_link_t *dev_list;
+} driver_info_t;
+
+static dev_link_t *cb_attach(int n);
+#define MK_ENTRY(fn, n) \
+static dev_link_t *fn(void) { return cb_attach(n); }
+
+#define MAX_DRIVER 4
+
+MK_ENTRY(attach_0, 0);
+MK_ENTRY(attach_1, 1);
+MK_ENTRY(attach_2, 2);
+MK_ENTRY(attach_3, 3);
+
+static driver_info_t driver[4] = {
+ { attach_0 }, { attach_1 }, { attach_2 }, { attach_3 }
+};
+
+typedef struct bus_info_t {
+ u_char bus;
+ int flags, ncfg, nuse;
+ dev_link_t *owner;
+} bus_info_t;
+
+#define DID_REQUEST 1
+#define DID_CONFIG 2
+
+static void cb_release(u_long arg);
+static int cb_event(event_t event, int priority,
+ event_callback_args_t *args);
+
+static void cb_detach(dev_link_t *);
+
+static bus_info_t bus_table[MAX_DRIVER];
+
+/*====================================================================*/
+
+static void cs_error(client_handle_t handle, int func, int ret)
+{
+ error_info_t err = { func, ret };
+ CardServices(ReportError, handle, &err);
+}
+
+/*====================================================================*/
+
+struct dev_link_t *cb_attach(int n)
+{
+ client_reg_t client_reg;
+ dev_link_t *link;
+ int ret;
+
+ DEBUG(0, "cb_attach(%d)\n", n);
+
+ MOD_INC_USE_COUNT;
+ link = kmalloc(sizeof(struct dev_link_t), GFP_KERNEL);
+ memset(link, 0, sizeof(struct dev_link_t));
+
+ link->conf.IntType = INT_CARDBUS;
+ link->conf.Vcc = 33;
+
+ /* Insert into instance chain for this driver */
+ link->priv = &driver[n];
+ link->next = driver[n].dev_list;
+ driver[n].dev_list = link;
+
+ /* Register with Card Services */
+ client_reg.dev_info = &driver[n].dev_info;
+ client_reg.Attributes = INFO_IO_CLIENT | INFO_CARD_SHARE;
+ client_reg.event_handler = &cb_event;
+ client_reg.EventMask =
+ CS_EVENT_RESET_REQUEST | CS_EVENT_CARD_RESET |
+ CS_EVENT_CARD_INSERTION | CS_EVENT_CARD_REMOVAL |
+ CS_EVENT_PM_SUSPEND | CS_EVENT_PM_RESUME;
+ client_reg.Version = 0x0210;
+ client_reg.event_callback_args.client_data = link;
+ ret = CardServices(RegisterClient, &link->handle, &client_reg);
+ if (ret != 0) {
+ cs_error(link->handle, RegisterClient, ret);
+ cb_detach(link);
+ return NULL;
+ }
+ return link;
+}
+
+/*====================================================================*/
+
+static void cb_detach(dev_link_t *link)
+{
+ driver_info_t *dev = link->priv;
+ dev_link_t **linkp;
+ bus_info_t *b = (void *)link->win;
+
+ DEBUG(0, "cb_detach(0x%p)\n", link);
+
+ /* Locate device structure */
+ for (linkp = &dev->dev_list; *linkp; linkp = &(*linkp)->next)
+ if (*linkp == link) break;
+ if (*linkp == NULL)
+ return;
+
+ if (link->state & DEV_CONFIG)
+ cb_release((u_long)link);
+
+ /* Don't drop Card Services connection if we are the bus owner */
+ if ((b->flags != 0) && (link == b->owner)) {
+ link->state |= DEV_STALE_LINK;
+ return;
+ }
+
+ if (link->handle)
+ CardServices(DeregisterClient, link->handle);
+
+ *linkp = link->next;
+ kfree_s(link, sizeof(struct dev_link_t));
+ MOD_DEC_USE_COUNT;
+}
+
+/*====================================================================*/
+
+static void cb_config(dev_link_t *link)
+{
+ client_handle_t handle = link->handle;
+ driver_info_t *drv = link->priv;
+ dev_locator_t loc;
+ bus_info_t *b;
+ config_info_t config;
+ u_char bus, devfn;
+ int i;
+
+ DEBUG(0, "cb_config(0x%p)\n", link);
+ link->state |= DEV_CONFIG;
+
+ /* Get PCI bus info */
+ CardServices(GetConfigurationInfo, handle, &config);
+ bus = config.Option; devfn = config.Function;
+
+ /* Is this a new bus? */
+ for (i = 0; i < MAX_DRIVER; i++)
+ if (bus == bus_table[i].bus) break;
+ if (i == MAX_DRIVER) {
+ for (i = 0; i < MAX_DRIVER; i++)
+ if (bus_table[i].bus == 0) break;
+ b = &bus_table[i]; link->win = (void *)b;
+ b->bus = bus;
+ b->flags = 0;
+ b->ncfg = b->nuse = 1;
+
+ /* Special hook: CS know what to do... */
+ i = CardServices(RequestIO, handle, NULL);
+ if (i != CS_SUCCESS) {
+ cs_error(handle, RequestIO, i);
+ return;
+ }
+ b->flags |= DID_REQUEST;
+ b->owner = link;
+ i = CardServices(RequestConfiguration, handle, &link->conf);
+ if (i != CS_SUCCESS) {
+ cs_error(handle, RequestConfiguration, i);
+ return;
+ }
+ b->flags |= DID_CONFIG;
+ } else {
+ b = &bus_table[i]; link->win = (void *)b;
+ if (b->flags & DID_CONFIG) {
+ b->ncfg++; b->nuse++;
+ }
+ }
+ loc.bus = LOC_PCI;
+ loc.b.pci.bus = bus;
+ loc.b.pci.devfn = devfn;
+ link->dev = drv->ops->attach(&loc);
+
+ link->state &= ~DEV_CONFIG_PENDING;
+}
+
+/*====================================================================*/
+
+static void cb_release(u_long arg)
+{
+ dev_link_t *link = (dev_link_t *)arg;
+ driver_info_t *drv = link->priv;
+ bus_info_t *b = (void *)link->win;
+
+ DEBUG(0, "cb_release(0x%p)\n", link);
+
+ if (link->dev != NULL) {
+ drv->ops->detach(link->dev);
+ link->dev = NULL;
+ }
+ if (link->state & DEV_CONFIG) {
+ /* If we're suspended, config was already released */
+ if (link->state & DEV_SUSPEND)
+ b->flags &= ~DID_CONFIG;
+ else if ((b->flags & DID_CONFIG) && (--b->ncfg == 0)) {
+ CardServices(ReleaseConfiguration, b->owner->handle,
+ &b->owner->conf);
+ b->flags &= ~DID_CONFIG;
+ }
+ if ((b->flags & DID_REQUEST) && (--b->nuse == 0)) {
+ CardServices(ReleaseIO, b->owner->handle, NULL);
+ b->flags &= ~DID_REQUEST;
+ }
+ if (b->flags == 0) {
+ if (b->owner && (b->owner->state & DEV_STALE_LINK))
+ cb_detach(b->owner);
+ b->bus = 0; b->owner = NULL;
+ }
+ }
+ link->state &= ~DEV_CONFIG;
+}
+
+/*====================================================================*/
+
+static int cb_event(event_t event, int priority,
+ event_callback_args_t *args)
+{
+ dev_link_t *link = args->client_data;
+ driver_info_t *drv = link->priv;
+ bus_info_t *b = (void *)link->win;
+
+ DEBUG(0, "cb_event(0x%06x)\n", event);
+
+ switch (event) {
+ case CS_EVENT_CARD_REMOVAL:
+ link->state &= ~DEV_PRESENT;
+ if (link->state & DEV_CONFIG)
+ cb_release((u_long)link);
+ break;
+ case CS_EVENT_CARD_INSERTION:
+ link->state |= DEV_PRESENT | DEV_CONFIG_PENDING;
+ cb_config(link);
+ break;
+ case CS_EVENT_PM_SUSPEND:
+ link->state |= DEV_SUSPEND;
+ /* Fall through... */
+ case CS_EVENT_RESET_PHYSICAL:
+ if (link->state & DEV_CONFIG) {
+ if (drv->ops->suspend != NULL)
+ drv->ops->suspend(link->dev);
+ b->ncfg--;
+ if (b->ncfg == 0)
+ CardServices(ReleaseConfiguration, link->handle,
+ &link->conf);
+ }
+ break;
+ case CS_EVENT_PM_RESUME:
+ link->state &= ~DEV_SUSPEND;
+ /* Fall through... */
+ case CS_EVENT_CARD_RESET:
+ if (link->state & DEV_CONFIG) {
+ b->ncfg++;
+ if (b->ncfg == 1)
+ CardServices(RequestConfiguration, link->handle,
+ &link->conf);
+ if (drv->ops->resume != NULL)
+ drv->ops->resume(link->dev);
+ }
+ break;
+ }
+ return 0;
+}
+
+/*====================================================================*/
+
+int register_driver(struct driver_operations *ops)
+{
+ int i;
+
+ DEBUG(0, "register_driver('%s')\n", ops->name);
+
+ for (i = 0; i < MAX_DRIVER; i++)
+ if (driver[i].ops == NULL) break;
+ if (i == MAX_DRIVER)
+ return -1;
+
+ MOD_INC_USE_COUNT;
+ driver[i].ops = ops;
+ strcpy(driver[i].dev_info, ops->name);
+ register_pccard_driver(&driver[i].dev_info, driver[i].attach,
+ &cb_detach);
+ return 0;
+}
+
+void unregister_driver(struct driver_operations *ops)
+{
+ int i;
+
+ DEBUG(0, "unregister_driver('%s')\n", ops->name);
+ for (i = 0; i < MAX_DRIVER; i++)
+ if (driver[i].ops == ops) break;
+ if (i < MAX_DRIVER) {
+ unregister_pccard_driver(&driver[i].dev_info);
+ driver[i].ops = NULL;
+ MOD_DEC_USE_COUNT;
+ }
+}
+
+/*====================================================================*/
+
+static int __init init_cb_enabler(void)
+{
+ servinfo_t serv;
+ DEBUG(0, "%s\n", version);
+ CardServices(GetCardServicesInfo, &serv);
+ if (serv.Revision != CS_RELEASE_CODE) {
+ printk(KERN_NOTICE "cb_enabler: Card Services release "
+ "does not match!\n");
+ return -1;
+ }
+ return 0;
+}
+
+static void __exit exit_cb_enabler(void)
+{
+ DEBUG(0, "cb_enabler: unloading\n");
+}
+
+module_init(init_cb_enabler);
+module_exit(exit_cb_enabler);
+
+/*====================================================================*/
+
diff --git a/drivers/pcmcia/cirrus.h b/drivers/pcmcia/cirrus.h
new file mode 100644
index 000000000..43527010a
--- /dev/null
+++ b/drivers/pcmcia/cirrus.h
@@ -0,0 +1,157 @@
+/*
+ * cirrus.h 1.3 1999/08/28 04:01:46
+ *
+ * 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 David A. Hinds
+ * <dhinds@hyper.stanford.edu>. Portions created by David A. Hinds
+ * are Copyright (C) 1999 David A. Hinds. 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.
+ */
+
+#ifndef _LINUX_CIRRUS_H
+#define _LINUX_CIRRUS_H
+
+#ifndef PCI_VENDOR_ID_CIRRUS
+#define PCI_VENDOR_ID_CIRRUS 0x1013
+#endif
+#ifndef PCI_DEVICE_ID_CIRRUS_6729
+#define PCI_DEVICE_ID_CIRRUS_6729 0x1100
+#endif
+#ifndef PCI_DEVICE_ID_CIRRUS_6832
+#define PCI_DEVICE_ID_CIRRUS_6832 0x1110
+#endif
+
+#define PD67_MISC_CTL_1 0x16 /* Misc control 1 */
+#define PD67_FIFO_CTL 0x17 /* FIFO control */
+#define PD67_MISC_CTL_2 0x1E /* Misc control 2 */
+#define PD67_CHIP_INFO 0x1f /* Chip information */
+#define PD67_ATA_CTL 0x026 /* 6730: ATA control */
+#define PD67_EXT_INDEX 0x2e /* Extension index */
+#define PD67_EXT_DATA 0x2f /* Extension data */
+
+/* PD6722 extension registers -- indexed in PD67_EXT_INDEX */
+#define PD67_DATA_MASK0 0x01 /* Data mask 0 */
+#define PD67_DATA_MASK1 0x02 /* Data mask 1 */
+#define PD67_DMA_CTL 0x03 /* DMA control */
+
+/* PD6730 extension registers -- indexed in PD67_EXT_INDEX */
+#define PD67_EXT_CTL_1 0x03 /* Extension control 1 */
+#define PD67_MEM_PAGE(n) ((n)+5) /* PCI window bits 31:24 */
+#define PD67_EXTERN_DATA 0x0a
+#define PD67_MISC_CTL_3 0x25
+#define PD67_SMB_PWR_CTL 0x26
+
+/* I/O window address offset */
+#define PD67_IO_OFF(w) (0x36+((w)<<1))
+
+/* Timing register sets */
+#define PD67_TIME_SETUP(n) (0x3a + 3*(n))
+#define PD67_TIME_CMD(n) (0x3b + 3*(n))
+#define PD67_TIME_RECOV(n) (0x3c + 3*(n))
+
+/* Flags for PD67_MISC_CTL_1 */
+#define PD67_MC1_5V_DET 0x01 /* 5v detect */
+#define PD67_MC1_MEDIA_ENA 0x01 /* 6730: Multimedia enable */
+#define PD67_MC1_VCC_3V 0x02 /* 3.3v Vcc */
+#define PD67_MC1_PULSE_MGMT 0x04
+#define PD67_MC1_PULSE_IRQ 0x08
+#define PD67_MC1_SPKR_ENA 0x10
+#define PD67_MC1_INPACK_ENA 0x80
+
+/* Flags for PD67_FIFO_CTL */
+#define PD67_FIFO_EMPTY 0x80
+
+/* Flags for PD67_MISC_CTL_2 */
+#define PD67_MC2_FREQ_BYPASS 0x01
+#define PD67_MC2_DYNAMIC_MODE 0x02
+#define PD67_MC2_SUSPEND 0x04
+#define PD67_MC2_5V_CORE 0x08
+#define PD67_MC2_LED_ENA 0x10 /* IRQ 12 is LED enable */
+#define PD67_MC2_FAST_PCI 0x10 /* 6729: PCI bus > 25 MHz */
+#define PD67_MC2_3STATE_BIT7 0x20 /* Floppy change bit */
+#define PD67_MC2_DMA_MODE 0x40
+#define PD67_MC2_IRQ15_RI 0x80 /* IRQ 15 is ring enable */
+
+/* Flags for PD67_CHIP_INFO */
+#define PD67_INFO_SLOTS 0x20 /* 0 = 1 slot, 1 = 2 slots */
+#define PD67_INFO_CHIP_ID 0xc0
+#define PD67_INFO_REV 0x1c
+
+/* Fields in PD67_TIME_* registers */
+#define PD67_TIME_SCALE 0xc0
+#define PD67_TIME_SCALE_1 0x00
+#define PD67_TIME_SCALE_16 0x40
+#define PD67_TIME_SCALE_256 0x80
+#define PD67_TIME_SCALE_4096 0xc0
+#define PD67_TIME_MULT 0x3f
+
+/* Fields in PD67_DMA_CTL */
+#define PD67_DMA_MODE 0xc0
+#define PD67_DMA_OFF 0x00
+#define PD67_DMA_DREQ_INPACK 0x40
+#define PD67_DMA_DREQ_WP 0x80
+#define PD67_DMA_DREQ_BVD2 0xc0
+#define PD67_DMA_PULLUP 0x20 /* Disable socket pullups? */
+
+/* Fields in PD67_EXT_CTL_1 */
+#define PD67_EC1_VCC_PWR_LOCK 0x01
+#define PD67_EC1_AUTO_PWR_CLEAR 0x02
+#define PD67_EC1_LED_ENA 0x04
+#define PD67_EC1_INV_CARD_IRQ 0x08
+#define PD67_EC1_INV_MGMT_IRQ 0x10
+#define PD67_EC1_PULLUP_CTL 0x20
+
+/* Fields in PD67_MISC_CTL_3 */
+#define PD67_MC3_IRQ_MASK 0x03
+#define PD67_MC3_IRQ_PCPCI 0x00
+#define PD67_MC3_IRQ_EXTERN 0x01
+#define PD67_MC3_IRQ_PCIWAY 0x02
+#define PD67_MC3_IRQ_PCI 0x03
+#define PD67_MC3_PWR_MASK 0x0c
+#define PD67_MC3_PWR_SERIAL 0x00
+#define PD67_MC3_PWR_TI2202 0x08
+#define PD67_MC3_PWR_SMB 0x0c
+
+/* Register definitions for Cirrus PD6832 PCI-to-CardBus bridge */
+
+/* PD6832 extension registers -- indexed in PD67_EXT_INDEX */
+#define PD68_EXT_CTL_2 0x0b
+#define PD68_PCI_SPACE 0x22
+#define PD68_PCCARD_SPACE 0x23
+#define PD68_WINDOW_TYPE 0x24
+#define PD68_EXT_CSC 0x2e
+#define PD68_MISC_CTL_4 0x2f
+#define PD68_MISC_CTL_5 0x30
+#define PD68_MISC_CTL_6 0x31
+
+/* Extra flags in PD67_MISC_CTL_3 */
+#define PD68_MC3_HW_SUSP 0x10
+#define PD68_MC3_MM_EXPAND 0x40
+#define PD68_MC3_MM_ARM 0x80
+
+/* Bridge Control Register */
+#define PD6832_BCR_MGMT_IRQ_ENA 0x0800
+
+/* Socket Number Register */
+#define PD6832_SOCKET_NUMBER 0x004c /* 8 bit */
+
+#endif /* _LINUX_CIRRUS_H */
diff --git a/drivers/pcmcia/cistpl.c b/drivers/pcmcia/cistpl.c
new file mode 100644
index 000000000..690ea894f
--- /dev/null
+++ b/drivers/pcmcia/cistpl.c
@@ -0,0 +1,1394 @@
+/*======================================================================
+
+ PCMCIA Card Information Structure parser
+
+ cistpl.c 1.72 1999/09/15 15:32:19
+
+ 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 David A. Hinds
+ <dhinds@hyper.stanford.edu>. Portions created by David A. Hinds
+ are Copyright (C) 1999 David A. Hinds. 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.
+
+======================================================================*/
+
+#define __NO_VERSION__
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/string.h>
+#include <linux/major.h>
+#include <linux/errno.h>
+#include <linux/timer.h>
+#include <linux/malloc.h>
+#include <linux/mm.h>
+#include <linux/sched.h>
+#include <linux/pci.h>
+#include <linux/ioport.h>
+#include <asm/io.h>
+
+#include <pcmcia/cs_types.h>
+#include <pcmcia/bus_ops.h>
+#include <pcmcia/ss.h>
+#include <pcmcia/cs.h>
+#include <pcmcia/bulkmem.h>
+#include <pcmcia/cistpl.h>
+#include "cs_internal.h"
+#include "rsrc_mgr.h"
+
+static const u_char mantissa[] = {
+ 10, 12, 13, 15, 20, 25, 30, 35,
+ 40, 45, 50, 55, 60, 70, 80, 90
+};
+
+static const u_int exponent[] = {
+ 1, 10, 100, 1000, 10000, 100000, 1000000, 10000000
+};
+
+/* Convert an extended speed byte to a time in nanoseconds */
+#define SPEED_CVT(v) \
+ (mantissa[(((v)>>3)&15)-1] * exponent[(v)&7] / 10)
+/* Convert a power byte to a current in 0.1 microamps */
+#define POWER_CVT(v) \
+ (mantissa[((v)>>3)&15] * exponent[(v)&7] / 10)
+#define POWER_SCALE(v) (exponent[(v)&7])
+
+/* Upper limit on reasonable # of tuples */
+#define MAX_TUPLES 200
+
+/*======================================================================
+
+ Low-level functions to read and write CIS memory. I think the
+ write routine is only useful for writing one-byte registers.
+
+======================================================================*/
+
+void read_cis_mem(socket_info_t *s, int attr, u_int addr,
+ u_int len, void *ptr)
+{
+ pccard_mem_map *mem = &s->cis_mem;
+ u_char *sys;
+ u_int inc = 1;
+
+ DEBUG(3, "cs: read_cis_mem(%d, %#x, %u)\n", attr, addr, len);
+ if (setup_cis_mem(s) != 0) {
+ memset(ptr, 0xff, len);
+ return;
+ }
+ mem->flags |= MAP_ACTIVE; mem->flags &= ~MAP_ATTRIB;
+ if (attr) { mem->flags |= MAP_ATTRIB; inc++; addr *= 2; }
+ sys = s->cis_virt + (addr & (s->cap.map_size-1));
+ mem->card_start = addr & ~(s->cap.map_size-1);
+
+ for (; len > 0; sys = s->cis_virt) {
+ s->ss_entry(s->sock, SS_SetMemMap, mem);
+ DEBUG(3, "cs: %#2.2x %#2.2x %#2.2x %#2.2x %#2.2x ...\n",
+ bus_readb(s->cap.bus, sys),
+ bus_readb(s->cap.bus, sys+inc),
+ bus_readb(s->cap.bus, sys+2*inc),
+ bus_readb(s->cap.bus, sys+3*inc),
+ bus_readb(s->cap.bus, sys+4*inc));
+ for ( ; len > 0; len--, ((u_char *)ptr)++, sys += inc) {
+ if (sys == s->cis_virt+s->cap.map_size) break;
+ *(u_char *)ptr = bus_readb(s->cap.bus, sys);
+ }
+ mem->card_start += s->cap.map_size;
+ }
+}
+
+void write_cis_mem(socket_info_t *s, int attr, u_int addr,
+ u_int len, void *ptr)
+{
+ pccard_mem_map *mem = &s->cis_mem;
+ u_char *sys;
+ int inc = 1;
+
+ DEBUG(3, "cs: write_cis_mem(%d, %#x, %u)\n", attr, addr, len);
+ if (setup_cis_mem(s) != 0) return;
+ mem->flags &= ~MAP_ATTRIB;
+ if (attr) { mem->flags |= MAP_ATTRIB; inc++; addr *= 2; }
+ sys = s->cis_virt + (addr & (s->cap.map_size-1));
+ mem->card_start = addr & ~(s->cap.map_size-1);
+
+ for (; len > 0; sys = s->cis_virt) {
+ s->ss_entry(s->sock, SS_SetMemMap, mem);
+ for ( ; len > 0; len--, ((u_char *)ptr)++, sys += inc) {
+ if (sys == s->cis_virt+s->cap.map_size) break;
+ bus_writeb(s->cap.bus, *(u_char *)ptr, sys);
+ }
+ mem->card_start += s->cap.map_size;
+ }
+}
+
+/*======================================================================
+
+ This is tricky... when we set up CIS memory, we try to validate
+ the memory window space allocations.
+
+======================================================================*/
+
+/* Scratch pointer to the socket we use for validation */
+static socket_info_t *vs = NULL;
+
+/* Validation function for cards with a valid CIS */
+static int cis_readable(u_long base)
+{
+ cisinfo_t info1, info2;
+ int ret;
+ vs->cis_mem.sys_start = base;
+ vs->cis_mem.sys_stop = base+vs->cap.map_size-1;
+ vs->cis_virt = bus_ioremap(vs->cap.bus, base, vs->cap.map_size);
+ ret = validate_cis(vs->clients, &info1);
+ /* invalidate mapping and CIS cache */
+ bus_iounmap(vs->cap.bus, vs->cis_virt); vs->cis_used = 0;
+ if ((ret != 0) || (info1.Chains == 0))
+ return 0;
+ vs->cis_mem.sys_start = base+vs->cap.map_size;
+ vs->cis_mem.sys_stop = base+2*vs->cap.map_size-1;
+ vs->cis_virt = bus_ioremap(vs->cap.bus, base+vs->cap.map_size,
+ vs->cap.map_size);
+ ret = validate_cis(vs->clients, &info2);
+ bus_iounmap(vs->cap.bus, vs->cis_virt); vs->cis_used = 0;
+ return ((ret == 0) && (info1.Chains == info2.Chains));
+}
+
+/* Validation function for simple memory cards */
+static int checksum(u_long base)
+{
+ int i, a, b, d;
+ vs->cis_mem.sys_start = base;
+ vs->cis_mem.sys_stop = base+vs->cap.map_size-1;
+ vs->cis_virt = bus_ioremap(vs->cap.bus, base, vs->cap.map_size);
+ vs->cis_mem.card_start = 0;
+ vs->cis_mem.flags = MAP_ACTIVE;
+ vs->ss_entry(vs->sock, SS_SetMemMap, &vs->cis_mem);
+ /* Don't bother checking every word... */
+ a = 0; b = -1;
+ for (i = 0; i < vs->cap.map_size; i += 44) {
+ d = bus_readl(vs->cap.bus, vs->cis_virt+i);
+ a += d; b &= d;
+ }
+ bus_iounmap(vs->cap.bus, vs->cis_virt);
+ return (b == -1) ? -1 : (a>>1);
+}
+
+static int checksum_match(u_long base)
+{
+ int a = checksum(base), b = checksum(base+vs->cap.map_size);
+ return ((a == b) && (a >= 0));
+}
+
+int setup_cis_mem(socket_info_t *s)
+{
+ if (s->cis_mem.sys_start == 0) {
+ int low = !(s->cap.features & SS_CAP_PAGE_REGS);
+ vs = s;
+ validate_mem(cis_readable, checksum_match, low);
+ s->cis_mem.sys_start = 0;
+ vs = NULL;
+ if (find_mem_region(&s->cis_mem.sys_start, s->cap.map_size,
+ "card services", s->cap.map_size, low)) {
+ printk(KERN_NOTICE "cs: unable to map card memory!\n");
+ return CS_OUT_OF_RESOURCE;
+ }
+ s->cis_mem.sys_stop = s->cis_mem.sys_start+s->cap.map_size-1;
+ s->cis_mem.flags |= MAP_ACTIVE;
+ s->cis_virt = bus_ioremap(s->cap.bus, s->cis_mem.sys_start,
+ s->cap.map_size);
+ }
+ return 0;
+}
+
+void release_cis_mem(socket_info_t *s)
+{
+ if (s->cis_mem.sys_start != 0) {
+ s->cis_mem.flags &= ~MAP_ACTIVE;
+ s->ss_entry(s->sock, SS_SetMemMap, &s->cis_mem);
+ release_mem_region(s->cis_mem.sys_start, s->cap.map_size);
+ bus_iounmap(s->cap.bus, s->cis_virt);
+ s->cis_mem.sys_start = 0;
+ }
+}
+
+/*======================================================================
+
+ This is a wrapper around read_cis_mem, with the same interface,
+ but which caches information, for cards whose CIS may not be
+ readable all the time.
+
+======================================================================*/
+
+static void read_cis_cache(socket_info_t *s, int attr, u_int addr,
+ u_int len, void *ptr)
+{
+ int i;
+ char *caddr;
+
+ if (s->fake_cis) {
+ if (s->fake_cis_len > addr+len)
+ memcpy(ptr, s->fake_cis+addr, len);
+ else
+ memset(ptr, 0xff, len);
+ return;
+ }
+ caddr = s->cis_cache;
+ for (i = 0; i < s->cis_used; i++) {
+ if ((s->cis_table[i].addr == addr) &&
+ (s->cis_table[i].len == len) &&
+ (s->cis_table[i].attr == attr)) break;
+ caddr += s->cis_table[i].len;
+ }
+ if (i < s->cis_used) {
+ memcpy(ptr, caddr, len);
+ return;
+ }
+#ifdef CONFIG_CARDBUS
+ if (s->state & SOCKET_CARDBUS)
+ read_cb_mem(s, 0, attr, addr, len, ptr);
+ else
+#endif
+ read_cis_mem(s, attr, addr, len, ptr);
+ /* Copy data into the cache, if there is room */
+ if ((i < MAX_CIS_TABLE) &&
+ (caddr+len < s->cis_cache+MAX_CIS_DATA)) {
+ s->cis_table[i].addr = addr;
+ s->cis_table[i].len = len;
+ s->cis_table[i].attr = attr;
+ s->cis_used++;
+ memcpy(caddr, ptr, len);
+ }
+}
+
+/*======================================================================
+
+ This verifies if the CIS of a card matches what is in the CIS
+ cache.
+
+======================================================================*/
+
+int verify_cis_cache(socket_info_t *s)
+{
+ char buf[256], *caddr;
+ int i;
+
+ caddr = s->cis_cache;
+ for (i = 0; i < s->cis_used; i++) {
+#ifdef CONFIG_CARDBUS
+ if (s->state & SOCKET_CARDBUS)
+ read_cb_mem(s, 0, s->cis_table[i].attr, s->cis_table[i].addr,
+ s->cis_table[i].len, buf);
+ else
+#endif
+ read_cis_mem(s, s->cis_table[i].attr, s->cis_table[i].addr,
+ s->cis_table[i].len, buf);
+ if (memcmp(buf, caddr, s->cis_table[i].len) != 0)
+ break;
+ caddr += s->cis_table[i].len;
+ }
+ return (i < s->cis_used);
+}
+
+/*======================================================================
+
+ For really bad cards, we provide a facility for uploading a
+ replacement CIS.
+
+======================================================================*/
+
+int replace_cis(client_handle_t handle, cisdump_t *cis)
+{
+ socket_info_t *s;
+ if (CHECK_HANDLE(handle))
+ return CS_BAD_HANDLE;
+ s = SOCKET(handle);
+ if (s->fake_cis != NULL) {
+ kfree(s->fake_cis);
+ s->fake_cis = NULL;
+ }
+ if (cis->Length > CISTPL_MAX_CIS_SIZE)
+ return CS_BAD_SIZE;
+ s->fake_cis = kmalloc(cis->Length, GFP_KERNEL);
+ if (s->fake_cis == NULL)
+ return CS_OUT_OF_RESOURCE;
+ s->fake_cis_len = cis->Length;
+ memcpy(s->fake_cis, cis->Data, cis->Length);
+ return CS_SUCCESS;
+}
+
+/*======================================================================
+
+ The high-level CIS tuple services
+
+======================================================================*/
+
+typedef struct tuple_flags {
+ u_int link_space:3;
+ u_int has_link:1;
+ u_int mfc_fn:3;
+ u_int space:3;
+} tuple_flags;
+
+#define LINK_SPACE(f) (((tuple_flags *)(&(f)))->link_space)
+#define HAS_LINK(f) (((tuple_flags *)(&(f)))->has_link)
+#define MFC_FN(f) (((tuple_flags *)(&(f)))->mfc_fn)
+#define SPACE(f) (((tuple_flags *)(&(f)))->space)
+
+int get_next_tuple(client_handle_t handle, tuple_t *tuple);
+
+int get_first_tuple(client_handle_t handle, tuple_t *tuple)
+{
+ socket_info_t *s;
+ if (CHECK_HANDLE(handle))
+ return CS_BAD_HANDLE;
+ s = SOCKET(handle);
+ if (!(s->state & SOCKET_PRESENT))
+ return CS_NO_CARD;
+ tuple->TupleLink = tuple->Flags = 0;
+#ifdef CONFIG_CARDBUS
+ if (s->state & SOCKET_CARDBUS) {
+ u_int ptr;
+ pcibios_read_config_dword(s->cap.cardbus, 0, 0x28, &ptr);
+ tuple->CISOffset = ptr & ~7;
+ SPACE(tuple->Flags) = (ptr & 7);
+ } else
+#endif
+ {
+ /* Assume presence of a LONGLINK_C to address 0 */
+ tuple->CISOffset = tuple->LinkOffset = 0;
+ SPACE(tuple->Flags) = HAS_LINK(tuple->Flags) = 1;
+ }
+ if (!(s->state & SOCKET_CARDBUS) && (s->functions > 1) &&
+ !(tuple->Attributes & TUPLE_RETURN_COMMON)) {
+ cisdata_t req = tuple->DesiredTuple;
+ tuple->DesiredTuple = CISTPL_LONGLINK_MFC;
+ if (get_next_tuple(handle, tuple) == CS_SUCCESS) {
+ tuple->DesiredTuple = CISTPL_LINKTARGET;
+ if (get_next_tuple(handle, tuple) != CS_SUCCESS)
+ return CS_NO_MORE_ITEMS;
+ } else
+ tuple->CISOffset = tuple->TupleLink = 0;
+ tuple->DesiredTuple = req;
+ }
+ return get_next_tuple(handle, tuple);
+}
+
+static int follow_link(socket_info_t *s, tuple_t *tuple)
+{
+ u_char link[5];
+ u_int ofs;
+
+ if (MFC_FN(tuple->Flags)) {
+ /* Get indirect link from the MFC tuple */
+ read_cis_cache(s, LINK_SPACE(tuple->Flags),
+ tuple->LinkOffset, 5, link);
+ ofs = le32_to_cpu(*(u_int *)(link+1));
+ SPACE(tuple->Flags) = (link[0] == CISTPL_MFC_ATTR);
+ /* Move to the next indirect link */
+ tuple->LinkOffset += 5;
+ MFC_FN(tuple->Flags)--;
+ } else if (HAS_LINK(tuple->Flags)) {
+ ofs = tuple->LinkOffset;
+ SPACE(tuple->Flags) = LINK_SPACE(tuple->Flags);
+ HAS_LINK(tuple->Flags) = 0;
+ } else {
+ return -1;
+ }
+ if (!(s->state & SOCKET_CARDBUS) && SPACE(tuple->Flags)) {
+ /* This is ugly, but a common CIS error is to code the long
+ link offset incorrectly, so we check the right spot... */
+ read_cis_cache(s, SPACE(tuple->Flags), ofs, 5, link);
+ if ((link[0] == CISTPL_LINKTARGET) && (link[1] >= 3) &&
+ (strncmp(link+2, "CIS", 3) == 0))
+ return ofs;
+ /* Then, we try the wrong spot... */
+ ofs = ofs >> 1;
+ }
+ read_cis_cache(s, SPACE(tuple->Flags), ofs, 5, link);
+ if ((link[0] != CISTPL_LINKTARGET) || (link[1] < 3) ||
+ (strncmp(link+2, "CIS", 3) != 0))
+ return -1;
+ return ofs;
+}
+
+int get_next_tuple(client_handle_t handle, tuple_t *tuple)
+{
+ socket_info_t *s;
+ u_char link[2], tmp;
+ int ofs, i, attr;
+
+ if (CHECK_HANDLE(handle))
+ return CS_BAD_HANDLE;
+ s = SOCKET(handle);
+ if (!(s->state & SOCKET_PRESENT))
+ return CS_NO_CARD;
+
+ link[1] = tuple->TupleLink;
+ ofs = tuple->CISOffset + tuple->TupleLink;
+ attr = SPACE(tuple->Flags);
+
+ for (i = 0; i < MAX_TUPLES; i++) {
+ if (link[1] == 0xff) {
+ link[0] = CISTPL_END;
+ } else {
+ read_cis_cache(s, attr, ofs, 2, link);
+ if (link[0] == CISTPL_NULL) {
+ ofs++; continue;
+ }
+ }
+
+ /* End of chain? Follow long link if possible */
+ if (link[0] == CISTPL_END) {
+ if ((ofs = follow_link(s, tuple)) < 0)
+ return CS_NO_MORE_ITEMS;
+ attr = SPACE(tuple->Flags);
+ read_cis_cache(s, attr, ofs, 2, link);
+ }
+
+ /* Is this a link tuple? Make a note of it */
+ if ((link[0] == CISTPL_LONGLINK_A) ||
+ (link[0] == CISTPL_LONGLINK_C) ||
+ (link[0] == CISTPL_LONGLINK_MFC) ||
+ (link[0] == CISTPL_LINKTARGET) ||
+ (link[0] == CISTPL_NO_LINK)) {
+ switch (link[0]) {
+ case CISTPL_LONGLINK_A:
+ HAS_LINK(tuple->Flags) = 1;
+ LINK_SPACE(tuple->Flags) = 1;
+ read_cis_cache(s, attr, ofs+2, 4, &tuple->LinkOffset);
+ break;
+ case CISTPL_LONGLINK_C:
+ HAS_LINK(tuple->Flags) = 1;
+ LINK_SPACE(tuple->Flags) = 0;
+ read_cis_cache(s, attr, ofs+2, 4, &tuple->LinkOffset);
+ break;
+ case CISTPL_LONGLINK_MFC:
+ tuple->LinkOffset = ofs + 3;
+ LINK_SPACE(tuple->Flags) = attr;
+ if (handle->Function == BIND_FN_ALL) {
+ /* Follow all the MFC links */
+ read_cis_cache(s, attr, ofs+2, 1, &tmp);
+ MFC_FN(tuple->Flags) = tmp;
+ } else {
+ /* Follow exactly one of the links */
+ MFC_FN(tuple->Flags) = 1;
+ tuple->LinkOffset += handle->Function * 5;
+ }
+ break;
+ case CISTPL_NO_LINK:
+ HAS_LINK(tuple->Flags) = 0;
+ break;
+ }
+ if ((tuple->Attributes & TUPLE_RETURN_LINK) &&
+ (tuple->DesiredTuple == RETURN_FIRST_TUPLE))
+ break;
+ } else
+ if (tuple->DesiredTuple == RETURN_FIRST_TUPLE)
+ break;
+
+ if (link[0] == tuple->DesiredTuple)
+ break;
+ ofs += link[1] + 2;
+ }
+ if (i == MAX_TUPLES) {
+ DEBUG(1, "cs: overrun in get_next_tuple for socket %d\n",
+ handle->Socket);
+ return CS_NO_MORE_ITEMS;
+ }
+
+ tuple->TupleCode = link[0];
+ tuple->TupleLink = link[1];
+ tuple->CISOffset = ofs + 2;
+ return CS_SUCCESS;
+}
+
+/*====================================================================*/
+
+#define _MIN(a, b) (((a) < (b)) ? (a) : (b))
+
+int get_tuple_data(client_handle_t handle, tuple_t *tuple)
+{
+ socket_info_t *s;
+ u_int len;
+
+ if (CHECK_HANDLE(handle))
+ return CS_BAD_HANDLE;
+
+ s = SOCKET(handle);
+
+ if (tuple->TupleLink < tuple->TupleOffset)
+ return CS_NO_MORE_ITEMS;
+ len = tuple->TupleLink - tuple->TupleOffset;
+ tuple->TupleDataLen = tuple->TupleLink;
+ if (len == 0)
+ return CS_SUCCESS;
+ read_cis_cache(s, SPACE(tuple->Flags),
+ tuple->CISOffset + tuple->TupleOffset,
+ _MIN(len, tuple->TupleDataMax), tuple->TupleData);
+ return CS_SUCCESS;
+}
+
+/*======================================================================
+
+ Parsing routines for individual tuples
+
+======================================================================*/
+
+static int parse_device(tuple_t *tuple, cistpl_device_t *device)
+{
+ int i;
+ u_char scale;
+ u_char *p, *q;
+
+ p = (u_char *)tuple->TupleData;
+ q = p + tuple->TupleDataLen;
+
+ device->ndev = 0;
+ for (i = 0; i < CISTPL_MAX_DEVICES; i++) {
+
+ if (*p == 0xff) break;
+ device->dev[i].type = (*p >> 4);
+ device->dev[i].wp = (*p & 0x08) ? 1 : 0;
+ switch (*p & 0x07) {
+ case 0: device->dev[i].speed = 0; break;
+ case 1: device->dev[i].speed = 250; break;
+ case 2: device->dev[i].speed = 200; break;
+ case 3: device->dev[i].speed = 150; break;
+ case 4: device->dev[i].speed = 100; break;
+ case 7:
+ if (++p == q) return CS_BAD_TUPLE;
+ if (p == q)
+ return CS_BAD_TUPLE;
+ device->dev[i].speed = SPEED_CVT(*p);
+ while (*p & 0x80)
+ if (++p == q) return CS_BAD_TUPLE;
+ break;
+ default:
+ return CS_BAD_TUPLE;
+ }
+
+ if (++p == q) return CS_BAD_TUPLE;
+ if (*p == 0xff) break;
+ scale = *p & 7;
+ if (scale == 7) return CS_BAD_TUPLE;
+ device->dev[i].size = ((*p >> 3) + 1) * (512 << (scale*2));
+ device->ndev++;
+ if (++p == q) break;
+ }
+
+ return CS_SUCCESS;
+}
+
+/*====================================================================*/
+
+static int parse_checksum(tuple_t *tuple, cistpl_checksum_t *csum)
+{
+ u_char *p;
+ if (tuple->TupleDataLen < 5)
+ return CS_BAD_TUPLE;
+ p = (u_char *)tuple->TupleData;
+ csum->addr = tuple->CISOffset+(short)le16_to_cpu(*(u_short *)p)-2;
+ csum->len = le16_to_cpu(*(u_short *)(p + 2));
+ csum->sum = *(p+4);
+ return CS_SUCCESS;
+}
+
+/*====================================================================*/
+
+static int parse_longlink(tuple_t *tuple, cistpl_longlink_t *link)
+{
+ if (tuple->TupleDataLen < 4)
+ return CS_BAD_TUPLE;
+ link->addr = le32_to_cpu(*(u_int *)tuple->TupleData);
+ return CS_SUCCESS;
+}
+
+/*====================================================================*/
+
+static int parse_longlink_mfc(tuple_t *tuple,
+ cistpl_longlink_mfc_t *link)
+{
+ u_char *p;
+ int i;
+
+ p = (u_char *)tuple->TupleData;
+
+ link->nfn = *p; p++;
+ if (tuple->TupleDataLen <= link->nfn*5)
+ return CS_BAD_TUPLE;
+ for (i = 0; i < link->nfn; i++) {
+ link->fn[i].space = *p; p++;
+ link->fn[i].addr = le32_to_cpu(*(u_int *)p); p += 4;
+ }
+ return CS_SUCCESS;
+}
+
+/*====================================================================*/
+
+static int parse_strings(u_char *p, u_char *q, int max,
+ char *s, u_char *ofs, u_char *found)
+{
+ int i, j, ns;
+
+ if (p == q) return CS_BAD_TUPLE;
+ ns = 0; j = 0;
+ for (i = 0; i < max; i++) {
+ if (*p == 0xff) break;
+ ofs[i] = j;
+ ns++;
+ for (;;) {
+ s[j++] = (*p == 0xff) ? '\0' : *p;
+ if ((*p == '\0') || (*p == 0xff)) break;
+ if (++p == q) return CS_BAD_TUPLE;
+ }
+ if ((*p == 0xff) || (++p == q)) break;
+ }
+ if (found) {
+ *found = ns;
+ return CS_SUCCESS;
+ } else {
+ return (ns == max) ? CS_SUCCESS : CS_BAD_TUPLE;
+ }
+}
+
+/*====================================================================*/
+
+static int parse_vers_1(tuple_t *tuple, cistpl_vers_1_t *vers_1)
+{
+ u_char *p, *q;
+
+ p = (u_char *)tuple->TupleData;
+ q = p + tuple->TupleDataLen;
+
+ vers_1->major = *p; p++;
+ vers_1->minor = *p; p++;
+ if (p >= q) return CS_BAD_TUPLE;
+
+ return parse_strings(p, q, CISTPL_VERS_1_MAX_PROD_STRINGS,
+ vers_1->str, vers_1->ofs, &vers_1->ns);
+}
+
+/*====================================================================*/
+
+static int parse_altstr(tuple_t *tuple, cistpl_altstr_t *altstr)
+{
+ u_char *p, *q;
+
+ p = (u_char *)tuple->TupleData;
+ q = p + tuple->TupleDataLen;
+
+ return parse_strings(p, q, CISTPL_MAX_ALTSTR_STRINGS,
+ altstr->str, altstr->ofs, &altstr->ns);
+}
+
+/*====================================================================*/
+
+static int parse_jedec(tuple_t *tuple, cistpl_jedec_t *jedec)
+{
+ u_char *p, *q;
+ int nid;
+
+ p = (u_char *)tuple->TupleData;
+ q = p + tuple->TupleDataLen;
+
+ for (nid = 0; nid < CISTPL_MAX_DEVICES; nid++) {
+ if (p > q-2) break;
+ jedec->id[nid].mfr = p[0];
+ jedec->id[nid].info = p[1];
+ p += 2;
+ }
+ jedec->nid = nid;
+ return CS_SUCCESS;
+}
+
+/*====================================================================*/
+
+static int parse_manfid(tuple_t *tuple, cistpl_manfid_t *m)
+{
+ u_short *p;
+ if (tuple->TupleDataLen < 4)
+ return CS_BAD_TUPLE;
+ p = (u_short *)tuple->TupleData;
+ m->manf = le16_to_cpu(p[0]);
+ m->card = le16_to_cpu(p[1]);
+ return CS_SUCCESS;
+}
+
+/*====================================================================*/
+
+static int parse_funcid(tuple_t *tuple, cistpl_funcid_t *f)
+{
+ u_char *p;
+ if (tuple->TupleDataLen < 2)
+ return CS_BAD_TUPLE;
+ p = (u_char *)tuple->TupleData;
+ f->func = p[0];
+ f->sysinit = p[1];
+ return CS_SUCCESS;
+}
+
+/*====================================================================*/
+
+static int parse_funce(tuple_t *tuple, cistpl_funce_t *f)
+{
+ u_char *p;
+ int i;
+ if (tuple->TupleDataLen < 1)
+ return CS_BAD_TUPLE;
+ p = (u_char *)tuple->TupleData;
+ f->type = p[0];
+ for (i = 1; i < tuple->TupleDataLen; i++)
+ f->data[i-1] = p[i];
+ return CS_SUCCESS;
+}
+
+/*====================================================================*/
+
+static int parse_config(tuple_t *tuple, cistpl_config_t *config)
+{
+ int rasz, rmsz, i;
+ u_char *p;
+
+ p = (u_char *)tuple->TupleData;
+ rasz = *p & 0x03;
+ rmsz = (*p & 0x3c) >> 2;
+ if (tuple->TupleDataLen < rasz+rmsz+4)
+ return CS_BAD_TUPLE;
+ config->last_idx = *(++p);
+ p++;
+ config->base = 0;
+ for (i = 0; i <= rasz; i++)
+ config->base += p[i] << (8*i);
+ p += rasz+1;
+ for (i = 0; i < 4; i++)
+ config->rmask[i] = 0;
+ for (i = 0; i <= rmsz; i++)
+ config->rmask[i>>2] += p[i] << (8*(i%4));
+ config->subtuples = tuple->TupleDataLen - (rasz+rmsz+4);
+ return CS_SUCCESS;
+}
+
+/*======================================================================
+
+ The following routines are all used to parse the nightmarish
+ config table entries.
+
+======================================================================*/
+
+static u_char *parse_power(u_char *p, u_char *q,
+ cistpl_power_t *pwr)
+{
+ int i;
+ u_int scale;
+
+ if (p == q) return NULL;
+ pwr->present = *p;
+ pwr->flags = 0;
+ p++;
+ for (i = 0; i < 7; i++)
+ if (pwr->present & (1<<i)) {
+ if (p == q) return NULL;
+ pwr->param[i] = POWER_CVT(*p);
+ scale = POWER_SCALE(*p);
+ while (*p & 0x80) {
+ if (++p == q) return NULL;
+ if ((*p & 0x7f) < 100)
+ pwr->param[i] += (*p & 0x7f) * scale / 100;
+ else if (*p == 0x7d)
+ pwr->flags |= CISTPL_POWER_HIGHZ_OK;
+ else if (*p == 0x7e)
+ pwr->param[i] = 0;
+ else if (*p == 0x7f)
+ pwr->flags |= CISTPL_POWER_HIGHZ_REQ;
+ else
+ return NULL;
+ }
+ p++;
+ }
+ return p;
+}
+
+/*====================================================================*/
+
+static u_char *parse_timing(u_char *p, u_char *q,
+ cistpl_timing_t *timing)
+{
+ u_char scale;
+
+ if (p == q) return NULL;
+ scale = *p;
+ if ((scale & 3) != 3) {
+ if (++p == q) return NULL;
+ timing->wait = SPEED_CVT(*p);
+ timing->waitscale = exponent[scale & 3];
+ } else
+ timing->wait = 0;
+ scale >>= 2;
+ if ((scale & 7) != 7) {
+ if (++p == q) return NULL;
+ timing->ready = SPEED_CVT(*p);
+ timing->rdyscale = exponent[scale & 7];
+ } else
+ timing->ready = 0;
+ scale >>= 3;
+ if (scale != 7) {
+ if (++p == q) return NULL;
+ timing->reserved = SPEED_CVT(*p);
+ timing->rsvscale = exponent[scale];
+ } else
+ timing->reserved = 0;
+ p++;
+ return p;
+}
+
+/*====================================================================*/
+
+static u_char *parse_io(u_char *p, u_char *q, cistpl_io_t *io)
+{
+ int i, j, bsz, lsz;
+
+ if (p == q) return NULL;
+ io->flags = *p;
+
+ if (!(*p & 0x80)) {
+ io->nwin = 1;
+ io->win[0].base = 0;
+ io->win[0].len = (1 << (io->flags & CISTPL_IO_LINES_MASK));
+ return p+1;
+ }
+
+ if (++p == q) return NULL;
+ io->nwin = (*p & 0x0f) + 1;
+ bsz = (*p & 0x30) >> 4;
+ if (bsz == 3) bsz++;
+ lsz = (*p & 0xc0) >> 6;
+ if (lsz == 3) lsz++;
+ p++;
+
+ for (i = 0; i < io->nwin; i++) {
+ io->win[i].base = 0;
+ io->win[i].len = 1;
+ for (j = 0; j < bsz; j++, p++) {
+ if (p == q) return NULL;
+ io->win[i].base += *p << (j*8);
+ }
+ for (j = 0; j < lsz; j++, p++) {
+ if (p == q) return NULL;
+ io->win[i].len += *p << (j*8);
+ }
+ }
+ return p;
+}
+
+/*====================================================================*/
+
+static u_char *parse_mem(u_char *p, u_char *q, cistpl_mem_t *mem)
+{
+ int i, j, asz, lsz, has_ha;
+ u_int len, ca, ha;
+
+ if (p == q) return NULL;
+
+ mem->nwin = (*p & 0x07) + 1;
+ lsz = (*p & 0x18) >> 3;
+ asz = (*p & 0x60) >> 5;
+ has_ha = (*p & 0x80);
+ if (++p == q) return NULL;
+
+ for (i = 0; i < mem->nwin; i++) {
+ len = ca = ha = 0;
+ for (j = 0; j < lsz; j++, p++) {
+ if (p == q) return NULL;
+ len += *p << (j*8);
+ }
+ for (j = 0; j < asz; j++, p++) {
+ if (p == q) return NULL;
+ ca += *p << (j*8);
+ }
+ if (has_ha)
+ for (j = 0; j < asz; j++, p++) {
+ if (p == q) return NULL;
+ ha += *p << (j*8);
+ }
+ mem->win[i].len = len << 8;
+ mem->win[i].card_addr = ca << 8;
+ mem->win[i].host_addr = ha << 8;
+ }
+ return p;
+}
+
+/*====================================================================*/
+
+static u_char *parse_irq(u_char *p, u_char *q, cistpl_irq_t *irq)
+{
+ if (p == q) return NULL;
+ irq->IRQInfo1 = *p; p++;
+ if (irq->IRQInfo1 & IRQ_INFO2_VALID) {
+ if (p+2 > q) return NULL;
+ irq->IRQInfo2 = (p[1]<<8) + p[0];
+ p += 2;
+ }
+ return p;
+}
+
+/*====================================================================*/
+
+static int parse_cftable_entry(tuple_t *tuple,
+ cistpl_cftable_entry_t *entry)
+{
+ u_char *p, *q, features;
+
+ p = tuple->TupleData;
+ q = p + tuple->TupleDataLen;
+ entry->index = *p & 0x3f;
+ entry->flags = 0;
+ if (*p & 0x40)
+ entry->flags |= CISTPL_CFTABLE_DEFAULT;
+ if (*p & 0x80) {
+ if (++p == q) return CS_BAD_TUPLE;
+ if (*p & 0x10)
+ entry->flags |= CISTPL_CFTABLE_BVDS;
+ if (*p & 0x20)
+ entry->flags |= CISTPL_CFTABLE_WP;
+ if (*p & 0x40)
+ entry->flags |= CISTPL_CFTABLE_RDYBSY;
+ if (*p & 0x80)
+ entry->flags |= CISTPL_CFTABLE_MWAIT;
+ entry->interface = *p & 0x0f;
+ } else
+ entry->interface = 0;
+
+ /* Process optional features */
+ if (++p == q) return CS_BAD_TUPLE;
+ features = *p; p++;
+
+ /* Power options */
+ if ((features & 3) > 0) {
+ p = parse_power(p, q, &entry->vcc);
+ if (p == NULL) return CS_BAD_TUPLE;
+ } else
+ entry->vcc.present = 0;
+ if ((features & 3) > 1) {
+ p = parse_power(p, q, &entry->vpp1);
+ if (p == NULL) return CS_BAD_TUPLE;
+ } else
+ entry->vpp1.present = 0;
+ if ((features & 3) > 2) {
+ p = parse_power(p, q, &entry->vpp2);
+ if (p == NULL) return CS_BAD_TUPLE;
+ } else
+ entry->vpp2.present = 0;
+
+ /* Timing options */
+ if (features & 0x04) {
+ p = parse_timing(p, q, &entry->timing);
+ if (p == NULL) return CS_BAD_TUPLE;
+ } else {
+ entry->timing.wait = 0;
+ entry->timing.ready = 0;
+ entry->timing.reserved = 0;
+ }
+
+ /* I/O window options */
+ if (features & 0x08) {
+ p = parse_io(p, q, &entry->io);
+ if (p == NULL) return CS_BAD_TUPLE;
+ } else
+ entry->io.nwin = 0;
+
+ /* Interrupt options */
+ if (features & 0x10) {
+ p = parse_irq(p, q, &entry->irq);
+ if (p == NULL) return CS_BAD_TUPLE;
+ } else
+ entry->irq.IRQInfo1 = 0;
+
+ switch (features & 0x60) {
+ case 0x00:
+ entry->mem.nwin = 0;
+ break;
+ case 0x20:
+ entry->mem.nwin = 1;
+ entry->mem.win[0].len = le16_to_cpu(*(u_short *)p) << 8;
+ entry->mem.win[0].card_addr = 0;
+ entry->mem.win[0].host_addr = 0;
+ p += 2;
+ if (p > q) return CS_BAD_TUPLE;
+ break;
+ case 0x40:
+ entry->mem.nwin = 1;
+ entry->mem.win[0].len = le16_to_cpu(*(u_short *)p) << 8;
+ entry->mem.win[0].card_addr =
+ le16_to_cpu(*(u_short *)(p+2)) << 8;
+ entry->mem.win[0].host_addr = 0;
+ p += 4;
+ if (p > q) return CS_BAD_TUPLE;
+ break;
+ case 0x60:
+ p = parse_mem(p, q, &entry->mem);
+ if (p == NULL) return CS_BAD_TUPLE;
+ break;
+ }
+
+ /* Misc features */
+ if (features & 0x80) {
+ if (p == q) return CS_BAD_TUPLE;
+ entry->flags |= (*p << 8);
+ while (*p & 0x80)
+ if (++p == q) return CS_BAD_TUPLE;
+ p++;
+ }
+
+ entry->subtuples = q-p;
+
+ return CS_SUCCESS;
+}
+
+/*====================================================================*/
+
+#ifdef CONFIG_CARDBUS
+
+static int parse_bar(tuple_t *tuple, cistpl_bar_t *bar)
+{
+ u_char *p;
+ if (tuple->TupleDataLen < 6)
+ return CS_BAD_TUPLE;
+ p = (u_char *)tuple->TupleData;
+ bar->attr = *p;
+ p += 2;
+ bar->size = le32_to_cpu(*(u_int *)p);
+ return CS_SUCCESS;
+}
+
+static int parse_config_cb(tuple_t *tuple, cistpl_config_t *config)
+{
+ u_char *p;
+
+ p = (u_char *)tuple->TupleData;
+ if ((*p != 3) || (tuple->TupleDataLen < 6))
+ return CS_BAD_TUPLE;
+ config->last_idx = *(++p);
+ p++;
+ config->base = le32_to_cpu(*(u_int *)p);
+ config->subtuples = tuple->TupleDataLen - 6;
+ return CS_SUCCESS;
+}
+
+static int parse_cftable_entry_cb(tuple_t *tuple,
+ cistpl_cftable_entry_cb_t *entry)
+{
+ u_char *p, *q, features;
+
+ p = tuple->TupleData;
+ q = p + tuple->TupleDataLen;
+ entry->index = *p & 0x3f;
+ entry->flags = 0;
+ if (*p & 0x40)
+ entry->flags |= CISTPL_CFTABLE_DEFAULT;
+
+ /* Process optional features */
+ if (++p == q) return CS_BAD_TUPLE;
+ features = *p; p++;
+
+ /* Power options */
+ if ((features & 3) > 0) {
+ p = parse_power(p, q, &entry->vcc);
+ if (p == NULL) return CS_BAD_TUPLE;
+ } else
+ entry->vcc.present = 0;
+ if ((features & 3) > 1) {
+ p = parse_power(p, q, &entry->vpp1);
+ if (p == NULL) return CS_BAD_TUPLE;
+ } else
+ entry->vpp1.present = 0;
+ if ((features & 3) > 2) {
+ p = parse_power(p, q, &entry->vpp2);
+ if (p == NULL) return CS_BAD_TUPLE;
+ } else
+ entry->vpp2.present = 0;
+
+ /* I/O window options */
+ if (features & 0x08) {
+ if (p == q) return CS_BAD_TUPLE;
+ entry->io = *p; p++;
+ } else
+ entry->io = 0;
+
+ /* Interrupt options */
+ if (features & 0x10) {
+ p = parse_irq(p, q, &entry->irq);
+ if (p == NULL) return CS_BAD_TUPLE;
+ } else
+ entry->irq.IRQInfo1 = 0;
+
+ if (features & 0x20) {
+ if (p == q) return CS_BAD_TUPLE;
+ entry->mem = *p; p++;
+ } else
+ entry->mem = 0;
+
+ /* Misc features */
+ if (features & 0x80) {
+ if (p == q) return CS_BAD_TUPLE;
+ entry->flags |= (*p << 8);
+ if (*p & 0x80) {
+ if (++p == q) return CS_BAD_TUPLE;
+ entry->flags |= (*p << 16);
+ }
+ while (*p & 0x80)
+ if (++p == q) return CS_BAD_TUPLE;
+ p++;
+ }
+
+ entry->subtuples = q-p;
+
+ return CS_SUCCESS;
+}
+
+#endif
+
+/*====================================================================*/
+
+static int parse_device_geo(tuple_t *tuple, cistpl_device_geo_t *geo)
+{
+ u_char *p, *q;
+ int n;
+
+ p = (u_char *)tuple->TupleData;
+ q = p + tuple->TupleDataLen;
+
+ for (n = 0; n < CISTPL_MAX_DEVICES; n++) {
+ if (p > q-6) break;
+ geo->geo[n].buswidth = p[0];
+ geo->geo[n].erase_block = 1 << (p[1]-1);
+ geo->geo[n].read_block = 1 << (p[2]-1);
+ geo->geo[n].write_block = 1 << (p[3]-1);
+ geo->geo[n].partition = 1 << (p[4]-1);
+ geo->geo[n].interleave = 1 << (p[5]-1);
+ p += 6;
+ }
+ geo->ngeo = n;
+ return CS_SUCCESS;
+}
+
+/*====================================================================*/
+
+static int parse_vers_2(tuple_t *tuple, cistpl_vers_2_t *v2)
+{
+ u_char *p, *q;
+
+ if (tuple->TupleDataLen < 10)
+ return CS_BAD_TUPLE;
+
+ p = tuple->TupleData;
+ q = p + tuple->TupleDataLen;
+
+ v2->vers = p[0];
+ v2->comply = p[1];
+ v2->dindex = le16_to_cpu(*(u_short *)(p+2));
+ v2->vspec8 = p[6];
+ v2->vspec9 = p[7];
+ v2->nhdr = p[8];
+ p += 9;
+ return parse_strings(p, q, 2, v2->str, &v2->vendor, NULL);
+}
+
+/*====================================================================*/
+
+static int parse_org(tuple_t *tuple, cistpl_org_t *org)
+{
+ u_char *p, *q;
+ int i;
+
+ p = tuple->TupleData;
+ q = p + tuple->TupleDataLen;
+ if (p == q) return CS_BAD_TUPLE;
+ org->data_org = *p;
+ if (++p == q) return CS_BAD_TUPLE;
+ for (i = 0; i < 30; i++) {
+ org->desc[i] = *p;
+ if (*p == '\0') break;
+ if (++p == q) return CS_BAD_TUPLE;
+ }
+ return CS_SUCCESS;
+}
+
+/*====================================================================*/
+
+int parse_tuple(client_handle_t handle, tuple_t *tuple, cisparse_t *parse)
+{
+ int ret = CS_SUCCESS;
+
+ if (tuple->TupleDataLen > tuple->TupleDataMax)
+ return CS_BAD_TUPLE;
+ switch (tuple->TupleCode) {
+ case CISTPL_DEVICE:
+ case CISTPL_DEVICE_A:
+ ret = parse_device(tuple, &parse->device);
+ break;
+#ifdef CONFIG_CARDBUS
+ case CISTPL_BAR:
+ ret = parse_bar(tuple, &parse->bar);
+ break;
+ case CISTPL_CONFIG_CB:
+ ret = parse_config_cb(tuple, &parse->config);
+ break;
+ case CISTPL_CFTABLE_ENTRY_CB:
+ ret = parse_cftable_entry_cb(tuple, &parse->cftable_entry_cb);
+ break;
+#endif
+ case CISTPL_CHECKSUM:
+ ret = parse_checksum(tuple, &parse->checksum);
+ break;
+ case CISTPL_LONGLINK_A:
+ case CISTPL_LONGLINK_C:
+ ret = parse_longlink(tuple, &parse->longlink);
+ break;
+ case CISTPL_LONGLINK_MFC:
+ ret = parse_longlink_mfc(tuple, &parse->longlink_mfc);
+ break;
+ case CISTPL_VERS_1:
+ ret = parse_vers_1(tuple, &parse->version_1);
+ break;
+ case CISTPL_ALTSTR:
+ ret = parse_altstr(tuple, &parse->altstr);
+ break;
+ case CISTPL_JEDEC_A:
+ case CISTPL_JEDEC_C:
+ ret = parse_jedec(tuple, &parse->jedec);
+ break;
+ case CISTPL_MANFID:
+ ret = parse_manfid(tuple, &parse->manfid);
+ break;
+ case CISTPL_FUNCID:
+ ret = parse_funcid(tuple, &parse->funcid);
+ break;
+ case CISTPL_FUNCE:
+ ret = parse_funce(tuple, &parse->funce);
+ break;
+ case CISTPL_CONFIG:
+ ret = parse_config(tuple, &parse->config);
+ break;
+ case CISTPL_CFTABLE_ENTRY:
+ ret = parse_cftable_entry(tuple, &parse->cftable_entry);
+ break;
+ case CISTPL_DEVICE_GEO:
+ case CISTPL_DEVICE_GEO_A:
+ ret = parse_device_geo(tuple, &parse->device_geo);
+ break;
+ case CISTPL_VERS_2:
+ ret = parse_vers_2(tuple, &parse->vers_2);
+ break;
+ case CISTPL_ORG:
+ ret = parse_org(tuple, &parse->org);
+ break;
+ case CISTPL_NO_LINK:
+ case CISTPL_LINKTARGET:
+ ret = CS_SUCCESS;
+ break;
+ default:
+ ret = CS_UNSUPPORTED_FUNCTION;
+ break;
+ }
+ return ret;
+}
+
+/*======================================================================
+
+ This is used internally by Card Services to look up CIS stuff.
+
+======================================================================*/
+
+int read_tuple(client_handle_t handle, cisdata_t code, void *parse)
+{
+ tuple_t tuple;
+ cisdata_t buf[255];
+ int ret;
+
+ tuple.DesiredTuple = code;
+ tuple.Attributes = TUPLE_RETURN_COMMON;
+ ret = CardServices(GetFirstTuple, handle, &tuple, NULL);
+ if (ret != CS_SUCCESS) return ret;
+ tuple.TupleData = buf;
+ tuple.TupleOffset = 0;
+ tuple.TupleDataMax = sizeof(buf);
+ ret = CardServices(GetTupleData, handle, &tuple, NULL);
+ if (ret != CS_SUCCESS) return ret;
+ ret = CardServices(ParseTuple, handle, &tuple, parse);
+ return ret;
+}
+
+/*======================================================================
+
+ This tries to determine if a card has a sensible CIS. It returns
+ the number of tuples in the CIS, or 0 if the CIS looks bad. The
+ checks include making sure several critical tuples are present and
+ valid; seeing if the total number of tuples is reasonable; and
+ looking for tuples that use reserved codes.
+
+======================================================================*/
+
+int validate_cis(client_handle_t handle, cisinfo_t *info)
+{
+ tuple_t tuple;
+ cisparse_t p;
+ int ret, reserved, errors;
+
+ if (CHECK_HANDLE(handle))
+ return CS_BAD_HANDLE;
+
+ info->Chains = reserved = errors = 0;
+ tuple.DesiredTuple = RETURN_FIRST_TUPLE;
+ tuple.Attributes = TUPLE_RETURN_COMMON;
+ ret = get_first_tuple(handle, &tuple);
+ if (ret != CS_SUCCESS)
+ return CS_SUCCESS;
+
+ /* First tuple should be DEVICE */
+ if (tuple.TupleCode != CISTPL_DEVICE)
+ errors++;
+ /* All cards should have a MANFID tuple */
+ if (read_tuple(handle, CISTPL_MANFID, &p) != CS_SUCCESS)
+ errors++;
+ /* All cards should have either a VERS_1 or a VERS_2 tuple. But
+ at worst, we'll accept a CFTABLE_ENTRY that parses. */
+ if ((read_tuple(handle, CISTPL_VERS_1, &p) != CS_SUCCESS) &&
+ (read_tuple(handle, CISTPL_VERS_2, &p) != CS_SUCCESS) &&
+ (read_tuple(handle, CISTPL_CFTABLE_ENTRY, &p) != CS_SUCCESS) &&
+ (read_tuple(handle, CISTPL_CFTABLE_ENTRY_CB, &p) != CS_SUCCESS))
+ errors++;
+ if (errors > 1)
+ return CS_SUCCESS;
+
+ for (info->Chains = 1; info->Chains < MAX_TUPLES; info->Chains++) {
+ ret = get_next_tuple(handle, &tuple);
+ if (ret != CS_SUCCESS) break;
+ if (((tuple.TupleCode > 0x23) && (tuple.TupleCode < 0x40)) ||
+ ((tuple.TupleCode > 0x47) && (tuple.TupleCode < 0x80)) ||
+ ((tuple.TupleCode > 0x90) && (tuple.TupleCode < 0xff)))
+ reserved++;
+ }
+ if ((info->Chains == MAX_TUPLES) || (reserved > 5))
+ info->Chains = 0;
+
+ return CS_SUCCESS;
+}
+
diff --git a/drivers/pcmcia/cs.c b/drivers/pcmcia/cs.c
new file mode 100644
index 000000000..470514c55
--- /dev/null
+++ b/drivers/pcmcia/cs.c
@@ -0,0 +1,2243 @@
+/*======================================================================
+
+ PCMCIA Card Services -- core services
+
+ cs.c 1.228 1999/09/15 15:32:19
+
+ 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 David A. Hinds
+ <dhinds@hyper.stanford.edu>. Portions created by David A. Hinds
+ are Copyright (C) 1999 David A. Hinds. 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/kernel.h>
+#include <linux/config.h>
+#include <linux/string.h>
+#include <linux/major.h>
+#include <linux/errno.h>
+#include <linux/malloc.h>
+#include <linux/mm.h>
+#include <linux/sched.h>
+#include <linux/timer.h>
+#include <linux/ioport.h>
+#include <linux/delay.h>
+#include <linux/proc_fs.h>
+#include <linux/compile.h>
+#include <asm/system.h>
+#include <asm/irq.h>
+
+#define IN_CARD_SERVICES
+#include <pcmcia/version.h>
+#include <pcmcia/cs_types.h>
+#include <pcmcia/ss.h>
+#include <pcmcia/cs.h>
+#include <pcmcia/bulkmem.h>
+#include <pcmcia/cistpl.h>
+#include <pcmcia/cisreg.h>
+#include <pcmcia/bus_ops.h>
+#include "cs_internal.h"
+#include "rsrc_mgr.h"
+
+#ifdef CONFIG_APM
+#include <linux/apm_bios.h>
+static int handle_apm_event(apm_event_t event);
+#endif
+
+#ifdef PCMCIA_DEBUG
+int pc_debug = PCMCIA_DEBUG;
+MODULE_PARM(pc_debug, "i");
+static const char *version =
+"cs.c 1.228 1999/09/15 15:32:19 (David Hinds)";
+#endif
+
+static const char *release = "Linux PCMCIA Card Services " CS_RELEASE;
+#ifdef MODULE
+static const char *kernel = "kernel build: " UTS_RELEASE " " UTS_VERSION;
+#endif
+static const char *options = "options: "
+#ifdef CONFIG_PCI
+" [pci]"
+#endif
+#ifdef CONFIG_CARDBUS
+" [cardbus]"
+#endif
+#ifdef CONFIG_APM
+" [apm]"
+#endif
+#if !defined(CONFIG_CARDBUS) && !defined(CONFIG_PCI) && \
+ !defined(CONFIG_APM) && !defined(CONFIG_PNP_BIOS)
+" none"
+#endif
+;
+
+/*====================================================================*/
+
+/* Parameters that can be set with 'insmod' */
+
+static int setup_delay = HZ/20; /* ticks */
+static int resume_delay = HZ/5; /* ticks */
+static int shutdown_delay = HZ/40; /* ticks */
+static int vcc_settle = HZ*3/10; /* ticks */
+static int reset_time = 10; /* usecs */
+static int unreset_delay = HZ/10; /* ticks */
+static int unreset_check = HZ/10; /* ticks */
+static int unreset_limit = 30; /* unreset_check's */
+
+/* Access speed for attribute memory windows */
+static int cis_speed = 300; /* ns */
+
+/* Access speed for IO windows */
+static int io_speed = 0; /* ns */
+
+/* Optional features */
+#ifdef CONFIG_APM
+static int do_apm = 1;
+MODULE_PARM(do_apm, "i");
+#endif
+
+MODULE_PARM(setup_delay, "i");
+MODULE_PARM(resume_delay, "i");
+MODULE_PARM(shutdown_delay, "i");
+MODULE_PARM(vcc_settle, "i");
+MODULE_PARM(reset_time, "i");
+MODULE_PARM(unreset_delay, "i");
+MODULE_PARM(unreset_check, "i");
+MODULE_PARM(unreset_limit, "i");
+MODULE_PARM(cis_speed, "i");
+MODULE_PARM(io_speed, "i");
+
+/*====================================================================*/
+
+static socket_state_t dead_socket = {
+ 0, SS_DETECT, 0, 0, 0
+};
+
+/* Table of sockets */
+socket_t sockets = 0;
+socket_info_t *socket_table[MAX_SOCK];
+
+#ifdef CONFIG_PROC_FS
+struct proc_dir_entry *proc_pccard = NULL;
+#endif
+
+/*====================================================================*/
+
+/* String tables for error messages */
+
+typedef struct lookup_t {
+ int key;
+ char *msg;
+} lookup_t;
+
+static const lookup_t error_table[] = {
+ { CS_SUCCESS, "Operation succeeded" },
+ { CS_BAD_ADAPTER, "Bad adapter" },
+ { CS_BAD_ATTRIBUTE, "Bad attribute", },
+ { CS_BAD_BASE, "Bad base address" },
+ { CS_BAD_EDC, "Bad EDC" },
+ { CS_BAD_IRQ, "Bad IRQ" },
+ { CS_BAD_OFFSET, "Bad offset" },
+ { CS_BAD_PAGE, "Bad page number" },
+ { CS_READ_FAILURE, "Read failure" },
+ { CS_BAD_SIZE, "Bad size" },
+ { CS_BAD_SOCKET, "Bad socket" },
+ { CS_BAD_TYPE, "Bad type" },
+ { CS_BAD_VCC, "Bad Vcc" },
+ { CS_BAD_VPP, "Bad Vpp" },
+ { CS_BAD_WINDOW, "Bad window" },
+ { CS_WRITE_FAILURE, "Write failure" },
+ { CS_NO_CARD, "No card present" },
+ { CS_UNSUPPORTED_FUNCTION, "Usupported function" },
+ { CS_UNSUPPORTED_MODE, "Unsupported mode" },
+ { CS_BAD_SPEED, "Bad speed" },
+ { CS_BUSY, "Resource busy" },
+ { CS_GENERAL_FAILURE, "General failure" },
+ { CS_WRITE_PROTECTED, "Write protected" },
+ { CS_BAD_ARG_LENGTH, "Bad argument length" },
+ { CS_BAD_ARGS, "Bad arguments" },
+ { CS_CONFIGURATION_LOCKED, "Configuration locked" },
+ { CS_IN_USE, "Resource in use" },
+ { CS_NO_MORE_ITEMS, "No more items" },
+ { CS_OUT_OF_RESOURCE, "Out of resource" },
+ { CS_BAD_HANDLE, "Bad handle" },
+ { CS_BAD_TUPLE, "Bad CIS tuple" }
+};
+#define ERROR_COUNT (sizeof(error_table)/sizeof(lookup_t))
+
+static const lookup_t service_table[] = {
+ { AccessConfigurationRegister, "AccessConfigurationRegister" },
+ { AddSocketServices, "AddSocketServices" },
+ { AdjustResourceInfo, "AdjustResourceInfo" },
+ { CheckEraseQueue, "CheckEraseQueue" },
+ { CloseMemory, "CloseMemory" },
+ { DeregisterClient, "DeregisterClient" },
+ { DeregisterEraseQueue, "DeregisterEraseQueue" },
+ { GetCardServicesInfo, "GetCardServicesInfo" },
+ { GetClientInfo, "GetClientInfo" },
+ { GetConfigurationInfo, "GetConfigurationInfo" },
+ { GetEventMask, "GetEventMask" },
+ { GetFirstClient, "GetFirstClient" },
+ { GetFirstRegion, "GetFirstRegion" },
+ { GetFirstTuple, "GetFirstTuple" },
+ { GetNextClient, "GetNextClient" },
+ { GetNextRegion, "GetNextRegion" },
+ { GetNextTuple, "GetNextTuple" },
+ { GetStatus, "GetStatus" },
+ { GetTupleData, "GetTupleData" },
+ { MapMemPage, "MapMemPage" },
+ { ModifyConfiguration, "ModifyConfiguration" },
+ { ModifyWindow, "ModifyWindow" },
+ { OpenMemory, "OpenMemory" },
+ { ParseTuple, "ParseTuple" },
+ { ReadMemory, "ReadMemory" },
+ { RegisterClient, "RegisterClient" },
+ { RegisterEraseQueue, "RegisterEraseQueue" },
+ { RegisterMTD, "RegisterMTD" },
+ { ReleaseConfiguration, "ReleaseConfiguration" },
+ { ReleaseIO, "ReleaseIO" },
+ { ReleaseIRQ, "ReleaseIRQ" },
+ { ReleaseWindow, "ReleaseWindow" },
+ { RequestConfiguration, "RequestConfiguration" },
+ { RequestIO, "RequestIO" },
+ { RequestIRQ, "RequestIRQ" },
+ { RequestSocketMask, "RequestSocketMask" },
+ { RequestWindow, "RequestWindow" },
+ { ResetCard, "ResetCard" },
+ { SetEventMask, "SetEventMask" },
+ { ValidateCIS, "ValidateCIS" },
+ { WriteMemory, "WriteMemory" },
+ { BindDevice, "BindDevice" },
+ { BindMTD, "BindMTD" },
+ { ReportError, "ReportError" },
+ { SuspendCard, "SuspendCard" },
+ { ResumeCard, "ResumeCard" },
+ { EjectCard, "EjectCard" },
+ { InsertCard, "InsertCard" },
+ { ReplaceCIS, "ReplaceCIS" }
+};
+#define SERVICE_COUNT (sizeof(service_table)/sizeof(lookup_t))
+
+/*======================================================================
+
+ Reset a socket to the default state
+
+======================================================================*/
+
+static void init_socket(socket_info_t *s)
+{
+ int i;
+ pccard_io_map io = { 0, 0, 0, 0, 1 };
+ pccard_mem_map mem = { 0, 0, 0, 0, 0, 0 };
+
+ mem.sys_stop = s->cap.map_size;
+ s->socket = dead_socket;
+ s->ss_entry(s->sock, SS_SetSocket, &s->socket);
+ for (i = 0; i < 2; i++) {
+ io.map = i;
+ s->ss_entry(s->sock, SS_SetIOMap, &io);
+ }
+ for (i = 0; i < 5; i++) {
+ mem.map = i;
+ s->ss_entry(s->sock, SS_SetMemMap, &mem);
+ }
+}
+
+/*====================================================================*/
+
+#if defined(CONFIG_PROC_FS) && defined(PCMCIA_DEBUG)
+int proc_read_clients(char *buf, char **start, off_t pos,
+ int count, int *eof, void *data)
+{
+ socket_info_t *s = data;
+ client_handle_t c;
+ char *p = buf;
+
+ for (c = s->clients; c; c = c->next)
+ p += sprintf(p, "fn %x: '%s' [attr 0x%04x] [state 0x%04x]\n",
+ c->Function, c->dev_info, c->Attributes, c->state);
+ return (p - buf);
+}
+#endif
+
+/*======================================================================
+
+ Low-level PC Card interface drivers need to register with Card
+ Services using these calls.
+
+======================================================================*/
+
+static void setup_socket(u_long i);
+static void shutdown_socket(u_long i);
+static void reset_socket(u_long i);
+static void unreset_socket(u_long i);
+static void parse_events(void *info, u_int events);
+
+int register_ss_entry(int nsock, ss_entry_t ss_entry)
+{
+ int i, ns;
+ socket_info_t *s;
+
+ DEBUG(0, "cs: register_ss_entry(%d, 0x%p)\n", nsock, ss_entry);
+
+ for (ns = 0; ns < nsock; ns++) {
+ s = kmalloc(sizeof(struct socket_info_t), GFP_KERNEL);
+ memset(s, 0, sizeof(socket_info_t));
+
+ s->ss_entry = ss_entry;
+ s->sock = ns;
+ s->setup.data = sockets;
+ s->setup.function = &setup_socket;
+ s->shutdown.data = sockets;
+ s->shutdown.function = &shutdown_socket;
+ /* base address = 0, map = 0 */
+ s->cis_mem.flags = 0;
+ s->cis_mem.speed = cis_speed;
+ s->erase_busy.next = s->erase_busy.prev = &s->erase_busy;
+
+ for (i = 0; i < sockets; i++)
+ if (socket_table[i] == NULL) break;
+ socket_table[i] = s;
+ if (i == sockets) sockets++;
+
+ init_socket(s);
+ ss_entry(ns, SS_InquireSocket, &s->cap);
+#ifdef CONFIG_PROC_FS
+ if (proc_pccard) {
+ char name[3];
+#ifdef PCMCIA_DEBUG
+ struct proc_dir_entry *ent;
+#endif
+ sprintf(name, "%02d", i);
+ s->proc = create_proc_entry(name, S_IFDIR, proc_pccard);
+#ifdef PCMCIA_DEBUG
+ ent = create_proc_entry("clients", 0, s->proc);
+ ent->read_proc = proc_read_clients;
+ ent->data = s;
+#endif
+ ss_entry(ns, SS_ProcSetup, s->proc);
+ }
+#endif
+ }
+
+ return 0;
+} /* register_ss_entry */
+
+/*====================================================================*/
+
+void unregister_ss_entry(ss_entry_t ss_entry)
+{
+ int i, j;
+ socket_info_t *s = NULL;
+ client_t *client;
+
+ for (;;) {
+ for (i = 0; i < sockets; i++) {
+ s = socket_table[i];
+ if (s->ss_entry == ss_entry) break;
+ }
+ if (i == sockets) {
+ break;
+ } else {
+#ifdef CONFIG_PROC_FS
+ if (proc_pccard) {
+ char name[3];
+ sprintf(name, "%02d", i);
+#ifdef PCMCIA_DEBUG
+ remove_proc_entry("clients", s->proc);
+#endif
+ remove_proc_entry(name, proc_pccard);
+ }
+#endif
+ while (s->clients) {
+ client = s->clients;
+ s->clients = s->clients->next;
+ kfree(client);
+ }
+ init_socket(s);
+ release_cis_mem(s);
+#ifdef CONFIG_CARDBUS
+ cb_release_cis_mem(s);
+#endif
+ s->ss_entry = NULL;
+ kfree(s);
+ socket_table[i] = NULL;
+ for (j = i; j < sockets-1; j++)
+ socket_table[j] = socket_table[j+1];
+ sockets--;
+ }
+ }
+
+} /* unregister_ss_entry */
+
+/*======================================================================
+
+ Shutdown_Socket() and setup_socket() are scheduled using add_timer
+ calls by the main event handler when card insertion and removal
+ events are received. Shutdown_Socket() unconfigures a socket and
+ turns off socket power. Setup_socket() turns on socket power
+ and resets the socket, in two stages.
+
+======================================================================*/
+
+static void free_regions(memory_handle_t *list)
+{
+ memory_handle_t tmp;
+ while (*list != NULL) {
+ tmp = *list;
+ *list = tmp->info.next;
+ tmp->region_magic = 0;
+ kfree(tmp);
+ }
+}
+
+static int send_event(socket_info_t *s, event_t event, int priority);
+
+static void shutdown_socket(u_long i)
+{
+ socket_info_t *s = socket_table[i];
+ client_t **c;
+
+ DEBUG(1, "cs: shutdown_socket(%ld)\n", i);
+
+ /* Blank out the socket state */
+ s->state &= SOCKET_PRESENT|SOCKET_SETUP_PENDING;
+ init_socket(s);
+ s->irq.AssignedIRQ = s->irq.Config = 0;
+ s->functions = 0;
+ s->lock_count = 0;
+ s->cis_used = 0;
+ if (s->fake_cis) {
+ kfree(s->fake_cis);
+ s->fake_cis = NULL;
+ }
+#ifdef CONFIG_CARDBUS
+ cb_release_cis_mem(s);
+ cb_free(s);
+#endif
+ if (s->config) {
+ kfree(s->config);
+ s->config = NULL;
+ }
+ for (c = &s->clients; *c; ) {
+ if ((*c)->state & CLIENT_UNBOUND) {
+ client_t *d = *c;
+ *c = (*c)->next;
+ kfree(d);
+ } else {
+ c = &((*c)->next);
+ }
+ }
+ free_regions(&s->a_region);
+ free_regions(&s->c_region);
+} /* shutdown_socket */
+
+static void setup_socket(u_long i)
+{
+ int val;
+ socket_info_t *s = socket_table[i];
+
+ s->ss_entry(s->sock, SS_GetStatus, &val);
+ if (val & SS_DETECT) {
+ DEBUG(1, "cs: setup_socket(%ld): applying power\n", i);
+ s->state |= SOCKET_PRESENT;
+ s->socket.flags = 0;
+ if (val & SS_3VCARD)
+ s->socket.Vcc = s->socket.Vpp = 33;
+ else if (!(val & SS_XVCARD))
+ s->socket.Vcc = s->socket.Vpp = 50;
+ else {
+ printk(KERN_NOTICE "cs: socket %ld: unsupported "
+ "voltage key\n", i);
+ s->socket.Vcc = 0;
+ }
+ if (val & SS_CARDBUS) {
+ s->state |= SOCKET_CARDBUS;
+#ifndef CONFIG_CARDBUS
+ printk(KERN_NOTICE "cs: unsupported card type detected!\n");
+#endif
+ }
+ s->ss_entry(s->sock, SS_SetSocket, &s->socket);
+ s->setup.function = &reset_socket;
+ s->setup.expires = jiffies + vcc_settle;
+ add_timer(&s->setup);
+ } else
+ DEBUG(0, "cs: setup_socket(%ld): no card!\n", i);
+} /* setup_socket */
+
+/*======================================================================
+
+ Reset_socket() and unreset_socket() handle hard resets. Resets
+ have several causes: card insertion, a call to reset_socket, or
+ recovery from a suspend/resume cycle. Unreset_socket() sends
+ a CS event that matches the cause of the reset.
+
+======================================================================*/
+
+static void reset_socket(u_long i)
+{
+ socket_info_t *s = socket_table[i];
+
+ DEBUG(1, "cs: resetting socket %ld\n", i);
+ s->socket.flags |= SS_OUTPUT_ENA | SS_RESET;
+ s->ss_entry(s->sock, SS_SetSocket, &s->socket);
+ udelay((long)reset_time);
+ s->socket.flags &= ~SS_RESET;
+ s->ss_entry(s->sock, SS_SetSocket, &s->socket);
+ s->unreset_timeout = 0;
+ s->setup.expires = jiffies + unreset_delay;
+ s->setup.function = &unreset_socket;
+ add_timer(&s->setup);
+} /* reset_socket */
+
+#define EVENT_MASK \
+(SOCKET_SETUP_PENDING|SOCKET_SUSPEND|SOCKET_RESET_PENDING)
+
+static void unreset_socket(u_long i)
+{
+ socket_info_t *s = socket_table[i];
+ int val;
+
+ s->ss_entry(s->sock, SS_GetStatus, &val);
+ if (val & SS_READY) {
+ DEBUG(1, "cs: reset done on socket %ld\n", i);
+ if (s->state & SOCKET_SUSPEND) {
+ s->state &= ~EVENT_MASK;
+ if (verify_cis_cache(s) != 0)
+ parse_events(s, SS_DETECT);
+ else
+ send_event(s, CS_EVENT_PM_RESUME, CS_EVENT_PRI_LOW);
+ } else if (s->state & SOCKET_SETUP_PENDING) {
+#ifdef CONFIG_CARDBUS
+ if (s->state & SOCKET_CARDBUS)
+ cb_alloc(s);
+#endif
+ send_event(s, CS_EVENT_CARD_INSERTION, CS_EVENT_PRI_LOW);
+ s->state &= ~SOCKET_SETUP_PENDING;
+ } else {
+ send_event(s, CS_EVENT_CARD_RESET, CS_EVENT_PRI_LOW);
+ s->reset_handle->event_callback_args.info = NULL;
+ EVENT(s->reset_handle, CS_EVENT_RESET_COMPLETE,
+ CS_EVENT_PRI_LOW);
+ s->state &= ~EVENT_MASK;
+ }
+ } else {
+ DEBUG(2, "cs: socket %ld not ready yet\n", i);
+ if (s->unreset_timeout > unreset_limit) {
+ printk(KERN_NOTICE "cs: socket %ld timed out during"
+ " reset\n", i);
+ s->state &= ~EVENT_MASK;
+ } else {
+ s->unreset_timeout++;
+ s->setup.expires = jiffies + unreset_check;
+ add_timer(&s->setup);
+ }
+ }
+} /* unreset_socket */
+
+/*======================================================================
+
+ The central event handler. Send_event() sends an event to all
+ valid clients. Parse_events() interprets the event bits from
+ a card status change report. Do_shotdown() handles the high
+ priority stuff associated with a card removal.
+
+======================================================================*/
+
+static int send_event(socket_info_t *s, event_t event, int priority)
+{
+ client_t *client = s->clients;
+ int ret;
+ DEBUG(1, "cs: send_event(sock %d, event %d, pri %d)\n",
+ s->sock, event, priority);
+ ret = 0;
+ for (; client; client = client->next) {
+ if (client->state & (CLIENT_UNBOUND|CLIENT_STALE))
+ continue;
+ if (client->EventMask & event) {
+ ret = EVENT(client, event, priority);
+ if (ret != 0)
+ return ret;
+ }
+ }
+ return ret;
+} /* send_event */
+
+static void do_shutdown(socket_info_t *s)
+{
+ client_t *client;
+ if (s->state & SOCKET_SHUTDOWN_PENDING)
+ return;
+ s->state |= SOCKET_SHUTDOWN_PENDING;
+ send_event(s, CS_EVENT_CARD_REMOVAL, CS_EVENT_PRI_HIGH);
+ for (client = s->clients; client; client = client->next)
+ if (!(client->Attributes & INFO_MASTER_CLIENT))
+ client->state |= CLIENT_STALE;
+ if (s->state & (SOCKET_SETUP_PENDING|SOCKET_RESET_PENDING)) {
+ DEBUG(0, "cs: flushing pending setup\n");
+ del_timer(&s->setup);
+ s->state &= ~EVENT_MASK;
+ }
+ s->shutdown.expires = jiffies + shutdown_delay;
+ add_timer(&s->shutdown);
+ s->state &= ~SOCKET_PRESENT;
+}
+
+static void parse_events(void *info, u_int events)
+{
+ socket_info_t *s = info;
+ if (events & SS_DETECT) {
+ int status;
+ u_long flags;
+ spin_lock_irqsave(&s->lock, flags);
+ s->ss_entry(s->sock, SS_GetStatus, &status);
+ if ((s->state & SOCKET_PRESENT) &&
+ (!(s->state & SOCKET_SUSPEND) ||
+ !(status & SS_DETECT)))
+ do_shutdown(s);
+ if (status & SS_DETECT) {
+ if (s->state & SOCKET_SETUP_PENDING) {
+ del_timer(&s->setup);
+ DEBUG(1, "cs: delaying pending setup\n");
+ }
+ s->state |= SOCKET_SETUP_PENDING;
+ s->setup.function = &setup_socket;
+ if (s->state & SOCKET_SUSPEND)
+ s->setup.expires = jiffies + resume_delay;
+ else
+ s->setup.expires = jiffies + setup_delay;
+ add_timer(&s->setup);
+ }
+ spin_unlock_irqrestore(&s->lock, flags);
+ }
+ if (events & SS_BATDEAD)
+ send_event(s, CS_EVENT_BATTERY_DEAD, CS_EVENT_PRI_LOW);
+ if (events & SS_BATWARN)
+ send_event(s, CS_EVENT_BATTERY_LOW, CS_EVENT_PRI_LOW);
+ if (events & SS_READY) {
+ if (!(s->state & SOCKET_RESET_PENDING))
+ send_event(s, CS_EVENT_READY_CHANGE, CS_EVENT_PRI_LOW);
+ else DEBUG(1, "cs: ready change during reset\n");
+ }
+} /* parse_events */
+
+/*======================================================================
+
+ Another event handler, for power management events.
+
+ This does not comply with the latest PC Card spec for handling
+ power management events.
+
+======================================================================*/
+
+#ifdef CONFIG_APM
+static int handle_apm_event(apm_event_t event)
+{
+ int i, stat;
+ socket_info_t *s;
+ static int down = 0;
+
+ switch (event) {
+ case APM_SYS_SUSPEND:
+ case APM_USER_SUSPEND:
+ DEBUG(1, "cs: received suspend notification\n");
+ if (down) {
+ printk(KERN_DEBUG "cs: received extra suspend event\n");
+ break;
+ }
+ down = 1;
+ for (i = 0; i < sockets; i++) {
+ s = socket_table[i];
+ if ((s->state & SOCKET_PRESENT) &&
+ !(s->state & SOCKET_SUSPEND)){
+ send_event(s, CS_EVENT_PM_SUSPEND, CS_EVENT_PRI_LOW);
+ s->ss_entry(s->sock, SS_SetSocket, &dead_socket);
+ s->state |= SOCKET_SUSPEND;
+ }
+ }
+ break;
+ case APM_NORMAL_RESUME:
+ case APM_CRITICAL_RESUME:
+ DEBUG(1, "cs: received resume notification\n");
+ if (!down) {
+ printk(KERN_DEBUG "cs: received bogus resume event\n");
+ break;
+ }
+ down = 0;
+ for (i = 0; i < sockets; i++) {
+ s = socket_table[i];
+ /* Do this just to reinitialize the socket */
+ init_socket(s);
+ s->ss_entry(s->sock, SS_GetStatus, &stat);
+ /* If there was or is a card here, we need to do something
+ about it... but parse_events will sort it all out. */
+ if ((s->state & SOCKET_PRESENT) || (stat & SS_DETECT))
+ parse_events(s, SS_DETECT);
+ }
+ break;
+ }
+ return 0;
+} /* handle_apm_event */
+#endif
+
+/*======================================================================
+
+ Special stuff for managing IO windows, because they are scarce.
+
+======================================================================*/
+
+static int alloc_io_space(socket_info_t *s, u_int attr, ioaddr_t *base,
+ ioaddr_t num, char *name)
+{
+ int i;
+ ioaddr_t try;
+
+ for (i = 0; i < MAX_IO_WIN; i++) {
+ if (s->io[i].NumPorts == 0) {
+ if (find_io_region(base, num, name) == 0) {
+ s->io[i].Attributes = attr;
+ s->io[i].BasePort = *base;
+ s->io[i].NumPorts = s->io[i].InUse = num;
+ break;
+ } else
+ return 1;
+ } else if (s->io[i].Attributes != attr)
+ continue;
+ /* Try to extend top of window */
+ try = s->io[i].BasePort + s->io[i].NumPorts;
+ if ((*base == 0) || (*base == try))
+ if (find_io_region(&try, num, name) == 0) {
+ *base = try;
+ s->io[i].NumPorts += num;
+ s->io[i].InUse += num;
+ break;
+ }
+ /* Try to extend bottom of window */
+ try = s->io[i].BasePort - num;
+ if ((*base == 0) || (*base == try))
+ if (find_io_region(&try, num, name) == 0) {
+ s->io[i].BasePort = *base = try;
+ s->io[i].NumPorts += num;
+ s->io[i].InUse += num;
+ break;
+ }
+ }
+ return (i == MAX_IO_WIN);
+} /* alloc_io_space */
+
+static void release_io_space(socket_info_t *s, ioaddr_t base,
+ ioaddr_t num)
+{
+ int i;
+ release_region(base, num);
+ for (i = 0; i < MAX_IO_WIN; i++) {
+ if ((s->io[i].BasePort <= base) &&
+ (s->io[i].BasePort+s->io[i].NumPorts >= base+num)) {
+ s->io[i].InUse -= num;
+ /* Free the window if no one else is using it */
+ if (s->io[i].InUse == 0)
+ s->io[i].NumPorts = 0;
+ }
+ }
+}
+
+/*======================================================================
+
+ Access_configuration_register() reads and writes configuration
+ registers in attribute memory. Memory window 0 is reserved for
+ this and the tuple reading services.
+
+======================================================================*/
+
+static int access_configuration_register(client_handle_t handle,
+ conf_reg_t *reg)
+{
+ socket_info_t *s;
+ config_t *c;
+ int addr;
+ u_char val;
+
+ if (CHECK_HANDLE(handle))
+ return CS_BAD_HANDLE;
+ s = SOCKET(handle);
+ if (handle->Function == BIND_FN_ALL) {
+ if (reg->Function >= s->functions)
+ return CS_BAD_ARGS;
+ c = &s->config[reg->Function];
+ } else
+ c = CONFIG(handle);
+ if (!(c->state & CONFIG_LOCKED))
+ return CS_CONFIGURATION_LOCKED;
+
+ addr = (c->ConfigBase + reg->Offset) >> 1;
+
+ switch (reg->Action) {
+ case CS_READ:
+ read_cis_mem(s, 1, addr, 1, &val);
+ reg->Value = val;
+ break;
+ case CS_WRITE:
+ val = reg->Value;
+ write_cis_mem(s, 1, addr, 1, &val);
+ break;
+ default:
+ return CS_BAD_ARGS;
+ break;
+ }
+ return CS_SUCCESS;
+} /* access_configuration_register */
+
+/*======================================================================
+
+ Bind_device() associates a device driver with a particular socket.
+ It is normally called by Driver Services after it has identified
+ a newly inserted card. An instance of that driver will then be
+ eligible to register as a client of this socket.
+
+======================================================================*/
+
+static int bind_device(bind_req_t *req)
+{
+ client_t *client;
+ socket_info_t *s;
+
+ if (CHECK_SOCKET(req->Socket))
+ return CS_BAD_SOCKET;
+ s = SOCKET(req);
+
+ client = (client_t *)kmalloc(sizeof(client_t), GFP_KERNEL);
+ memset(client, '\0', sizeof(client_t));
+ client->client_magic = CLIENT_MAGIC;
+ strncpy(client->dev_info, (char *)req->dev_info, DEV_NAME_LEN);
+ client->Socket = req->Socket;
+ client->Function = req->Function;
+ client->state = CLIENT_UNBOUND;
+ client->erase_busy.next = &client->erase_busy;
+ client->erase_busy.prev = &client->erase_busy;
+ init_waitqueue_head(&client->mtd_req);
+ client->next = s->clients;
+ s->clients = client;
+ DEBUG(1, "cs: bind_device(): client 0x%p, sock %d, dev %s\n",
+ client, client->Socket, client->dev_info);
+ return CS_SUCCESS;
+} /* bind_device */
+
+/*======================================================================
+
+ Bind_mtd() associates a device driver with a particular memory
+ region. It is normally called by Driver Services after it has
+ identified a memory device type. An instance of the corresponding
+ driver will then be able to register to control this region.
+
+======================================================================*/
+
+static int bind_mtd(mtd_bind_t *req)
+{
+ socket_info_t *s;
+ memory_handle_t region;
+
+ if (CHECK_SOCKET(req->Socket))
+ return CS_BAD_SOCKET;
+ s = SOCKET(req);
+
+ if (req->Attributes & REGION_TYPE_AM)
+ region = s->a_region;
+ else
+ region = s->c_region;
+
+ while (region) {
+ if (region->info.CardOffset == req->CardOffset) break;
+ region = region->info.next;
+ }
+ if (!region || (region->mtd != NULL))
+ return CS_BAD_OFFSET;
+ strncpy(region->dev_info, (char *)req->dev_info, DEV_NAME_LEN);
+
+ DEBUG(1, "cs: bind_mtd(): attr 0x%x, offset 0x%x, dev %s\n",
+ req->Attributes, req->CardOffset, (char *)req->dev_info);
+ return CS_SUCCESS;
+} /* bind_mtd */
+
+/*====================================================================*/
+
+static int deregister_client(client_handle_t handle)
+{
+ client_t **client;
+ socket_info_t *s;
+ memory_handle_t region;
+ u_long flags;
+ int i, sn;
+
+ DEBUG(1, "cs: deregister_client(%p)\n", handle);
+ if (CHECK_HANDLE(handle))
+ return CS_BAD_HANDLE;
+ if (handle->state &
+ (CLIENT_IRQ_REQ|CLIENT_IO_REQ|CLIENT_CONFIG_LOCKED))
+ return CS_IN_USE;
+ for (i = 0; i < MAX_WIN; i++)
+ if (handle->state & CLIENT_WIN_REQ(i))
+ return CS_IN_USE;
+
+ /* Disconnect all MTD links */
+ s = SOCKET(handle);
+ if (handle->mtd_count) {
+ for (region = s->a_region; region; region = region->info.next)
+ if (region->mtd == handle) region->mtd = NULL;
+ for (region = s->c_region; region; region = region->info.next)
+ if (region->mtd == handle) region->mtd = NULL;
+ }
+
+ sn = handle->Socket; s = socket_table[sn];
+
+ if ((handle->state & CLIENT_STALE) ||
+ (handle->Attributes & INFO_MASTER_CLIENT)) {
+ spin_lock_irqsave(&s->lock, flags);
+ client = &s->clients;
+ while ((*client) && ((*client) != handle))
+ client = &(*client)->next;
+ if (*client == NULL)
+ return CS_BAD_HANDLE;
+ *client = handle->next;
+ handle->client_magic = 0;
+ kfree(handle);
+ spin_unlock_irqrestore(&s->lock, flags);
+ } else {
+ handle->state = CLIENT_UNBOUND;
+ handle->mtd_count = 0;
+ handle->event_handler = NULL;
+ }
+
+ if (--s->real_clients == 0)
+ s->ss_entry(sn, SS_RegisterCallback, NULL);
+
+ return CS_SUCCESS;
+} /* deregister_client */
+
+/*====================================================================*/
+
+static int get_configuration_info(client_handle_t handle,
+ config_info_t *config)
+{
+ socket_info_t *s;
+ config_t *c;
+
+ if (CHECK_HANDLE(handle))
+ return CS_BAD_HANDLE;
+ s = SOCKET(handle);
+ if (!(s->state & SOCKET_PRESENT))
+ return CS_NO_CARD;
+
+ if (handle->Function == BIND_FN_ALL) {
+ if (config->Function && (config->Function >= s->functions))
+ return CS_BAD_ARGS;
+ } else
+ config->Function = handle->Function;
+
+#ifdef CONFIG_CARDBUS
+ if (s->state & SOCKET_CARDBUS) {
+ u_char fn = config->Function;
+ memset(config, 0, sizeof(config_info_t));
+ config->Function = fn;
+ config->Vcc = s->socket.Vcc;
+ config->Vpp1 = config->Vpp2 = s->socket.Vpp;
+ config->Option = s->cap.cardbus;
+ if (s->cb_config) {
+ config->Attributes = CONF_VALID_CLIENT;
+ config->IntType = INT_CARDBUS;
+ config->AssignedIRQ = s->irq.AssignedIRQ;
+ if (config->AssignedIRQ)
+ config->Attributes |= CONF_ENABLE_IRQ;
+ config->BasePort1 = s->io[0].BasePort;
+ config->NumPorts1 = s->io[0].NumPorts;
+ }
+ return CS_SUCCESS;
+ }
+#endif
+
+ c = (s->config != NULL) ? &s->config[config->Function] : NULL;
+
+ if ((c == NULL) || !(c->state & CONFIG_LOCKED)) {
+ config->Attributes = 0;
+ config->Vcc = s->socket.Vcc;
+ config->Vpp1 = config->Vpp2 = s->socket.Vpp;
+ return CS_SUCCESS;
+ }
+
+ /* !!! This is a hack !!! */
+ memcpy(&config->Attributes, &c->Attributes, sizeof(config_t));
+ config->Attributes |= CONF_VALID_CLIENT;
+ config->CardValues = c->CardValues;
+ config->IRQAttributes = c->irq.Attributes;
+ config->AssignedIRQ = s->irq.AssignedIRQ;
+ config->BasePort1 = c->io.BasePort1;
+ config->NumPorts1 = c->io.NumPorts1;
+ config->Attributes1 = c->io.Attributes1;
+ config->BasePort2 = c->io.BasePort2;
+ config->NumPorts2 = c->io.NumPorts2;
+ config->Attributes2 = c->io.Attributes2;
+ config->IOAddrLines = c->io.IOAddrLines;
+
+ return CS_SUCCESS;
+} /* get_configuration_info */
+
+/*======================================================================
+
+ Return information about this version of Card Services.
+
+======================================================================*/
+
+static int get_card_services_info(servinfo_t *info)
+{
+ info->Signature[0] = 'C';
+ info->Signature[1] = 'S';
+ info->Count = sockets;
+ info->Revision = CS_RELEASE_CODE;
+ info->CSLevel = 0x0210;
+ info->VendorString = (char *)release;
+ return CS_SUCCESS;
+} /* get_card_services_info */
+
+/*======================================================================
+
+ Note that get_first_client() *does* recognize the Socket field
+ in the request structure.
+
+======================================================================*/
+
+static int get_first_client(client_handle_t *handle, client_req_t *req)
+{
+ socket_t s;
+ if (req->Attributes & CLIENT_THIS_SOCKET)
+ s = req->Socket;
+ else
+ s = 0;
+ if (CHECK_SOCKET(req->Socket))
+ return CS_BAD_SOCKET;
+ if (socket_table[s]->clients == NULL)
+ return CS_NO_MORE_ITEMS;
+ *handle = socket_table[s]->clients;
+ return CS_SUCCESS;
+} /* get_first_client */
+
+/*====================================================================*/
+
+static int get_next_client(client_handle_t *handle, client_req_t *req)
+{
+ socket_info_t *s;
+ if ((handle == NULL) || CHECK_HANDLE(*handle))
+ return CS_BAD_HANDLE;
+ if ((*handle)->next == NULL) {
+ if (req->Attributes & CLIENT_THIS_SOCKET)
+ return CS_NO_MORE_ITEMS;
+ s = SOCKET(*handle);
+ if (s->clients == NULL)
+ return CS_NO_MORE_ITEMS;
+ *handle = s->clients;
+ } else
+ *handle = (*handle)->next;
+ return CS_SUCCESS;
+} /* get_next_client */
+
+/*====================================================================*/
+
+static int get_window(window_handle_t *handle, int idx, win_req_t *req)
+{
+ socket_info_t *s;
+ window_t *win;
+ int w;
+
+ if (idx == 0)
+ s = SOCKET((client_handle_t)*handle);
+ else
+ s = (*handle)->sock;
+ if (!(s->state & SOCKET_PRESENT))
+ return CS_NO_CARD;
+ for (w = idx; w < MAX_WIN; w++)
+ if (s->state & SOCKET_WIN_REQ(w)) break;
+ if (w == MAX_WIN)
+ return CS_NO_MORE_ITEMS;
+ win = &s->win[w];
+ req->Base = win->ctl.sys_start;
+ req->Size = win->ctl.sys_stop - win->ctl.sys_start + 1;
+ req->AccessSpeed = win->ctl.speed;
+ req->Attributes = 0;
+ if (win->ctl.flags & MAP_ATTRIB)
+ req->Attributes |= WIN_MEMORY_TYPE_AM;
+ if (win->ctl.flags & MAP_ACTIVE)
+ req->Attributes |= WIN_ENABLE;
+ if (win->ctl.flags & MAP_16BIT)
+ req->Attributes |= WIN_DATA_WIDTH;
+ if (win->ctl.flags & MAP_USE_WAIT)
+ req->Attributes |= WIN_USE_WAIT;
+ *handle = win;
+ return CS_SUCCESS;
+} /* get_window */
+
+static int get_first_window(client_handle_t *handle, win_req_t *req)
+{
+ if ((handle == NULL) || CHECK_HANDLE(*handle))
+ return CS_BAD_HANDLE;
+ return get_window((window_handle_t *)handle, 0, req);
+}
+
+static int get_next_window(window_handle_t *win, win_req_t *req)
+{
+ if ((win == NULL) || ((*win)->magic != WINDOW_MAGIC))
+ return CS_BAD_HANDLE;
+ return get_window(win, (*win)->index+1, req);
+}
+
+/*======================================================================
+
+ Get the current socket state bits. We don't support the latched
+ SocketState yet: I haven't seen any point for it.
+
+======================================================================*/
+
+static int get_status(client_handle_t handle, cs_status_t *status)
+{
+ socket_info_t *s;
+ config_t *c;
+ int val;
+
+ if (CHECK_HANDLE(handle))
+ return CS_BAD_HANDLE;
+ s = SOCKET(handle);
+ s->ss_entry(s->sock, SS_GetStatus, &val);
+ status->CardState = status->SocketState = 0;
+ status->CardState |= (val & SS_DETECT) ? CS_EVENT_CARD_DETECT : 0;
+ status->CardState |= (val & SS_CARDBUS) ? CS_EVENT_CB_DETECT : 0;
+ status->CardState |= (val & SS_3VCARD) ? CS_EVENT_3VCARD : 0;
+ status->CardState |= (val & SS_XVCARD) ? CS_EVENT_XVCARD : 0;
+ if (s->state & SOCKET_SUSPEND)
+ status->CardState |= CS_EVENT_PM_SUSPEND;
+ if (!(s->state & SOCKET_PRESENT))
+ return CS_NO_CARD;
+ if (s->state & SOCKET_SETUP_PENDING)
+ status->CardState |= CS_EVENT_CARD_INSERTION;
+
+ /* Get info from the PRR, if necessary */
+ if (handle->Function == BIND_FN_ALL) {
+ if (status->Function && (status->Function >= s->functions))
+ return CS_BAD_ARGS;
+ c = (s->config != NULL) ? &s->config[status->Function] : NULL;
+ } else
+ c = CONFIG(handle);
+ if ((c != NULL) && (c->state & CONFIG_LOCKED) &&
+ (c->IntType & INT_MEMORY_AND_IO)) {
+ u_char reg;
+ if (c->Present & PRESENT_PIN_REPLACE) {
+ read_cis_mem(s, 1, (c->ConfigBase+CISREG_PRR)>>1, 1, &reg);
+ status->CardState |=
+ (reg & PRR_WP_STATUS) ? CS_EVENT_WRITE_PROTECT : 0;
+ status->CardState |=
+ (reg & PRR_READY_STATUS) ? CS_EVENT_READY_CHANGE : 0;
+ status->CardState |=
+ (reg & PRR_BVD2_STATUS) ? CS_EVENT_BATTERY_LOW : 0;
+ status->CardState |=
+ (reg & PRR_BVD1_STATUS) ? CS_EVENT_BATTERY_DEAD : 0;
+ } else {
+ /* No PRR? Then assume we're always ready */
+ status->CardState |= CS_EVENT_READY_CHANGE;
+ }
+ if (c->Present & PRESENT_EXT_STATUS) {
+ read_cis_mem(s, 1, (c->ConfigBase+CISREG_ESR)>>1, 1, &reg);
+ status->CardState |=
+ (reg & ESR_REQ_ATTN) ? CS_EVENT_REQUEST_ATTENTION : 0;
+ }
+ return CS_SUCCESS;
+ }
+ status->CardState |=
+ (val & SS_WRPROT) ? CS_EVENT_WRITE_PROTECT : 0;
+ status->CardState |=
+ (val & SS_BATDEAD) ? CS_EVENT_BATTERY_DEAD : 0;
+ status->CardState |=
+ (val & SS_BATWARN) ? CS_EVENT_BATTERY_LOW : 0;
+ status->CardState |=
+ (val & SS_READY) ? CS_EVENT_READY_CHANGE : 0;
+ return CS_SUCCESS;
+} /* get_status */
+
+/*======================================================================
+
+ Change the card address of an already open memory window.
+
+======================================================================*/
+
+static int get_mem_page(window_handle_t win, memreq_t *req)
+{
+ if ((win == NULL) || (win->magic != WINDOW_MAGIC))
+ return CS_BAD_HANDLE;
+ req->Page = 0;
+ req->CardOffset = win->ctl.card_start;
+ return CS_SUCCESS;
+} /* get_mem_page */
+
+static int map_mem_page(window_handle_t win, memreq_t *req)
+{
+ socket_info_t *s;
+ if ((win == NULL) || (win->magic != WINDOW_MAGIC))
+ return CS_BAD_HANDLE;
+ if (req->Page != 0)
+ return CS_BAD_PAGE;
+ s = win->sock;
+ win->ctl.card_start = req->CardOffset;
+ if (s->ss_entry(s->sock, SS_SetMemMap, &win->ctl) != 0)
+ return CS_BAD_OFFSET;
+ return CS_SUCCESS;
+} /* map_mem_page */
+
+/*======================================================================
+
+ Modify a locked socket configuration
+
+======================================================================*/
+
+static int modify_configuration(client_handle_t handle,
+ modconf_t *mod)
+{
+ socket_info_t *s;
+ config_t *c;
+
+ if (CHECK_HANDLE(handle))
+ return CS_BAD_HANDLE;
+ s = SOCKET(handle); c = CONFIG(handle);
+ if (!(s->state & SOCKET_PRESENT))
+ return CS_NO_CARD;
+ if (!(c->state & CONFIG_LOCKED))
+ return CS_CONFIGURATION_LOCKED;
+
+ if (mod->Attributes & CONF_IRQ_CHANGE_VALID) {
+ if (mod->Attributes & CONF_ENABLE_IRQ) {
+ c->Attributes |= CONF_ENABLE_IRQ;
+ s->socket.io_irq = s->irq.AssignedIRQ;
+ } else {
+ c->Attributes &= ~CONF_ENABLE_IRQ;
+ s->socket.io_irq = 0;
+ }
+ s->ss_entry(s->sock, SS_SetSocket, &s->socket);
+ }
+
+ if (mod->Attributes & CONF_VCC_CHANGE_VALID)
+ return CS_BAD_VCC;
+
+ /* We only allow changing Vpp1 and Vpp2 to the same value */
+ if ((mod->Attributes & CONF_VPP1_CHANGE_VALID) &&
+ (mod->Attributes & CONF_VPP2_CHANGE_VALID)) {
+ if (mod->Vpp1 != mod->Vpp2)
+ return CS_BAD_VPP;
+ c->Vpp1 = c->Vpp2 = s->socket.Vpp = mod->Vpp1;
+ if (s->ss_entry(s->sock, SS_SetSocket, &s->socket))
+ return CS_BAD_VPP;
+ } else if ((mod->Attributes & CONF_VPP1_CHANGE_VALID) ||
+ (mod->Attributes & CONF_VPP2_CHANGE_VALID))
+ return CS_BAD_VPP;
+
+ return CS_SUCCESS;
+} /* modify_configuration */
+
+/*======================================================================
+
+ Modify the attributes of a window returned by RequestWindow.
+
+======================================================================*/
+
+static int modify_window(window_handle_t win, modwin_t *req)
+{
+ if ((win == NULL) || (win->magic != WINDOW_MAGIC))
+ return CS_BAD_HANDLE;
+
+ win->ctl.flags &= ~(MAP_ATTRIB|MAP_ACTIVE);
+ if (req->Attributes & WIN_MEMORY_TYPE)
+ win->ctl.flags |= MAP_ATTRIB;
+ if (req->Attributes & WIN_ENABLE)
+ win->ctl.flags |= MAP_ACTIVE;
+ if (req->Attributes & WIN_DATA_WIDTH)
+ win->ctl.flags |= MAP_16BIT;
+ if (req->Attributes & WIN_USE_WAIT)
+ win->ctl.flags |= MAP_USE_WAIT;
+ win->ctl.speed = req->AccessSpeed;
+ win->sock->ss_entry(win->sock->sock, SS_SetMemMap, &win->ctl);
+
+ return CS_SUCCESS;
+} /* modify_window */
+
+/*======================================================================
+
+ Register_client() uses the dev_info_t handle to match the
+ caller with a socket. The driver must have already been bound
+ to a socket with bind_device() -- in fact, bind_device()
+ allocates the client structure that will be used.
+
+======================================================================*/
+
+static int register_client(client_handle_t *handle, client_reg_t *req)
+{
+ client_t *client;
+ socket_info_t *s;
+ socket_t ns;
+
+ /* Look for unbound client with matching dev_info */
+ client = NULL;
+ for (ns = 0; ns < sockets; ns++) {
+ client = socket_table[ns]->clients;
+ while (client != NULL) {
+ if ((strcmp(client->dev_info, (char *)req->dev_info) == 0)
+ && (client->state & CLIENT_UNBOUND)) break;
+ client = client->next;
+ }
+ if (client != NULL) break;
+ }
+ if (client == NULL)
+ return CS_OUT_OF_RESOURCE;
+
+ s = socket_table[ns];
+ if (++s->real_clients == 1) {
+ ss_callback_t call;
+ int status;
+ call.handler = &parse_events;
+ call.info = s;
+ s->ss_entry(ns, SS_RegisterCallback, &call);
+ s->ss_entry(ns, SS_GetStatus, &status);
+ if ((status & SS_DETECT) &&
+ !(s->state & SOCKET_SETUP_PENDING)) {
+ s->state |= SOCKET_SETUP_PENDING;
+ setup_socket(ns);
+ }
+ }
+
+ *handle = client;
+ client->state &= ~CLIENT_UNBOUND;
+ client->Socket = ns;
+ client->Attributes = req->Attributes;
+ client->EventMask = req->EventMask;
+ client->event_handler = req->event_handler;
+ client->event_callback_args = req->event_callback_args;
+ client->event_callback_args.client_handle = client;
+ client->event_callback_args.bus = s->cap.bus;
+
+ if (s->state & SOCKET_CARDBUS)
+ client->state |= CLIENT_CARDBUS;
+
+ if ((!(s->state & SOCKET_CARDBUS)) && (s->functions == 0) &&
+ (client->Function != BIND_FN_ALL)) {
+ cistpl_longlink_mfc_t mfc;
+ if (read_tuple(client, CISTPL_LONGLINK_MFC, &mfc)
+ == CS_SUCCESS)
+ s->functions = mfc.nfn;
+ else
+ s->functions = 1;
+ s->config = kmalloc(sizeof(config_t) * s->functions,
+ GFP_KERNEL);
+ memset(s->config, 0, sizeof(config_t) * s->functions);
+ }
+
+ DEBUG(1, "cs: register_client(): client 0x%p, sock %d, dev %s\n",
+ client, client->Socket, client->dev_info);
+ if (client->EventMask & CS_EVENT_REGISTRATION_COMPLETE)
+ EVENT(client, CS_EVENT_REGISTRATION_COMPLETE, CS_EVENT_PRI_LOW);
+ if ((socket_table[ns]->state & SOCKET_PRESENT) &&
+ !(socket_table[ns]->state & SOCKET_SETUP_PENDING)) {
+ if (client->EventMask & CS_EVENT_CARD_INSERTION)
+ EVENT(client, CS_EVENT_CARD_INSERTION, CS_EVENT_PRI_LOW);
+ else
+ client->PendingEvents |= CS_EVENT_CARD_INSERTION;
+ }
+ return CS_SUCCESS;
+} /* register_client */
+
+/*====================================================================*/
+
+static int release_configuration(client_handle_t handle,
+ socket_t *Socket)
+{
+ pccard_io_map io;
+ socket_info_t *s;
+ int i;
+
+ if (CHECK_HANDLE(handle) ||
+ !(handle->state & CLIENT_CONFIG_LOCKED))
+ return CS_BAD_HANDLE;
+ handle->state &= ~CLIENT_CONFIG_LOCKED;
+ s = SOCKET(handle);
+
+#ifdef CONFIG_CARDBUS
+ if (handle->state & CLIENT_CARDBUS) {
+ cb_disable(s);
+ s->lock_count = 0;
+ return CS_SUCCESS;
+ }
+#endif
+
+ if (!(handle->state & CLIENT_STALE)) {
+ config_t *c = CONFIG(handle);
+ if (--(s->lock_count) == 0) {
+ s->socket.flags = SS_OUTPUT_ENA;
+ s->socket.Vpp = 0;
+ s->socket.io_irq = 0;
+ s->ss_entry(s->sock, SS_SetSocket, &s->socket);
+ }
+ if (c->state & CONFIG_IO_REQ)
+ for (i = 0; i < MAX_IO_WIN; i++) {
+ if (s->io[i].NumPorts == 0)
+ continue;
+ s->io[i].Config--;
+ if (s->io[i].Config != 0)
+ continue;
+ io.map = i;
+ s->ss_entry(s->sock, SS_GetIOMap, &io);
+ io.flags &= ~MAP_ACTIVE;
+ s->ss_entry(s->sock, SS_SetIOMap, &io);
+ }
+ c->state &= ~CONFIG_LOCKED;
+ }
+
+ return CS_SUCCESS;
+} /* release_configuration */
+
+/*======================================================================
+
+ Release_io() releases the I/O ranges allocated by a client. This
+ may be invoked some time after a card ejection has already dumped
+ the actual socket configuration, so if the client is "stale", we
+ don't bother checking the port ranges against the current socket
+ values.
+
+======================================================================*/
+
+static int release_io(client_handle_t handle, io_req_t *req)
+{
+ socket_info_t *s;
+
+ if (CHECK_HANDLE(handle) || !(handle->state & CLIENT_IO_REQ))
+ return CS_BAD_HANDLE;
+ handle->state &= ~CLIENT_IO_REQ;
+ s = SOCKET(handle);
+
+#ifdef CONFIG_CARDBUS
+ if (handle->state & CLIENT_CARDBUS) {
+ cb_release(s);
+ return CS_SUCCESS;
+ }
+#endif
+
+ if (!(handle->state & CLIENT_STALE)) {
+ config_t *c = CONFIG(handle);
+ if (c->state & CONFIG_LOCKED)
+ return CS_CONFIGURATION_LOCKED;
+ if ((c->io.BasePort1 != req->BasePort1) ||
+ (c->io.NumPorts1 != req->NumPorts1) ||
+ (c->io.BasePort2 != req->BasePort2) ||
+ (c->io.NumPorts2 != req->NumPorts2))
+ return CS_BAD_ARGS;
+ c->state &= ~CONFIG_IO_REQ;
+ }
+
+ release_io_space(s, req->BasePort1, req->NumPorts1);
+ if (req->NumPorts2)
+ release_io_space(s, req->BasePort2, req->NumPorts2);
+
+ return CS_SUCCESS;
+} /* release_io */
+
+/*====================================================================*/
+
+static int cs_release_irq(client_handle_t handle, irq_req_t *req)
+{
+ socket_info_t *s;
+ if (CHECK_HANDLE(handle) || !(handle->state & CLIENT_IRQ_REQ))
+ return CS_BAD_HANDLE;
+ handle->state &= ~CLIENT_IRQ_REQ;
+ s = SOCKET(handle);
+
+ if (!(handle->state & CLIENT_STALE)) {
+ config_t *c = CONFIG(handle);
+ if (c->state & CONFIG_LOCKED)
+ return CS_CONFIGURATION_LOCKED;
+ if (c->irq.Attributes != req->Attributes)
+ return CS_BAD_ATTRIBUTE;
+ if (s->irq.AssignedIRQ != req->AssignedIRQ)
+ return CS_BAD_IRQ;
+ if (--s->irq.Config == 0) {
+ c->state &= ~CONFIG_IRQ_REQ;
+ s->irq.AssignedIRQ = 0;
+ }
+ }
+
+ if (req->Attributes & IRQ_HANDLE_PRESENT) {
+ bus_free_irq(s->cap.bus, req->AssignedIRQ, req->Instance);
+ }
+
+#ifdef CONFIG_ISA
+ if (req->AssignedIRQ != s->cap.pci_irq)
+ undo_irq(req->Attributes, req->AssignedIRQ);
+#endif
+
+ return CS_SUCCESS;
+} /* cs_release_irq */
+
+/*====================================================================*/
+
+static int release_window(window_handle_t win)
+{
+ socket_info_t *s;
+
+ if ((win == NULL) || (win->magic != WINDOW_MAGIC))
+ return CS_BAD_HANDLE;
+ s = win->sock;
+ if (!(win->handle->state & CLIENT_WIN_REQ(win->index)))
+ return CS_BAD_HANDLE;
+
+ /* Shut down memory window */
+ win->ctl.flags &= ~MAP_ACTIVE;
+ s->ss_entry(s->sock, SS_SetMemMap, &win->ctl);
+ s->state &= ~SOCKET_WIN_REQ(win->index);
+
+ /* Release system memory */
+ release_mem_region(win->base, win->size);
+ win->handle->state &= ~CLIENT_WIN_REQ(win->index);
+
+ win->magic = 0;
+
+ return CS_SUCCESS;
+} /* release_window */
+
+/*====================================================================*/
+
+static int request_configuration(client_handle_t handle,
+ config_req_t *req)
+{
+ int i;
+ u_int base;
+ socket_info_t *s;
+ config_t *c;
+ pccard_io_map iomap;
+
+ if (CHECK_HANDLE(handle))
+ return CS_BAD_HANDLE;
+ i = handle->Socket; s = socket_table[i];
+ if (!(s->state & SOCKET_PRESENT))
+ return CS_NO_CARD;
+
+#ifdef CONFIG_CARDBUS
+ if (handle->state & CLIENT_CARDBUS) {
+ if (!(req->IntType & INT_CARDBUS))
+ return CS_UNSUPPORTED_MODE;
+ if (s->lock_count != 0)
+ return CS_CONFIGURATION_LOCKED;
+ cb_enable(s);
+ handle->state |= CLIENT_CONFIG_LOCKED;
+ s->lock_count++;
+ return CS_SUCCESS;
+ }
+#endif
+
+ if (req->IntType & INT_CARDBUS)
+ return CS_UNSUPPORTED_MODE;
+ c = CONFIG(handle);
+ if (c->state & CONFIG_LOCKED)
+ return CS_CONFIGURATION_LOCKED;
+
+ /* Do power control. We don't allow changes in Vcc. */
+ if (s->socket.Vcc != req->Vcc)
+ return CS_BAD_VCC;
+ if (req->Vpp1 != req->Vpp2)
+ return CS_BAD_VPP;
+ s->socket.Vpp = req->Vpp1;
+ if (s->ss_entry(s->sock, SS_SetSocket, &s->socket))
+ return CS_BAD_VPP;
+
+ c->Vcc = req->Vcc; c->Vpp1 = c->Vpp2 = req->Vpp1;
+
+ /* Pick memory or I/O card, DMA mode, interrupt */
+ c->IntType = req->IntType;
+ c->Attributes = req->Attributes;
+ if (req->IntType & INT_MEMORY_AND_IO)
+ s->socket.flags |= SS_IOCARD;
+ if (req->Attributes & CONF_ENABLE_DMA)
+ s->socket.flags |= SS_DMA_MODE;
+ if (req->Attributes & CONF_ENABLE_SPKR)
+ s->socket.flags |= SS_SPKR_ENA;
+ if (req->Attributes & CONF_ENABLE_IRQ)
+ s->socket.io_irq = s->irq.AssignedIRQ;
+ else
+ s->socket.io_irq = 0;
+ s->ss_entry(s->sock, SS_SetSocket, &s->socket);
+ s->lock_count++;
+
+ /* Set up CIS configuration registers */
+ base = c->ConfigBase = req->ConfigBase;
+ c->Present = c->CardValues = req->Present;
+ if (req->Present & PRESENT_COPY) {
+ c->Copy = req->Copy;
+ write_cis_mem(s, 1, (base + CISREG_SCR)>>1, 1, &c->Copy);
+ }
+ if (req->Present & PRESENT_OPTION) {
+ if (s->functions == 1)
+ c->Option = req->ConfigIndex & COR_CONFIG_MASK;
+ else {
+ c->Option = req->ConfigIndex & COR_MFC_CONFIG_MASK;
+ c->Option |= COR_FUNC_ENA|COR_ADDR_DECODE|COR_IREQ_ENA;
+ }
+ if (c->state & CONFIG_IRQ_REQ)
+ if (!(c->irq.Attributes & IRQ_FORCED_PULSE))
+ c->Option |= COR_LEVEL_REQ;
+ write_cis_mem(s, 1, (base + CISREG_COR)>>1, 1, &c->Option);
+ udelay(40*1000);
+ }
+ if (req->Present & PRESENT_STATUS) {
+ c->Status = req->Status;
+ write_cis_mem(s, 1, (base + CISREG_CCSR)>>1, 1, &c->Status);
+ }
+ if (req->Present & PRESENT_PIN_REPLACE) {
+ c->Pin = req->Pin;
+ write_cis_mem(s, 1, (base + CISREG_PRR)>>1, 1, &c->Pin);
+ }
+ if (req->Present & PRESENT_EXT_STATUS) {
+ c->ExtStatus = req->ExtStatus;
+ write_cis_mem(s, 1, (base + CISREG_ESR)>>1, 1, &c->ExtStatus);
+ }
+ if (req->Present & PRESENT_IOBASE_0) {
+ i = c->io.BasePort1 & 0xff;
+ write_cis_mem(s, 1, (base + CISREG_IOBASE_0)>>1, 1, &i);
+ i = (c->io.BasePort1 >> 8) & 0xff;
+ write_cis_mem(s, 1, (base + CISREG_IOBASE_1)>>1, 1, &i);
+ }
+ if (req->Present & PRESENT_IOSIZE) {
+ i = c->io.NumPorts1 + c->io.NumPorts2 - 1;
+ write_cis_mem(s, 1, (base + CISREG_IOSIZE)>>1, 1, &i);
+ }
+
+ /* Configure I/O windows */
+ if (c->state & CONFIG_IO_REQ) {
+ iomap.speed = io_speed;
+ for (i = 0; i < MAX_IO_WIN; i++)
+ if (s->io[i].NumPorts != 0) {
+ iomap.map = i;
+ iomap.flags = MAP_ACTIVE;
+ switch (s->io[i].Attributes & IO_DATA_PATH_WIDTH) {
+ case IO_DATA_PATH_WIDTH_16:
+ iomap.flags |= MAP_16BIT; break;
+ case IO_DATA_PATH_WIDTH_AUTO:
+ iomap.flags |= MAP_AUTOSZ; break;
+ default:
+ break;
+ }
+ iomap.start = s->io[i].BasePort;
+ iomap.stop = iomap.start + s->io[i].NumPorts - 1;
+ s->ss_entry(s->sock, SS_SetIOMap, &iomap);
+ s->io[i].Config++;
+ }
+ }
+
+ c->state |= CONFIG_LOCKED;
+ handle->state |= CLIENT_CONFIG_LOCKED;
+ return CS_SUCCESS;
+} /* request_configuration */
+
+/*======================================================================
+
+ Request_io() reserves ranges of port addresses for a socket.
+ I have not implemented range sharing or alias addressing.
+
+======================================================================*/
+
+static int request_io(client_handle_t handle, io_req_t *req)
+{
+ socket_info_t *s;
+ config_t *c;
+
+ if (CHECK_HANDLE(handle))
+ return CS_BAD_HANDLE;
+ s = SOCKET(handle);
+ if (!(s->state & SOCKET_PRESENT))
+ return CS_NO_CARD;
+
+ if (handle->state & CLIENT_CARDBUS) {
+#ifdef CONFIG_CARDBUS
+ int ret = cb_config(s);
+ if (ret == CS_SUCCESS)
+ handle->state |= CLIENT_IO_REQ;
+ return ret;
+#else
+ return CS_UNSUPPORTED_FUNCTION;
+#endif
+ }
+
+ if (!req)
+ return CS_UNSUPPORTED_MODE;
+ c = CONFIG(handle);
+ if (c->state & CONFIG_LOCKED)
+ return CS_CONFIGURATION_LOCKED;
+ if (c->state & CONFIG_IO_REQ)
+ return CS_IN_USE;
+ if (req->Attributes1 & (IO_SHARED | IO_FORCE_ALIAS_ACCESS))
+ return CS_BAD_ATTRIBUTE;
+ if ((req->NumPorts2 > 0) &&
+ (req->Attributes2 & (IO_SHARED | IO_FORCE_ALIAS_ACCESS)))
+ return CS_BAD_ATTRIBUTE;
+
+ if (alloc_io_space(s, req->Attributes1, &req->BasePort1,
+ req->NumPorts1, handle->dev_info))
+ return CS_IN_USE;
+
+ if (req->NumPorts2) {
+ if (alloc_io_space(s, req->Attributes2, &req->BasePort2,
+ req->NumPorts2, handle->dev_info)) {
+ release_io_space(s, req->BasePort1, req->NumPorts1);
+ return CS_IN_USE;
+ }
+ }
+
+ c->io = *req;
+ c->state |= CONFIG_IO_REQ;
+ handle->state |= CLIENT_IO_REQ;
+ return CS_SUCCESS;
+} /* request_io */
+
+/*======================================================================
+
+ Request_irq() reserves an irq for this client.
+
+ Also, since Linux only reserves irq's when they are actually
+ hooked, we don't guarantee that an irq will still be available
+ when the configuration is locked. Now that I think about it,
+ there might be a way to fix this using a dummy handler.
+
+======================================================================*/
+
+static int cs_request_irq(client_handle_t handle, irq_req_t *req)
+{
+ socket_info_t *s;
+ config_t *c;
+ int try, ret = 0, irq = 0;
+ u_int mask;
+
+ if (CHECK_HANDLE(handle))
+ return CS_BAD_HANDLE;
+ s = SOCKET(handle);
+ if (!(s->state & SOCKET_PRESENT))
+ return CS_NO_CARD;
+ c = CONFIG(handle);
+ if (c->state & CONFIG_LOCKED)
+ return CS_CONFIGURATION_LOCKED;
+ if (c->state & CONFIG_IRQ_REQ)
+ return CS_IN_USE;
+
+ /* Short cut: if the interrupt is PCI, there are no options */
+ if (s->cap.irq_mask == (1 << s->cap.pci_irq))
+ irq = s->cap.pci_irq;
+#ifdef CONFIG_ISA
+ else if (s->irq.AssignedIRQ != 0) {
+ /* If the interrupt is already assigned, it must match */
+ irq = s->irq.AssignedIRQ;
+ if (req->IRQInfo1 & IRQ_INFO2_VALID) {
+ mask = req->IRQInfo2 & s->cap.irq_mask;
+ ret = ((mask >> irq) & 1) ? 0 : CS_BAD_ARGS;
+ } else
+ ret = ((req->IRQInfo1&IRQ_MASK) == irq) ? 0 : CS_BAD_ARGS;
+ } else {
+ ret = CS_IN_USE;
+ if (req->IRQInfo1 & IRQ_INFO2_VALID) {
+ mask = req->IRQInfo2 & s->cap.irq_mask;
+ mask &= ~(1 << s->cap.pci_irq);
+ for (try = 0; try < 2; try++) {
+ for (irq = 0; irq < 32; irq++)
+ if ((mask >> irq) & 1) {
+ ret = try_irq(req->Attributes, irq, try);
+ if (ret == 0) break;
+ }
+ if (ret == 0) break;
+ }
+ } else {
+ irq = req->IRQInfo1 & IRQ_MASK;
+ ret = try_irq(req->Attributes, irq, 1);
+ }
+ }
+#endif
+ if (ret != 0) return ret;
+
+ if (req->Attributes & IRQ_HANDLE_PRESENT) {
+ if (bus_request_irq(s->cap.bus, irq, req->Handler,
+ ((req->Attributes & IRQ_TYPE_DYNAMIC_SHARING) ||
+ (s->functions > 1) ||
+ (irq == s->cap.pci_irq)) ? SA_SHIRQ : 0,
+ handle->dev_info, req->Instance))
+ return CS_IN_USE;
+ }
+
+ c->irq.Attributes = req->Attributes;
+ s->irq.AssignedIRQ = req->AssignedIRQ = irq;
+ s->irq.Config++;
+
+ c->state |= CONFIG_IRQ_REQ;
+ handle->state |= CLIENT_IRQ_REQ;
+ return CS_SUCCESS;
+} /* cs_request_irq */
+
+/*======================================================================
+
+ Request_window() establishes a mapping between card memory space
+ and system memory space.
+
+======================================================================*/
+
+static int request_window(client_handle_t *handle, win_req_t *req)
+{
+ socket_info_t *s;
+ window_t *win;
+ int w;
+
+ if (CHECK_HANDLE(*handle))
+ return CS_BAD_HANDLE;
+ s = SOCKET(*handle);
+ if (!(s->state & SOCKET_PRESENT))
+ return CS_NO_CARD;
+ if (req->Attributes & (WIN_PAGED | WIN_SHARED))
+ return CS_BAD_ATTRIBUTE;
+
+ for (w = 0; w < MAX_WIN; w++)
+ if (!(s->state & SOCKET_WIN_REQ(w))) break;
+ if (w == MAX_WIN)
+ return CS_OUT_OF_RESOURCE;
+
+ /* Window size defaults to smallest available */
+ if (req->Size == 0)
+ req->Size = s->cap.map_size;
+
+ /* Allocate system memory window */
+ win = &s->win[w];
+ win->magic = WINDOW_MAGIC;
+ win->index = w;
+ win->handle = *handle;
+ win->sock = s;
+ win->base = req->Base;
+ win->size = req->Size;
+ if (find_mem_region(&win->base, win->size, (*handle)->dev_info,
+ ((s->cap.features & SS_CAP_MEM_ALIGN) ?
+ req->Size : s->cap.map_size),
+ (req->Attributes & WIN_MAP_BELOW_1MB) ||
+ !(s->cap.features & SS_CAP_PAGE_REGS)))
+ return CS_IN_USE;
+ req->Base = win->base;
+ (*handle)->state |= CLIENT_WIN_REQ(w);
+
+ /* Configure the socket controller */
+ win->ctl.map = w+1;
+ win->ctl.flags = 0;
+ win->ctl.speed = req->AccessSpeed;
+ if (req->Attributes & WIN_MEMORY_TYPE)
+ win->ctl.flags |= MAP_ATTRIB;
+ if (req->Attributes & WIN_ENABLE)
+ win->ctl.flags |= MAP_ACTIVE;
+ if (req->Attributes & WIN_DATA_WIDTH)
+ win->ctl.flags |= MAP_16BIT;
+ if (req->Attributes & WIN_USE_WAIT)
+ win->ctl.flags |= MAP_USE_WAIT;
+ win->ctl.sys_start = req->Base;
+ win->ctl.sys_stop = req->Base + req->Size-1;
+ win->ctl.card_start = 0;
+ if (s->ss_entry(s->sock, SS_SetMemMap, &win->ctl) != 0)
+ return CS_BAD_ARGS;
+ s->state |= SOCKET_WIN_REQ(w);
+
+ /* Return window handle */
+ *handle = (client_handle_t)win;
+
+ return CS_SUCCESS;
+} /* request_window */
+
+/*======================================================================
+
+ I'm not sure which "reset" function this is supposed to use,
+ but for now, it uses the low-level interface's reset, not the
+ CIS register.
+
+======================================================================*/
+
+static int reset_card(client_handle_t handle, client_req_t *req)
+{
+ int i, ret;
+ socket_info_t *s;
+
+ if (CHECK_HANDLE(handle))
+ return CS_BAD_HANDLE;
+ i = handle->Socket; s = socket_table[i];
+ if (!(s->state & SOCKET_PRESENT))
+ return CS_NO_CARD;
+ if (s->state & SOCKET_RESET_PENDING)
+ return CS_IN_USE;
+ s->state |= SOCKET_RESET_PENDING;
+
+ ret = send_event(s, CS_EVENT_RESET_REQUEST, CS_EVENT_PRI_LOW);
+ if (ret != 0) {
+ s->state &= ~SOCKET_RESET_PENDING;
+ handle->event_callback_args.info = (void *)(u_long)ret;
+ EVENT(handle, CS_EVENT_RESET_COMPLETE, CS_EVENT_PRI_LOW);
+ } else {
+ DEBUG(1, "cs: resetting socket %d\n", i);
+ send_event(s, CS_EVENT_RESET_PHYSICAL, CS_EVENT_PRI_LOW);
+ s->reset_handle = handle;
+ reset_socket(i);
+ }
+ return CS_SUCCESS;
+} /* reset_card */
+
+/*======================================================================
+
+ These shut down or wake up a socket. They are sort of user
+ initiated versions of the APM suspend and resume actions.
+
+======================================================================*/
+
+static int suspend_card(client_handle_t handle, client_req_t *req)
+{
+ int i;
+ socket_info_t *s;
+
+ if (CHECK_HANDLE(handle))
+ return CS_BAD_HANDLE;
+ i = handle->Socket; s = socket_table[i];
+ if (!(s->state & SOCKET_PRESENT))
+ return CS_NO_CARD;
+ if (s->state & SOCKET_SUSPEND)
+ return CS_IN_USE;
+
+ DEBUG(1, "cs: suspending socket %d\n", i);
+ send_event(s, CS_EVENT_PM_SUSPEND, CS_EVENT_PRI_LOW);
+ s->ss_entry(s->sock, SS_SetSocket, &dead_socket);
+ s->state |= SOCKET_SUSPEND;
+
+ return CS_SUCCESS;
+} /* suspend_card */
+
+static int resume_card(client_handle_t handle, client_req_t *req)
+{
+ int i;
+ socket_info_t *s;
+
+ if (CHECK_HANDLE(handle))
+ return CS_BAD_HANDLE;
+ i = handle->Socket; s = socket_table[i];
+ if (!(s->state & SOCKET_PRESENT))
+ return CS_NO_CARD;
+ if (!(s->state & SOCKET_SUSPEND))
+ return CS_IN_USE;
+
+ DEBUG(1, "cs: waking up socket %d\n", i);
+ setup_socket(i);
+
+ return CS_SUCCESS;
+} /* resume_card */
+
+/*======================================================================
+
+ These handle user requests to eject or insert a card.
+
+======================================================================*/
+
+static int eject_card(client_handle_t handle, client_req_t *req)
+{
+ int i, ret;
+ socket_info_t *s;
+ u_long flags;
+
+ if (CHECK_HANDLE(handle))
+ return CS_BAD_HANDLE;
+ i = handle->Socket; s = socket_table[i];
+ if (!(s->state & SOCKET_PRESENT))
+ return CS_NO_CARD;
+
+ DEBUG(1, "cs: user eject request on socket %d\n", i);
+
+ ret = send_event(s, CS_EVENT_EJECTION_REQUEST, CS_EVENT_PRI_LOW);
+ if (ret != 0)
+ return ret;
+
+ spin_lock_irqsave(&s->lock, flags);
+ do_shutdown(s);
+ spin_unlock_irqrestore(&s->lock, flags);
+
+ return CS_SUCCESS;
+
+} /* eject_card */
+
+static int insert_card(client_handle_t handle, client_req_t *req)
+{
+ int i, status;
+ socket_info_t *s;
+ u_long flags;
+
+ if (CHECK_HANDLE(handle))
+ return CS_BAD_HANDLE;
+ i = handle->Socket; s = socket_table[i];
+ if (s->state & SOCKET_PRESENT)
+ return CS_IN_USE;
+
+ DEBUG(1, "cs: user insert request on socket %d\n", i);
+
+ spin_lock_irqsave(&s->lock, flags);
+ if (!(s->state & SOCKET_SETUP_PENDING)) {
+ s->state |= SOCKET_SETUP_PENDING;
+ spin_unlock_irqrestore(&s->lock, flags);
+ s->ss_entry(i, SS_GetStatus, &status);
+ if (status & SS_DETECT)
+ setup_socket(i);
+ else {
+ s->state &= ~SOCKET_SETUP_PENDING;
+ return CS_NO_CARD;
+ }
+ } else
+ spin_unlock_irqrestore(&s->lock, flags);
+
+ return CS_SUCCESS;
+} /* insert_card */
+
+/*======================================================================
+
+ Maybe this should send a CS_EVENT_CARD_INSERTION event if we
+ haven't sent one to this client yet?
+
+======================================================================*/
+
+static int set_event_mask(client_handle_t handle, eventmask_t *mask)
+{
+ u_int events, bit;
+ if (CHECK_HANDLE(handle))
+ return CS_BAD_HANDLE;
+ if (handle->Attributes & CONF_EVENT_MASK_VALID)
+ return CS_BAD_SOCKET;
+ handle->EventMask = mask->EventMask;
+ events = handle->PendingEvents & handle->EventMask;
+ handle->PendingEvents -= events;
+ while (events != 0) {
+ bit = ((events ^ (events-1)) + 1) >> 1;
+ EVENT(handle, bit, CS_EVENT_PRI_LOW);
+ events -= bit;
+ }
+ return CS_SUCCESS;
+} /* set_event_mask */
+
+/*====================================================================*/
+
+static int report_error(client_handle_t handle, error_info_t *err)
+{
+ int i;
+ char *serv;
+
+ if (CHECK_HANDLE(handle))
+ printk(KERN_NOTICE);
+ else
+ printk(KERN_NOTICE "%s: ", handle->dev_info);
+
+ for (i = 0; i < SERVICE_COUNT; i++)
+ if (service_table[i].key == err->func) break;
+ if (i < SERVICE_COUNT)
+ serv = service_table[i].msg;
+ else
+ serv = "Unknown service number";
+
+ for (i = 0; i < ERROR_COUNT; i++)
+ if (error_table[i].key == err->retcode) break;
+ if (i < ERROR_COUNT)
+ printk("%s: %s\n", serv, error_table[i].msg);
+ else
+ printk("%s: Unknown error code %#x\n", serv, err->retcode);
+
+ return CS_SUCCESS;
+} /* report_error */
+
+/*====================================================================*/
+
+int CardServices(int func, void *a1, void *a2, void *a3)
+{
+
+#ifdef PCMCIA_DEBUG
+ if (pc_debug > 1) {
+ int i;
+ for (i = 0; i < SERVICE_COUNT; i++)
+ if (service_table[i].key == func) break;
+ if (i < SERVICE_COUNT)
+ printk(KERN_DEBUG "cs: CardServices(%s, 0x%p, 0x%p)\n",
+ service_table[i].msg, a1, a2);
+ else
+ printk(KERN_DEBUG "cs: CardServices(Unknown func %d, "
+ "0x%p, 0x%p)\n", func, a1, a2);
+ }
+#endif
+ switch (func) {
+ case AccessConfigurationRegister:
+ return access_configuration_register(a1, a2); break;
+ case AdjustResourceInfo:
+ return adjust_resource_info(a1, a2); break;
+ case CheckEraseQueue:
+ return check_erase_queue(a1); break;
+ case CloseMemory:
+ return close_memory(a1); break;
+ case CopyMemory:
+ return copy_memory(a1, a2); break;
+ case DeregisterClient:
+ return deregister_client(a1); break;
+ case DeregisterEraseQueue:
+ return deregister_erase_queue(a1); break;
+ case GetFirstClient:
+ return get_first_client(a1, a2); break;
+ case GetCardServicesInfo:
+ return get_card_services_info(a1); break;
+ case GetConfigurationInfo:
+ return get_configuration_info(a1, a2); break;
+ case GetNextClient:
+ return get_next_client(a1, a2); break;
+ case GetFirstRegion:
+ return get_first_region(a1, a2); break;
+ case GetFirstTuple:
+ return get_first_tuple(a1, a2); break;
+ case GetNextRegion:
+ return get_next_region(a1, a2); break;
+ case GetNextTuple:
+ return get_next_tuple(a1, a2); break;
+ case GetStatus:
+ return get_status(a1, a2); break;
+ case GetTupleData:
+ return get_tuple_data(a1, a2); break;
+ case MapMemPage:
+ return map_mem_page(a1, a2); break;
+ case ModifyConfiguration:
+ return modify_configuration(a1, a2); break;
+ case ModifyWindow:
+ return modify_window(a1, a2); break;
+ case OpenMemory:
+ return open_memory(a1, a2);
+ case ParseTuple:
+ return parse_tuple(a1, a2, a3); break;
+ case ReadMemory:
+ return read_memory(a1, a2, a3); break;
+ case RegisterClient:
+ return register_client(a1, a2); break;
+ case RegisterEraseQueue:
+ return register_erase_queue(a1, a2); break;
+ case RegisterMTD:
+ return register_mtd(a1, a2); break;
+ case ReleaseConfiguration:
+ return release_configuration(a1, a2); break;
+ case ReleaseIO:
+ return release_io(a1, a2); break;
+ case ReleaseIRQ:
+ return cs_release_irq(a1, a2); break;
+ case ReleaseWindow:
+ return release_window(a1); break;
+ case RequestConfiguration:
+ return request_configuration(a1, a2); break;
+ case RequestIO:
+ return request_io(a1, a2); break;
+ case RequestIRQ:
+ return cs_request_irq(a1, a2); break;
+ case RequestWindow:
+ return request_window(a1, a2); break;
+ case ResetCard:
+ return reset_card(a1, a2); break;
+ case SetEventMask:
+ return set_event_mask(a1, a2); break;
+ case ValidateCIS:
+ return validate_cis(a1, a2); break;
+ case WriteMemory:
+ return write_memory(a1, a2, a3); break;
+ case BindDevice:
+ return bind_device(a1); break;
+ case BindMTD:
+ return bind_mtd(a1); break;
+ case ReportError:
+ return report_error(a1, a2); break;
+ case SuspendCard:
+ return suspend_card(a1, a2); break;
+ case ResumeCard:
+ return resume_card(a1, a2); break;
+ case EjectCard:
+ return eject_card(a1, a2); break;
+ case InsertCard:
+ return insert_card(a1, a2); break;
+ case ReplaceCIS:
+ return replace_cis(a1, a2); break;
+ case GetFirstWindow:
+ return get_first_window(a1, a2); break;
+ case GetNextWindow:
+ return get_next_window(a1, a2); break;
+ case GetMemPage:
+ return get_mem_page(a1, a2); break;
+ default:
+ return CS_UNSUPPORTED_FUNCTION; break;
+ }
+
+} /* CardServices */
+
+/*======================================================================
+
+ OS-specific module glue goes here
+
+======================================================================*/
+
+EXPORT_SYMBOL(register_ss_entry);
+EXPORT_SYMBOL(unregister_ss_entry);
+EXPORT_SYMBOL(CardServices);
+EXPORT_SYMBOL(MTDHelperEntry);
+
+static int __init init_pcmcia_cs(void)
+{
+ printk(KERN_INFO "%s\n", release);
+#ifdef MODULE
+ printk(KERN_INFO " %s\n", kernel);
+#endif
+ printk(KERN_INFO " %s\n", options);
+ DEBUG(0, "%s\n", version);
+#ifdef CONFIG_APM
+ if (do_apm)
+ apm_register_callback(&handle_apm_event);
+#endif
+#ifdef CONFIG_PROC_FS
+ proc_pccard = create_proc_entry("pccard", S_IFDIR, proc_bus);
+#endif
+ return 0;
+}
+
+static void __exit exit_pcmcia_cs(void)
+{
+ printk(KERN_INFO "unloading PCMCIA Card Services\n");
+#ifdef CONFIG_PROC_FS
+ if (proc_pccard) {
+ remove_proc_entry("pccard", proc_bus);
+ }
+#endif
+#ifdef CONFIG_APM
+ if (do_apm)
+ apm_unregister_callback(&handle_apm_event);
+#endif
+ release_resource_db();
+}
+
+module_init(init_pcmcia_cs);
+module_exit(exit_pcmcia_cs);
+
+/*====================================================================*/
+
diff --git a/drivers/pcmcia/cs_internal.h b/drivers/pcmcia/cs_internal.h
new file mode 100644
index 000000000..aca0a0d0c
--- /dev/null
+++ b/drivers/pcmcia/cs_internal.h
@@ -0,0 +1,280 @@
+/*
+ * cs_internal.h 1.43 1999/09/07 15:19:04
+ *
+ * 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 David A. Hinds
+ * <dhinds@hyper.stanford.edu>. Portions created by David A. Hinds
+ * are Copyright (C) 1999 David A. Hinds. All Rights Reserved.
+ */
+
+#ifndef _LINUX_CS_INTERNAL_H
+#define _LINUX_CS_INTERNAL_H
+
+typedef struct erase_busy_t {
+ eraseq_entry_t *erase;
+ client_handle_t client;
+ struct timer_list timeout;
+ struct erase_busy_t *prev, *next;
+} erase_busy_t;
+
+#define ERASEQ_MAGIC 0xFA67
+typedef struct eraseq_t {
+ u_short eraseq_magic;
+ client_handle_t handle;
+ int count;
+ eraseq_entry_t *entry;
+} eraseq_t;
+
+#define CLIENT_MAGIC 0x51E6
+typedef struct client_t {
+ u_short client_magic;
+ socket_t Socket;
+ u_char Function;
+ dev_info_t dev_info;
+ u_int Attributes;
+ u_int state;
+ event_t EventMask, PendingEvents;
+ int (*event_handler)(event_t event, int priority,
+ event_callback_args_t *);
+ event_callback_args_t event_callback_args;
+ struct client_t *next;
+ u_int mtd_count;
+ wait_queue_head_t mtd_req;
+ erase_busy_t erase_busy;
+} client_t;
+
+/* Flags in client state */
+#define CLIENT_CONFIG_LOCKED 0x0001
+#define CLIENT_IRQ_REQ 0x0002
+#define CLIENT_IO_REQ 0x0004
+#define CLIENT_UNBOUND 0x0008
+#define CLIENT_STALE 0x0010
+#define CLIENT_WIN_REQ(i) (0x20<<(i))
+#define CLIENT_CARDBUS 0x8000
+
+typedef struct io_window_t {
+ u_int Attributes;
+ ioaddr_t BasePort, NumPorts;
+ ioaddr_t InUse, Config;
+} io_window_t;
+
+#define WINDOW_MAGIC 0xB35C
+typedef struct window_t {
+ u_short magic;
+ u_short index;
+ client_handle_t handle;
+ struct socket_info_t *sock;
+ u_long base;
+ u_long size;
+ pccard_mem_map ctl;
+} window_t;
+
+#define REGION_MAGIC 0xE3C9
+typedef struct region_t {
+ u_short region_magic;
+ u_short state;
+ dev_info_t dev_info;
+ client_handle_t mtd;
+ u_int MediaID;
+ region_info_t info;
+} region_t;
+
+#define REGION_STALE 0x01
+
+/* Each card function gets one of these guys */
+typedef struct config_t {
+ u_int state;
+ u_int Attributes;
+ u_int Vcc, Vpp1, Vpp2;
+ u_int IntType;
+ u_int ConfigBase;
+ u_char Status, Pin, Copy, Option, ExtStatus;
+ u_int Present;
+ u_int CardValues;
+ io_req_t io;
+ struct {
+ u_int Attributes;
+ } irq;
+} config_t;
+
+/* Maximum number of IO windows per socket */
+#define MAX_IO_WIN 2
+
+/* Maximum number of memory windows per socket */
+#define MAX_WIN 4
+
+/* The size of the CIS cache */
+#define MAX_CIS_TABLE 64
+#define MAX_CIS_DATA 512
+
+typedef struct socket_info_t {
+ spinlock_t lock;
+ ss_entry_t ss_entry;
+ u_int sock;
+ socket_state_t socket;
+ socket_cap_t cap;
+ u_int state;
+ u_short functions;
+ u_short lock_count;
+ client_handle_t clients;
+ u_int real_clients;
+ client_handle_t reset_handle;
+ struct timer_list setup, shutdown;
+ u_long unreset_timeout;
+ pccard_mem_map cis_mem;
+ u_char *cis_virt;
+ config_t *config;
+#ifdef CONFIG_CARDBUS
+ u_int cb_cis_space;
+ cb_bridge_map cb_cis_map;
+ u_char *cb_cis_virt;
+ struct cb_config_t *cb_config;
+#endif
+ struct {
+ u_int AssignedIRQ;
+ u_int Config;
+ } irq;
+ io_window_t io[MAX_IO_WIN];
+ window_t win[MAX_WIN];
+ region_t *c_region, *a_region;
+ erase_busy_t erase_busy;
+ int cis_used;
+ struct {
+ u_int addr;
+ u_short len;
+ u_short attr;
+ } cis_table[MAX_CIS_TABLE];
+ char cis_cache[MAX_CIS_DATA];
+ u_int fake_cis_len;
+ char *fake_cis;
+#ifdef CONFIG_PROC_FS
+ struct proc_dir_entry *proc;
+#endif
+} socket_info_t;
+
+/* Flags in config state */
+#define CONFIG_LOCKED 0x01
+#define CONFIG_IRQ_REQ 0x02
+#define CONFIG_IO_REQ 0x04
+
+/* Flags in socket state */
+#define SOCKET_PRESENT 0x0008
+#define SOCKET_SETUP_PENDING 0x0010
+#define SOCKET_SHUTDOWN_PENDING 0x0020
+#define SOCKET_RESET_PENDING 0x0040
+#define SOCKET_SUSPEND 0x0080
+#define SOCKET_WIN_REQ(i) (0x0100<<(i))
+#define SOCKET_IO_REQ(i) (0x1000<<(i))
+#define SOCKET_REGION_INFO 0x4000
+#define SOCKET_CARDBUS 0x8000
+
+#define CHECK_HANDLE(h) \
+ (((h) == NULL) || ((h)->client_magic != CLIENT_MAGIC))
+
+#define CHECK_SOCKET(s) \
+ (((s) >= sockets) || (socket_table[s]->ss_entry == NULL))
+
+#define SOCKET(h) (socket_table[(h)->Socket])
+#define CONFIG(h) (&SOCKET(h)->config[(h)->Function])
+
+#define CHECK_REGION(r) \
+ (((r) == NULL) || ((r)->region_magic != REGION_MAGIC))
+
+#define CHECK_ERASEQ(q) \
+ (((q) == NULL) || ((q)->eraseq_magic != ERASEQ_MAGIC))
+
+#define EVENT(h, e, p) \
+ ((h)->event_handler((e), (p), &(h)->event_callback_args))
+
+/* In cardbus.c */
+int cb_alloc(socket_info_t *s);
+void cb_free(socket_info_t *s);
+int cb_config(socket_info_t *s);
+void cb_release(socket_info_t *s);
+void cb_enable(socket_info_t *s);
+void cb_disable(socket_info_t *s);
+void read_cb_mem(socket_info_t *s, u_char fn, int space,
+ u_int addr, u_int len, void *ptr);
+int cb_setup_cis_mem(socket_info_t *s, int space);
+void cb_release_cis_mem(socket_info_t *s);
+
+/* In cistpl.c */
+void read_cis_mem(socket_info_t *s, int attr,
+ u_int addr, u_int len, void *ptr);
+void write_cis_mem(socket_info_t *s, int attr,
+ u_int addr, u_int len, void *ptr);
+int setup_cis_mem(socket_info_t *s);
+void release_cis_mem(socket_info_t *s);
+int verify_cis_cache(socket_info_t *s);
+void preload_cis_cache(socket_info_t *s);
+int get_first_tuple(client_handle_t handle, tuple_t *tuple);
+int get_next_tuple(client_handle_t handle, tuple_t *tuple);
+int get_tuple_data(client_handle_t handle, tuple_t *tuple);
+int parse_tuple(client_handle_t handle, tuple_t *tuple, cisparse_t *parse);
+int validate_cis(client_handle_t handle, cisinfo_t *info);
+int replace_cis(client_handle_t handle, cisdump_t *cis);
+int read_tuple(client_handle_t handle, cisdata_t code, void *parse);
+
+/* In bulkmem.c */
+void retry_erase_list(struct erase_busy_t *list, u_int cause);
+int get_first_region(client_handle_t handle, region_info_t *rgn);
+int get_next_region(client_handle_t handle, region_info_t *rgn);
+int register_mtd(client_handle_t handle, mtd_reg_t *reg);
+int register_erase_queue(client_handle_t *handle, eraseq_hdr_t *header);
+int deregister_erase_queue(eraseq_handle_t eraseq);
+int check_erase_queue(eraseq_handle_t eraseq);
+int open_memory(client_handle_t *handle, open_mem_t *open);
+int close_memory(memory_handle_t handle);
+int read_memory(memory_handle_t handle, mem_op_t *req, caddr_t buf);
+int write_memory(memory_handle_t handle, mem_op_t *req, caddr_t buf);
+int copy_memory(memory_handle_t handle, copy_op_t *req);
+
+/* In rsrc_mgr */
+void validate_mem(int (*is_valid)(u_long), int (*do_cksum)(u_long),
+ int force_low);
+int find_io_region(ioaddr_t *base, ioaddr_t num, char *name);
+int find_mem_region(u_long *base, u_long num, char *name,
+ u_long align, int force_low);
+int try_irq(u_int Attributes, int irq, int specific);
+void undo_irq(u_int Attributes, int irq);
+int adjust_resource_info(client_handle_t handle, adjust_t *adj);
+void release_resource_db(void);
+int proc_read_io(char *buf, char **start, off_t pos,
+ int count, int *eof, void *data);
+int proc_read_mem(char *buf, char **start, off_t pos,
+ int count, int *eof, void *data);
+
+/* in pnp components */
+int proc_read_irq(char *buf, char **start, off_t pos,
+ int count, int *eof, void *data);
+void pnp_bios_init(void);
+void pnp_proc_init(void);
+void pnp_proc_done(void);
+void pnp_rsrc_init(void);
+void pnp_rsrc_done(void);
+
+#define MAX_SOCK 8
+extern socket_t sockets;
+extern socket_info_t *socket_table[MAX_SOCK];
+
+#ifdef CONFIG_PROC_FS
+extern struct proc_dir_entry *proc_pccard;
+#endif
+
+#ifdef PCMCIA_DEBUG
+extern int pc_debug;
+#define DEBUG(n, args...) do { if (pc_debug>(n)) printk(KERN_DEBUG args); } while (0)
+#else
+#define DEBUG(n, args...) do { } while (0)
+#endif
+
+#endif /* _LINUX_CS_INTERNAL_H */
diff --git a/drivers/pcmcia/ds.c b/drivers/pcmcia/ds.c
new file mode 100644
index 000000000..71550228b
--- /dev/null
+++ b/drivers/pcmcia/ds.c
@@ -0,0 +1,899 @@
+/*======================================================================
+
+ PC Card Driver Services
+
+ ds.c 1.98 1999/09/15 15:32:19
+
+ 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 David A. Hinds
+ <dhinds@hyper.stanford.edu>. Portions created by David A. Hinds
+ are Copyright (C) 1999 David A. Hinds. 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/kernel.h>
+#include <linux/major.h>
+#include <linux/string.h>
+#include <linux/errno.h>
+#include <linux/malloc.h>
+#include <linux/mm.h>
+#include <linux/fcntl.h>
+#include <linux/sched.h>
+#include <linux/timer.h>
+#include <linux/ioctl.h>
+#include <linux/poll.h>
+
+#include <pcmcia/version.h>
+#include <pcmcia/cs_types.h>
+#include <pcmcia/cs.h>
+#include <pcmcia/bulkmem.h>
+#include <pcmcia/cistpl.h>
+#include <pcmcia/ds.h>
+
+#ifdef PCMCIA_DEBUG
+int pc_debug = PCMCIA_DEBUG;
+MODULE_PARM(pc_debug, "i");
+#define DEBUG(n, args...) if (pc_debug>(n)) printk(KERN_DEBUG args)
+static const char *version =
+"ds.c 1.98 1999/09/15 15:32:19 (David Hinds)";
+#else
+#define DEBUG(n, args...)
+#endif
+
+/*====================================================================*/
+
+typedef struct driver_info_t {
+ dev_info_t dev_info;
+ int use_count;
+ dev_link_t *(*attach)(void);
+ void (*detach)(dev_link_t *);
+ struct driver_info_t *next;
+} driver_info_t;
+
+typedef struct socket_bind_t {
+ driver_info_t *driver;
+ dev_link_t *instance;
+ struct socket_bind_t *next;
+} socket_bind_t;
+
+/* Device user information */
+#define MAX_EVENTS 32
+#define USER_MAGIC 0x7ea4
+#define CHECK_USER(u) \
+ (((u) == NULL) || ((u)->user_magic != USER_MAGIC))
+typedef struct user_info_t {
+ u_int user_magic;
+ int event_head, event_tail;
+ event_t event[MAX_EVENTS];
+ struct user_info_t *next;
+} user_info_t;
+
+/* Socket state information */
+typedef struct socket_info_t {
+ client_handle_t handle;
+ int state;
+ user_info_t *user;
+ int req_pending, req_result;
+ wait_queue_head_t queue, request;
+ struct timer_list removal;
+ socket_bind_t *bind;
+} socket_info_t;
+
+#define SOCKET_PRESENT 0x01
+#define SOCKET_BUSY 0x02
+#define SOCKET_REMOVAL_PENDING 0x10
+
+/*====================================================================*/
+
+/* Device driver ID passed to Card Services */
+static dev_info_t dev_info = "Driver Services";
+
+/* Linked list of all registered device drivers */
+static driver_info_t *root_driver = NULL;
+
+static int sockets = 0, major_dev = -1;
+static socket_info_t *socket_table = NULL;
+
+/*====================================================================*/
+
+static void cs_error(client_handle_t handle, int func, int ret)
+{
+ error_info_t err = { func, ret };
+ CardServices(ReportError, handle, &err);
+}
+
+/*======================================================================
+
+ Register_pccard_driver() and unregister_pccard_driver() are used
+ tell Driver Services that a PC Card client driver is available to
+ be bound to sockets.
+
+======================================================================*/
+
+int register_pccard_driver(dev_info_t *dev_info,
+ dev_link_t *(*attach)(void),
+ void (*detach)(dev_link_t *))
+{
+ driver_info_t *driver;
+ socket_bind_t *b;
+ int i;
+
+ DEBUG(0, "ds: register_pccard_driver('%s')\n", (char *)dev_info);
+ for (driver = root_driver; driver; driver = driver->next)
+ if (strncmp((char *)dev_info, (char *)driver->dev_info,
+ DEV_NAME_LEN) == 0)
+ break;
+ if (!driver) {
+ driver = kmalloc(sizeof(driver_info_t), GFP_KERNEL);
+ strncpy(driver->dev_info, (char *)dev_info, DEV_NAME_LEN);
+ driver->use_count = 0;
+ driver->next = root_driver;
+ root_driver = driver;
+ }
+
+ driver->attach = attach;
+ driver->detach = detach;
+ if (driver->use_count == 0) return 0;
+
+ /* Instantiate any already-bound devices */
+ for (i = 0; i < sockets; i++)
+ for (b = socket_table[i].bind; b; b = b->next) {
+ if (b->driver != driver) continue;
+ b->instance = driver->attach();
+ if (b->instance == NULL)
+ printk(KERN_NOTICE "ds: unable to create instance "
+ "of '%s'!\n", driver->dev_info);
+ }
+
+ return 0;
+} /* register_pccard_driver */
+
+/*====================================================================*/
+
+int unregister_pccard_driver(dev_info_t *dev_info)
+{
+ driver_info_t *target, **d = &root_driver;
+ socket_bind_t *b;
+ int i;
+
+ DEBUG(0, "ds: unregister_pccard_driver('%s')\n",
+ (char *)dev_info);
+ while ((*d) && (strncmp((*d)->dev_info, (char *)dev_info,
+ DEV_NAME_LEN) != 0))
+ d = &(*d)->next;
+ if (*d == NULL)
+ return -1;
+
+ target = *d;
+ if (target->use_count == 0) {
+ *d = target->next;
+ kfree(target);
+ } else {
+ /* Blank out any left-over device instances */
+ target->attach = NULL; target->detach = NULL;
+ for (i = 0; i < sockets; i++)
+ for (b = socket_table[i].bind; b; b = b->next)
+ if (b->driver == target) b->instance = NULL;
+ }
+ return 0;
+} /* unregister_pccard_driver */
+
+/*======================================================================
+
+ These manage a ring buffer of events pending for one user process
+
+======================================================================*/
+
+static int queue_empty(user_info_t *user)
+{
+ return (user->event_head == user->event_tail);
+}
+
+static event_t get_queued_event(user_info_t *user)
+{
+ user->event_tail = (user->event_tail+1) % MAX_EVENTS;
+ return user->event[user->event_tail];
+}
+
+static void queue_event(user_info_t *user, event_t event)
+{
+ user->event_head = (user->event_head+1) % MAX_EVENTS;
+ if (user->event_head == user->event_tail)
+ user->event_tail = (user->event_tail+1) % MAX_EVENTS;
+ user->event[user->event_head] = event;
+}
+
+static void handle_event(socket_info_t *s, event_t event)
+{
+ user_info_t *user;
+ for (user = s->user; user; user = user->next)
+ queue_event(user, event);
+ wake_up_interruptible(&s->queue);
+}
+
+static int handle_request(socket_info_t *s, event_t event)
+{
+ if (s->req_pending != 0)
+ return CS_IN_USE;
+ if (s->state & SOCKET_BUSY)
+ s->req_pending = 1;
+ handle_event(s, event);
+ if (s->req_pending > 0) {
+ interruptible_sleep_on(&s->request);
+ if (signal_pending(current))
+ return CS_IN_USE;
+ else
+ return s->req_result;
+ }
+ return CS_SUCCESS;
+}
+
+static void handle_removal(u_long sn)
+{
+ socket_info_t *s = &socket_table[sn];
+ handle_event(s, CS_EVENT_CARD_REMOVAL);
+ s->state &= ~SOCKET_REMOVAL_PENDING;
+}
+
+/*======================================================================
+
+ The card status event handler.
+
+======================================================================*/
+
+static int ds_event(event_t event, int priority,
+ event_callback_args_t *args)
+{
+ socket_info_t *s;
+ int i;
+
+ DEBUG(1, "ds: ds_event(0x%06x, %d, 0x%p)\n",
+ event, priority, args->client_handle);
+ s = args->client_data;
+ i = s - socket_table;
+
+ switch (event) {
+
+ case CS_EVENT_CARD_REMOVAL:
+ s->state &= ~SOCKET_PRESENT;
+ if (!(s->state & SOCKET_REMOVAL_PENDING)) {
+ s->state |= SOCKET_REMOVAL_PENDING;
+ s->removal.expires = jiffies + HZ/10;
+ add_timer(&s->removal);
+ }
+ break;
+
+ case CS_EVENT_CARD_INSERTION:
+ s->state |= SOCKET_PRESENT;
+ handle_event(s, event);
+ break;
+
+ case CS_EVENT_EJECTION_REQUEST:
+ return handle_request(s, event);
+ break;
+
+ default:
+ handle_event(s, event);
+ break;
+ }
+
+ return 0;
+} /* ds_event */
+
+/*======================================================================
+
+ bind_mtd() connects a memory region with an MTD client.
+
+======================================================================*/
+
+static int bind_mtd(int i, mtd_info_t *mtd_info)
+{
+ mtd_bind_t bind_req;
+ int ret;
+
+ bind_req.dev_info = &mtd_info->dev_info;
+ bind_req.Attributes = mtd_info->Attributes;
+ bind_req.Socket = i;
+ bind_req.CardOffset = mtd_info->CardOffset;
+ ret = CardServices(BindMTD, &bind_req);
+ if (ret != CS_SUCCESS) {
+ cs_error(NULL, BindMTD, ret);
+ printk(KERN_NOTICE "ds: unable to bind MTD '%s' to socket %d"
+ " offset 0x%x\n",
+ (char *)bind_req.dev_info, i, bind_req.CardOffset);
+ return -ENODEV;
+ }
+ return 0;
+} /* bind_mtd */
+
+/*======================================================================
+
+ bind_request() connects a socket to a particular client driver.
+ It looks up the specified device ID in the list of registered
+ drivers, binds it to the socket, and tries to create an instance
+ of the device. unbind_request() deletes a driver instance.
+
+======================================================================*/
+
+static int bind_request(int i, bind_info_t *bind_info)
+{
+ struct driver_info_t *driver;
+ socket_bind_t *b;
+ bind_req_t bind_req;
+ socket_info_t *s = &socket_table[i];
+ int ret;
+
+ DEBUG(2, "bind_request(%d, '%s')\n", i,
+ (char *)bind_info->dev_info);
+ for (driver = root_driver; driver; driver = driver->next)
+ if (strcmp((char *)driver->dev_info,
+ (char *)bind_info->dev_info) == 0)
+ break;
+ if (driver == NULL) {
+ driver = kmalloc(sizeof(driver_info_t), GFP_KERNEL);
+ strncpy(driver->dev_info, bind_info->dev_info, DEV_NAME_LEN);
+ driver->use_count = 0;
+ driver->next = root_driver;
+ driver->attach = NULL; driver->detach = NULL;
+ root_driver = driver;
+ }
+
+ for (b = s->bind; b; b = b->next)
+ if (driver == b->driver)
+ break;
+ if (b != NULL) {
+ bind_info->instance = b->instance;
+ return -EBUSY;
+ }
+
+ bind_req.Socket = i;
+ bind_req.Function = bind_info->function;
+ bind_req.dev_info = &driver->dev_info;
+ ret = CardServices(BindDevice, &bind_req);
+ if (ret != CS_SUCCESS) {
+ cs_error(NULL, BindDevice, ret);
+ printk(KERN_NOTICE "ds: unable to bind '%s' to socket %d\n",
+ (char *)dev_info, i);
+ return -ENODEV;
+ }
+
+ /* Add binding to list for this socket */
+ driver->use_count++;
+ b = kmalloc(sizeof(socket_bind_t), GFP_KERNEL);
+ b->driver = driver;
+ b->instance = NULL;
+ b->next = s->bind;
+ s->bind = b;
+
+ if (driver->attach) {
+ b->instance = driver->attach();
+ if (b->instance == NULL) {
+ printk(KERN_NOTICE "ds: unable to create instance "
+ "of '%s'!\n", (char *)bind_info->dev_info);
+ return -ENODEV;
+ }
+ }
+
+ return 0;
+} /* bind_request */
+
+/*====================================================================*/
+
+static int get_device_info(int i, bind_info_t *bind_info, int first)
+{
+ socket_info_t *s = &socket_table[i];
+ socket_bind_t *b;
+ dev_node_t *node;
+
+ for (b = s->bind; b; b = b->next)
+ if (strcmp((char *)b->driver->dev_info,
+ (char *)bind_info->dev_info) == 0)
+ break;
+ if (b == NULL) return -ENODEV;
+ if ((b->instance == NULL) ||
+ (b->instance->state & DEV_CONFIG_PENDING))
+ return -EAGAIN;
+ if (first)
+ node = b->instance->dev;
+ else
+ for (node = b->instance->dev; node; node = node->next)
+ if (node == bind_info->next) break;
+ if (node == NULL) return -ENODEV;
+
+ strncpy(bind_info->name, node->dev_name, DEV_NAME_LEN);
+ bind_info->name[DEV_NAME_LEN-1] = '\0';
+ bind_info->major = node->major;
+ bind_info->minor = node->minor;
+ bind_info->next = node->next;
+
+ return 0;
+} /* get_device_info */
+
+/*====================================================================*/
+
+static int unbind_request(int i, bind_info_t *bind_info)
+{
+ socket_info_t *s = &socket_table[i];
+ socket_bind_t **b, *c;
+
+ DEBUG(2, "unbind_request(%d, '%s')\n", i,
+ (char *)bind_info->dev_info);
+ for (b = &s->bind; *b; b = &(*b)->next)
+ if (strcmp((char *)(*b)->driver->dev_info,
+ (char *)bind_info->dev_info) == 0)
+ break;
+ if (*b == NULL)
+ return -ENODEV;
+
+ c = *b;
+ c->driver->use_count--;
+ if (c->driver->detach) {
+ if (c->instance)
+ c->driver->detach(c->instance);
+ } else {
+ if (c->driver->use_count == 0) {
+ driver_info_t **d;
+ for (d = &root_driver; *d; d = &((*d)->next))
+ if (c->driver == *d) break;
+ *d = (*d)->next;
+ kfree_s(c->driver, sizeof(driver_info_t));
+ }
+ }
+ *b = c->next;
+ kfree_s(c, sizeof(socket_bind_t));
+
+ return 0;
+} /* unbind_request */
+
+/*======================================================================
+
+ The user-mode PC Card device interface
+
+======================================================================*/
+
+static int ds_open(struct inode *inode, struct file *file)
+{
+ socket_t i = MINOR(inode->i_rdev);
+ socket_info_t *s;
+ user_info_t *user;
+
+ DEBUG(0, "ds_open(socket %d)\n", i);
+ if ((i >= sockets) || (sockets == 0))
+ return -ENODEV;
+ s = &socket_table[i];
+ if ((file->f_flags & O_ACCMODE) != O_RDONLY) {
+ if (s->state & SOCKET_BUSY)
+ return -EBUSY;
+ else
+ s->state |= SOCKET_BUSY;
+ }
+
+ MOD_INC_USE_COUNT;
+ user = kmalloc(sizeof(user_info_t), GFP_KERNEL);
+ user->event_tail = user->event_head = 0;
+ user->next = s->user;
+ user->user_magic = USER_MAGIC;
+ s->user = user;
+ file->private_data = user;
+
+ if (s->state & SOCKET_PRESENT)
+ queue_event(user, CS_EVENT_CARD_INSERTION);
+ return 0;
+} /* ds_open */
+
+/*====================================================================*/
+
+static int ds_release(struct inode *inode, struct file *file)
+{
+ socket_t i = MINOR(inode->i_rdev);
+ socket_info_t *s;
+ user_info_t *user, **link;
+
+ DEBUG(0, "ds_release(socket %d)\n", i);
+ if ((i >= sockets) || (sockets == 0))
+ return 0;
+ s = &socket_table[i];
+ user = file->private_data;
+ if (CHECK_USER(user))
+ return 0;
+
+ /* Unlink user data structure */
+ if ((file->f_flags & O_ACCMODE) != O_RDONLY)
+ s->state &= ~SOCKET_BUSY;
+ file->private_data = NULL;
+ for (link = &s->user; *link; link = &(*link)->next)
+ if (*link == user) break;
+ if (link == NULL)
+ return 0;
+ *link = user->next;
+ user->user_magic = 0;
+ kfree_s(user, sizeof(user_info_t));
+
+ MOD_DEC_USE_COUNT;
+ return 0;
+} /* ds_release */
+
+/*====================================================================*/
+
+static ssize_t ds_read(struct file *file, char *buf,
+ size_t count, loff_t *ppos)
+{
+ socket_t i = MINOR(file->f_dentry->d_inode->i_rdev);
+ socket_info_t *s;
+ user_info_t *user;
+
+ DEBUG(2, "ds_read(socket %d)\n", i);
+
+ if ((i >= sockets) || (sockets == 0))
+ return -ENODEV;
+ if (count < 4)
+ return -EINVAL;
+ s = &socket_table[i];
+ user = file->private_data;
+ if (CHECK_USER(user))
+ return -EIO;
+
+ if (queue_empty(user)) {
+ interruptible_sleep_on(&s->queue);
+ if (signal_pending(current))
+ return -EINTR;
+ }
+ put_user(get_queued_event(user), (int *)buf);
+ return 4;
+} /* ds_read */
+
+/*====================================================================*/
+
+static ssize_t ds_write(struct file *file, const char *buf,
+ size_t count, loff_t *ppos)
+{
+ socket_t i = MINOR(file->f_dentry->d_inode->i_rdev);
+ socket_info_t *s;
+ user_info_t *user;
+
+ DEBUG(2, "ds_write(socket %d)\n", i);
+
+ if ((i >= sockets) || (sockets == 0))
+ return -ENODEV;
+ if (count != 4)
+ return -EINVAL;
+ if ((file->f_flags & O_ACCMODE) == O_RDONLY)
+ return -EBADF;
+ s = &socket_table[i];
+ user = file->private_data;
+ if (CHECK_USER(user))
+ return -EIO;
+
+ if (s->req_pending) {
+ s->req_pending--;
+ get_user(s->req_result, (int *)buf);
+ if ((s->req_result != 0) || (s->req_pending == 0))
+ wake_up_interruptible(&s->request);
+ } else
+ return -EIO;
+
+ return 4;
+} /* ds_write */
+
+/*====================================================================*/
+
+static u_int ds_poll(struct file *file, poll_table *wait)
+{
+ socket_t i = MINOR(file->f_dentry->d_inode->i_rdev);
+ socket_info_t *s;
+ user_info_t *user;
+
+ DEBUG(2, "ds_poll(socket %d)\n", i);
+
+ if ((i >= sockets) || (sockets == 0))
+ return POLLERR;
+ s = &socket_table[i];
+ user = file->private_data;
+ if (CHECK_USER(user))
+ return POLLERR;
+ poll_wait(file, &s->queue, wait);
+ if (!queue_empty(user))
+ return POLLIN | POLLRDNORM;
+ return 0;
+} /* ds_poll */
+
+/*====================================================================*/
+
+static int ds_ioctl(struct inode * inode, struct file * file,
+ u_int cmd, u_long arg)
+{
+ socket_t i = MINOR(inode->i_rdev);
+ socket_info_t *s;
+ u_int size;
+ int ret, err;
+ ds_ioctl_arg_t buf;
+
+ DEBUG(2, "ds_ioctl(socket %d, %#x, %#lx)\n", i, cmd, arg);
+
+ if ((i >= sockets) || (sockets == 0))
+ return -ENODEV;
+ s = &socket_table[i];
+
+ size = (cmd & IOCSIZE_MASK) >> IOCSIZE_SHIFT;
+ if (size > sizeof(ds_ioctl_arg_t)) return -EINVAL;
+
+ /* Permission check */
+ if (!(cmd & IOC_OUT) && !suser())
+ return -EPERM;
+
+ if (cmd & IOC_IN) {
+ err = verify_area(VERIFY_READ, (char *)arg, size);
+ if (err) {
+ DEBUG(3, "ds_ioctl(): verify_read = %d\n", err);
+ return err;
+ }
+ }
+ if (cmd & IOC_OUT) {
+ err = verify_area(VERIFY_WRITE, (char *)arg, size);
+ if (err) {
+ DEBUG(3, "ds_ioctl(): verify_write = %d\n", err);
+ return err;
+ }
+ }
+
+ err = ret = 0;
+
+ if (cmd & IOC_IN) copy_from_user((char *)&buf, (char *)arg, size);
+
+ switch (cmd) {
+ case DS_ADJUST_RESOURCE_INFO:
+ ret = CardServices(AdjustResourceInfo, s->handle, &buf.adjust);
+ break;
+ case DS_GET_CARD_SERVICES_INFO:
+ ret = CardServices(GetCardServicesInfo, &buf.servinfo);
+ break;
+ case DS_GET_CONFIGURATION_INFO:
+ ret = CardServices(GetConfigurationInfo, s->handle, &buf.config);
+ break;
+ case DS_GET_FIRST_TUPLE:
+ ret = CardServices(GetFirstTuple, s->handle, &buf.tuple);
+ break;
+ case DS_GET_NEXT_TUPLE:
+ ret = CardServices(GetNextTuple, s->handle, &buf.tuple);
+ break;
+ case DS_GET_TUPLE_DATA:
+ buf.tuple.TupleData = buf.tuple_parse.data;
+ buf.tuple.TupleDataMax = sizeof(buf.tuple_parse.data);
+ ret = CardServices(GetTupleData, s->handle, &buf.tuple);
+ break;
+ case DS_PARSE_TUPLE:
+ buf.tuple.TupleData = buf.tuple_parse.data;
+ ret = CardServices(ParseTuple, s->handle, &buf.tuple,
+ &buf.tuple_parse.parse);
+ break;
+ case DS_RESET_CARD:
+ ret = CardServices(ResetCard, s->handle, NULL);
+ break;
+ case DS_GET_STATUS:
+ ret = CardServices(GetStatus, s->handle, &buf.status);
+ break;
+ case DS_VALIDATE_CIS:
+ ret = CardServices(ValidateCIS, s->handle, &buf.cisinfo);
+ break;
+ case DS_SUSPEND_CARD:
+ ret = CardServices(SuspendCard, s->handle, NULL);
+ break;
+ case DS_RESUME_CARD:
+ ret = CardServices(ResumeCard, s->handle, NULL);
+ break;
+ case DS_EJECT_CARD:
+ ret = CardServices(EjectCard, s->handle, NULL);
+ break;
+ case DS_INSERT_CARD:
+ ret = CardServices(InsertCard, s->handle, NULL);
+ break;
+ case DS_ACCESS_CONFIGURATION_REGISTER:
+ if ((buf.conf_reg.Action == CS_WRITE) && !suser())
+ return -EPERM;
+ ret = CardServices(AccessConfigurationRegister, s->handle,
+ &buf.conf_reg);
+ break;
+ case DS_GET_FIRST_REGION:
+ ret = CardServices(GetFirstRegion, s->handle, &buf.region);
+ break;
+ case DS_GET_NEXT_REGION:
+ ret = CardServices(GetNextRegion, s->handle, &buf.region);
+ break;
+ case DS_GET_FIRST_WINDOW:
+ buf.win_info.handle = (window_handle_t)s->handle;
+ ret = CardServices(GetFirstWindow, &buf.win_info.handle,
+ &buf.win_info.window);
+ break;
+ case DS_GET_NEXT_WINDOW:
+ ret = CardServices(GetNextWindow, &buf.win_info.handle,
+ &buf.win_info.window);
+ break;
+ case DS_GET_MEM_PAGE:
+ ret = CardServices(GetMemPage, buf.win_info.handle,
+ &buf.win_info.map);
+ break;
+ case DS_REPLACE_CIS:
+ ret = CardServices(ReplaceCIS, s->handle, &buf.cisdump);
+ break;
+ case DS_BIND_REQUEST:
+ if (!suser()) return -EPERM;
+ err = bind_request(i, &buf.bind_info);
+ break;
+ case DS_GET_DEVICE_INFO:
+ err = get_device_info(i, &buf.bind_info, 1);
+ break;
+ case DS_GET_NEXT_DEVICE:
+ err = get_device_info(i, &buf.bind_info, 0);
+ break;
+ case DS_UNBIND_REQUEST:
+ err = unbind_request(i, &buf.bind_info);
+ break;
+ case DS_BIND_MTD:
+ if (!suser()) return -EPERM;
+ err = bind_mtd(i, &buf.mtd_info);
+ break;
+ default:
+ err = -EINVAL;
+ }
+
+ if ((err == 0) && (ret != CS_SUCCESS)) {
+ DEBUG(2, "ds_ioctl: ret = %d\n", ret);
+ switch (ret) {
+ case CS_BAD_SOCKET: case CS_NO_CARD:
+ err = -ENODEV; break;
+ case CS_BAD_ARGS: case CS_BAD_ATTRIBUTE: case CS_BAD_IRQ:
+ case CS_BAD_TUPLE:
+ err = -EINVAL; break;
+ case CS_IN_USE:
+ err = -EBUSY; break;
+ case CS_OUT_OF_RESOURCE:
+ err = -ENOSPC; break;
+ case CS_NO_MORE_ITEMS:
+ err = -ENODATA; break;
+ case CS_UNSUPPORTED_FUNCTION:
+ err = -ENOSYS; break;
+ default:
+ err = -EIO; break;
+ }
+ }
+
+ if (cmd & IOC_OUT) copy_to_user((char *)arg, (char *)&buf, size);
+
+ return err;
+} /* ds_ioctl */
+
+/*====================================================================*/
+
+static struct file_operations ds_fops = {
+ NULL, /* lseek */
+ ds_read, /* read */
+ ds_write, /* write */
+ NULL, /* readdir */
+ ds_poll, /* poll */
+ ds_ioctl, /* ioctl */
+ NULL, /* mmap */
+ ds_open, /* open */
+ NULL, /* flush */
+ ds_release, /* release */
+ NULL /* fsync */
+};
+
+EXPORT_SYMBOL(register_pccard_driver);
+EXPORT_SYMBOL(unregister_pccard_driver);
+
+/*====================================================================*/
+
+int __init init_pcmcia_ds(void)
+{
+ client_reg_t client_reg;
+ servinfo_t serv;
+ bind_req_t bind;
+ socket_info_t *s;
+ int i, ret;
+
+ DEBUG(0, "%s\n", version);
+
+ CardServices(GetCardServicesInfo, &serv);
+ if (serv.Revision != CS_RELEASE_CODE) {
+ printk(KERN_NOTICE "ds: Card Services release does not match!\n");
+ return -1;
+ }
+ if (serv.Count == 0) {
+ printk(KERN_NOTICE "ds: no socket drivers loaded!\n");
+ return -1;
+ }
+
+ sockets = serv.Count;
+ socket_table = kmalloc(sockets*sizeof(socket_info_t), GFP_KERNEL);
+ for (i = 0, s = socket_table; i < sockets; i++, s++) {
+ s->state = 0;
+ s->user = NULL;
+ s->req_pending = 0;
+ init_waitqueue_head(&s->queue);
+ init_waitqueue_head(&s->request);
+ s->handle = NULL;
+ s->removal.prev = s->removal.next = NULL;
+ s->removal.data = i;
+ s->removal.function = &handle_removal;
+ s->bind = NULL;
+ }
+
+ /* Set up hotline to Card Services */
+ client_reg.dev_info = bind.dev_info = &dev_info;
+ client_reg.Attributes = INFO_MASTER_CLIENT;
+ client_reg.EventMask =
+ CS_EVENT_CARD_INSERTION | CS_EVENT_CARD_REMOVAL |
+ CS_EVENT_RESET_PHYSICAL | CS_EVENT_CARD_RESET |
+ CS_EVENT_EJECTION_REQUEST | CS_EVENT_INSERTION_REQUEST |
+ CS_EVENT_PM_SUSPEND | CS_EVENT_PM_RESUME;
+ client_reg.event_handler = &ds_event;
+ client_reg.Version = 0x0210;
+ for (i = 0; i < sockets; i++) {
+ bind.Socket = i;
+ bind.Function = BIND_FN_ALL;
+ ret = CardServices(BindDevice, &bind);
+ if (ret != CS_SUCCESS) {
+ cs_error(NULL, BindDevice, ret);
+ break;
+ }
+ client_reg.event_callback_args.client_data = &socket_table[i];
+ ret = CardServices(RegisterClient, &socket_table[i].handle,
+ &client_reg);
+ if (ret != CS_SUCCESS) {
+ cs_error(NULL, RegisterClient, ret);
+ break;
+ }
+ }
+
+ /* Set up character device for user mode clients */
+ i = register_chrdev(0, "pcmcia", &ds_fops);
+ if (i == -EBUSY)
+ printk(KERN_NOTICE "unable to find a free device # for "
+ "Driver Services\n");
+ else
+ major_dev = i;
+
+ return 0;
+}
+
+#ifdef MODULE
+
+int __init init_module(void)
+{
+ return init_pcmcia_ds();
+}
+
+void __exit cleanup_module(void)
+{
+ int i;
+ if (major_dev != -1)
+ unregister_chrdev(major_dev, "pcmcia");
+ for (i = 0; i < sockets; i++)
+ CardServices(DeregisterClient, socket_table[i].handle);
+ sockets = 0;
+ kfree(socket_table);
+}
+
+#endif
diff --git a/drivers/pcmcia/i82365.c b/drivers/pcmcia/i82365.c
new file mode 100644
index 000000000..9e80943e4
--- /dev/null
+++ b/drivers/pcmcia/i82365.c
@@ -0,0 +1,2804 @@
+/*======================================================================
+
+ Device driver for Intel 82365 and compatible PC Card controllers,
+ and Yenta-compatible PCI-to-CardBus controllers.
+
+ i82365.c 1.254 1999/09/15 15:32:19
+
+ 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 David A. Hinds
+ <dhinds@hyper.stanford.edu>. Portions created by David A. Hinds
+ are Copyright (C) 1999 David A. Hinds. 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/types.h>
+#include <linux/fcntl.h>
+#include <linux/string.h>
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/timer.h>
+#include <linux/sched.h>
+#include <linux/malloc.h>
+#include <linux/pci.h>
+#include <linux/ioport.h>
+#include <linux/delay.h>
+#include <linux/proc_fs.h>
+#include <asm/irq.h>
+#include <asm/io.h>
+#include <asm/bitops.h>
+#include <asm/segment.h>
+#include <asm/system.h>
+
+#include <pcmcia/version.h>
+#include <pcmcia/cs_types.h>
+#include <pcmcia/ss.h>
+#include <pcmcia/cs.h>
+
+/* ISA-bus controllers */
+#include "i82365.h"
+#include "cirrus.h"
+#include "vg468.h"
+#include "ricoh.h"
+#include "o2micro.h"
+
+/* PCI-bus controllers */
+#include "yenta.h"
+#include "ti113x.h"
+#include "smc34c90.h"
+#include "topic.h"
+
+#ifdef PCMCIA_DEBUG
+static int pc_debug = PCMCIA_DEBUG;
+MODULE_PARM(pc_debug, "i");
+#define DEBUG(n, args...) if (pc_debug>(n)) printk(KERN_DEBUG args)
+static const char *version =
+"i82365.c 1.254 1999/09/15 15:32:19 (David Hinds)";
+#else
+#define DEBUG(n, args...) do { } while (0)
+#endif
+
+static void irq_count(int, void *, struct pt_regs *);
+static inline int _check_irq(int irq, int flags)
+{
+ if (request_irq(irq, irq_count, flags, "x", NULL) != 0)
+ return -1;
+ free_irq(irq, NULL);
+ return 0;
+}
+
+/*====================================================================*/
+
+/* Parameters that can be set with 'insmod' */
+
+#ifdef CONFIG_ISA
+/* Default base address for i82365sl and other ISA chips */
+static int i365_base = 0x3e0;
+/* Should we probe at 0x3e2 for an extra ISA controller? */
+static int extra_sockets = 0;
+/* Specify a socket number to ignore */
+static int ignore = -1;
+/* Bit map or list of interrupts to choose from */
+static u_int irq_mask = 0xffff;
+static int irq_list[16] = { -1 };
+/* The card status change interrupt -- 0 means autoselect */
+static int cs_irq = 0;
+#endif
+
+/* Probe for safe interrupts? */
+static int do_scan = 1;
+/* Poll status interval -- 0 means default to interrupt */
+static int poll_interval = 0;
+/* External clock time, in nanoseconds. 120 ns = 8.33 MHz */
+static int cycle_time = 120;
+
+/* Cirrus options */
+static int has_dma = -1;
+static int has_led = -1;
+static int has_ring = -1;
+static int dynamic_mode = 0;
+static int freq_bypass = -1;
+static int setup_time = -1;
+static int cmd_time = -1;
+static int recov_time = -1;
+
+#ifdef CONFIG_ISA
+/* Vadem options */
+static int async_clock = -1;
+static int cable_mode = -1;
+static int wakeup = 0;
+#endif
+
+#ifdef CONFIG_ISA
+MODULE_PARM(i365_base, "i");
+MODULE_PARM(ignore, "i");
+MODULE_PARM(extra_sockets, "i");
+MODULE_PARM(irq_mask, "i");
+MODULE_PARM(irq_list, "1-16i");
+MODULE_PARM(cs_irq, "i");
+MODULE_PARM(async_clock, "i");
+MODULE_PARM(cable_mode, "i");
+MODULE_PARM(wakeup, "i");
+#endif
+
+MODULE_PARM(do_scan, "i");
+MODULE_PARM(poll_interval, "i");
+MODULE_PARM(cycle_time, "i");
+MODULE_PARM(has_dma, "i");
+MODULE_PARM(has_led, "i");
+MODULE_PARM(has_ring, "i");
+MODULE_PARM(dynamic_mode, "i");
+MODULE_PARM(freq_bypass, "i");
+MODULE_PARM(setup_time, "i");
+MODULE_PARM(cmd_time, "i");
+MODULE_PARM(recov_time, "i");
+
+#ifdef CONFIG_PCI
+/* Scan PCI bus? */
+static int do_pci_probe = 1;
+/* Default memory base address for CardBus controllers */
+static u_int cb_mem_base[] = { 0x68000000, 0xf8000000 };
+static int fast_pci = -1;
+static int hold_time = -1;
+/* Override BIOS interrupt routing mode? */
+static int irq_mode = -1;
+static int has_clkrun = -1;
+static int clkrun_sel = -1;
+static int pci_latency = -1;
+static int cb_latency = -1;
+static int cb_bus_base = 0;
+static int cb_bus_step = 2;
+static int cb_write_post = -1;
+MODULE_PARM(do_pci_probe, "i");
+MODULE_PARM(cb_mem_base, "i");
+MODULE_PARM(fast_pci, "i");
+MODULE_PARM(hold_time, "i");
+MODULE_PARM(irq_mode, "i");
+MODULE_PARM(has_clkrun, "i");
+MODULE_PARM(clkrun_sel, "i");
+MODULE_PARM(pci_latency, "i");
+MODULE_PARM(cb_latency, "i");
+MODULE_PARM(cb_bus_base, "i");
+MODULE_PARM(cb_bus_step, "i");
+MODULE_PARM(cb_write_post, "i");
+#endif
+
+#ifdef CONFIG_ISA
+#ifdef CONFIG_PCI
+/* PCI card status change interrupts? */
+static int pci_csc = 0;
+/* PCI IO card functional interrupts? */
+static int pci_int = 0;
+MODULE_PARM(pci_csc, "i");
+MODULE_PARM(pci_int, "i");
+#else /* no PCI */
+#define pci_csc 0
+#define pci_int 0
+#endif
+#else /* no ISA */
+#ifdef CONFIG_PCI
+#define pci_csc 0
+#define pci_int 1
+#else
+#error "No bus architectures defined!"
+#endif
+#endif
+
+/*====================================================================*/
+
+typedef struct cirrus_state_t {
+ u_char misc1, misc2;
+ u_char timer[6];
+} cirrus_state_t;
+
+typedef struct vg46x_state_t {
+ u_char ctl, ema;
+} vg46x_state_t;
+
+typedef struct ti113x_state_t {
+ u_int sysctl;
+ u_char cardctl, devctl, diag;
+} ti113x_state_t;
+
+typedef struct rl5c4xx_state_t {
+ u_short misc, ctl, io, mem;
+} rl5c4xx_state_t;
+
+typedef struct o2micro_state_t {
+ u_char mode_a, mode_b, mode_c, mode_d;
+ u_char mhpg, fifo, mode_e;
+} o2micro_state_t;
+
+typedef struct topic_state_t {
+ u_char slot, ccr, cdr;
+ u_int rcr;
+} topic_state_t;
+
+typedef struct socket_info_t {
+ u_short type, flags;
+ socket_cap_t cap;
+ u_short ioaddr;
+ u_short psock;
+ u_char cs_irq, intr;
+ void (*handler)(void *info, u_int events);
+ void *info;
+#ifdef CONFIG_PROC_FS
+ struct proc_dir_entry *proc;
+#endif
+#ifdef CONFIG_PCI
+ u_short vendor, device;
+ u_char revision, bus, devfn;
+ u_short bcr;
+ u_char pci_lat, cb_lat, sub_bus;
+ u_char cache, pmcs;
+ u_int cb_phys;
+ char *cb_virt;
+#endif
+ union {
+ cirrus_state_t cirrus;
+ vg46x_state_t vg46x;
+#ifdef CONFIG_PCI
+ o2micro_state_t o2micro;
+ ti113x_state_t ti113x;
+ rl5c4xx_state_t rl5c4xx;
+ topic_state_t topic;
+#endif
+ } state;
+} socket_info_t;
+
+/* Where we keep track of our sockets... */
+static int sockets = 0;
+static socket_info_t socket[8] = {
+ { 0, }, /* ... */
+};
+
+/* Default ISA interrupt mask */
+#define I365_MASK 0xdeb8 /* irq 15,14,12,11,10,9,7,5,4,3 */
+
+static void pcic_interrupt_wrapper(u_long);
+static void pcic_interrupt(int irq, void *dev,
+ struct pt_regs *regs);
+static int pcic_service(u_int sock, u_int cmd, void *arg);
+#ifdef CONFIG_PROC_FS
+static void pcic_proc_remove(u_short sock);
+#endif
+
+#ifdef CONFIG_ISA
+static int grab_irq;
+static spinlock_t isa_lock = SPIN_LOCK_UNLOCKED;
+#endif
+static struct timer_list poll_timer;
+
+/*====================================================================*/
+
+#ifdef CONFIG_PCI
+
+#ifndef PCI_VENDOR_ID_INTEL
+#define PCI_VENDOR_ID_INTEL 0x8086
+#endif
+#ifndef PCI_VENDOR_ID_OMEGA
+#define PCI_VENDOR_ID_OMEGA 0x119b
+#endif
+#ifndef PCI_DEVICE_ID_OMEGA_PCMCIA
+#define PCI_DEVICE_ID_OMEGA_PCMCIA 0x1221
+#endif
+
+/* Default settings for PCI command configuration register */
+#define CMD_DFLT (PCI_COMMAND_IO|PCI_COMMAND_MEMORY| \
+ PCI_COMMAND_MASTER|PCI_COMMAND_WAIT)
+
+#endif
+
+/* These definitions must match the pcic table! */
+typedef enum pcic_id {
+#ifdef CONFIG_ISA
+ IS_I82365A, IS_I82365B, IS_I82365DF,
+ IS_IBM, IS_RF5Cx96, IS_VLSI, IS_VG468, IS_VG469,
+ IS_PD6710, IS_PD672X, IS_VT83C469,
+#endif
+#ifdef CONFIG_PCI
+ IS_PD6729, IS_PD6730, IS_OZ6729, IS_OZ6730,
+ IS_I82092AA, IS_OM82C092G,
+ IS_PD6832, IS_OZ6832, IS_OZ6836,
+ IS_RL5C465, IS_RL5C466, IS_RL5C475, IS_RL5C476, IS_RL5C478,
+ IS_SMC34C90,
+ IS_TI1130, IS_TI1131, IS_TI1250A, IS_TI1220, IS_TI1221, IS_TI1210,
+ IS_TI1251A, IS_TI1251B, IS_TI1450, IS_TI1225, IS_TI1211, IS_TI1420,
+ IS_TOPIC95_A, IS_TOPIC95_B, IS_TOPIC97,
+ IS_UNK_PCI, IS_UNK_CARDBUS
+#endif
+} pcic_id;
+
+/* Flags for classifying groups of controllers */
+#define IS_VADEM 0x0001
+#define IS_CIRRUS 0x0002
+#define IS_TI 0x0004
+#define IS_O2MICRO 0x0008
+#define IS_VIA 0x0010
+#define IS_TOPIC 0x0020
+#define IS_RICOH 0x0040
+#define IS_UNKNOWN 0x0400
+#define IS_VG_PWR 0x0800
+#define IS_DF_PWR 0x1000
+#define IS_PCI 0x2000
+#define IS_CARDBUS 0x4000
+#define IS_ALIVE 0x8000
+
+typedef struct pcic_t {
+ char *name;
+ u_short flags;
+#ifdef CONFIG_PCI
+ u_short vendor, device;
+#endif
+} pcic_t;
+
+static pcic_t pcic[] = {
+#ifdef CONFIG_ISA
+ { "Intel i82365sl A step", 0 },
+ { "Intel i82365sl B step", 0 },
+ { "Intel i82365sl DF", IS_DF_PWR },
+ { "IBM Clone", 0 },
+ { "Ricoh RF5C296/396", 0 },
+ { "VLSI 82C146", 0 },
+ { "Vadem VG-468", IS_VADEM },
+ { "Vadem VG-469", IS_VADEM|IS_VG_PWR },
+ { "Cirrus PD6710", IS_CIRRUS },
+ { "Cirrus PD672x", IS_CIRRUS },
+ { "VIA VT83C469", IS_CIRRUS|IS_VIA },
+#endif
+#ifdef CONFIG_PCI
+ { "Cirrus PD6729", IS_CIRRUS|IS_PCI,
+ PCI_VENDOR_ID_CIRRUS, PCI_DEVICE_ID_CIRRUS_6729 },
+ { "Cirrus PD6730", IS_CIRRUS|IS_PCI,
+ PCI_VENDOR_ID_CIRRUS, 0xffff },
+ { "O2Micro OZ6729", IS_O2MICRO|IS_PCI|IS_VG_PWR,
+ PCI_VENDOR_ID_O2, PCI_DEVICE_ID_O2_6729 },
+ { "O2Micro OZ6730", IS_O2MICRO|IS_PCI|IS_VG_PWR,
+ PCI_VENDOR_ID_O2, PCI_DEVICE_ID_O2_6730 },
+ { "Intel 82092AA", IS_PCI,
+ PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_OMEGA_PCMCIA },
+ { "Omega Micro 82C092G", IS_PCI,
+ PCI_VENDOR_ID_OMEGA, PCI_DEVICE_ID_OMEGA_PCMCIA },
+ { "Cirrus PD6832", IS_CIRRUS|IS_CARDBUS,
+ PCI_VENDOR_ID_CIRRUS, PCI_DEVICE_ID_CIRRUS_6832 },
+ { "O2Micro OZ6832/OZ6833", IS_O2MICRO|IS_CARDBUS|IS_VG_PWR,
+ PCI_VENDOR_ID_O2, PCI_DEVICE_ID_O2_6832 },
+ { "O2Micro OZ6836/OZ6860", IS_O2MICRO|IS_CARDBUS|IS_VG_PWR,
+ PCI_VENDOR_ID_O2, PCI_DEVICE_ID_O2_6836 },
+ { "Ricoh RL5C465", IS_RICOH|IS_CARDBUS|IS_DF_PWR,
+ PCI_VENDOR_ID_RICOH, PCI_DEVICE_ID_RICOH_RL5C465 },
+ { "Ricoh RL5C466", IS_RICOH|IS_CARDBUS|IS_DF_PWR,
+ PCI_VENDOR_ID_RICOH, PCI_DEVICE_ID_RICOH_RL5C466 },
+ { "Ricoh RL5C475", IS_RICOH|IS_CARDBUS|IS_DF_PWR,
+ PCI_VENDOR_ID_RICOH, PCI_DEVICE_ID_RICOH_RL5C475 },
+ { "Ricoh RL5C476", IS_RICOH|IS_CARDBUS|IS_DF_PWR,
+ PCI_VENDOR_ID_RICOH, PCI_DEVICE_ID_RICOH_RL5C476 },
+ { "Ricoh RL5C478", IS_RICOH|IS_CARDBUS|IS_DF_PWR,
+ PCI_VENDOR_ID_RICOH, PCI_DEVICE_ID_RICOH_RL5C478 },
+ { "SMC 34C90", IS_CARDBUS|IS_DF_PWR,
+ PCI_VENDOR_ID_SMC, PCI_DEVICE_ID_SMC_34C90 },
+ { "TI 1130", IS_TI|IS_CARDBUS|IS_DF_PWR,
+ PCI_VENDOR_ID_TI, PCI_DEVICE_ID_TI_1130 },
+ { "TI 1131", IS_TI|IS_CARDBUS|IS_DF_PWR,
+ PCI_VENDOR_ID_TI, PCI_DEVICE_ID_TI_1131 },
+ { "TI 1250A", IS_TI|IS_CARDBUS|IS_DF_PWR,
+ PCI_VENDOR_ID_TI, PCI_DEVICE_ID_TI_1250A },
+ { "TI 1220", IS_TI|IS_CARDBUS|IS_DF_PWR,
+ PCI_VENDOR_ID_TI, PCI_DEVICE_ID_TI_1220 },
+ { "TI 1221", IS_TI|IS_CARDBUS|IS_DF_PWR,
+ PCI_VENDOR_ID_TI, PCI_DEVICE_ID_TI_1221 },
+ { "TI 1210", IS_TI|IS_CARDBUS|IS_DF_PWR,
+ PCI_VENDOR_ID_TI, PCI_DEVICE_ID_TI_1210 },
+ { "TI 1251A", IS_TI|IS_CARDBUS|IS_DF_PWR,
+ PCI_VENDOR_ID_TI, PCI_DEVICE_ID_TI_1251A },
+ { "TI 1251B", IS_TI|IS_CARDBUS|IS_DF_PWR,
+ PCI_VENDOR_ID_TI, PCI_DEVICE_ID_TI_1251B },
+ { "TI 1450", IS_TI|IS_CARDBUS|IS_DF_PWR,
+ PCI_VENDOR_ID_TI, PCI_DEVICE_ID_TI_1450 },
+ { "TI 1225", IS_TI|IS_CARDBUS|IS_DF_PWR,
+ PCI_VENDOR_ID_TI, PCI_DEVICE_ID_TI_1225 },
+ { "TI 1211", IS_TI|IS_CARDBUS|IS_DF_PWR,
+ PCI_VENDOR_ID_TI, PCI_DEVICE_ID_TI_1211 },
+ { "TI 1420", IS_TI|IS_CARDBUS|IS_DF_PWR,
+ PCI_VENDOR_ID_TI, PCI_DEVICE_ID_TI_1420 },
+ { "Toshiba ToPIC95-A", IS_CARDBUS|IS_TOPIC|IS_DF_PWR,
+ PCI_VENDOR_ID_TOSHIBA, PCI_DEVICE_ID_TOSHIBA_TOPIC95_A },
+ { "Toshiba ToPIC95-B", IS_CARDBUS|IS_TOPIC|IS_DF_PWR,
+ PCI_VENDOR_ID_TOSHIBA, PCI_DEVICE_ID_TOSHIBA_TOPIC95_B },
+ { "Toshiba ToPIC97", IS_CARDBUS|IS_TOPIC|IS_DF_PWR,
+ PCI_VENDOR_ID_TOSHIBA, PCI_DEVICE_ID_TOSHIBA_TOPIC97 },
+ { "Unknown", IS_PCI|IS_UNKNOWN, 0, 0 },
+ { "Unknown", IS_CARDBUS|IS_DF_PWR|IS_UNKNOWN, 0, 0 }
+#endif
+};
+
+#define PCIC_COUNT (sizeof(pcic)/sizeof(pcic_t))
+
+/*====================================================================*/
+
+/* Some PCI shortcuts */
+
+#ifdef CONFIG_PCI
+
+#define pci_readb pcibios_read_config_byte
+#define pci_writeb pcibios_write_config_byte
+#define pci_readw pcibios_read_config_word
+#define pci_writew pcibios_write_config_word
+#define pci_readl pcibios_read_config_dword
+#define pci_writel pcibios_write_config_dword
+
+#define cb_readb(s, r) readb(socket[s].cb_virt + (r))
+#define cb_readl(s, r) readl(socket[s].cb_virt + (r))
+#define cb_writeb(s, r, v) writeb(v, socket[s].cb_virt + (r))
+#define cb_writel(s, r, v) writel(v, socket[s].cb_virt + (r))
+
+static void cb_get_power(u_short sock, socket_state_t *state);
+static void cb_set_power(u_short sock, socket_state_t *state);
+#endif
+
+/*====================================================================*/
+
+static u_char i365_get(u_short sock, u_short reg)
+{
+#ifdef CONFIG_PCI
+ if (socket[sock].cb_virt)
+ return cb_readb(sock, 0x0800 + reg);
+ else
+#endif
+ {
+ u_short port = socket[sock].ioaddr;
+ u_char val;
+ reg = I365_REG(socket[sock].psock, reg);
+ outb(reg, port); val = inb(port+1);
+ return val;
+ }
+}
+
+static void i365_set(u_short sock, u_short reg, u_char data)
+{
+#ifdef CONFIG_PCI
+ if (socket[sock].cb_virt)
+ cb_writeb(sock, 0x0800 + reg, data);
+ else
+#endif
+ {
+ u_short port = socket[sock].ioaddr;
+ u_char val = I365_REG(socket[sock].psock, reg);
+ outb(val, port); outb(data, port+1);
+ }
+}
+
+static void i365_bset(u_short sock, u_short reg, u_char mask)
+{
+ u_char d = i365_get(sock, reg);
+ d |= mask;
+ i365_set(sock, reg, d);
+}
+
+static void i365_bclr(u_short sock, u_short reg, u_char mask)
+{
+ u_char d = i365_get(sock, reg);
+ d &= ~mask;
+ i365_set(sock, reg, d);
+}
+
+static void i365_bflip(u_short sock, u_short reg, u_char mask, int b)
+{
+ u_char d = i365_get(sock, reg);
+ if (b)
+ d |= mask;
+ else
+ d &= ~mask;
+ i365_set(sock, reg, d);
+}
+
+static u_short i365_get_pair(u_short sock, u_short reg)
+{
+ u_short a, b;
+ a = i365_get(sock, reg);
+ b = i365_get(sock, reg+1);
+ return (a + (b<<8));
+}
+
+static void i365_set_pair(u_short sock, u_short reg, u_short data)
+{
+ i365_set(sock, reg, data & 0xff);
+ i365_set(sock, reg+1, data >> 8);
+}
+
+/*======================================================================
+
+ Code to save and restore global state information for Cirrus
+ PD67xx controllers, and to set and report global configuration
+ options.
+
+ The VIA controllers also use these routines, as they are mostly
+ Cirrus lookalikes, without the timing registers.
+
+======================================================================*/
+
+#define flip(v,b,f) (v = ((f)<0) ? v : ((f) ? ((v)|(b)) : ((v)&(~b))))
+
+static void cirrus_get_state(u_short s)
+{
+ int i;
+ cirrus_state_t *p = &socket[s].state.cirrus;
+ p->misc1 = i365_get(s, PD67_MISC_CTL_1);
+ p->misc1 &= (PD67_MC1_MEDIA_ENA | PD67_MC1_INPACK_ENA);
+ p->misc2 = i365_get(s, PD67_MISC_CTL_2);
+ for (i = 0; i < 6; i++)
+ p->timer[i] = i365_get(s, PD67_TIME_SETUP(0)+i);
+}
+
+static void cirrus_set_state(u_short s)
+{
+ int i;
+ u_char misc;
+ cirrus_state_t *p = &socket[s].state.cirrus;
+
+ misc = i365_get(s, PD67_MISC_CTL_2);
+ i365_set(s, PD67_MISC_CTL_2, p->misc2);
+ if (misc & PD67_MC2_SUSPEND) mdelay(50);
+ misc = i365_get(s, PD67_MISC_CTL_1);
+ misc &= ~(PD67_MC1_MEDIA_ENA | PD67_MC1_INPACK_ENA);
+ i365_set(s, PD67_MISC_CTL_1, misc | p->misc1);
+ for (i = 0; i < 6; i++)
+ i365_set(s, PD67_TIME_SETUP(0)+i, p->timer[i]);
+}
+
+#ifdef CONFIG_PCI
+static int cirrus_set_irq_mode(u_short s, int pcsc, int pint)
+{
+ flip(socket[s].bcr, PD6832_BCR_MGMT_IRQ_ENA, !pcsc);
+ return 0;
+}
+#endif /* CONFIG_PCI */
+
+static u_int __init cirrus_set_opts(u_short s, char *buf)
+{
+ socket_info_t *t = &socket[s];
+ cirrus_state_t *p = &socket[s].state.cirrus;
+ u_int mask = 0xffff;
+
+ if (has_ring == -1) has_ring = 1;
+ flip(p->misc2, PD67_MC2_IRQ15_RI, has_ring);
+ flip(p->misc2, PD67_MC2_DYNAMIC_MODE, dynamic_mode);
+ if (p->misc2 & PD67_MC2_IRQ15_RI)
+ strcat(buf, " [ring]");
+ if (p->misc2 & PD67_MC2_DYNAMIC_MODE)
+ strcat(buf, " [dyn mode]");
+ if (p->misc1 & PD67_MC1_INPACK_ENA)
+ strcat(buf, " [inpack]");
+ if (!(t->flags & (IS_PCI | IS_CARDBUS))) {
+ if (p->misc2 & PD67_MC2_IRQ15_RI)
+ mask &= ~0x8000;
+ if (has_led > 0) {
+ strcat(buf, " [led]");
+ mask &= ~0x1000;
+ }
+ if (has_dma > 0) {
+ strcat(buf, " [dma]");
+ mask &= ~0x0600;
+ flip(p->misc2, PD67_MC2_FREQ_BYPASS, freq_bypass);
+ if (p->misc2 & PD67_MC2_FREQ_BYPASS)
+ strcat(buf, " [freq bypass]");
+ }
+#ifdef CONFIG_PCI
+ } else {
+ p->misc1 &= ~PD67_MC1_MEDIA_ENA;
+ flip(p->misc2, PD67_MC2_FAST_PCI, fast_pci);
+ if (p->misc2 & PD67_MC2_IRQ15_RI)
+ mask &= (socket[s].type == IS_PD6730) ? ~0x0400 : ~0x8000;
+#endif
+ }
+ if (!(t->flags & IS_VIA)) {
+ if (setup_time >= 0)
+ p->timer[0] = p->timer[3] = setup_time;
+ if (cmd_time > 0) {
+ p->timer[1] = cmd_time;
+ p->timer[4] = cmd_time*2+4;
+ }
+ if (p->timer[1] == 0) {
+ p->timer[1] = 6; p->timer[4] = 16;
+ if (p->timer[0] == 0)
+ p->timer[0] = p->timer[3] = 1;
+ }
+ if (recov_time >= 0)
+ p->timer[2] = p->timer[5] = recov_time;
+ buf += strlen(buf);
+ sprintf(buf, " [%d/%d/%d] [%d/%d/%d]", p->timer[0], p->timer[1],
+ p->timer[2], p->timer[3], p->timer[4], p->timer[5]);
+ }
+ return mask;
+}
+
+/*======================================================================
+
+ Code to save and restore global state information for Vadem VG468
+ and VG469 controllers, and to set and report global configuration
+ options.
+
+======================================================================*/
+
+#ifdef CONFIG_ISA
+
+static void vg46x_get_state(u_short s)
+{
+ vg46x_state_t *p = &socket[s].state.vg46x;
+ p->ctl = i365_get(s, VG468_CTL);
+ if (socket[s].type == IS_VG469)
+ p->ema = i365_get(s, VG469_EXT_MODE);
+}
+
+static void vg46x_set_state(u_short s)
+{
+ vg46x_state_t *p = &socket[s].state.vg46x;
+ i365_set(s, VG468_CTL, p->ctl);
+ if (socket[s].type == IS_VG469)
+ i365_set(s, VG469_EXT_MODE, p->ema);
+}
+
+static u_int __init vg46x_set_opts(u_short s, char *buf)
+{
+ vg46x_state_t *p = &socket[s].state.vg46x;
+
+ flip(p->ctl, VG468_CTL_ASYNC, async_clock);
+ flip(p->ema, VG469_MODE_CABLE, cable_mode);
+ if (p->ctl & VG468_CTL_ASYNC)
+ strcat(buf, " [async]");
+ if (p->ctl & VG468_CTL_INPACK)
+ strcat(buf, " [inpack]");
+ if (socket[s].type == IS_VG469) {
+ u_char vsel = i365_get(s, VG469_VSELECT);
+ if (vsel & VG469_VSEL_EXT_STAT) {
+ strcat(buf, " [ext mode]");
+ if (vsel & VG469_VSEL_EXT_BUS)
+ strcat(buf, " [isa buf]");
+ }
+ if (p->ema & VG469_MODE_CABLE)
+ strcat(buf, " [cable]");
+ if (p->ema & VG469_MODE_COMPAT)
+ strcat(buf, " [c step]");
+ }
+ return 0xffff;
+}
+
+#endif
+
+/*======================================================================
+
+ Code to save and restore global state information for TI 1130 and
+ TI 1131 controllers, and to set and report global configuration
+ options.
+
+======================================================================*/
+
+#ifdef CONFIG_PCI
+
+static void ti113x_get_state(u_short s)
+{
+ socket_info_t *t = &socket[s];
+ ti113x_state_t *p = &socket[s].state.ti113x;
+ pci_readl(t->bus, t->devfn, TI113X_SYSTEM_CONTROL, &p->sysctl);
+ pci_readb(t->bus, t->devfn, TI113X_CARD_CONTROL, &p->cardctl);
+ pci_readb(t->bus, t->devfn, TI113X_DEVICE_CONTROL, &p->devctl);
+ pci_readb(t->bus, t->devfn, TI1250_DIAGNOSTIC, &p->diag);
+}
+
+static void ti113x_set_state(u_short s)
+{
+ socket_info_t *t = &socket[s];
+ ti113x_state_t *p = &socket[s].state.ti113x;
+ pci_writel(t->bus, t->devfn, TI113X_SYSTEM_CONTROL, p->sysctl);
+ pci_writeb(t->bus, t->devfn, TI113X_CARD_CONTROL, p->cardctl);
+ pci_writeb(t->bus, t->devfn, TI113X_DEVICE_CONTROL, p->devctl);
+ pci_writeb(t->bus, t->devfn, TI1250_MULTIMEDIA_CTL, 0);
+ pci_writeb(t->bus, t->devfn, TI1250_DIAGNOSTIC, p->diag);
+ i365_set_pair(s, TI113X_IO_OFFSET(0), 0);
+ i365_set_pair(s, TI113X_IO_OFFSET(1), 0);
+}
+
+static int ti113x_set_irq_mode(u_short s, int pcsc, int pint)
+{
+ socket_info_t *t = &socket[s];
+ ti113x_state_t *p = &t->state.ti113x;
+ t->intr = (pcsc) ? I365_INTR_ENA : 0;
+ if (t->type <= IS_TI1131) {
+ p->cardctl &= ~(TI113X_CCR_PCI_IRQ_ENA |
+ TI113X_CCR_PCI_IREQ | TI113X_CCR_PCI_CSC);
+ if (pcsc)
+ p->cardctl |= TI113X_CCR_PCI_IRQ_ENA | TI113X_CCR_PCI_CSC;
+ if (pint)
+ p->cardctl |= TI113X_CCR_PCI_IRQ_ENA | TI113X_CCR_PCI_IREQ;
+ } else if (t->type == IS_TI1250A) {
+ p->diag &= TI1250_DIAG_PCI_CSC | TI1250_DIAG_PCI_IREQ;
+ if (pcsc)
+ p->diag |= TI1250_DIAG_PCI_CSC;
+ if (pint)
+ p->diag |= TI1250_DIAG_PCI_IREQ;
+ }
+ return 0;
+}
+
+static u_int __init ti113x_set_opts(u_short s, char *buf)
+{
+ socket_info_t *t = &socket[s];
+ ti113x_state_t *p = &t->state.ti113x;
+ u_int mask = 0xffff;
+ int old = (t->type <= IS_TI1131);
+
+ flip(p->sysctl, TI113X_SCR_CLKRUN_ENA, has_clkrun);
+ flip(p->sysctl, TI113X_SCR_CLKRUN_SEL, clkrun_sel);
+ flip(p->cardctl, TI113X_CCR_RIENB, has_ring);
+ p->cardctl &= ~TI113X_CCR_ZVENABLE;
+ switch (irq_mode) {
+ case 1:
+ p->devctl &= ~TI113X_DCR_IMODE_MASK;
+ p->devctl |= TI113X_DCR_IMODE_ISA;
+ break;
+ case 2:
+ p->devctl &= ~TI113X_DCR_IMODE_MASK;
+ p->devctl |= TI113X_DCR_IMODE_SERIAL;
+ break;
+ case 3:
+ p->devctl &= ~TI113X_DCR_IMODE_MASK;
+ p->devctl |= TI12XX_DCR_IMODE_ALL_SERIAL;
+ break;
+ default:
+ if ((p->devctl & TI113X_DCR_IMODE_MASK) == 0)
+ p->devctl |= TI113X_DCR_IMODE_ISA;
+ }
+ if (p->cardctl & TI113X_CCR_RIENB) {
+ strcat(buf, " [ring]");
+ if (old) mask &= ~0x8000;
+ }
+ if (old && (p->sysctl & TI113X_SCR_CLKRUN_ENA)) {
+ if (p->sysctl & TI113X_SCR_CLKRUN_SEL) {
+ strcat(buf, " [clkrun irq 12]");
+ mask &= ~0x1000;
+ } else {
+ strcat(buf, " [clkrun irq 10]");
+ mask &= ~0x0400;
+ }
+ }
+ if (p->sysctl & TI113X_SCR_PWRSAVINGS)
+ strcat(buf, " [pwr save]");
+ switch (p->devctl & TI113X_DCR_IMODE_MASK) {
+ case TI12XX_DCR_IMODE_PCI_ONLY:
+ strcat(buf, " [pci only]");
+ mask = 0;
+ break;
+ case TI113X_DCR_IMODE_ISA:
+ strcat(buf, " [isa irq]");
+ if (old) mask &= ~0x0018;
+ break;
+ case TI113X_DCR_IMODE_SERIAL:
+ strcat(buf, " [pci + serial irq]");
+ mask = 0xffff;
+ break;
+ case TI12XX_DCR_IMODE_ALL_SERIAL:
+ strcat(buf, " [serial pci & irq]");
+ mask = 0xffff;
+ break;
+ }
+ return mask;
+}
+
+#endif
+
+/*======================================================================
+
+ Code to save and restore global state information for the Ricoh
+ RL5C4XX controllers, and to set and report global configuration
+ options.
+
+======================================================================*/
+
+#ifdef CONFIG_PCI
+
+static void rl5c4xx_get_state(u_short s)
+{
+ socket_info_t *t = &socket[s];
+ rl5c4xx_state_t *p = &socket[s].state.rl5c4xx;
+ pci_readw(t->bus, t->devfn, RL5C4XX_MISC, &p->misc);
+ pci_readw(t->bus, t->devfn, RL5C4XX_16BIT_CTL, &p->ctl);
+ pci_readw(t->bus, t->devfn, RL5C4XX_16BIT_IO_0, &p->io);
+ pci_readw(t->bus, t->devfn, RL5C4XX_16BIT_MEM_0, &p->mem);
+}
+
+static void rl5c4xx_set_state(u_short s)
+{
+ socket_info_t *t = &socket[s];
+ rl5c4xx_state_t *p = &socket[s].state.rl5c4xx;
+ pci_writew(t->bus, t->devfn, RL5C4XX_MISC, p->misc);
+ pci_writew(t->bus, t->devfn, RL5C4XX_16BIT_CTL, p->ctl);
+ pci_writew(t->bus, t->devfn, RL5C4XX_16BIT_IO_0, p->io);
+ pci_writew(t->bus, t->devfn, RL5C4XX_16BIT_MEM_0, p->mem);
+}
+
+static u_int __init rl5c4xx_set_opts(u_short s, char *buf)
+{
+ rl5c4xx_state_t *p = &socket[s].state.rl5c4xx;
+ u_int mask = 0xffff;
+ int old = (socket[s].type < IS_RL5C475);
+
+ p->ctl = RL5C4XX_16CTL_IO_TIMING | RL5C4XX_16CTL_MEM_TIMING;
+ if (old) p->ctl |= RL5C46X_16CTL_LEVEL_1 | RL5C46X_16CTL_LEVEL_2;
+
+ if (setup_time >= 0) {
+ p->io = (p->io & ~RL5C4XX_SETUP_MASK) +
+ ((setup_time+1) << RL5C4XX_SETUP_SHIFT);
+ p->mem = (p->mem & ~RL5C4XX_SETUP_MASK) +
+ (setup_time << RL5C4XX_SETUP_SHIFT);
+ }
+ if (cmd_time >= 0) {
+ p->io = (p->io & ~RL5C4XX_CMD_MASK) +
+ (cmd_time << RL5C4XX_CMD_SHIFT);
+ p->mem = (p->mem & ~RL5C4XX_CMD_MASK) +
+ (cmd_time << RL5C4XX_CMD_SHIFT);
+ }
+ if (hold_time >= 0) {
+ p->io = (p->io & ~RL5C4XX_HOLD_MASK) +
+ (hold_time << RL5C4XX_HOLD_SHIFT);
+ p->mem = (p->mem & ~RL5C4XX_HOLD_MASK) +
+ (hold_time << RL5C4XX_HOLD_SHIFT);
+ }
+ if (!old) {
+ switch (irq_mode) {
+ case 1:
+ p->misc &= ~RL5C47X_MISC_SRIRQ_ENA; break;
+ case 2:
+ p->misc |= RL5C47X_MISC_SRIRQ_ENA; break;
+ }
+ if (p->misc & RL5C47X_MISC_SRIRQ_ENA)
+ sprintf(buf, " [serial irq]");
+ else
+ sprintf(buf, " [isa irq]");
+ buf += strlen(buf);
+ }
+ sprintf(buf, " [io %d/%d/%d] [mem %d/%d/%d]",
+ (p->io & RL5C4XX_SETUP_MASK) >> RL5C4XX_SETUP_SHIFT,
+ (p->io & RL5C4XX_CMD_MASK) >> RL5C4XX_CMD_SHIFT,
+ (p->io & RL5C4XX_HOLD_MASK) >> RL5C4XX_HOLD_SHIFT,
+ (p->mem & RL5C4XX_SETUP_MASK) >> RL5C4XX_SETUP_SHIFT,
+ (p->mem & RL5C4XX_CMD_MASK) >> RL5C4XX_CMD_SHIFT,
+ (p->mem & RL5C4XX_HOLD_MASK) >> RL5C4XX_HOLD_SHIFT);
+ return mask;
+}
+
+#endif
+
+/*======================================================================
+
+ Code to save and restore global state information for O2Micro
+ controllers, and to set and report global configuration options.
+
+======================================================================*/
+
+#ifdef CONFIG_PCI
+
+static void o2micro_get_state(u_short s)
+{
+ socket_info_t *t = &socket[s];
+ o2micro_state_t *p = &socket[s].state.o2micro;
+ if ((t->revision == 0x34) || (t->revision == 0x62)) {
+ p->mode_a = i365_get(s, O2_MODE_A_2);
+ p->mode_b = i365_get(s, O2_MODE_B_2);
+ } else {
+ p->mode_a = i365_get(s, O2_MODE_A);
+ p->mode_b = i365_get(s, O2_MODE_B);
+ }
+ p->mode_c = i365_get(s, O2_MODE_C);
+ p->mode_d = i365_get(s, O2_MODE_D);
+ if (t->flags & IS_CARDBUS) {
+ p->mhpg = i365_get(s, O2_MHPG_DMA);
+ p->fifo = i365_get(s, O2_FIFO_ENA);
+ p->mode_e = i365_get(s, O2_MODE_E);
+ }
+}
+
+static void o2micro_set_state(u_short s)
+{
+ socket_info_t *t = &socket[s];
+ o2micro_state_t *p = &socket[s].state.o2micro;
+ if ((t->revision == 0x34) || (t->revision == 0x62)) {
+ i365_set(s, O2_MODE_A_2, p->mode_a);
+ i365_set(s, O2_MODE_B_2, p->mode_b);
+ } else {
+ i365_set(s, O2_MODE_A, p->mode_a);
+ i365_set(s, O2_MODE_B, p->mode_b);
+ }
+ i365_set(s, O2_MODE_C, p->mode_c);
+ i365_set(s, O2_MODE_D, p->mode_d);
+ if (t->flags & IS_CARDBUS) {
+ i365_set(s, O2_MHPG_DMA, p->mhpg);
+ i365_set(s, O2_FIFO_ENA, p->fifo);
+ i365_set(s, O2_MODE_E, p->mode_e);
+ }
+}
+
+static u_int __init o2micro_set_opts(u_short s, char *buf)
+{
+ socket_info_t *t = &socket[s];
+ o2micro_state_t *p = &socket[s].state.o2micro;
+ u_int mask = 0xffff;
+
+ p->mode_b = (p->mode_b & ~O2_MODE_B_IDENT) | O2_MODE_B_ID_CSTEP;
+ flip(p->mode_b, O2_MODE_B_IRQ15_RI, has_ring);
+ p->mode_c &= ~(O2_MODE_C_ZVIDEO | O2_MODE_C_DREQ_MASK);
+ if (t->flags & IS_CARDBUS) {
+ p->mode_d &= ~O2_MODE_D_W97_IRQ;
+ p->mode_e &= ~O2_MODE_E_MHPG_DMA;
+ p->mhpg |= O2_MHPG_CINT_ENA | O2_MHPG_CSC_ENA;
+ p->mhpg &= ~O2_MHPG_CHANNEL;
+ } else {
+ if (p->mode_b & O2_MODE_B_IRQ15_RI) mask &= ~0x8000;
+ }
+ sprintf(buf, " [a %02x] [b %02x] [c %02x] [d %02x]",
+ p->mode_a, p->mode_b, p->mode_c, p->mode_d);
+ if (t->flags & IS_CARDBUS) {
+ buf += strlen(buf);
+ sprintf(buf, " [mhpg %02x] [fifo %02x] [e %02x]",
+ p->mhpg, p->fifo, p->mode_e);
+ }
+ return mask;
+}
+
+#endif
+
+/*======================================================================
+
+ Code to save and restore global state information for the Toshiba
+ ToPIC 95 and 97 controllers, and to set and report global
+ configuration options.
+
+======================================================================*/
+
+#ifdef CONFIG_PCI
+
+static void topic_get_state(u_short s)
+{
+ socket_info_t *t = &socket[s];
+ topic_state_t *p = &socket[s].state.topic;
+ pci_readb(t->bus, t->devfn, TOPIC_SLOT_CONTROL, &p->slot);
+ pci_readb(t->bus, t->devfn, TOPIC_CARD_CONTROL, &p->ccr);
+ pci_readb(t->bus, t->devfn, TOPIC_CARD_DETECT, &p->cdr);
+ pci_readl(t->bus, t->devfn, TOPIC_REGISTER_CONTROL, &p->rcr);
+}
+
+static void topic_set_state(u_short s)
+{
+ socket_info_t *t = &socket[s];
+ topic_state_t *p = &socket[s].state.topic;
+ pci_writeb(t->bus, t->devfn, TOPIC_SLOT_CONTROL, p->slot);
+ pci_writeb(t->bus, t->devfn, TOPIC_CARD_CONTROL, p->ccr);
+ pci_writeb(t->bus, t->devfn, TOPIC_CARD_DETECT, p->cdr);
+ pci_writel(t->bus, t->devfn, TOPIC_REGISTER_CONTROL, p->rcr);
+}
+
+static int topic_set_irq_mode(u_short s, int pcsc, int pint)
+{
+ if (socket[s].type == IS_TOPIC97) {
+ topic_state_t *p = &socket[s].state.topic;
+ flip(p->ccr, TOPIC97_ICR_IRQSEL, pcsc);
+ return 0;
+ } else {
+ return !pcsc;
+ }
+}
+
+static u_int __init topic_set_opts(u_short s, char *buf)
+{
+ topic_state_t *p = &socket[s].state.topic;
+
+ p->slot |= TOPIC_SLOT_SLOTON|TOPIC_SLOT_SLOTEN|TOPIC_SLOT_ID_LOCK;
+ p->cdr |= TOPIC_CDR_MODE_PC32;
+ p->cdr &= ~(TOPIC_CDR_SW_DETECT);
+ sprintf(buf, " [slot 0x%02x] [ccr 0x%02x] [cdr 0x%02x] [rcr 0x%02x]",
+ p->slot, p->ccr, p->cdr, p->rcr);
+ return 0xffff;
+}
+
+#endif
+
+/*======================================================================
+
+ Routines to handle common CardBus options
+
+======================================================================*/
+
+#ifdef CONFIG_PCI
+
+static void cb_get_state(u_short s)
+{
+ socket_info_t *t = &socket[s];
+
+ pci_readb(t->bus, t->devfn, PCI_CACHE_LINE_SIZE, &t->cache);
+ pci_readb(t->bus, t->devfn, PCI_LATENCY_TIMER, &t->pci_lat);
+ pci_readb(t->bus, t->devfn, CB_LATENCY_TIMER, &t->cb_lat);
+ pci_readb(t->bus, t->devfn, CB_CARDBUS_BUS, &t->cap.cardbus);
+ pci_readb(t->bus, t->devfn, CB_SUBORD_BUS, &t->sub_bus);
+ pci_readw(t->bus, t->devfn, CB_BRIDGE_CONTROL, &t->bcr);
+ {
+ struct pci_dev *pdev = pci_find_slot(t->bus, t->devfn);
+ t->cap.pci_irq = (pdev) ? pdev->irq : 0;
+ }
+ if (t->cap.pci_irq >= NR_IRQS) t->cap.pci_irq = 0;
+}
+
+static void cb_set_state(u_short s)
+{
+ socket_info_t *t = &socket[s];
+ if (t->pmcs)
+ pci_writew(t->bus, t->devfn, t->pmcs, PCI_PMCS_PWR_STATE_D0);
+ pci_writel(t->bus, t->devfn, CB_LEGACY_MODE_BASE, 0);
+ pci_writel(t->bus, t->devfn, PCI_BASE_ADDRESS_0, t->cb_phys);
+ pci_writew(t->bus, t->devfn, PCI_COMMAND, CMD_DFLT);
+ pci_writeb(t->bus, t->devfn, PCI_CACHE_LINE_SIZE, t->cache);
+ pci_writeb(t->bus, t->devfn, PCI_LATENCY_TIMER, t->pci_lat);
+ pci_writeb(t->bus, t->devfn, CB_LATENCY_TIMER, t->cb_lat);
+ pci_writeb(t->bus, t->devfn, CB_CARDBUS_BUS, t->cap.cardbus);
+ pci_writeb(t->bus, t->devfn, CB_SUBORD_BUS, t->sub_bus);
+ pci_writew(t->bus, t->devfn, CB_BRIDGE_CONTROL, t->bcr);
+}
+
+static int cb_get_irq_mode(u_short s)
+{
+ return (!(socket[s].bcr & CB_BCR_ISA_IRQ));
+}
+
+static int cb_set_irq_mode(u_short s, int pcsc, int pint)
+{
+ socket_info_t *t = &socket[s];
+ flip(t->bcr, CB_BCR_ISA_IRQ, !(pint));
+ if (t->flags & IS_CIRRUS)
+ return cirrus_set_irq_mode(s, pcsc, pint);
+ else if (t->flags & IS_TI)
+ return ti113x_set_irq_mode(s, pcsc, pint);
+ else if (t->flags & IS_TOPIC)
+ return topic_set_irq_mode(s, pcsc, pint);
+ return 0;
+}
+
+static void __init pci_scan(u_short sock);
+
+static void __init cb_set_opts(u_short s, char *buf)
+{
+ socket_info_t *t = &socket[s];
+ t->bcr |= CB_BCR_WRITE_POST;
+ /* some TI1130's seem to exhibit problems with write posting */
+ if (((t->type == IS_TI1130) && (t->revision == 4) &&
+ (cb_write_post < 0)) || (cb_write_post == 0))
+ t->bcr &= ~CB_BCR_WRITE_POST;
+ if (t->cache == 0) t->cache = 8;
+ if (pci_latency >= 0) t->pci_lat = pci_latency;
+ if (t->pci_lat == 0) t->pci_lat = 0xa8;
+ if (cb_latency >= 0) t->cb_lat = cb_latency;
+ if (t->cb_lat == 0) t->cb_lat = 0xb0;
+ if ((t->cap.pci_irq == 0) && (pci_csc || pci_int) && do_scan)
+ pci_scan(s);
+ if (t->cap.pci_irq == 0)
+ strcat(buf, " [no pci irq]");
+ else
+ sprintf(buf, " [pci irq %d]", t->cap.pci_irq);
+ buf += strlen(buf);
+ if ((cb_bus_base > 0) || (t->cap.cardbus == 0)) {
+ if (cb_bus_base <= 0) cb_bus_base = 0x20;
+ t->cap.cardbus = cb_bus_base;
+ t->sub_bus = cb_bus_base+cb_bus_step;
+ cb_bus_base += cb_bus_step+1;
+ }
+ if (!(t->flags & IS_TOPIC))
+ t->cap.features |= SS_CAP_PAGE_REGS;
+ sprintf(buf, " [lat %d/%d] [bus %d/%d]",
+ t->pci_lat, t->cb_lat, t->cap.cardbus, t->sub_bus);
+}
+
+#endif
+
+/*======================================================================
+
+ Generic routines to get and set controller options
+
+======================================================================*/
+
+static void get_host_state(u_short s)
+{
+ socket_info_t *t = &socket[s];
+ if (t->flags & IS_CIRRUS)
+ cirrus_get_state(s);
+#ifdef CONFIG_ISA
+ else if (t->flags & IS_VADEM)
+ vg46x_get_state(s);
+#endif
+#ifdef CONFIG_PCI
+ else if (t->flags & IS_O2MICRO)
+ o2micro_get_state(s);
+ else if (t->flags & IS_TI)
+ ti113x_get_state(s);
+ else if (t->flags & IS_RICOH)
+ rl5c4xx_get_state(s);
+ else if (t->flags & IS_TOPIC)
+ topic_get_state(s);
+ if (t->flags & IS_CARDBUS)
+ cb_get_state(s);
+#endif
+}
+
+static void set_host_state(u_short s)
+{
+ socket_info_t *t = &socket[s];
+#ifdef CONFIG_PCI
+ if (t->flags & IS_CARDBUS)
+ cb_set_state(s);
+#endif
+ if (t->flags & IS_CIRRUS)
+ cirrus_set_state(s);
+ else {
+ i365_set(s, I365_GBLCTL, 0x00);
+ i365_set(s, I365_GENCTL, 0x00);
+ }
+ i365_bflip(s, I365_INTCTL, I365_INTR_ENA, t->intr);
+#ifdef CONFIG_ISA
+ if (t->flags & IS_VADEM)
+ vg46x_set_state(s);
+#endif
+#ifdef CONFIG_PCI
+ if (t->flags & IS_O2MICRO)
+ o2micro_set_state(s);
+ else if (t->flags & IS_TI)
+ ti113x_set_state(s);
+ else if (t->flags & IS_RICOH)
+ rl5c4xx_set_state(s);
+ else if (t->flags & IS_TOPIC)
+ topic_set_state(s);
+#endif
+}
+
+static u_int __init set_host_opts(u_short s, u_short ns)
+{
+ u_short i;
+ u_int m = 0xffff;
+ char buf[128];
+
+ for (i = s; i < s+ns; i++) {
+ if (socket[i].flags & IS_ALIVE) {
+ printk(KERN_INFO " host opts [%d]: already alive!\n", i);
+ continue;
+ }
+ buf[0] = '\0';
+ get_host_state(i);
+ if (socket[i].flags & IS_CIRRUS)
+ m = cirrus_set_opts(i, buf);
+#ifdef CONFIG_ISA
+ else if (socket[i].flags & IS_VADEM)
+ m = vg46x_set_opts(i, buf);
+#endif
+#ifdef CONFIG_PCI
+ else if (socket[i].flags & IS_O2MICRO)
+ m = o2micro_set_opts(i, buf);
+ else if (socket[i].flags & IS_TI)
+ m = ti113x_set_opts(i, buf);
+ else if (socket[i].flags & IS_RICOH)
+ m = rl5c4xx_set_opts(i, buf);
+ else if (socket[i].flags & IS_TOPIC)
+ m = topic_set_opts(i, buf);
+ if (socket[i].flags & IS_CARDBUS)
+ cb_set_opts(i, buf+strlen(buf));
+#endif
+ set_host_state(i);
+ printk(KERN_INFO " host opts [%d]:%s\n", i,
+ (*buf) ? buf : " none");
+ }
+#ifdef CONFIG_PCI
+ /* Mask out all PCI interrupts */
+ for (i = 0; i < sockets; i++)
+ m &= ~(1<<socket[i].cap.pci_irq);
+ {
+ struct pci_dev *p;
+ for (p = pci_devices; p; p = p->next)
+ m &= ~(1<<p->irq);
+ }
+#endif
+ return m;
+}
+
+/*======================================================================
+
+ Interrupt testing code, for ISA and PCI interrupts
+
+======================================================================*/
+
+static volatile u_int irq_hits;
+static u_short irq_sock;
+
+static void irq_count(int irq, void *dev, struct pt_regs *regs)
+{
+#ifdef CONFIG_PCI
+ if (socket[irq_sock].flags & IS_CARDBUS) {
+ cb_writel(irq_sock, CB_SOCKET_EVENT, -1);
+ } else
+#endif
+ i365_get(irq_sock, I365_CSC);
+ irq_hits++;
+ DEBUG(2, "-> hit on irq %d\n", irq);
+}
+
+static u_int __init test_irq(u_short sock, int irq, int pci)
+{
+ u_char csc = (pci) ? 0 : irq;
+ DEBUG(2, " testing %s irq %d\n", pci ? "PCI" : "ISA", irq);
+
+ if (request_irq(irq, irq_count, (pci?SA_SHIRQ:0), "scan", NULL) != 0)
+ return 1;
+ irq_hits = 0; irq_sock = sock;
+ current->state = TASK_INTERRUPTIBLE;
+ schedule_timeout(HZ/100);
+ if (irq_hits) {
+ free_irq(irq, NULL);
+ DEBUG(2, " spurious hit!\n");
+ return 1;
+ }
+
+ /* Generate one interrupt */
+#ifdef CONFIG_PCI
+ if (socket[sock].flags & IS_CARDBUS) {
+ cb_writel(sock, CB_SOCKET_EVENT, -1);
+ i365_set(sock, I365_CSCINT, I365_CSC_STSCHG | (csc << 4));
+ cb_writel(sock, CB_SOCKET_EVENT, -1);
+ cb_writel(sock, CB_SOCKET_MASK, CB_SM_CSTSCHG);
+ cb_writel(sock, CB_SOCKET_FORCE, CB_SE_CSTSCHG);
+ udelay(1000);
+ cb_writel(sock, CB_SOCKET_EVENT, -1);
+ cb_writel(sock, CB_SOCKET_MASK, 0);
+ } else
+#endif
+ {
+ i365_set(sock, I365_CSCINT, I365_CSC_DETECT | (csc << 4));
+ i365_bset(sock, I365_GENCTL, I365_CTL_SW_IRQ);
+ udelay(1000);
+ }
+
+ free_irq(irq, NULL);
+
+ /* mask all interrupts */
+ i365_set(sock, I365_CSCINT, 0);
+ DEBUG(2, " hits = %d\n", irq_hits);
+
+ return (irq_hits != 1);
+}
+
+#ifdef CONFIG_ISA
+
+static u_int __init isa_scan(u_short sock, u_int mask0)
+{
+ u_int mask1 = 0;
+ int i;
+
+#ifdef __alpha__
+#define PIC 0x4d0
+ /* Don't probe level-triggered interrupts -- reserved for PCI */
+ mask0 &= ~(inb(PIC) | (inb(PIC+1) << 8));
+#endif
+
+#ifdef CONFIG_PCI
+ /* Only scan if we can select ISA csc irq's */
+ if (!(socket[sock].flags & IS_CARDBUS) ||
+ (cb_set_irq_mode(sock, 0, 0) == 0))
+#endif
+ if (do_scan) {
+ set_host_state(sock);
+ i365_set(sock, I365_CSCINT, 0);
+ for (i = 0; i < 16; i++)
+ if ((mask0 & (1 << i)) && (test_irq(sock, i, 0) == 0))
+ mask1 |= (1 << i);
+ for (i = 0; i < 16; i++)
+ if ((mask1 & (1 << i)) && (test_irq(sock, i, 0) != 0))
+ mask1 ^= (1 << i);
+ }
+
+ printk(KERN_INFO " ISA irqs (");
+ if (mask1) {
+ printk("scanned");
+ } else {
+ /* Fallback: just find interrupts that aren't in use */
+ for (i = 0; i < 16; i++)
+ if ((mask0 & (1 << i)) && (_check_irq(i, 0) == 0))
+ mask1 |= (1 << i);
+ printk("default");
+ /* If scan failed, default to polled status */
+ if (!cs_irq && (poll_interval == 0)) poll_interval = HZ;
+ }
+ printk(") = ");
+
+ for (i = 0; i < 16; i++)
+ if (mask1 & (1<<i))
+ printk("%s%d", ((mask1 & ((1<<i)-1)) ? "," : ""), i);
+ if (mask1 == 0) printk("none!");
+
+ return mask1;
+}
+
+#endif /* CONFIG_ISA */
+
+#ifdef CONFIG_PCI
+
+static void __init pci_scan(u_short sock)
+{
+ u_int i;
+
+ cb_set_irq_mode(sock, 1, 0);
+ set_host_state(sock);
+ i365_set(sock, I365_CSCINT, 0);
+ /* Only probe irq's 9..11, to be conservative */
+ for (i = 9; i < 12; i++) {
+ if ((test_irq(sock, i, 1) == 0) &&
+ (test_irq(sock, i, 1) == 0))
+ break;
+ }
+ if (i < 12) socket[sock].cap.pci_irq = i;
+}
+
+#endif /* CONFIG_PCI */
+
+/*====================================================================*/
+
+/* Time conversion functions */
+
+static int to_cycles(int ns)
+{
+ return ns/cycle_time;
+} /* speed_convert */
+
+static int to_ns(int cycles)
+{
+ return cycle_time*cycles;
+}
+
+/*====================================================================*/
+
+#ifdef CONFIG_ISA
+
+static int __init identify(u_short port, u_short sock)
+{
+ u_char val;
+ int type = -1;
+
+ /* Use the next free entry in the socket table */
+ socket[sockets].ioaddr = port;
+ socket[sockets].psock = sock;
+
+ /* Wake up a sleepy Cirrus controller */
+ if (wakeup) {
+ i365_bclr(sockets, PD67_MISC_CTL_2, PD67_MC2_SUSPEND);
+ /* Pause at least 50 ms */
+ mdelay(50);
+ }
+
+ if ((val = i365_get(sockets, I365_IDENT)) & 0x70)
+ return -1;
+ switch (val) {
+ case 0x82:
+ type = IS_I82365A; break;
+ case 0x83:
+ type = IS_I82365B; break;
+ case 0x84:
+ type = IS_I82365DF; break;
+ case 0x88: case 0x89: case 0x8a:
+ type = IS_IBM; break;
+ }
+
+ /* Check for Vadem VG-468 chips */
+ outb(0x0e, port);
+ outb(0x37, port);
+ i365_bset(sockets, VG468_MISC, VG468_MISC_VADEMREV);
+ val = i365_get(sockets, I365_IDENT);
+ if (val & I365_IDENT_VADEM) {
+ i365_bclr(sockets, VG468_MISC, VG468_MISC_VADEMREV);
+ type = ((val & 7) >= 4) ? IS_VG469 : IS_VG468;
+ }
+
+ /* Check for Ricoh chips */
+ val = i365_get(sockets, RF5C_CHIP_ID);
+ if ((val == RF5C_CHIP_RF5C296) || (val == RF5C_CHIP_RF5C396))
+ type = IS_RF5Cx96;
+
+ /* Check for Cirrus CL-PD67xx chips */
+ i365_set(sockets, PD67_CHIP_INFO, 0);
+ val = i365_get(sockets, PD67_CHIP_INFO);
+ if ((val & PD67_INFO_CHIP_ID) == PD67_INFO_CHIP_ID) {
+ val = i365_get(sockets, PD67_CHIP_INFO);
+ if ((val & PD67_INFO_CHIP_ID) == 0) {
+ type = (val & PD67_INFO_SLOTS) ? IS_PD672X : IS_PD6710;
+ i365_set(sockets, PD67_EXT_INDEX, 0xe5);
+ if (i365_get(sockets, PD67_EXT_INDEX) != 0xe5)
+ type = IS_VT83C469;
+ }
+ }
+ return type;
+} /* identify */
+
+#endif
+
+/*======================================================================
+
+ See if a card is present, powered up, in IO mode, and already
+ bound to a (non PC Card) Linux driver. We leave these alone.
+
+ We make an exception for cards that seem to be serial devices.
+
+======================================================================*/
+
+static int __init is_alive(u_short sock)
+{
+ u_char stat;
+ u_short start, stop;
+
+ stat = i365_get(sock, I365_STATUS);
+ start = i365_get_pair(sock, I365_IO(0)+I365_W_START);
+ stop = i365_get_pair(sock, I365_IO(0)+I365_W_STOP);
+ if ((stat & I365_CS_DETECT) && (stat & I365_CS_POWERON) &&
+ (i365_get(sock, I365_INTCTL) & I365_PC_IOCARD) &&
+ (i365_get(sock, I365_ADDRWIN) & I365_ENA_IO(0)) &&
+ (check_region(start, stop-start+1) != 0) &&
+ ((start & 0xfeef) != 0x02e8))
+ return 1;
+ else
+ return 0;
+}
+
+/*====================================================================*/
+
+static void __init add_socket(u_short port, int psock, int type)
+{
+ socket[sockets].ioaddr = port;
+ socket[sockets].psock = psock;
+ socket[sockets].type = type;
+ socket[sockets].flags = pcic[type].flags;
+ if (is_alive(sockets))
+ socket[sockets].flags |= IS_ALIVE;
+ sockets++;
+}
+
+static void __init add_pcic(int ns, int type)
+{
+ u_int mask = 0, i, base;
+ int use_pci = 0, isa_irq = 0;
+ socket_info_t *t = &socket[sockets-ns];
+
+ base = sockets-ns;
+ if (t->ioaddr > 0) request_region(t->ioaddr, 2, "i82365");
+
+ if (base == 0) printk("\n");
+ printk(KERN_INFO " %s", pcic[type].name);
+#ifdef CONFIG_PCI
+ if (t->flags & IS_UNKNOWN)
+ printk(" [0x%04x 0x%04x]", t->vendor, t->device);
+ if (t->flags & IS_CARDBUS)
+ printk(" PCI-to-CardBus at bus %d slot %d, mem 0x%08x",
+ t->bus, PCI_SLOT(t->devfn), t->cb_phys);
+ else if (t->flags & IS_PCI)
+ printk(" PCI-to-PCMCIA at bus %d slot %d, port %#x",
+ t->bus, PCI_SLOT(t->devfn), t->ioaddr);
+ else
+#endif
+ printk(" ISA-to-PCMCIA at port %#x ofs 0x%02x",
+ t->ioaddr, t->psock*0x40);
+ printk(", %d socket%s\n", ns, ((ns > 1) ? "s" : ""));
+
+#ifdef CONFIG_ISA
+ /* Set host options, build basic interrupt mask */
+ if (irq_list[0] == -1)
+ mask = irq_mask;
+ else
+ for (i = mask = 0; i < 16; i++)
+ mask |= (1<<irq_list[i]);
+#endif
+ mask &= I365_MASK & set_host_opts(base, ns);
+#ifdef CONFIG_ISA
+ /* Scan for ISA interrupts */
+ mask = isa_scan(base, mask);
+#else
+ printk(KERN_INFO " PCI card interrupts,");
+#endif
+
+#ifdef CONFIG_PCI
+ /* Can we use a PCI interrupt for card status changes? */
+ if (pci_csc && t->cap.pci_irq) {
+ for (i = 0; i < ns; i++)
+ if (_check_irq(t[i].cap.pci_irq, SA_SHIRQ)) break;
+ if (i == ns) {
+ use_pci = 1;
+ printk(" PCI status changes\n");
+ }
+ }
+#endif
+
+#ifdef CONFIG_ISA
+ /* Poll if only two interrupts available */
+ if (!use_pci && !poll_interval) {
+ u_int tmp = (mask & (mask-1));
+ if ((tmp & (tmp-1)) == 0)
+ poll_interval = HZ;
+ }
+ /* Only try an ISA cs_irq if this is the first controller */
+ if (!use_pci && !grab_irq && (cs_irq || !poll_interval)) {
+ /* Avoid irq 12 unless it is explicitly requested */
+ u_int cs_mask = mask & ((cs_irq) ? (1<<cs_irq) : ~(1<<12));
+ for (cs_irq = 15; cs_irq > 0; cs_irq--)
+ if ((cs_mask & (1 << cs_irq)) &&
+ (_check_irq(cs_irq, 0) == 0))
+ break;
+ if (cs_irq) {
+ grab_irq = 1;
+ isa_irq = cs_irq;
+ printk(" status change on irq %d\n", cs_irq);
+ }
+ }
+#endif
+
+ if (!use_pci && !isa_irq) {
+ if (poll_interval == 0)
+ poll_interval = HZ;
+ printk(" polling interval = %d ms\n",
+ poll_interval * 1000 / HZ);
+
+ }
+
+ /* Update socket interrupt information, capabilities */
+ for (i = 0; i < ns; i++) {
+ t[i].cap.features |= SS_CAP_PCCARD;
+ t[i].cap.map_size = 0x1000;
+ t[i].cap.irq_mask = mask;
+ if (pci_int && t[i].cap.pci_irq)
+ t[i].cap.irq_mask |= (1 << t[i].cap.pci_irq);
+ t[i].cs_irq = isa_irq;
+#ifdef CONFIG_PCI
+ if (t[i].flags & IS_CARDBUS) {
+ t[i].cap.features |= SS_CAP_CARDBUS;
+ cb_set_irq_mode(i, pci_csc && t[i].cap.pci_irq,
+ pci_int && t[i].cap.pci_irq);
+ }
+#endif
+ }
+
+} /* add_pcic */
+
+/*====================================================================*/
+
+#ifdef CONFIG_PCI
+
+typedef struct pci_dev *pci_id_t;
+static int __init pci_lookup(u_int class, pci_id_t *id,
+ u_char *bus, u_char *devfn)
+{
+ if ((*id = pci_find_class(class<<8, *id)) != NULL) {
+ *bus = (*id)->bus->number;
+ *devfn = (*id)->devfn;
+ return 0;
+ } else return -1;
+}
+
+static void __init add_pci_bridge(int type, u_char bus, u_char devfn,
+ u_short v, u_short d)
+{
+ socket_info_t *s = &socket[sockets];
+ u_short i, ns;
+ u_int addr;
+
+ if (type == PCIC_COUNT) type = IS_UNK_PCI;
+ pci_readl(bus, devfn, PCI_BASE_ADDRESS_0, &addr);
+ addr &= ~0x1;
+ pci_writew(bus, devfn, PCI_COMMAND, CMD_DFLT);
+ for (i = ns = 0; i < ((type == IS_I82092AA) ? 4 : 2); i++) {
+ s->bus = bus; s->devfn = devfn;
+ s->vendor = v; s->device = d;
+ add_socket(addr, i, type);
+ ns++; s++;
+ }
+ add_pcic(ns, type);
+}
+
+static void __init add_cb_bridge(int type, u_char bus, u_char devfn,
+ u_short v, u_short d0)
+{
+ socket_info_t *s = &socket[sockets];
+ u_short d, ns;
+ u_char a, b, r, max;
+
+ /* PCI bus enumeration is broken on some systems */
+ for (ns = 0; ns < sockets; ns++)
+ if ((socket[ns].bus == bus) && (socket[ns].devfn == devfn))
+ return;
+
+ if (type == PCIC_COUNT) type = IS_UNK_CARDBUS;
+ pci_readb(bus, devfn, PCI_HEADER_TYPE, &a);
+ pci_readb(bus, devfn, PCI_CLASS_REVISION, &r);
+ max = (a & 0x80) ? 8 : 1;
+ for (ns = 0; ns < max; ns++, s++, devfn++) {
+ if (pci_readw(bus, devfn, PCI_DEVICE_ID, &d) || (d != d0))
+ break;
+ s->bus = bus; s->devfn = devfn;
+ s->vendor = v; s->device = d; s->revision = r;
+
+ /* Check for power management capabilities */
+ pci_readb(bus, devfn, PCI_STATUS, &a);
+ if (a & PCI_STATUS_CAPLIST) {
+ pci_readb(bus, devfn, PCI_CB_CAPABILITY_POINTER, &b);
+ while (b != 0) {
+ pci_readb(bus, devfn, b+PCI_CAPABILITY_ID, &a);
+ if (a == PCI_CAPABILITY_PM) {
+ s->pmcs = b + PCI_PM_CONTROL_STATUS;
+ break;
+ }
+ pci_readb(bus, devfn, b+PCI_NEXT_CAPABILITY, &b);
+ }
+ }
+ /* If capability exists, make sure we're in D0 state */
+ if (s->pmcs)
+ pci_writew(bus, devfn, s->pmcs, PCI_PMCS_PWR_STATE_D0);
+
+ /* Map CardBus registers if they are not already mapped */
+ pci_writel(bus, devfn, CB_LEGACY_MODE_BASE, 0);
+ pci_readl(bus, devfn, PCI_BASE_ADDRESS_0, &s->cb_phys);
+ if (s->cb_phys == 0) {
+ int i;
+ pci_writew(bus, devfn, PCI_COMMAND, CMD_DFLT);
+ for (i = 0; i < sizeof(cb_mem_base)/sizeof(u_int); i++) {
+ s->cb_phys = cb_mem_base[i];
+ s->cb_virt = ioremap(s->cb_phys, 0x1000);
+ pci_writel(bus, devfn, PCI_BASE_ADDRESS_0, s->cb_phys);
+ /* Simple sanity checks */
+ if (((readb(s->cb_virt+0x800+I365_IDENT) & 0xf0)
+ == 0x80) &&
+ !(readb(s->cb_virt+0x800+I365_CSC) &&
+ readb(s->cb_virt+0x800+I365_CSC) &&
+ readb(s->cb_virt+0x800+I365_CSC)))
+ break;
+ iounmap(s->cb_virt);
+ }
+ if (i == sizeof(cb_mem_base)/sizeof(u_int)) {
+ pci_writel(bus, devfn, PCI_BASE_ADDRESS_0, 0);
+ s->cb_phys = 0; s->cb_virt = NULL;
+ printk("\n");
+ printk(KERN_NOTICE " Bridge register mapping failed:"
+ " check cb_mem_base setting\n");
+ break;
+ }
+ cb_mem_base[0] = cb_mem_base[i] + PAGE_SIZE;
+ } else {
+ s->cb_virt = ioremap(s->cb_phys, 0x1000);
+ }
+
+ request_mem_region(s->cb_phys, 0x1000, "i82365");
+ add_socket(0, 0, type);
+ }
+ if (ns == 0) return;
+
+ s -= ns;
+ if (ns == 2) {
+ /* Nasty special check for bad bus mapping */
+ pci_readb(bus, s[0].devfn, CB_CARDBUS_BUS, &a);
+ pci_readb(bus, s[1].devfn, CB_CARDBUS_BUS, &b);
+ if (a == b) {
+ pci_writeb(bus, s[0].devfn, CB_CARDBUS_BUS, 0);
+ pci_writeb(bus, s[1].devfn, CB_CARDBUS_BUS, 0);
+ }
+ }
+ add_pcic(ns, type);
+
+ /* Re-do card type & voltage detection */
+ cb_writel(sockets-ns, CB_SOCKET_FORCE, CB_SF_CVSTEST);
+ current->state = TASK_INTERRUPTIBLE;
+ schedule_timeout(HZ/5);
+
+ /* Set up PCI bus bridge structures if needed */
+ for (a = 0; a < ns; a++) {
+ struct pci_dev *self = pci_find_slot(bus, s[a].devfn);
+ struct pci_bus *child, *parent = self->bus;
+ for (child = parent->children; child; child = child->next)
+ if (child->number == s[a].cap.cardbus) break;
+ if (!child) {
+ child = kmalloc(sizeof(struct pci_bus), GFP_KERNEL);
+ memset(child, 0, sizeof(struct pci_bus));
+ child->self = self;
+ child->primary = bus;
+ child->number = child->secondary = s[a].cap.cardbus;
+ child->subordinate = s[a].sub_bus;
+ child->parent = parent;
+ child->ops = parent->ops;
+ child->next = parent->children;
+ }
+ s[a].cap.cb_bus = parent->children = child;
+ }
+}
+
+static void __init pci_probe(u_int class, void (add_fn)
+ (int, u_char, u_char, u_short, u_short))
+{
+ u_short i, v, d;
+ u_char bus, devfn;
+ pci_id_t id;
+
+ id = 0;
+ while (pci_lookup(class, &id, &bus, &devfn) == 0) {
+ if (PCI_FUNC(devfn) != 0) continue;
+ pci_readw(bus, devfn, PCI_VENDOR_ID, &v);
+ pci_readw(bus, devfn, PCI_DEVICE_ID, &d);
+ for (i = 0; i < PCIC_COUNT; i++)
+ if ((pcic[i].vendor == v) && (pcic[i].device == d)) break;
+ add_fn(i, bus, devfn, v, d);
+ }
+}
+
+#endif /* CONFIG_PCI */
+
+/*====================================================================*/
+
+#ifdef CONFIG_ISA
+
+static void __init isa_probe(void)
+{
+ int i, j, sock, k;
+ int ns, id;
+ u_short port;
+
+ if (check_region(i365_base, 2) != 0) {
+ if (sockets == 0)
+ printk("port conflict at %#x\n", i365_base);
+ return;
+ }
+
+ id = identify(i365_base, 0);
+ if ((id == IS_I82365DF) && (identify(i365_base, 1) != id)) {
+ for (i = 0; i < 4; i++) {
+ if (i == ignore) continue;
+ port = i365_base + ((i & 1) << 2) + ((i & 2) << 1);
+ sock = (i & 1) << 1;
+ if (identify(port, sock) == IS_I82365DF) {
+ add_socket(port, sock, IS_VLSI);
+ add_pcic(1, IS_VLSI);
+ }
+ }
+ } else {
+ for (i = 0; i < (extra_sockets ? 8 : 4); i += 2) {
+ port = i365_base + 2*(i>>2);
+ sock = (i & 3);
+ id = identify(port, sock);
+ if (id < 0) continue;
+
+ for (j = ns = 0; j < 2; j++) {
+ /* Does the socket exist? */
+ if ((ignore == i+j) || (identify(port, sock+j) < 0))
+ continue;
+ /* Check for bad socket decode */
+ for (k = 0; k <= sockets; k++)
+ i365_set(k, I365_MEM(0)+I365_W_OFF, k);
+ for (k = 0; k <= sockets; k++)
+ if (i365_get(k, I365_MEM(0)+I365_W_OFF) != k)
+ break;
+ if (k <= sockets) break;
+ add_socket(port, sock+j, id); ns++;
+ }
+ if (ns != 0) add_pcic(ns, id);
+ }
+ }
+}
+
+#endif
+
+/*====================================================================*/
+
+static int __init init_i82365(void)
+{
+ servinfo_t serv;
+ CardServices(GetCardServicesInfo, &serv);
+ if (serv.Revision != CS_RELEASE_CODE) {
+ printk(KERN_NOTICE "i82365: Card Services release "
+ "does not match!\n");
+ return -1;
+ }
+ DEBUG(0, "%s\n", version);
+ printk(KERN_INFO "Intel PCIC probe: ");
+ sockets = 0;
+
+#ifdef CONFIG_PCI
+ if (do_pci_probe && pcibios_present()) {
+ pci_probe(PCI_CLASS_BRIDGE_CARDBUS, add_cb_bridge);
+ pci_probe(PCI_CLASS_BRIDGE_PCMCIA, add_pci_bridge);
+ }
+#endif
+
+#ifdef CONFIG_ISA
+ isa_probe();
+#endif
+
+ if (sockets == 0) {
+ printk("not found.\n");
+ return -ENODEV;
+ }
+
+ /* Set up interrupt handler(s) */
+#ifdef CONFIG_ISA
+ if (grab_irq != 0)
+ request_irq(cs_irq, pcic_interrupt, 0, "i82365", NULL);
+#endif
+#ifdef CONFIG_PCI
+ if (pci_csc) {
+ u_int i, irq, mask = 0;
+ for (i = 0; i < sockets; i++) {
+ irq = socket[i].cap.pci_irq;
+ if (irq && !(mask & (1<<irq)))
+ request_irq(irq, pcic_interrupt, SA_SHIRQ, "i82365", NULL);
+ mask |= (1<<irq);
+ }
+ }
+#endif
+
+ if (register_ss_entry(sockets, &pcic_service) != 0)
+ printk(KERN_NOTICE "i82365: register_ss_entry() failed\n");
+
+ /* Finally, schedule a polling interrupt */
+ if (poll_interval != 0) {
+ poll_timer.function = pcic_interrupt_wrapper;
+ poll_timer.data = 0;
+ poll_timer.prev = poll_timer.next = NULL;
+ poll_timer.expires = jiffies + poll_interval;
+ add_timer(&poll_timer);
+ }
+
+ return 0;
+
+} /* init_i82365 */
+
+/*====================================================================*/
+
+static void __exit exit_i82365(void)
+{
+ int i;
+#ifdef CONFIG_PROC_FS
+ for (i = 0; i < sockets; i++) pcic_proc_remove(i);
+#endif
+ unregister_ss_entry(&pcic_service);
+ if (poll_interval != 0)
+ del_timer(&poll_timer);
+#ifdef CONFIG_ISA
+ if (grab_irq != 0)
+ free_irq(cs_irq, NULL);
+#endif
+#ifdef CONFIG_PCI
+ if (pci_csc) {
+ u_int irq, mask = 0;
+ for (i = 0; i < sockets; i++) {
+ irq = socket[i].cap.pci_irq;
+ if (irq && !(mask & (1<<irq)))
+ free_irq(irq, NULL);
+ mask |= (1<<irq);
+ }
+ }
+#endif
+ for (i = 0; i < sockets; i++) {
+ i365_set(i, I365_CSCINT, 0);
+#ifdef CONFIG_PCI
+ if (socket[i].cb_virt) {
+ iounmap(socket[i].cb_virt);
+ release_mem_region(socket[i].cb_phys, 0x1000);
+ } else
+#endif
+ release_region(socket[i].ioaddr, 2);
+ }
+} /* exit_i82365 */
+
+/*====================================================================*/
+
+static void pcic_interrupt_wrapper(u_long data)
+{
+ pcic_interrupt(0, NULL, NULL);
+ poll_timer.expires = jiffies + poll_interval;
+ add_timer(&poll_timer);
+}
+
+static void pcic_interrupt(int irq, void *dev,
+ struct pt_regs *regs)
+{
+ int i, j, csc;
+ u_int events, active;
+#ifdef CONFIG_ISA
+ u_long flags = 0;
+#endif
+
+ DEBUG(4, "i82365: pcic_interrupt(%d)\n", irq);
+
+ for (j = 0; j < 20; j++) {
+ active = 0;
+ for (i = 0; i < sockets; i++) {
+ if ((socket[i].cs_irq != irq) &&
+ (socket[i].cap.pci_irq != irq))
+ continue;
+#ifdef CONFIG_ISA
+ if (!(socket[i].flags & IS_CARDBUS))
+ spin_lock_irqsave(&isa_lock, flags);
+#endif
+ csc = i365_get(i, I365_CSC);
+ if ((csc == 0) || (!socket[i].handler) ||
+ (i365_get(i, I365_IDENT) & 0x70)) {
+#ifdef CONFIG_ISA
+ if (!(socket[i].flags & IS_CARDBUS))
+ spin_unlock_irqrestore(&isa_lock, flags);
+#endif
+ continue;
+ }
+ events = (csc & I365_CSC_DETECT) ? SS_DETECT : 0;
+ if (i365_get(i, I365_INTCTL) & I365_PC_IOCARD)
+ events |= (csc & I365_CSC_STSCHG) ? SS_STSCHG : 0;
+ else {
+ events |= (csc & I365_CSC_BVD1) ? SS_BATDEAD : 0;
+ events |= (csc & I365_CSC_BVD2) ? SS_BATWARN : 0;
+ events |= (csc & I365_CSC_READY) ? SS_READY : 0;
+ }
+#ifdef CONFIG_ISA
+ if (!(socket[i].flags & IS_CARDBUS))
+ spin_unlock_irqrestore(&isa_lock, flags);
+#endif
+ DEBUG(2, "i82365: socket %d event 0x%02x\n", i, events);
+ if (events)
+ socket[i].handler(socket[i].info, events);
+ active |= events;
+ }
+ if (!active) break;
+ }
+ if (j == 20)
+ printk(KERN_NOTICE "i82365: infinite loop in interrupt handler\n");
+
+ DEBUG(4, "i82365: interrupt done\n");
+} /* pcic_interrupt */
+
+/*====================================================================*/
+
+static int pcic_register_callback(u_short sock, ss_callback_t *call)
+{
+ if (call == NULL) {
+ socket[sock].handler = NULL;
+ MOD_DEC_USE_COUNT;
+ } else {
+ MOD_INC_USE_COUNT;
+ socket[sock].handler = call->handler;
+ socket[sock].info = call->info;
+ }
+ return 0;
+} /* pcic_register_callback */
+
+/*====================================================================*/
+
+static int pcic_inquire_socket(u_short sock, socket_cap_t *cap)
+{
+ *cap = socket[sock].cap;
+ return 0;
+} /* pcic_inquire_socket */
+
+/*====================================================================*/
+
+static int i365_get_status(u_short sock, u_int *value)
+{
+ u_int status;
+
+ status = i365_get(sock, I365_STATUS);
+ *value = ((status & I365_CS_DETECT) == I365_CS_DETECT)
+ ? SS_DETECT : 0;
+ if (i365_get(sock, I365_INTCTL) & I365_PC_IOCARD)
+ *value |= (status & I365_CS_STSCHG) ? 0 : SS_STSCHG;
+ else {
+ *value |= (status & I365_CS_BVD1) ? 0 : SS_BATDEAD;
+ *value |= (status & I365_CS_BVD2) ? 0 : SS_BATWARN;
+ }
+ *value |= (status & I365_CS_WRPROT) ? SS_WRPROT : 0;
+ *value |= (status & I365_CS_READY) ? SS_READY : 0;
+ *value |= (status & I365_CS_POWERON) ? SS_POWERON : 0;
+
+#ifdef CONFIG_PCI
+ if (socket[sock].flags & IS_CARDBUS) {
+ status = cb_readl(sock, CB_SOCKET_STATE);
+#ifndef CONFIG_CARDBUS
+ *value |= (status & CB_SS_32BIT) ? SS_CARDBUS : 0;
+#endif
+ *value |= (status & CB_SS_3VCARD) ? SS_3VCARD : 0;
+ *value |= (status & CB_SS_XVCARD) ? SS_XVCARD : 0;
+ } else if (socket[sock].flags & IS_O2MICRO) {
+ status = i365_get(sock, O2_MODE_B);
+ *value |= (status & O2_MODE_B_VS1) ? 0 : SS_3VCARD;
+ *value |= (status & O2_MODE_B_VS2) ? 0 : SS_XVCARD;
+ }
+#endif
+#ifdef CONFIG_ISA
+ if (socket[sock].type == IS_VG469) {
+ status = i365_get(sock, VG469_VSENSE);
+ if (socket[sock].psock & 1) {
+ *value |= (status & VG469_VSENSE_B_VS1) ? 0 : SS_3VCARD;
+ *value |= (status & VG469_VSENSE_B_VS2) ? 0 : SS_XVCARD;
+ } else {
+ *value |= (status & VG469_VSENSE_A_VS1) ? 0 : SS_3VCARD;
+ *value |= (status & VG469_VSENSE_A_VS2) ? 0 : SS_XVCARD;
+ }
+ }
+#endif
+
+ DEBUG(1, "i82365: GetStatus(%d) = %#4.4x\n", sock, *value);
+ return 0;
+} /* i365_get_status */
+
+/*====================================================================*/
+
+static int i365_get_socket(u_short sock, socket_state_t *state)
+{
+ socket_info_t *t = &socket[sock];
+ u_char reg, vcc, vpp;
+
+ reg = i365_get(sock, I365_POWER);
+ state->flags = (reg & I365_PWR_AUTO) ? SS_PWR_AUTO : 0;
+ state->flags |= (reg & I365_PWR_OUT) ? SS_OUTPUT_ENA : 0;
+ vcc = reg & I365_VCC_MASK; vpp = reg & I365_VPP1_MASK;
+ state->Vcc = state->Vpp = 0;
+#ifdef CONFIG_PCI
+ if (t->flags & IS_CARDBUS) {
+ cb_get_power(sock, state);
+ } else
+#endif
+ if (t->flags & IS_CIRRUS) {
+ if (i365_get(sock, PD67_MISC_CTL_1) & PD67_MC1_VCC_3V) {
+ if (reg & I365_VCC_5V) state->Vcc = 33;
+ if (vpp == I365_VPP1_5V) state->Vpp = 33;
+ } else {
+ if (reg & I365_VCC_5V) state->Vcc = 50;
+ if (vpp == I365_VPP1_5V) state->Vpp = 50;
+ }
+ if (vpp == I365_VPP1_12V) state->Vpp = 120;
+ } else if (t->flags & IS_VG_PWR) {
+ if (i365_get(sock, VG469_VSELECT) & VG469_VSEL_VCC) {
+ if (reg & I365_VCC_5V) state->Vcc = 33;
+ if (vpp == I365_VPP1_5V) state->Vpp = 33;
+ } else {
+ if (reg & I365_VCC_5V) state->Vcc = 50;
+ if (vpp == I365_VPP1_5V) state->Vpp = 50;
+ }
+ if (vpp == I365_VPP1_12V) state->Vpp = 120;
+ } else if (t->flags & IS_DF_PWR) {
+ if (vcc == I365_VCC_3V) state->Vcc = 33;
+ if (vcc == I365_VCC_5V) state->Vcc = 50;
+ if (vpp == I365_VPP1_5V) state->Vpp = 50;
+ if (vpp == I365_VPP1_12V) state->Vpp = 120;
+ } else {
+ if (reg & I365_VCC_5V) {
+ state->Vcc = 50;
+ if (vpp == I365_VPP1_5V) state->Vpp = 50;
+ if (vpp == I365_VPP1_12V) state->Vpp = 120;
+ }
+ }
+
+ /* IO card, RESET flags, IO interrupt */
+ reg = i365_get(sock, I365_INTCTL);
+ state->flags |= (reg & I365_PC_RESET) ? 0 : SS_RESET;
+ if (reg & I365_PC_IOCARD) state->flags |= SS_IOCARD;
+#ifdef CONFIG_PCI
+ if (cb_get_irq_mode(sock) != 0)
+ state->io_irq = t->cap.pci_irq;
+ else
+#endif
+ state->io_irq = reg & I365_IRQ_MASK;
+
+ /* speaker control */
+ if (t->flags & IS_CIRRUS) {
+ if (i365_get(sock, PD67_MISC_CTL_1) & PD67_MC1_SPKR_ENA)
+ state->flags |= SS_SPKR_ENA;
+ }
+
+ /* Card status change mask */
+ reg = i365_get(sock, I365_CSCINT);
+ state->csc_mask = (reg & I365_CSC_DETECT) ? SS_DETECT : 0;
+ if (state->flags & SS_IOCARD)
+ state->csc_mask |= (reg & I365_CSC_STSCHG) ? SS_STSCHG : 0;
+ else {
+ state->csc_mask |= (reg & I365_CSC_BVD1) ? SS_BATDEAD : 0;
+ state->csc_mask |= (reg & I365_CSC_BVD2) ? SS_BATWARN : 0;
+ state->csc_mask |= (reg & I365_CSC_READY) ? SS_READY : 0;
+ }
+
+ DEBUG(1, "i82365: GetSocket(%d) = flags %#3.3x, Vcc %d, Vpp %d, "
+ "io_irq %d, csc_mask %#2.2x\n", sock, state->flags,
+ state->Vcc, state->Vpp, state->io_irq, state->csc_mask);
+ return 0;
+} /* i365_get_socket */
+
+/*====================================================================*/
+
+static int i365_set_socket(u_short sock, socket_state_t *state)
+{
+ socket_info_t *t = &socket[sock];
+ u_char reg;
+
+ DEBUG(1, "i82365: SetSocket(%d, flags %#3.3x, Vcc %d, Vpp %d, "
+ "io_irq %d, csc_mask %#2.2x)\n", sock, state->flags,
+ state->Vcc, state->Vpp, state->io_irq, state->csc_mask);
+
+ /* First set global controller options */
+#ifdef CONFIG_PCI
+ if ((t->flags & IS_CARDBUS) && t->cap.pci_irq)
+ cb_set_irq_mode(sock, pci_csc,
+ (t->cap.pci_irq == state->io_irq));
+ t->bcr &= ~CB_BCR_CB_RESET;
+#endif
+ set_host_state(sock);
+
+ /* IO card, RESET flag, IO interrupt */
+ reg = t->intr;
+ if (state->io_irq != t->cap.pci_irq) reg |= state->io_irq;
+ reg |= (state->flags & SS_RESET) ? 0 : I365_PC_RESET;
+ reg |= (state->flags & SS_IOCARD) ? I365_PC_IOCARD : 0;
+ i365_set(sock, I365_INTCTL, reg);
+
+ reg = I365_PWR_NORESET;
+ if (state->flags & SS_PWR_AUTO) reg |= I365_PWR_AUTO;
+ if (state->flags & SS_OUTPUT_ENA) reg |= I365_PWR_OUT;
+
+#ifdef CONFIG_PCI
+ if (t->flags & IS_CARDBUS) {
+ cb_set_power(sock, state);
+ reg |= i365_get(sock, I365_POWER) &
+ (I365_VCC_MASK|I365_VPP1_MASK);
+ } else
+#endif
+ if (t->flags & IS_CIRRUS) {
+ if (state->Vpp != 0) {
+ if (state->Vpp == 120)
+ reg |= I365_VPP1_12V;
+ else if (state->Vpp == state->Vcc)
+ reg |= I365_VPP1_5V;
+ else return -EINVAL;
+ }
+ if (state->Vcc != 0) {
+ reg |= I365_VCC_5V;
+ if (state->Vcc == 33)
+ i365_bset(sock, PD67_MISC_CTL_1, PD67_MC1_VCC_3V);
+ else if (state->Vcc == 50)
+ i365_bclr(sock, PD67_MISC_CTL_1, PD67_MC1_VCC_3V);
+ else return -EINVAL;
+ }
+ } else if (t->flags & IS_VG_PWR) {
+ if (state->Vpp != 0) {
+ if (state->Vpp == 120)
+ reg |= I365_VPP1_12V;
+ else if (state->Vpp == state->Vcc)
+ reg |= I365_VPP1_5V;
+ else return -EINVAL;
+ }
+ if (state->Vcc != 0) {
+ reg |= I365_VCC_5V;
+ if (state->Vcc == 33)
+ i365_bset(sock, VG469_VSELECT, VG469_VSEL_VCC);
+ else if (state->Vcc == 50)
+ i365_bclr(sock, VG469_VSELECT, VG469_VSEL_VCC);
+ else return -EINVAL;
+ }
+ } else if (t->flags & IS_DF_PWR) {
+ switch (state->Vcc) {
+ case 0: break;
+ case 33: reg |= I365_VCC_3V; break;
+ case 50: reg |= I365_VCC_5V; break;
+ default: return -EINVAL;
+ }
+ switch (state->Vpp) {
+ case 0: break;
+ case 50: reg |= I365_VPP1_5V; break;
+ case 120: reg |= I365_VPP1_12V; break;
+ default: return -EINVAL;
+ }
+ } else {
+ switch (state->Vcc) {
+ case 0: break;
+ case 50: reg |= I365_VCC_5V; break;
+ default: return -EINVAL;
+ }
+ switch (state->Vpp) {
+ case 0: break;
+ case 50: reg |= I365_VPP1_5V | I365_VPP2_5V; break;
+ case 120: reg |= I365_VPP1_12V | I365_VPP2_12V; break;
+ default: return -EINVAL;
+ }
+ }
+
+ if (reg != i365_get(sock, I365_POWER))
+ i365_set(sock, I365_POWER, reg);
+
+ /* Chipset-specific functions */
+ if (t->flags & IS_CIRRUS) {
+ /* Speaker control */
+ i365_bflip(sock, PD67_MISC_CTL_1, PD67_MC1_SPKR_ENA,
+ state->flags & SS_SPKR_ENA);
+ }
+
+ /* Card status change interrupt mask */
+ reg = t->cs_irq << 4;
+ if (state->csc_mask & SS_DETECT) reg |= I365_CSC_DETECT;
+ if (state->flags & SS_IOCARD) {
+ if (state->csc_mask & SS_STSCHG) reg |= I365_CSC_STSCHG;
+ } else {
+ if (state->csc_mask & SS_BATDEAD) reg |= I365_CSC_BVD1;
+ if (state->csc_mask & SS_BATWARN) reg |= I365_CSC_BVD2;
+ if (state->csc_mask & SS_READY) reg |= I365_CSC_READY;
+ }
+ i365_set(sock, I365_CSCINT, reg);
+ i365_get(sock, I365_CSC);
+
+ return 0;
+} /* i365_set_socket */
+
+/*====================================================================*/
+
+static int i365_get_io_map(u_short sock, struct pccard_io_map *io)
+{
+ u_char map, ioctl, addr;
+
+ map = io->map;
+ if (map > 1) return -EINVAL;
+ io->start = i365_get_pair(sock, I365_IO(map)+I365_W_START);
+ io->stop = i365_get_pair(sock, I365_IO(map)+I365_W_STOP);
+ ioctl = i365_get(sock, I365_IOCTL);
+ addr = i365_get(sock, I365_ADDRWIN);
+ io->speed = to_ns(ioctl & I365_IOCTL_WAIT(map)) ? 1 : 0;
+ io->flags = (addr & I365_ENA_IO(map)) ? MAP_ACTIVE : 0;
+ io->flags |= (ioctl & I365_IOCTL_0WS(map)) ? MAP_0WS : 0;
+ io->flags |= (ioctl & I365_IOCTL_16BIT(map)) ? MAP_16BIT : 0;
+ io->flags |= (ioctl & I365_IOCTL_IOCS16(map)) ? MAP_AUTOSZ : 0;
+ DEBUG(1, "i82365: GetIOMap(%d, %d) = %#2.2x, %d ns, "
+ "%#4.4x-%#4.4x\n", sock, map, io->flags, io->speed,
+ io->start, io->stop);
+ return 0;
+} /* i365_get_io_map */
+
+/*====================================================================*/
+
+static int i365_set_io_map(u_short sock, struct pccard_io_map *io)
+{
+ u_char map, ioctl;
+
+ DEBUG(1, "i82365: SetIOMap(%d, %d, %#2.2x, %d ns, "
+ "%#4.4x-%#4.4x)\n", sock, io->map, io->flags,
+ io->speed, io->start, io->stop);
+ map = io->map;
+ if ((map > 1) || (io->start > 0xffff) || (io->stop > 0xffff) ||
+ (io->stop < io->start)) return -EINVAL;
+ /* Turn off the window before changing anything */
+ if (i365_get(sock, I365_ADDRWIN) & I365_ENA_IO(map))
+ i365_bclr(sock, I365_ADDRWIN, I365_ENA_IO(map));
+ i365_set_pair(sock, I365_IO(map)+I365_W_START, io->start);
+ i365_set_pair(sock, I365_IO(map)+I365_W_STOP, io->stop);
+ ioctl = i365_get(sock, I365_IOCTL) & ~I365_IOCTL_MASK(map);
+ if (io->speed) ioctl |= I365_IOCTL_WAIT(map);
+ if (io->flags & MAP_0WS) ioctl |= I365_IOCTL_0WS(map);
+ if (io->flags & MAP_16BIT) ioctl |= I365_IOCTL_16BIT(map);
+ if (io->flags & MAP_AUTOSZ) ioctl |= I365_IOCTL_IOCS16(map);
+ i365_set(sock, I365_IOCTL, ioctl);
+ /* Turn on the window if necessary */
+ if (io->flags & MAP_ACTIVE)
+ i365_bset(sock, I365_ADDRWIN, I365_ENA_IO(map));
+ return 0;
+} /* i365_set_io_map */
+
+/*====================================================================*/
+
+static int i365_get_mem_map(u_short sock, struct pccard_mem_map *mem)
+{
+ u_short base, i;
+ u_char map, addr;
+
+ map = mem->map;
+ if (map > 4) return -EINVAL;
+ addr = i365_get(sock, I365_ADDRWIN);
+ mem->flags = (addr & I365_ENA_MEM(map)) ? MAP_ACTIVE : 0;
+ base = I365_MEM(map);
+
+ i = i365_get_pair(sock, base+I365_W_START);
+ mem->flags |= (i & I365_MEM_16BIT) ? MAP_16BIT : 0;
+ mem->flags |= (i & I365_MEM_0WS) ? MAP_0WS : 0;
+ mem->sys_start += ((u_long)(i & 0x0fff) << 12);
+
+ i = i365_get_pair(sock, base+I365_W_STOP);
+ mem->speed = (i & I365_MEM_WS0) ? 1 : 0;
+ mem->speed += (i & I365_MEM_WS1) ? 2 : 0;
+ mem->speed = to_ns(mem->speed);
+ mem->sys_stop = ((u_long)(i & 0x0fff) << 12) + 0x0fff;
+
+ i = i365_get_pair(sock, base+I365_W_OFF);
+ mem->flags |= (i & I365_MEM_WRPROT) ? MAP_WRPROT : 0;
+ mem->flags |= (i & I365_MEM_REG) ? MAP_ATTRIB : 0;
+ mem->card_start = ((u_int)(i & 0x3fff) << 12) + mem->sys_start;
+ mem->card_start &= 0x3ffffff;
+
+#ifdef CONFIG_PCI
+ /* Take care of high byte, for PCI controllers */
+ if (socket[sock].type == IS_PD6729) {
+ i365_set(sock, PD67_EXT_INDEX, PD67_MEM_PAGE(map));
+ addr = i365_get(sock, PD67_EXT_DATA) << 24;
+ } else if (socket[sock].flags & IS_CARDBUS) {
+ addr = i365_get(sock, CB_MEM_PAGE(map)) << 24;
+ mem->sys_stop += addr; mem->sys_start += addr;
+ }
+#endif
+
+ DEBUG(1, "i82365: GetMemMap(%d, %d) = %#2.2x, %d ns, %#5.5lx-%#5."
+ "5lx, %#5.5x\n", sock, mem->map, mem->flags, mem->speed,
+ mem->sys_start, mem->sys_stop, mem->card_start);
+ return 0;
+} /* i365_get_mem_map */
+
+/*====================================================================*/
+
+static int i365_set_mem_map(u_short sock, struct pccard_mem_map *mem)
+{
+ u_short base, i;
+ u_char map;
+
+ DEBUG(1, "i82365: SetMemMap(%d, %d, %#2.2x, %d ns, %#5.5lx-%#5.5"
+ "lx, %#5.5x)\n", sock, mem->map, mem->flags, mem->speed,
+ mem->sys_start, mem->sys_stop, mem->card_start);
+
+ map = mem->map;
+ if ((map > 4) || (mem->card_start > 0x3ffffff) ||
+ (mem->sys_start > mem->sys_stop) || (mem->speed > 1000))
+ return -EINVAL;
+ if (!(socket[sock].flags & (IS_PCI | IS_CARDBUS)) &&
+ ((mem->sys_start > 0xffffff) || (mem->sys_stop > 0xffffff)))
+ return -EINVAL;
+
+ /* Turn off the window before changing anything */
+ if (i365_get(sock, I365_ADDRWIN) & I365_ENA_MEM(map))
+ i365_bclr(sock, I365_ADDRWIN, I365_ENA_MEM(map));
+
+#ifdef CONFIG_PCI
+ /* Take care of high byte, for PCI controllers */
+ if (socket[sock].type == IS_PD6729) {
+ i365_set(sock, PD67_EXT_INDEX, PD67_MEM_PAGE(map));
+ i365_set(sock, PD67_EXT_DATA, (mem->sys_start >> 24));
+ } else if (socket[sock].flags & IS_CARDBUS)
+ i365_set(sock, CB_MEM_PAGE(map), mem->sys_start >> 24);
+#endif
+
+ base = I365_MEM(map);
+ i = (mem->sys_start >> 12) & 0x0fff;
+ if (mem->flags & MAP_16BIT) i |= I365_MEM_16BIT;
+ if (mem->flags & MAP_0WS) i |= I365_MEM_0WS;
+ i365_set_pair(sock, base+I365_W_START, i);
+
+ i = (mem->sys_stop >> 12) & 0x0fff;
+ switch (to_cycles(mem->speed)) {
+ case 0: break;
+ case 1: i |= I365_MEM_WS0; break;
+ case 2: i |= I365_MEM_WS1; break;
+ default: i |= I365_MEM_WS1 | I365_MEM_WS0; break;
+ }
+ i365_set_pair(sock, base+I365_W_STOP, i);
+
+ i = ((mem->card_start - mem->sys_start) >> 12) & 0x3fff;
+ if (mem->flags & MAP_WRPROT) i |= I365_MEM_WRPROT;
+ if (mem->flags & MAP_ATTRIB) i |= I365_MEM_REG;
+ i365_set_pair(sock, base+I365_W_OFF, i);
+
+ /* Turn on the window if necessary */
+ if (mem->flags & MAP_ACTIVE)
+ i365_bset(sock, I365_ADDRWIN, I365_ENA_MEM(map));
+ return 0;
+} /* i365_set_mem_map */
+
+/*======================================================================
+
+ Power control for Cardbus controllers: used both for 16-bit and
+ Cardbus cards.
+
+======================================================================*/
+
+#ifdef CONFIG_PCI
+
+static void cb_get_power(u_short sock, socket_state_t *state)
+{
+ u_int reg = cb_readl(sock, CB_SOCKET_CONTROL);
+ state->Vcc = state->Vpp = 0;
+ switch (reg & CB_SC_VCC_MASK) {
+ case CB_SC_VCC_3V: state->Vcc = 33; break;
+ case CB_SC_VCC_5V: state->Vcc = 50; break;
+ }
+ switch (reg & CB_SC_VCC_MASK) {
+ case CB_SC_VPP_3V: state->Vpp = 33; break;
+ case CB_SC_VPP_5V: state->Vpp = 50; break;
+ case CB_SC_VPP_12V: state->Vpp = 120; break;
+ }
+}
+
+static void cb_set_power(u_short sock, socket_state_t *state)
+{
+ u_int reg = 0;
+ switch (state->Vcc) {
+ case 33: reg = CB_SC_VCC_3V; break;
+ case 50: reg = CB_SC_VCC_5V; break;
+ default: reg = 0; break;
+ }
+ switch (state->Vpp) {
+ case 33: reg |= CB_SC_VPP_3V; break;
+ case 50: reg |= CB_SC_VPP_5V; break;
+ case 120: reg |= CB_SC_VPP_12V; break;
+ }
+ if (reg != cb_readl(sock, CB_SOCKET_CONTROL))
+ cb_writel(sock, CB_SOCKET_CONTROL, reg);
+}
+
+#endif
+
+/*======================================================================
+
+ All the stuff that is strictly for Cardbus cards goes here.
+
+======================================================================*/
+
+#ifdef CONFIG_CARDBUS
+
+static int cb_get_status(u_short sock, u_int *value)
+{
+ u_int s;
+ s = cb_readl(sock, CB_SOCKET_STATE);
+ *value = ((s & CB_SS_32BIT) ? SS_CARDBUS : 0);
+ *value |= ((s & CB_SS_CCD1) || (s & CB_SS_CCD2)) ? 0 : SS_DETECT;
+ *value |= (s & CB_SS_CSTSCHG) ? SS_STSCHG : 0;
+ *value |= (s & CB_SS_PWRCYCLE) ? (SS_POWERON|SS_READY) : 0;
+ *value |= (s & CB_SS_3VCARD) ? SS_3VCARD : 0;
+ *value |= (s & CB_SS_XVCARD) ? SS_XVCARD : 0;
+ DEBUG(1, "yenta: GetStatus(%d) = %#4.4x\n", sock, *value);
+ return 0;
+} /* cb_get_status */
+
+static int cb_get_socket(u_short sock, socket_state_t *state)
+{
+ socket_info_t *s = &socket[sock];
+ u_short bcr;
+
+ cb_get_power(sock, state);
+ pci_readw(s->bus, s->devfn, CB_BRIDGE_CONTROL, &bcr);
+ state->flags |= (bcr & CB_BCR_CB_RESET) ? SS_RESET : 0;
+ if (cb_get_irq_mode(sock) != 0)
+ state->io_irq = s->cap.pci_irq;
+ else
+ state->io_irq = i365_get(sock, I365_INTCTL) & I365_IRQ_MASK;
+ DEBUG(1, "yenta: GetSocket(%d) = flags %#3.3x, Vcc %d, Vpp %d, "
+ "io_irq %d, csc_mask %#2.2x\n", sock, state->flags,
+ state->Vcc, state->Vpp, state->io_irq, state->csc_mask);
+ return 0;
+} /* cb_get_socket */
+
+static int cb_set_socket(u_short sock, socket_state_t *state)
+{
+ socket_info_t *s = &socket[sock];
+ u_int reg;
+
+ DEBUG(1, "yenta: SetSocket(%d, flags %#3.3x, Vcc %d, Vpp %d, "
+ "io_irq %d, csc_mask %#2.2x)\n", sock, state->flags,
+ state->Vcc, state->Vpp, state->io_irq, state->csc_mask);
+
+ /* First set global controller options */
+ if (s->cap.pci_irq)
+ cb_set_irq_mode(sock, pci_csc,
+ (s->cap.pci_irq == state->io_irq));
+ s->bcr &= ~CB_BCR_CB_RESET;
+ s->bcr |= (state->flags & SS_RESET) ? CB_BCR_CB_RESET : 0;
+ set_host_state(sock);
+
+ cb_set_power(sock, state);
+
+ /* Handle IO interrupt using ISA routing */
+ reg = i365_get(sock, I365_INTCTL) & ~I365_IRQ_MASK;
+ if (state->io_irq != s->cap.pci_irq) reg |= state->io_irq;
+ i365_set(sock, I365_INTCTL, reg);
+
+ /* Handle CSC mask */
+ reg = (socket[sock].cs_irq << 4);
+ if (state->csc_mask & SS_DETECT) reg |= I365_CSC_DETECT;
+ i365_set(sock, I365_CSCINT, reg);
+ i365_get(sock, I365_CSC);
+
+ return 0;
+} /* cb_set_socket */
+
+static int cb_get_bridge(u_short sock, struct cb_bridge_map *m)
+{
+ socket_info_t *s = &socket[sock];
+ u_char map;
+
+ map = m->map;
+ if (map > 1) return -EINVAL;
+ m->flags &= MAP_IOSPACE;
+ map += (m->flags & MAP_IOSPACE) ? 2 : 0;
+ pci_readl(s->bus, s->devfn, CB_MEM_BASE(map), &m->start);
+ pci_readl(s->bus, s->devfn, CB_MEM_LIMIT(map), &m->stop);
+ if (m->start || m->stop) {
+ m->flags |= MAP_ACTIVE;
+ m->stop |= (map > 1) ? 3 : 0x0fff;
+ }
+ if (map > 1) {
+ u_short bcr;
+ pci_readw(s->bus, s->devfn, CB_BRIDGE_CONTROL, &bcr);
+ m->flags |= (bcr & CB_BCR_PREFETCH(map)) ? MAP_PREFETCH : 0;
+ }
+ DEBUG(1, "yenta: GetBridge(%d, %d) = %#2.2x, %#4.4x-%#4.4x\n",
+ sock, map, m->flags, m->start, m->stop);
+ return 0;
+}
+
+static int cb_set_bridge(u_short sock, struct cb_bridge_map *m)
+{
+ socket_info_t *s = &socket[sock];
+ u_char map;
+
+ DEBUG(1, "yenta: SetBridge(%d, %d, %#2.2x, %#4.4x-%#4.4x)\n",
+ sock, m->map, m->flags, m->start, m->stop);
+ map = m->map;
+ if (!(s->flags & IS_CARDBUS) || (map > 1) || (m->stop < m->start))
+ return -EINVAL;
+ if (m->flags & MAP_IOSPACE) {
+ if ((m->stop > 0xffff) || (m->start & 3) ||
+ ((m->stop & 3) != 3))
+ return -EINVAL;
+ map += 2;
+ } else {
+ u_short bcr;
+ if ((m->start & 0x0fff) || ((m->stop & 0x0fff) != 0x0fff))
+ return -EINVAL;
+ pci_readw(s->bus, s->devfn, CB_BRIDGE_CONTROL, &bcr);
+ bcr &= ~CB_BCR_PREFETCH(map);
+ bcr |= (m->flags & MAP_PREFETCH) ? CB_BCR_PREFETCH(map) : 0;
+ pci_writew(s->bus, s->devfn, CB_BRIDGE_CONTROL, bcr);
+ }
+ if (m->flags & MAP_ACTIVE) {
+ pci_writel(s->bus, s->devfn, CB_MEM_BASE(map), m->start);
+ pci_writel(s->bus, s->devfn, CB_MEM_LIMIT(map), m->stop);
+ } else {
+ pci_writel(s->bus, s->devfn, CB_IO_BASE(map), 0);
+ pci_writel(s->bus, s->devfn, CB_IO_LIMIT(map), 0);
+ }
+ return 0;
+}
+
+#endif /* CONFIG_CARDBUS */
+
+/*======================================================================
+
+ Routines for accessing socket information and register dumps via
+ /proc/bus/pccard/...
+
+======================================================================*/
+
+#ifdef CONFIG_PROC_FS
+
+static int proc_read_info(char *buf, char **start, off_t pos,
+ int count, int *eof, void *data)
+{
+ socket_info_t *s = data;
+ char *p = buf;
+ p += sprintf(p, "type: %s\npsock: %d\n",
+ pcic[s->type].name, s->psock);
+#ifdef CONFIG_PCI
+ if (s->flags & (IS_PCI|IS_CARDBUS))
+ p += sprintf(p, "bus: %02x\ndevfn: %02x.%1x\n",
+ s->bus, PCI_SLOT(s->devfn), PCI_FUNC(s->devfn));
+ if (s->flags & IS_CARDBUS)
+ p += sprintf(p, "cardbus: %02x\n", s->cap.cardbus);
+#endif
+ return (p - buf);
+}
+
+static int proc_read_exca(char *buf, char **start, off_t pos,
+ int count, int *eof, void *data)
+{
+ u_short sock = (socket_info_t *)data - socket;
+ char *p = buf;
+ int i, top;
+
+#ifdef CONFIG_ISA
+ u_long flags = 0;
+ if (!(socket[sock].flags & IS_CARDBUS))
+ spin_lock_irqsave(&isa_lock, flags);
+#endif
+ top = 0x40;
+ if (socket[sock].flags & IS_CARDBUS)
+ top = (socket[sock].flags & IS_CIRRUS) ? 0x140 : 0x50;
+ for (i = 0; i < top; i += 4) {
+ if (i == 0x50) {
+ p += sprintf(p, "\n");
+ i = 0x100;
+ }
+ p += sprintf(p, "%02x %02x %02x %02x%s",
+ i365_get(sock,i), i365_get(sock,i+1),
+ i365_get(sock,i+2), i365_get(sock,i+3),
+ ((i % 16) == 12) ? "\n" : " ");
+ }
+#ifdef CONFIG_ISA
+ if (!(socket[sock].flags & IS_CARDBUS))
+ spin_unlock_irqrestore(&isa_lock, flags);
+#endif
+ return (p - buf);
+}
+
+#ifdef CONFIG_PCI
+static int proc_read_pci(char *buf, char **start, off_t pos,
+ int count, int *eof, void *data)
+{
+ socket_info_t *s = data;
+ u_char bus = s->bus, devfn = s->devfn;
+ char *p = buf;
+ u_int a, b, c, d;
+ int i;
+
+ for (i = 0; i < 0xc0; i += 0x10) {
+ pci_readl(bus, devfn, i, &a);
+ pci_readl(bus, devfn, i+4, &b);
+ pci_readl(bus, devfn, i+8, &c);
+ pci_readl(bus, devfn, i+12, &d);
+ p += sprintf(p, "%08x %08x %08x %08x\n", a, b, c, d);
+ }
+ return (p - buf);
+}
+#endif
+
+#ifdef CONFIG_CARDBUS
+static int proc_read_cardbus(char *buf, char **start, off_t pos,
+ int count, int *eof, void *data)
+{
+ u_short sock = (socket_info_t *)data - socket;
+ int len;
+
+ len = sprintf(buf, "%08x %08x %08x %08x %08x %08x\n",
+ cb_readl(sock,0), cb_readl(sock,4),
+ cb_readl(sock,8), cb_readl(sock,12),
+ cb_readl(sock,16), cb_readl(sock,32));
+ return len;
+}
+#endif
+
+static void pcic_proc_setup(u_short sock, struct proc_dir_entry *base)
+{
+ socket_info_t *s = &socket[sock];
+ struct proc_dir_entry *ent;
+ ent = create_proc_entry("info", 0, base);
+ ent->read_proc = proc_read_info;
+ ent->data = s;
+ ent = create_proc_entry("exca", 0, base);
+ ent->read_proc = proc_read_exca;
+ ent->data = s;
+#ifdef CONFIG_PCI
+ if (s->flags & (IS_PCI|IS_CARDBUS)) {
+ ent = create_proc_entry("pci", 0, base);
+ ent->read_proc = proc_read_pci;
+ ent->data = s;
+ }
+#endif
+#ifdef CONFIG_CARDBUS
+ if (s->flags & IS_CARDBUS) {
+ ent = create_proc_entry("cardbus", 0, base);
+ ent->read_proc = proc_read_cardbus;
+ ent->data = s;
+ }
+#endif
+ s->proc = base;
+}
+
+static void pcic_proc_remove(u_short sock)
+{
+ struct proc_dir_entry *base = socket[sock].proc;
+ if (base == NULL) return;
+ remove_proc_entry("info", base);
+ remove_proc_entry("exca", base);
+#ifdef CONFIG_PCI
+ if (socket[sock].flags & (IS_PCI|IS_CARDBUS))
+ remove_proc_entry("pci", base);
+#endif
+#ifdef CONFIG_CARDBUS
+ if (socket[sock].flags & IS_CARDBUS)
+ remove_proc_entry("cardbus", base);
+#endif
+}
+
+#endif /* CONFIG_PROC_FS */
+
+/*====================================================================*/
+
+typedef int (*subfn_t)(u_short, void *);
+
+static subfn_t pcic_service_table[] = {
+ (subfn_t)&pcic_register_callback,
+ (subfn_t)&pcic_inquire_socket,
+ (subfn_t)&i365_get_status,
+ (subfn_t)&i365_get_socket,
+ (subfn_t)&i365_set_socket,
+ (subfn_t)&i365_get_io_map,
+ (subfn_t)&i365_set_io_map,
+ (subfn_t)&i365_get_mem_map,
+ (subfn_t)&i365_set_mem_map,
+#ifdef CONFIG_CARDBUS
+ (subfn_t)&cb_get_bridge,
+ (subfn_t)&cb_set_bridge,
+#else
+ NULL, NULL,
+#endif
+#ifdef CONFIG_PROC_FS
+ (subfn_t)&pcic_proc_setup
+#endif
+};
+
+#define NFUNC (sizeof(pcic_service_table)/sizeof(subfn_t))
+
+static int pcic_service(u_int sock, u_int cmd, void *arg)
+{
+ subfn_t fn;
+
+ DEBUG(2, "pcic_ioctl(%d, %d, 0x%p)\n", sock, cmd, arg);
+
+ if (cmd >= NFUNC)
+ return -EINVAL;
+
+ if (socket[sock].flags & IS_ALIVE) {
+ if (cmd == SS_GetStatus)
+ *(u_int *)arg = 0;
+ return -EINVAL;
+ }
+
+ fn = pcic_service_table[cmd];
+#ifdef CONFIG_CARDBUS
+ if ((socket[sock].flags & IS_CARDBUS) &&
+ (cb_readl(sock, CB_SOCKET_STATE) & CB_SS_32BIT)) {
+ if (cmd == SS_GetStatus)
+ fn = (subfn_t)&cb_get_status;
+ else if (cmd == SS_GetSocket)
+ fn = (subfn_t)&cb_get_socket;
+ else if (cmd == SS_SetSocket)
+ fn = (subfn_t)&cb_set_socket;
+ }
+#endif
+
+#ifdef CONFIG_ISA
+ if (!(socket[sock].flags & IS_CARDBUS)) {
+ int ret;
+ u_long flags;
+ spin_lock_irqsave(&isa_lock, flags);
+ ret = (fn == NULL) ? -EINVAL : fn(sock, arg);
+ spin_unlock_irqrestore(&isa_lock, flags);
+ return ret;
+ }
+#endif
+ return (fn == NULL) ? -EINVAL : fn(sock, arg);
+} /* pcic_service */
+
+/*====================================================================*/
+
+module_init(init_i82365);
+module_exit(exit_i82365);
+
+/*====================================================================*/
+
diff --git a/drivers/pcmcia/i82365.h b/drivers/pcmcia/i82365.h
new file mode 100644
index 000000000..ac3ba4e87
--- /dev/null
+++ b/drivers/pcmcia/i82365.h
@@ -0,0 +1,135 @@
+/*
+ * i82365.h 1.14 1999/08/28 04:01:47
+ *
+ * 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 David A. Hinds
+ * <dhinds@hyper.stanford.edu>. Portions created by David A. Hinds
+ * are Copyright (C) 1999 David A. Hinds. 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.
+ */
+
+#ifndef _LINUX_I82365_H
+#define _LINUX_I82365_H
+
+/* register definitions for the Intel 82365SL PCMCIA controller */
+
+/* Offsets for PCIC registers */
+#define I365_IDENT 0x00 /* Identification and revision */
+#define I365_STATUS 0x01 /* Interface status */
+#define I365_POWER 0x02 /* Power and RESETDRV control */
+#define I365_INTCTL 0x03 /* Interrupt and general control */
+#define I365_CSC 0x04 /* Card status change */
+#define I365_CSCINT 0x05 /* Card status change interrupt control */
+#define I365_ADDRWIN 0x06 /* Address window enable */
+#define I365_IOCTL 0x07 /* I/O control */
+#define I365_GENCTL 0x16 /* Card detect and general control */
+#define I365_GBLCTL 0x1E /* Global control register */
+
+/* Offsets for I/O and memory window registers */
+#define I365_IO(map) (0x08+((map)<<2))
+#define I365_MEM(map) (0x10+((map)<<3))
+#define I365_W_START 0
+#define I365_W_STOP 2
+#define I365_W_OFF 4
+
+/* Flags for I365_STATUS */
+#define I365_CS_BVD1 0x01
+#define I365_CS_STSCHG 0x01
+#define I365_CS_BVD2 0x02
+#define I365_CS_SPKR 0x02
+#define I365_CS_DETECT 0x0C
+#define I365_CS_WRPROT 0x10
+#define I365_CS_READY 0x20 /* Inverted */
+#define I365_CS_POWERON 0x40
+#define I365_CS_GPI 0x80
+
+/* Flags for I365_POWER */
+#define I365_PWR_OFF 0x00 /* Turn off the socket */
+#define I365_PWR_OUT 0x80 /* Output enable */
+#define I365_PWR_NORESET 0x40 /* Disable RESETDRV on resume */
+#define I365_PWR_AUTO 0x20 /* Auto pwr switch enable */
+#define I365_VCC_MASK 0x18 /* Mask for turning off Vcc */
+/* There are different layouts for B-step and DF-step chips: the B
+ step has independent Vpp1/Vpp2 control, and the DF step has only
+ Vpp1 control, plus 3V control */
+#define I365_VCC_5V 0x10 /* Vcc = 5.0v */
+#define I365_VCC_3V 0x18 /* Vcc = 3.3v */
+#define I365_VPP2_MASK 0x0c /* Mask for turning off Vpp2 */
+#define I365_VPP2_5V 0x04 /* Vpp2 = 5.0v */
+#define I365_VPP2_12V 0x08 /* Vpp2 = 12.0v */
+#define I365_VPP1_MASK 0x03 /* Mask for turning off Vpp1 */
+#define I365_VPP1_5V 0x01 /* Vpp2 = 5.0v */
+#define I365_VPP1_12V 0x02 /* Vpp2 = 12.0v */
+
+/* Flags for I365_INTCTL */
+#define I365_RING_ENA 0x80
+#define I365_PC_RESET 0x40
+#define I365_PC_IOCARD 0x20
+#define I365_INTR_ENA 0x10
+#define I365_IRQ_MASK 0x0F
+
+/* Flags for I365_CSC and I365_CSCINT*/
+#define I365_CSC_BVD1 0x01
+#define I365_CSC_STSCHG 0x01
+#define I365_CSC_BVD2 0x02
+#define I365_CSC_READY 0x04
+#define I365_CSC_DETECT 0x08
+#define I365_CSC_ANY 0x0F
+#define I365_CSC_GPI 0x10
+
+/* Flags for I365_ADDRWIN */
+#define I365_ENA_IO(map) (0x40 << (map))
+#define I365_ENA_MEM(map) (0x01 << (map))
+
+/* Flags for I365_IOCTL */
+#define I365_IOCTL_MASK(map) (0x0F << (map<<2))
+#define I365_IOCTL_WAIT(map) (0x08 << (map<<2))
+#define I365_IOCTL_0WS(map) (0x04 << (map<<2))
+#define I365_IOCTL_IOCS16(map) (0x02 << (map<<2))
+#define I365_IOCTL_16BIT(map) (0x01 << (map<<2))
+
+/* Flags for I365_GENCTL */
+#define I365_CTL_16DELAY 0x01
+#define I365_CTL_RESET 0x02
+#define I365_CTL_GPI_ENA 0x04
+#define I365_CTL_GPI_CTL 0x08
+#define I365_CTL_RESUME 0x10
+#define I365_CTL_SW_IRQ 0x20
+
+/* Flags for I365_GBLCTL */
+#define I365_GBL_PWRDOWN 0x01
+#define I365_GBL_CSC_LEV 0x02
+#define I365_GBL_WRBACK 0x04
+#define I365_GBL_IRQ_0_LEV 0x08
+#define I365_GBL_IRQ_1_LEV 0x10
+
+/* Flags for memory window registers */
+#define I365_MEM_16BIT 0x8000 /* In memory start high byte */
+#define I365_MEM_0WS 0x4000
+#define I365_MEM_WS1 0x8000 /* In memory stop high byte */
+#define I365_MEM_WS0 0x4000
+#define I365_MEM_WRPROT 0x8000 /* In offset high byte */
+#define I365_MEM_REG 0x4000
+
+#define I365_REG(slot, reg) (((slot) << 6) + reg)
+
+#endif /* _LINUX_I82365_H */
diff --git a/drivers/pcmcia/o2micro.h b/drivers/pcmcia/o2micro.h
new file mode 100644
index 000000000..89cb6cf01
--- /dev/null
+++ b/drivers/pcmcia/o2micro.h
@@ -0,0 +1,98 @@
+/*
+ * o2micro.h 1.10 1999/09/03 16:43:35
+ *
+ * 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 David A. Hinds
+ * <dhinds@hyper.stanford.edu>. Portions created by David A. Hinds
+ * are Copyright (C) 1999 David A. Hinds. 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.
+ */
+
+#ifndef _LINUX_O2MICRO_H
+#define _LINUX_O2MICRO_H
+
+#ifndef PCI_VENDOR_ID_O2
+#define PCI_VENDOR_ID_O2 0x1217
+#endif
+#ifndef PCI_DEVICE_ID_O2_6729
+#define PCI_DEVICE_ID_O2_6729 0x6729
+#endif
+#ifndef PCI_DEVICE_ID_O2_6730
+#define PCI_DEVICE_ID_O2_6730 0x673a
+#endif
+#ifndef PCI_DEVICE_ID_O2_6832
+#define PCI_DEVICE_ID_O2_6832 0x6832
+#endif
+#ifndef PCI_DEVICE_ID_O2_6836
+#define PCI_DEVICE_ID_O2_6836 0x6836
+#endif
+
+#define O2_MODE_A 0x38
+#define O2_MODE_A_2 0x26 /* For 6833B, 6860C */
+#define O2_MODE_A_CD_PULSE 0x04
+#define O2_MODE_A_SUSP_EDGE 0x08
+#define O2_MODE_A_HOST_SUSP 0x10
+#define O2_MODE_A_PWRCHIP 0x60
+#define O2_MODE_A_QUIET 0x80
+
+#define O2_MODE_B 0x39
+#define O2_MODE_B_2 0x2e /* For 6833B, 6860C */
+#define O2_MODE_B_IDENT 0x03
+#define O2_MODE_B_ID_BSTEP 0x00
+#define O2_MODE_B_ID_CSTEP 0x01
+#define O2_MODE_B_ID_O2 0x02
+#define O2_MODE_B_VS1 0x04
+#define O2_MODE_B_VS2 0x08
+#define O2_MODE_B_IRQ15_RI 0x80
+
+#define O2_MODE_C 0x3a
+#define O2_MODE_C_DREQ_MASK 0x03
+#define O2_MODE_C_DREQ_INPACK 0x01
+#define O2_MODE_C_DREQ_WP 0x02
+#define O2_MODE_C_DREQ_BVD2 0x03
+#define O2_MODE_C_ZVIDEO 0x08
+
+#define O2_MODE_D 0x3b
+#define O2_MODE_D_IRQ_MODE 0x03
+#define O2_MODE_D_SKT_ACTV 0x20
+#define O2_MODE_D_PCI_FIFO 0x40 /* for OZ6729, OZ6730 */
+#define O2_MODE_D_W97_IRQ 0x40 /* for OZ6832 */
+#define O2_MODE_D_ISA_IRQ 0x80
+
+#define O2_MHPG_DMA 0x3c
+#define O2_MHPG_CHANNEL 0x07
+#define O2_MHPG_CINT_ENA 0x08
+#define O2_MHPG_CSC_ENA 0x10
+
+#define O2_FIFO_ENA 0x3d
+#define O2_FIFO_ZVIDEO_3 0x08
+#define O2_FIFO_PCI_FIFO 0x10
+#define O2_FIFO_POSTWR 0x40
+#define O2_FIFO_BUFFER 0x80
+
+#define O2_MODE_E 0x3e
+#define O2_MODE_E_MHPG_DMA 0x01
+#define O2_MODE_E_SPKR_OUT 0x02
+#define O2_MODE_E_LED_OUT 0x08
+#define O2_MODE_E_SKTA_ACTV 0x10
+
+#endif /* _LINUX_O2MICRO_H */
diff --git a/drivers/pcmcia/ricoh.h b/drivers/pcmcia/ricoh.h
new file mode 100644
index 000000000..c6fceac2e
--- /dev/null
+++ b/drivers/pcmcia/ricoh.h
@@ -0,0 +1,132 @@
+/*
+ * ricoh.h 1.8 1999/08/28 04:01:47
+ *
+ * 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 David A. Hinds
+ * <dhinds@hyper.stanford.edu>. Portions created by David A. Hinds
+ * are Copyright (C) 1999 David A. Hinds. 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.
+ */
+
+#ifndef _LINUX_RICOH_H
+#define _LINUX_RICOH_H
+
+#define RF5C_MODE_CTL 0x1f /* Mode control */
+#define RF5C_PWR_CTL 0x2f /* Mixed voltage control */
+#define RF5C_CHIP_ID 0x3a /* Chip identification */
+#define RF5C_MODE_CTL_3 0x3b /* Mode control 3 */
+
+/* I/O window address offset */
+#define RF5C_IO_OFF(w) (0x36+((w)<<1))
+
+/* Flags for RF5C_MODE_CTL */
+#define RF5C_MODE_ATA 0x01 /* ATA mode */
+#define RF5C_MODE_LED_ENA 0x02 /* IRQ 12 is LED */
+#define RF5C_MODE_CA21 0x04
+#define RF5C_MODE_CA22 0x08
+#define RF5C_MODE_CA23 0x10
+#define RF5C_MODE_CA24 0x20
+#define RF5C_MODE_CA25 0x40
+#define RF5C_MODE_3STATE_BIT7 0x80
+
+/* Flags for RF5C_PWR_CTL */
+#define RF5C_PWR_VCC_3V 0x01
+#define RF5C_PWR_IREQ_HIGH 0x02
+#define RF5C_PWR_INPACK_ENA 0x04
+#define RF5C_PWR_5V_DET 0x08
+#define RF5C_PWR_TC_SEL 0x10 /* Terminal Count: irq 11 or 15 */
+#define RF5C_PWR_DREQ_LOW 0x20
+#define RF5C_PWR_DREQ_OFF 0x00 /* DREQ steering control */
+#define RF5C_PWR_DREQ_INPACK 0x40
+#define RF5C_PWR_DREQ_SPKR 0x80
+#define RF5C_PWR_DREQ_IOIS16 0xc0
+
+/* Values for RF5C_CHIP_ID */
+#define RF5C_CHIP_RF5C296 0x32
+#define RF5C_CHIP_RF5C396 0xb2
+
+/* Flags for RF5C_MODE_CTL_3 */
+#define RF5C_MCTL3_DISABLE 0x01 /* Disable PCMCIA interface */
+#define RF5C_MCTL3_DMA_ENA 0x02
+
+/* Register definitions for Ricoh PCI-to-CardBus bridges */
+
+#ifndef PCI_VENDOR_ID_RICOH
+#define PCI_VENDOR_ID_RICOH 0x1180
+#endif
+#ifndef PCI_DEVICE_ID_RICOH_RL5C465
+#define PCI_DEVICE_ID_RICOH_RL5C465 0x0465
+#endif
+#ifndef PCI_DEVICE_ID_RICOH_RL5C466
+#define PCI_DEVICE_ID_RICOH_RL5C466 0x0466
+#endif
+#ifndef PCI_DEVICE_ID_RICOH_RL5C475
+#define PCI_DEVICE_ID_RICOH_RL5C475 0x0475
+#endif
+#ifndef PCI_DEVICE_ID_RICOH_RL5C476
+#define PCI_DEVICE_ID_RICOH_RL5C476 0x0476
+#endif
+#ifndef PCI_DEVICE_ID_RICOH_RL5C478
+#define PCI_DEVICE_ID_RICOH_RL5C478 0x0478
+#endif
+
+/* Extra bits in CB_BRIDGE_CONTROL */
+#define RL5C46X_BCR_3E0_ENA 0x0800
+#define RL5C46X_BCR_3E2_ENA 0x1000
+
+/* Misc Control Register */
+#define RL5C4XX_MISC 0x0082 /* 16 bit */
+#define RL5C4XX_MISC_HW_SUSPEND_ENA 0x0002
+#define RL5C4XX_MISC_VCCEN_POL 0x0100
+#define RL5C4XX_MISC_VPPEN_POL 0x0200
+#define RL5C46X_MISC_SUSPEND 0x0001
+#define RL5C46X_MISC_PWR_SAVE_2 0x0004
+#define RL5C46X_MISC_IFACE_BUSY 0x0008
+#define RL5C46X_MISC_B_LOCK 0x0010
+#define RL5C46X_MISC_A_LOCK 0x0020
+#define RL5C46X_MISC_PCI_LOCK 0x0040
+#define RL5C47X_MISC_IFACE_BUSY 0x0004
+#define RL5C47X_MISC_PCI_INT_MASK 0x0018
+#define RL5C47X_MISC_PCI_INT_DIS 0x0020
+#define RL5C47X_MISC_SUBSYS_WR 0x0040
+#define RL5C47X_MISC_SRIRQ_ENA 0x0080
+#define RL5C47X_MISC_5V_DISABLE 0x0400
+#define RL5C47X_MISC_LED_POL 0x0800
+
+/* 16-bit Interface Control Register */
+#define RL5C4XX_16BIT_CTL 0x0084 /* 16 bit */
+#define RL5C4XX_16CTL_IO_TIMING 0x0100
+#define RL5C4XX_16CTL_MEM_TIMING 0x0200
+#define RL5C46X_16CTL_LEVEL_1 0x0010
+#define RL5C46X_16CTL_LEVEL_2 0x0020
+
+/* 16-bit IO and memory timing registers */
+#define RL5C4XX_16BIT_IO_0 0x0088 /* 16 bit */
+#define RL5C4XX_16BIT_MEM_0 0x0088 /* 16 bit */
+#define RL5C4XX_SETUP_MASK 0x0007
+#define RL5C4XX_SETUP_SHIFT 0
+#define RL5C4XX_CMD_MASK 0x01f0
+#define RL5C4XX_CMD_SHIFT 4
+#define RL5C4XX_HOLD_MASK 0x1c00
+#define RL5C4XX_HOLD_SHIFT 10
+
+#endif /* _LINUX_RICOH_H */
diff --git a/drivers/pcmcia/rsrc_mgr.c b/drivers/pcmcia/rsrc_mgr.c
new file mode 100644
index 000000000..52813cb5a
--- /dev/null
+++ b/drivers/pcmcia/rsrc_mgr.c
@@ -0,0 +1,790 @@
+/*======================================================================
+
+ Resource management routines
+
+ rsrc_mgr.c 1.71 1999/09/15 15:32:19
+
+ 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 David A. Hinds
+ <dhinds@hyper.stanford.edu>. Portions created by David A. Hinds
+ are Copyright (C) 1999 David A. Hinds. 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.
+
+======================================================================*/
+
+#define __NO_VERSION__
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/sched.h>
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/types.h>
+#include <linux/malloc.h>
+#include <linux/ioport.h>
+#include <linux/timer.h>
+#include <asm/irq.h>
+#include <asm/io.h>
+
+#include <pcmcia/cs_types.h>
+#include <pcmcia/ss.h>
+#include <pcmcia/cs.h>
+#include <pcmcia/bulkmem.h>
+#include <pcmcia/cistpl.h>
+#include "cs_internal.h"
+#include "rsrc_mgr.h"
+
+/*====================================================================*/
+
+/* Parameters that can be set with 'insmod' */
+
+/* Should we probe resources for conflicts? */
+static int probe_mem = 1;
+MODULE_PARM(probe_mem, "i");
+#ifdef CONFIG_ISA
+static int probe_io = 1;
+static int mem_limit = 0x10000;
+MODULE_PARM(probe_io, "i");
+MODULE_PARM(mem_limit, "i");
+#endif
+
+/*======================================================================
+
+ The resource_map_t structures are used to track what resources are
+ available for allocation for PC Card devices.
+
+======================================================================*/
+
+typedef struct resource_map_t {
+ u_long base, num;
+ struct resource_map_t *next;
+} resource_map_t;
+
+/* Memory resource database */
+static resource_map_t mem_db = { 0, 0, &mem_db };
+
+/* IO port resource database */
+static resource_map_t io_db = { 0, 0, &io_db };
+
+#ifdef CONFIG_ISA
+
+typedef struct irq_info_t {
+ u_int Attributes;
+ int time_share, dyn_share;
+ struct socket_info_t *Socket;
+} irq_info_t;
+
+/* Table of IRQ assignments */
+static irq_info_t irq_table[NR_IRQS] = { { 0, 0, 0 }, /* etc */ };
+
+#endif
+
+static spinlock_t rsrc_lock = SPIN_LOCK_UNLOCKED;
+
+/*======================================================================
+
+ Linux resource management extensions
+
+======================================================================*/
+
+typedef struct resource_entry_t {
+ u_long base, num;
+ char *name;
+ struct resource_entry_t *next;
+} resource_entry_t;
+
+/* Ordered linked lists of allocated IO and memory blocks */
+static resource_entry_t io_list = { 0, 0, NULL, NULL };
+
+static resource_entry_t *find_gap(resource_entry_t *root,
+ resource_entry_t *entry)
+{
+ resource_entry_t *p;
+
+ if (entry->base > entry->base+entry->num-1)
+ return NULL;
+ for (p = root; ; p = p->next) {
+ if ((p != root) && (p->base+p->num-1 >= entry->base)) {
+ p = NULL;
+ break;
+ }
+ if ((p->next == NULL) ||
+ (p->next->base > entry->base+entry->num-1))
+ break;
+ }
+ return p;
+}
+
+static int register_my_resource(resource_entry_t *list,
+ u_long base, u_long num, char *name)
+{
+ u_long flags;
+ resource_entry_t *p, *entry;
+
+ entry = kmalloc(sizeof(resource_entry_t), GFP_ATOMIC);
+ entry->base = base;
+ entry->num = num;
+ entry->name = name;
+
+ spin_lock_irqsave(&rsrc_lock, flags);
+ p = find_gap(list, entry);
+ if (p == NULL) {
+ spin_unlock_irqrestore(&rsrc_lock, flags);
+ kfree(entry);
+ return -EBUSY;
+ }
+ entry->next = p->next;
+ p->next = entry;
+ spin_unlock_irqrestore(&rsrc_lock, flags);
+ return 0;
+}
+
+static void release_my_resource(resource_entry_t *list,
+ u_long base, u_long num)
+{
+ u_long flags;
+ resource_entry_t *p, *q;
+
+ spin_lock_irqsave(&rsrc_lock, flags);
+ for (p = list; ; p = q) {
+ q = p->next;
+ if (q == NULL) break;
+ if ((q->base == base) && (q->num == num)) {
+ p->next = q->next;
+ kfree(q);
+ spin_unlock_irqrestore(&rsrc_lock, flags);
+ return;
+ }
+ }
+ spin_unlock_irqrestore(&rsrc_lock, flags);
+ return;
+}
+
+static int check_my_resource(resource_entry_t *list,
+ u_long base, u_long num)
+{
+ if (register_my_resource(list, base, num, NULL) != 0)
+ return -EBUSY;
+ release_my_resource(list, base, num);
+ return 0;
+}
+
+int check_io_region(u_long base, u_long num)
+{
+ return check_my_resource(&io_list, base, num);
+}
+void request_io_region(u_long base, u_long num, char *name)
+{
+ register_my_resource(&io_list, base, num, name);
+}
+void release_io_region(u_long base, u_long num)
+{
+ release_my_resource(&io_list, base, num);
+}
+#ifdef CONFIG_PROC_FS
+int proc_read_io(char *buf, char **start, off_t pos,
+ int count, int *eof, void *data)
+{
+ resource_entry_t *r;
+ u_long flags;
+ char *p = buf;
+
+ spin_lock_irqsave(&rsrc_lock, flags);
+ for (r = io_list.next; r; r = r->next)
+ p += sprintf(p, "%04lx-%04lx : %s\n", r->base,
+ r->base+r->num-1, r->name);
+ spin_unlock_irqrestore(&rsrc_lock, flags);
+ return (p - buf);
+}
+#endif
+
+/*======================================================================
+
+ These manage the internal databases of available resources.
+
+======================================================================*/
+
+static int add_interval(resource_map_t *map, u_long base, u_long num)
+{
+ resource_map_t *p, *q;
+
+ for (p = map; ; p = p->next) {
+ if ((p != map) && (p->base+p->num-1 >= base))
+ return -1;
+ if ((p->next == map) || (p->next->base > base+num-1))
+ break;
+ }
+ q = kmalloc(sizeof(resource_map_t), GFP_KERNEL);
+ q->base = base; q->num = num;
+ q->next = p->next; p->next = q;
+ return 0;
+} /* add_interval */
+
+/*====================================================================*/
+
+static int sub_interval(resource_map_t *map, u_long base, u_long num)
+{
+ resource_map_t *p, *q;
+
+ for (p = map; ; p = q) {
+ q = p->next;
+ if (q == map)
+ break;
+ if ((q->base+q->num > base) && (base+num > q->base)) {
+ if (q->base >= base) {
+ if (q->base+q->num <= base+num) {
+ /* Delete whole block */
+ p->next = q->next;
+ kfree(q);
+ /* don't advance the pointer yet */
+ q = p;
+ } else {
+ /* Cut off bit from the front */
+ q->num = q->base + q->num - base - num;
+ q->base = base + num;
+ }
+ } else if (q->base+q->num <= base+num) {
+ /* Cut off bit from the end */
+ q->num = base - q->base;
+ } else {
+ /* Split the block into two pieces */
+ p = kmalloc(sizeof(resource_map_t), GFP_KERNEL);
+ p->base = base+num;
+ p->num = q->base+q->num - p->base;
+ q->num = base - q->base;
+ p->next = q->next ; q->next = p;
+ }
+ }
+ }
+ return 0;
+} /* sub_interval */
+
+/*======================================================================
+
+ These routines examine a region of IO or memory addresses to
+ determine what ranges might be genuinely available.
+
+======================================================================*/
+
+#ifdef CONFIG_ISA
+static void do_io_probe(ioaddr_t base, ioaddr_t num)
+{
+
+ ioaddr_t i, j, bad, any;
+ u_char *b, hole, most;
+
+ printk(KERN_INFO "cs: IO port probe 0x%04x-0x%04x:",
+ base, base+num-1);
+
+ /* First, what does a floating port look like? */
+ b = kmalloc(256, GFP_KERNEL);
+ memset(b, 0, 256);
+ for (i = base, most = 0; i < base+num; i += 8) {
+ if (check_region(i, 8) || check_io_region(i, 8))
+ continue;
+ hole = inb(i);
+ for (j = 1; j < 8; j++)
+ if (inb(i+j) != hole) break;
+ if ((j == 8) && (++b[hole] > b[most]))
+ most = hole;
+ if (b[most] == 127) break;
+ }
+ kfree(b);
+
+ bad = any = 0;
+ for (i = base; i < base+num; i += 8) {
+ if (check_region(i, 8) || check_io_region(i, 8))
+ continue;
+ for (j = 0; j < 8; j++)
+ if (inb(i+j) != most) break;
+ if (j < 8) {
+ if (!any)
+ printk(" excluding");
+ if (!bad)
+ bad = any = i;
+ } else {
+ if (bad) {
+ sub_interval(&io_db, bad, i-bad);
+ printk(" %#04x-%#04x", bad, i-1);
+ bad = 0;
+ }
+ }
+ }
+ if (bad) {
+ if ((num > 16) && (bad == base) && (i == base+num)) {
+ printk(" nothing: probe failed.\n");
+ return;
+ } else {
+ sub_interval(&io_db, bad, i-bad);
+ printk(" %#04x-%#04x", bad, i-1);
+ }
+ }
+
+ printk(any ? "\n" : " clean.\n");
+}
+#endif
+
+/*======================================================================
+
+ The memory probe. If the memory list includes a 64K-aligned block
+ below 1MB, we probe in 64K chunks, and as soon as we accumulate at
+ least mem_limit free space, we quit.
+
+======================================================================*/
+
+static int do_mem_probe(u_long base, u_long num,
+ int (*is_valid)(u_long), int (*do_cksum)(u_long))
+{
+ u_long i, j, bad, fail, step;
+
+ printk(KERN_INFO "cs: memory probe 0x%06lx-0x%06lx:",
+ base, base+num-1);
+ bad = fail = 0;
+ step = (num < 0x20000) ? 0x2000 : ((num>>4) & ~0x1fff);
+ for (i = base; i < base+num; i = j + step) {
+ if (!fail) {
+ for (j = i; j < base+num; j += step)
+ if ((check_mem_region(j, step) == 0) && is_valid(j))
+ break;
+ fail = ((i == base) && (j == base+num));
+ }
+ if (fail) {
+ for (j = i; j < base+num; j += 2*step)
+ if ((check_mem_region(j, 2*step) == 0) &&
+ do_cksum(j) && do_cksum(j+step))
+ break;
+ }
+ if (i != j) {
+ if (!bad) printk(" excluding");
+ printk(" %#05lx-%#05lx", i, j-1);
+ sub_interval(&mem_db, i, j-i);
+ bad += j-i;
+ }
+ }
+ printk(bad ? "\n" : " clean.\n");
+ return (num - bad);
+}
+
+#ifdef CONFIG_ISA
+
+static u_long inv_probe(int (*is_valid)(u_long),
+ int (*do_cksum)(u_long),
+ resource_map_t *m)
+{
+ u_long ok;
+ if (m == &mem_db)
+ return 0;
+ ok = inv_probe(is_valid, do_cksum, m->next);
+ if (ok) {
+ if (m->base >= 0x100000)
+ sub_interval(&mem_db, m->base, m->num);
+ return ok;
+ }
+ if (m->base < 0x100000)
+ return 0;
+ return do_mem_probe(m->base, m->num, is_valid, do_cksum);
+}
+
+void validate_mem(int (*is_valid)(u_long), int (*do_cksum)(u_long),
+ int force_low)
+{
+ resource_map_t *m, *n;
+ static u_char order[] = { 0xd0, 0xe0, 0xc0, 0xf0 };
+ static int hi = 0, lo = 0;
+ u_long b, i, ok = 0;
+
+ if (!probe_mem) return;
+ /* We do up to four passes through the list */
+ if (!force_low) {
+ if (hi++ || (inv_probe(is_valid, do_cksum, mem_db.next) > 0))
+ return;
+ printk(KERN_NOTICE "cs: warning: no high memory space "
+ "available!\n");
+ }
+ if (lo++) return;
+ for (m = mem_db.next; m != &mem_db; m = n) {
+ n = m->next;
+ /* Only probe < 1 MB */
+ if (m->base >= 0x100000) continue;
+ if ((m->base | m->num) & 0xffff) {
+ ok += do_mem_probe(m->base, m->num, is_valid, do_cksum);
+ continue;
+ }
+ /* Special probe for 64K-aligned block */
+ for (i = 0; i < 4; i++) {
+ b = order[i] << 12;
+ if ((b >= m->base) && (b+0x10000 <= m->base+m->num)) {
+ if (ok >= mem_limit)
+ sub_interval(&mem_db, b, 0x10000);
+ else
+ ok += do_mem_probe(b, 0x10000, is_valid, do_cksum);
+ }
+ }
+ }
+}
+
+#else /* CONFIG_ISA */
+
+void validate_mem(int (*is_valid)(u_long), int (*do_cksum)(u_long),
+ int force_low)
+{
+ resource_map_t *m;
+ static int done = 0;
+
+ if (!probe_mem || done++)
+ return;
+ for (m = mem_db.next; m != &mem_db; m = m->next)
+ if (do_mem_probe(m->base, m->num, is_valid, do_cksum))
+ return;
+}
+
+#endif /* CONFIG_ISA */
+
+/*======================================================================
+
+ These find ranges of I/O ports or memory addresses that are not
+ currently allocated by other devices.
+
+======================================================================*/
+
+int find_io_region(ioaddr_t *base, ioaddr_t num, char *name)
+{
+ ioaddr_t align;
+ resource_map_t *m;
+
+ if (*base != 0) {
+ for (m = io_db.next; m != &io_db; m = m->next) {
+ if ((*base >= m->base) && (*base+num <= m->base+m->num)) {
+ if (check_region(*base, num) ||
+ check_io_region(*base, num)) {
+ return -1;
+ } else {
+ request_region(*base, num, name);
+ return 0;
+ }
+ }
+ }
+ return -1;
+ }
+
+ for (align = 1; align < num; align *= 2) ;
+ for (m = io_db.next; m != &io_db; m = m->next) {
+ for (*base = (m->base + align - 1) & (~(align-1));
+ *base+align <= m->base + m->num;
+ *base += align)
+ if ((check_region(*base, num) == 0) &&
+ (check_io_region(*base, num) == 0)) {
+ request_region(*base, num, name);
+ return 0;
+ }
+ }
+ return -1;
+} /* find_io_region */
+
+int find_mem_region(u_long *base, u_long num, char *name,
+ u_long align, int force_low)
+{
+ resource_map_t *m;
+
+ if (*base != 0) {
+ for (m = mem_db.next; m != &mem_db; m = m->next) {
+ if ((*base >= m->base) && (*base+num <= m->base+m->num))
+ if (check_mem_region(*base, num) == 0) {
+ request_mem_region(*base, num, name);
+ return 0;
+ }
+ }
+ return -1;
+ }
+
+ while (1) {
+ for (m = mem_db.next; m != &mem_db; m = m->next) {
+ /* first pass >1MB, second pass <1MB */
+ if ((force_low != 0) ^ (m->base < 0x100000)) continue;
+ for (*base = (m->base + align - 1) & (~(align-1));
+ *base+num <= m->base+m->num; *base += align)
+ if (check_mem_region(*base, num) == 0) {
+ request_mem_region(*base, num, name);
+ return 0;
+ }
+ }
+ if (force_low) break;
+ force_low++;
+ }
+ return -1;
+} /* find_mem_region */
+
+/*======================================================================
+
+ This checks to see if an interrupt is available, with support
+ for interrupt sharing. We don't support reserving interrupts
+ yet. If the interrupt is available, we allocate it.
+
+======================================================================*/
+
+#ifdef CONFIG_ISA
+
+static void fake_irq(int i, void *d, struct pt_regs *r) { }
+static inline int check_irq(int irq)
+{
+ if (request_irq(irq, fake_irq, 0, "bogus", NULL) != 0)
+ return -1;
+ free_irq(irq, NULL);
+ return 0;
+}
+
+int try_irq(u_int Attributes, int irq, int specific)
+{
+ irq_info_t *info = &irq_table[irq];
+ if (info->Attributes & RES_ALLOCATED) {
+ switch (Attributes & IRQ_TYPE) {
+ case IRQ_TYPE_EXCLUSIVE:
+ return CS_IN_USE;
+ case IRQ_TYPE_TIME:
+ if ((info->Attributes & RES_IRQ_TYPE)
+ != RES_IRQ_TYPE_TIME)
+ return CS_IN_USE;
+ if (Attributes & IRQ_FIRST_SHARED)
+ return CS_BAD_ATTRIBUTE;
+ info->Attributes |= RES_IRQ_TYPE_TIME | RES_ALLOCATED;
+ info->time_share++;
+ break;
+ case IRQ_TYPE_DYNAMIC_SHARING:
+ if ((info->Attributes & RES_IRQ_TYPE)
+ != RES_IRQ_TYPE_DYNAMIC)
+ return CS_IN_USE;
+ if (Attributes & IRQ_FIRST_SHARED)
+ return CS_BAD_ATTRIBUTE;
+ info->Attributes |= RES_IRQ_TYPE_DYNAMIC | RES_ALLOCATED;
+ info->dyn_share++;
+ break;
+ }
+ } else {
+ if ((info->Attributes & RES_RESERVED) && !specific)
+ return CS_IN_USE;
+ if (check_irq(irq) != 0)
+ return CS_IN_USE;
+ switch (Attributes & IRQ_TYPE) {
+ case IRQ_TYPE_EXCLUSIVE:
+ info->Attributes |= RES_ALLOCATED;
+ break;
+ case IRQ_TYPE_TIME:
+ if (!(Attributes & IRQ_FIRST_SHARED))
+ return CS_BAD_ATTRIBUTE;
+ info->Attributes |= RES_IRQ_TYPE_TIME | RES_ALLOCATED;
+ info->time_share = 1;
+ break;
+ case IRQ_TYPE_DYNAMIC_SHARING:
+ if (!(Attributes & IRQ_FIRST_SHARED))
+ return CS_BAD_ATTRIBUTE;
+ info->Attributes |= RES_IRQ_TYPE_DYNAMIC | RES_ALLOCATED;
+ info->dyn_share = 1;
+ break;
+ }
+ }
+ return 0;
+} /* try_irq */
+
+#endif
+
+/*====================================================================*/
+
+#ifdef CONFIG_ISA
+
+void undo_irq(u_int Attributes, int irq)
+{
+ irq_info_t *info;
+
+ info = &irq_table[irq];
+ switch (Attributes & IRQ_TYPE) {
+ case IRQ_TYPE_EXCLUSIVE:
+ info->Attributes &= RES_RESERVED;
+ break;
+ case IRQ_TYPE_TIME:
+ info->time_share--;
+ if (info->time_share == 0)
+ info->Attributes &= RES_RESERVED;
+ break;
+ case IRQ_TYPE_DYNAMIC_SHARING:
+ info->dyn_share--;
+ if (info->dyn_share == 0)
+ info->Attributes &= RES_RESERVED;
+ break;
+ }
+}
+
+#endif
+
+/*======================================================================
+
+ The various adjust_* calls form the external interface to the
+ resource database.
+
+======================================================================*/
+
+static int adjust_memory(adjust_t *adj)
+{
+ u_long base, num;
+ int i;
+
+ base = adj->resource.memory.Base;
+ num = adj->resource.memory.Size;
+ if ((num == 0) || (base+num-1 < base))
+ return CS_BAD_SIZE;
+
+ switch (adj->Action) {
+ case ADD_MANAGED_RESOURCE:
+ if (add_interval(&mem_db, base, num) != 0)
+ return CS_IN_USE;
+ break;
+ case REMOVE_MANAGED_RESOURCE:
+ sub_interval(&mem_db, base, num);
+ for (i = 0; i < sockets; i++) {
+ release_cis_mem(socket_table[i]);
+#ifdef CONFIG_CARDBUS
+ cb_release_cis_mem(socket_table[i]);
+#endif
+ }
+ break;
+ default:
+ return CS_UNSUPPORTED_FUNCTION;
+ break;
+ }
+
+ return CS_SUCCESS;
+} /* adjust_mem */
+
+/*====================================================================*/
+
+static int adjust_io(adjust_t *adj)
+{
+ int base, num;
+
+ base = adj->resource.io.BasePort;
+ num = adj->resource.io.NumPorts;
+ if ((base < 0) || (base > 0xffff))
+ return CS_BAD_BASE;
+ if ((num <= 0) || (base+num > 0x10000) || (base+num <= base))
+ return CS_BAD_SIZE;
+
+ switch (adj->Action) {
+ case ADD_MANAGED_RESOURCE:
+ if (add_interval(&io_db, base, num) != 0)
+ return CS_IN_USE;
+#ifdef CONFIG_ISA
+ if (probe_io)
+ do_io_probe(base, num);
+#endif
+ break;
+ case REMOVE_MANAGED_RESOURCE:
+ sub_interval(&io_db, base, num);
+ break;
+ default:
+ return CS_UNSUPPORTED_FUNCTION;
+ break;
+ }
+
+ return CS_SUCCESS;
+} /* adjust_io */
+
+/*====================================================================*/
+
+static int adjust_irq(adjust_t *adj)
+{
+#ifdef CONFIG_ISA
+ int irq;
+ irq_info_t *info;
+
+ irq = adj->resource.irq.IRQ;
+ if ((irq < 0) || (irq > 15))
+ return CS_BAD_IRQ;
+ info = &irq_table[irq];
+
+ switch (adj->Action) {
+ case ADD_MANAGED_RESOURCE:
+ if (info->Attributes & RES_REMOVED)
+ info->Attributes &= ~(RES_REMOVED|RES_ALLOCATED);
+ else
+ if (adj->Attributes & RES_ALLOCATED)
+ return CS_IN_USE;
+ if (adj->Attributes & RES_RESERVED)
+ info->Attributes |= RES_RESERVED;
+ else
+ info->Attributes &= ~RES_RESERVED;
+ break;
+ case REMOVE_MANAGED_RESOURCE:
+ if (info->Attributes & RES_REMOVED)
+ return 0;
+ if (info->Attributes & RES_ALLOCATED)
+ return CS_IN_USE;
+ info->Attributes |= RES_ALLOCATED|RES_REMOVED;
+ info->Attributes &= ~RES_RESERVED;
+ break;
+ default:
+ return CS_UNSUPPORTED_FUNCTION;
+ break;
+ }
+#endif
+ return CS_SUCCESS;
+} /* adjust_irq */
+
+/*====================================================================*/
+
+int adjust_resource_info(client_handle_t handle, adjust_t *adj)
+{
+ if (CHECK_HANDLE(handle))
+ return CS_BAD_HANDLE;
+
+ switch (adj->Resource) {
+ case RES_MEMORY_RANGE:
+ return adjust_memory(adj);
+ break;
+ case RES_IO_RANGE:
+ return adjust_io(adj);
+ break;
+ case RES_IRQ:
+ return adjust_irq(adj);
+ break;
+ }
+ return CS_UNSUPPORTED_FUNCTION;
+} /* adjust_resource_info */
+
+/*====================================================================*/
+
+void release_resource_db(void)
+{
+ resource_map_t *p, *q;
+ resource_entry_t *u, *v;
+
+ for (p = mem_db.next; p != &mem_db; p = q) {
+ q = p->next;
+ kfree(p);
+ }
+ for (p = io_db.next; p != &io_db; p = q) {
+ q = p->next;
+ kfree(p);
+ }
+ for (u = io_list.next; u; u = v) {
+ v = u->next;
+ kfree(u);
+ }
+}
diff --git a/drivers/pcmcia/rsrc_mgr.h b/drivers/pcmcia/rsrc_mgr.h
new file mode 100644
index 000000000..3b7f12e09
--- /dev/null
+++ b/drivers/pcmcia/rsrc_mgr.h
@@ -0,0 +1,39 @@
+/*
+ * rsrc_mgr.h 1.18 1999/08/28 04:01:47
+ *
+ * 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 David A. Hinds
+ * <dhinds@hyper.stanford.edu>. Portions created by David A. Hinds
+ * are Copyright (C) 1999 David A. Hinds. 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.
+ */
+
+#ifndef _RSRC_MGR_H
+#define _RSRC_MGR_H
+
+#ifdef __BEOS__
+int check_resource(int type, u_long base, u_long num);
+int register_resource(int type, u_long base, u_long num);
+int release_resource(int type, u_long base, u_long num);
+#endif
+
+#endif /* _RSRC_MGR_H */
diff --git a/drivers/pcmcia/smc34c90.h b/drivers/pcmcia/smc34c90.h
new file mode 100644
index 000000000..8ac3854a4
--- /dev/null
+++ b/drivers/pcmcia/smc34c90.h
@@ -0,0 +1,52 @@
+/*
+ * smc34c90.h 1.6 1999/08/28 04:01:47
+ *
+ * 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 David A. Hinds
+ * <dhinds@hyper.stanford.edu>. Portions created by David A. Hinds
+ * are Copyright (C) 1999 David A. Hinds. 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.
+ */
+
+#ifndef _LINUX_SMC34C90_H
+#define _LINUX_SMC34C90_H
+
+#ifndef PCI_VENDOR_ID_SMC
+#define PCI_VENDOR_ID_SMC 0x10b3
+#endif
+
+#ifndef PCI_DEVICE_ID_SMC_34C90
+#define PCI_DEVICE_ID_SMC_34C90 0xb106
+#endif
+
+/* Register definitions for SMC 34C90 PCI-to-CardBus bridge */
+
+/* EEPROM Information Register */
+#define SMC34C90_EEINFO 0x0088
+#define SMC34C90_EEINFO_ONE_SOCKET 0x0001
+#define SMC34C90_EEINFO_5V_ONLY 0x0002
+#define SMC34C90_EEINFO_ISA_IRQ 0x0004
+#define SMC34C90_EEINFO_ZV_PORT 0x0008
+#define SMC34C90_EEINFO_RING 0x0010
+#define SMC34C90_EEINFO_LED 0x0020
+
+#endif /* _LINUX_SMC34C90_H */
diff --git a/drivers/pcmcia/tcic.c b/drivers/pcmcia/tcic.c
new file mode 100644
index 000000000..ca2818d7c
--- /dev/null
+++ b/drivers/pcmcia/tcic.c
@@ -0,0 +1,966 @@
+/*======================================================================
+
+ Device driver for Databook TCIC-2 PCMCIA controller
+
+ tcic.c 1.106 1999/09/15 15:32:19
+
+ 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 David A. Hinds
+ <dhinds@hyper.stanford.edu>. Portions created by David A. Hinds
+ are Copyright (C) 1999 David A. Hinds. 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/types.h>
+#include <linux/fcntl.h>
+#include <linux/string.h>
+
+#include <asm/io.h>
+#include <asm/bitops.h>
+#include <asm/segment.h>
+#include <asm/system.h>
+
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/sched.h>
+#include <linux/malloc.h>
+#include <linux/timer.h>
+#include <linux/ioport.h>
+#include <linux/delay.h>
+
+#include <pcmcia/version.h>
+#include <pcmcia/cs_types.h>
+#include <pcmcia/cs.h>
+#include <pcmcia/ss.h>
+#include "tcic.h"
+
+#ifdef PCMCIA_DEBUG
+static int pc_debug = PCMCIA_DEBUG;
+MODULE_PARM(pc_debug, "i");
+static const char *version =
+"tcic.c 1.106 1999/09/15 15:32:19 (David Hinds)";
+#define DEBUG(n, args...) if (pc_debug>(n)) printk(KERN_DEBUG args)
+#else
+#define DEBUG(n, args...)
+#endif
+
+/*====================================================================*/
+
+/* Parameters that can be set with 'insmod' */
+
+/* The base port address of the TCIC-2 chip */
+static int tcic_base = TCIC_BASE;
+
+/* Specify a socket number to ignore */
+static int ignore = -1;
+
+/* Probe for safe interrupts? */
+static int do_scan = 1;
+
+/* Bit map of interrupts to choose from */
+static u_int irq_mask = 0xffff;
+static int irq_list[16] = { -1 };
+
+/* The card status change interrupt -- 0 means autoselect */
+static int cs_irq = 0;
+
+/* Poll status interval -- 0 means default to interrupt */
+static int poll_interval = 0;
+
+/* Delay for card status double-checking */
+static int poll_quick = HZ/20;
+
+/* CCLK external clock time, in nanoseconds. 70 ns = 14.31818 MHz */
+static int cycle_time = 70;
+
+MODULE_PARM(tcic_base, "i");
+MODULE_PARM(ignore, "i");
+MODULE_PARM(do_scan, "i");
+MODULE_PARM(irq_mask, "i");
+MODULE_PARM(irq_list, "1-16i");
+MODULE_PARM(cs_irq, "i");
+MODULE_PARM(poll_interval, "i");
+MODULE_PARM(poll_quick, "i");
+MODULE_PARM(cycle_time, "i");
+
+/*====================================================================*/
+
+static void tcic_interrupt(int irq, void *dev, struct pt_regs *regs);
+static void tcic_timer(u_long data);
+static int tcic_service(u_int sock, u_int cmd, void *arg);
+
+typedef struct socket_info_t {
+ u_short psock;
+ void (*handler)(void *info, u_int events);
+ void *info;
+ u_char last_sstat;
+ u_char id;
+} socket_info_t;
+
+static struct timer_list poll_timer;
+static int tcic_timer_pending = 0;
+
+static int sockets;
+static socket_info_t socket_table[2];
+
+static socket_cap_t tcic_cap = {
+ /* only 16-bit cards, memory windows must be size-aligned */
+ SS_CAP_PCCARD | SS_CAP_MEM_ALIGN,
+ 0x4cf8, /* irq 14, 11, 10, 7, 6, 5, 4, 3 */
+ 0x1000, /* 4K minimum window size */
+ 0, 0 /* No PCI or CardBus support */
+};
+
+/*====================================================================*/
+
+/* Trick when selecting interrupts: the TCIC sktirq pin is supposed
+ to map to irq 11, but is coded as 0 or 1 in the irq registers. */
+#define TCIC_IRQ(x) ((x) ? (((x) == 11) ? 1 : (x)) : 15)
+
+#ifdef PCMCIA_DEBUG_X
+static u_char tcic_getb(u_char reg)
+{
+ u_char val = inb(tcic_base+reg);
+ printk(KERN_DEBUG "tcic_getb(%#x) = %#x\n", tcic_base+reg, val);
+ return val;
+}
+
+static u_short tcic_getw(u_char reg)
+{
+ u_short val = inw(tcic_base+reg);
+ printk(KERN_DEBUG "tcic_getw(%#x) = %#x\n", tcic_base+reg, val);
+ return val;
+}
+
+static void tcic_setb(u_char reg, u_char data)
+{
+ printk(KERN_DEBUG "tcic_setb(%#x, %#x)\n", tcic_base+reg, data);
+ outb(data, tcic_base+reg);
+}
+
+static void tcic_setw(u_char reg, u_short data)
+{
+ printk(KERN_DEBUG "tcic_setw(%#x, %#x)\n", tcic_base+reg, data);
+ outw(data, tcic_base+reg);
+}
+#else
+#define tcic_getb(reg) inb(tcic_base+reg)
+#define tcic_getw(reg) inw(tcic_base+reg)
+#define tcic_setb(reg, data) outb(data, tcic_base+reg)
+#define tcic_setw(reg, data) outw(data, tcic_base+reg)
+#endif
+
+static void tcic_setl(u_char reg, u_int data)
+{
+#ifdef PCMCIA_DEBUG_X
+ printk(KERN_DEBUG "tcic_setl(%#x, %#lx)\n", tcic_base+reg, data);
+#endif
+ outw(data & 0xffff, tcic_base+reg);
+ outw(data >> 16, tcic_base+reg+2);
+}
+
+static u_char tcic_aux_getb(u_short reg)
+{
+ u_char mode = (tcic_getb(TCIC_MODE) & TCIC_MODE_PGMMASK) | reg;
+ tcic_setb(TCIC_MODE, mode);
+ return tcic_getb(TCIC_AUX);
+}
+
+static void tcic_aux_setb(u_short reg, u_char data)
+{
+ u_char mode = (tcic_getb(TCIC_MODE) & TCIC_MODE_PGMMASK) | reg;
+ tcic_setb(TCIC_MODE, mode);
+ tcic_setb(TCIC_AUX, data);
+}
+
+static u_short tcic_aux_getw(u_short reg)
+{
+ u_char mode = (tcic_getb(TCIC_MODE) & TCIC_MODE_PGMMASK) | reg;
+ tcic_setb(TCIC_MODE, mode);
+ return tcic_getw(TCIC_AUX);
+}
+
+static void tcic_aux_setw(u_short reg, u_short data)
+{
+ u_char mode = (tcic_getb(TCIC_MODE) & TCIC_MODE_PGMMASK) | reg;
+ tcic_setb(TCIC_MODE, mode);
+ tcic_setw(TCIC_AUX, data);
+}
+
+/*====================================================================*/
+
+/* Time conversion functions */
+
+static int to_cycles(int ns)
+{
+ if (ns < 14)
+ return 0;
+ else
+ return 2*(ns-14)/cycle_time;
+}
+
+static int to_ns(int cycles)
+{
+ return (cycles*cycle_time)/2 + 14;
+}
+
+/*====================================================================*/
+
+static volatile u_int irq_hits;
+
+static void __init irq_count(int irq, void *dev, struct pt_regs *regs)
+{
+ irq_hits++;
+}
+
+static u_int __init try_irq(int irq)
+{
+ u_short cfg;
+
+ irq_hits = 0;
+ if (request_irq(irq, irq_count, 0, "irq scan", NULL) != 0)
+ return -1;
+ mdelay(10);
+ if (irq_hits) {
+ free_irq(irq, NULL);
+ return -1;
+ }
+
+ /* Generate one interrupt */
+ cfg = TCIC_SYSCFG_AUTOBUSY | 0x0a00;
+ tcic_aux_setw(TCIC_AUX_SYSCFG, cfg | TCIC_IRQ(irq));
+ tcic_setb(TCIC_IENA, TCIC_IENA_ERR | TCIC_IENA_CFG_HIGH);
+ tcic_setb(TCIC_ICSR, TCIC_ICSR_ERR | TCIC_ICSR_JAM);
+
+ udelay(1000);
+ free_irq(irq, NULL);
+
+ /* Turn off interrupts */
+ tcic_setb(TCIC_IENA, TCIC_IENA_CFG_OFF);
+ while (tcic_getb(TCIC_ICSR))
+ tcic_setb(TCIC_ICSR, TCIC_ICSR_JAM);
+ tcic_aux_setw(TCIC_AUX_SYSCFG, cfg);
+
+ return (irq_hits != 1);
+}
+
+static u_int __init irq_scan(u_int mask0)
+{
+ u_int mask1;
+ int i;
+
+#ifdef __alpha__
+#define PIC 0x4d0
+ /* Don't probe level-triggered interrupts -- reserved for PCI */
+ int level_mask = inb_p(PIC) | (inb_p(PIC+1) << 8);
+ if (level_mask)
+ mask0 &= ~level_mask;
+#endif
+
+ mask1 = 0;
+ if (do_scan) {
+ for (i = 0; i < 16; i++)
+ if ((mask0 & (1 << i)) && (try_irq(i) == 0))
+ mask1 |= (1 << i);
+ for (i = 0; i < 16; i++)
+ if ((mask1 & (1 << i)) && (try_irq(i) != 0)) {
+ mask1 ^= (1 << i);
+ }
+ }
+
+ if (mask1) {
+ printk("scanned");
+ } else {
+ /* Fallback: just find interrupts that aren't in use */
+ for (i = 0; i < 16; i++)
+ if ((mask0 & (1 << i)) &&
+ (request_irq(i, irq_count, 0, "x", NULL) == 0)) {
+ mask1 |= (1 << i);
+ free_irq(i, NULL);
+ }
+ printk("default");
+ }
+
+ printk(") = ");
+ for (i = 0; i < 16; i++)
+ if (mask1 & (1<<i))
+ printk("%s%d", ((mask1 & ((1<<i)-1)) ? "," : ""), i);
+ printk(" ");
+
+ return mask1;
+}
+
+/*======================================================================
+
+ See if a card is present, powered up, in IO mode, and already
+ bound to a (non-PCMCIA) Linux driver.
+
+ We make an exception for cards that look like serial devices.
+
+======================================================================*/
+
+static int __init is_active(int s)
+{
+ u_short scf1, ioctl, base, num;
+ u_char pwr, sstat;
+ u_int addr;
+
+ tcic_setl(TCIC_ADDR, (s << TCIC_ADDR_SS_SHFT)
+ | TCIC_ADDR_INDREG | TCIC_SCF1(s));
+ scf1 = tcic_getw(TCIC_DATA);
+ pwr = tcic_getb(TCIC_PWR);
+ sstat = tcic_getb(TCIC_SSTAT);
+ addr = TCIC_IWIN(s, 0);
+ tcic_setw(TCIC_ADDR, addr + TCIC_IBASE_X);
+ base = tcic_getw(TCIC_DATA);
+ tcic_setw(TCIC_ADDR, addr + TCIC_ICTL_X);
+ ioctl = tcic_getw(TCIC_DATA);
+
+ if (ioctl & TCIC_ICTL_TINY)
+ num = 1;
+ else {
+ num = (base ^ (base-1));
+ base = base & (base-1);
+ }
+
+ if ((sstat & TCIC_SSTAT_CD) && (pwr & TCIC_PWR_VCC(s)) &&
+ (scf1 & TCIC_SCF1_IOSTS) && (ioctl & TCIC_ICTL_ENA) &&
+ (check_region(base, num) != 0) && ((base & 0xfeef) != 0x02e8))
+ return 1;
+ else
+ return 0;
+}
+
+/*======================================================================
+
+ This returns the revision code for the specified socket.
+
+======================================================================*/
+
+static int __init get_tcic_id(void)
+{
+ u_short id;
+
+ tcic_aux_setw(TCIC_AUX_TEST, TCIC_TEST_DIAG);
+ id = tcic_aux_getw(TCIC_AUX_ILOCK);
+ id = (id & TCIC_ILOCKTEST_ID_MASK) >> TCIC_ILOCKTEST_ID_SH;
+ tcic_aux_setw(TCIC_AUX_TEST, 0);
+ return id;
+}
+
+/*====================================================================*/
+
+static int __init init_tcic(void)
+{
+ int i, sock;
+ u_int mask, scan;
+ servinfo_t serv;
+
+ DEBUG(0, "%s\n", version);
+ CardServices(GetCardServicesInfo, &serv);
+ if (serv.Revision != CS_RELEASE_CODE) {
+ printk(KERN_NOTICE "tcic: Card Services release "
+ "does not match!\n");
+ return -1;
+ }
+
+ printk(KERN_INFO "Databook TCIC-2 PCMCIA probe: ");
+ sock = 0;
+
+ if (check_region(tcic_base, 16) == 0) {
+ tcic_setw(TCIC_ADDR, 0);
+ if (tcic_getw(TCIC_ADDR) == 0) {
+ tcic_setw(TCIC_ADDR, 0xc3a5);
+ if (tcic_getw(TCIC_ADDR) == 0xc3a5) sock = 2;
+ }
+ if (sock == 0) {
+ /* See if resetting the controller does any good */
+ tcic_setb(TCIC_SCTRL, TCIC_SCTRL_RESET);
+ tcic_setb(TCIC_SCTRL, 0);
+ tcic_setw(TCIC_ADDR, 0);
+ if (tcic_getw(TCIC_ADDR) == 0) {
+ tcic_setw(TCIC_ADDR, 0xc3a5);
+ if (tcic_getw(TCIC_ADDR) == 0xc3a5) sock = 2;
+ }
+ }
+ } else
+ printk("could not allocate ports, ");
+
+ if (sock == 0) {
+ printk("not found.\n");
+ return -ENODEV;
+ }
+
+ request_region(tcic_base, 16, "tcic-2");
+
+ sockets = 0;
+ for (i = 0; i < sock; i++) {
+ if ((i == ignore) || is_active(i)) continue;
+ socket_table[sockets].psock = i;
+ socket_table[sockets].handler = NULL;
+ socket_table[sockets].info = NULL;
+ socket_table[sockets].id = get_tcic_id();
+ sockets++;
+ }
+
+ switch (socket_table[0].id) {
+ case TCIC_ID_DB86082:
+ printk("DB86082"); break;
+ case TCIC_ID_DB86082A:
+ printk("DB86082A"); break;
+ case TCIC_ID_DB86084:
+ printk("DB86084"); break;
+ case TCIC_ID_DB86084A:
+ printk("DB86084A"); break;
+ case TCIC_ID_DB86072:
+ printk("DB86072"); break;
+ case TCIC_ID_DB86184:
+ printk("DB86184"); break;
+ case TCIC_ID_DB86082B:
+ printk("DB86082B"); break;
+ default:
+ printk("Unknown ID 0x%02x", socket_table[0].id);
+ }
+
+ /* Set up polling */
+ poll_timer.function = &tcic_timer;
+ poll_timer.data = 0;
+ poll_timer.prev = poll_timer.next = NULL;
+
+ /* Build interrupt mask */
+ printk(", %d sockets\n" KERN_INFO " irq list (", sockets);
+ if (irq_list[0] == -1)
+ mask = irq_mask;
+ else
+ for (i = mask = 0; i < 16; i++)
+ mask |= (1<<irq_list[i]);
+ mask &= tcic_cap.irq_mask;
+
+ /* Scan interrupts */
+ mask = irq_scan(mask);
+ tcic_cap.irq_mask = mask;
+
+ /* Check for only two interrupts available */
+ scan = (mask & (mask-1));
+ if (((scan & (scan-1)) == 0) && (poll_interval == 0))
+ poll_interval = HZ;
+
+ if (poll_interval == 0) {
+ /* Avoid irq 12 unless it is explicitly requested */
+ u_int cs_mask = mask & ((cs_irq) ? (1<<cs_irq) : ~(1<<12));
+ for (i = 15; i > 0; i--)
+ if ((cs_mask & (1 << i)) &&
+ (request_irq(i, tcic_interrupt, 0, "tcic", NULL) == 0))
+ break;
+ cs_irq = i;
+ if (cs_irq == 0) poll_interval = HZ;
+ }
+
+ if (tcic_cap.irq_mask & (1 << 11))
+ printk("sktirq is irq 11, ");
+ if (cs_irq != 0)
+ printk("status change on irq %d\n", cs_irq);
+ else
+ printk("polled status, interval = %d ms\n",
+ poll_interval * 1000 / HZ);
+
+ for (i = 0; i < sockets; i++) {
+ tcic_setw(TCIC_ADDR+2, socket_table[i].psock << TCIC_SS_SHFT);
+ socket_table[i].last_sstat = tcic_getb(TCIC_SSTAT);
+ }
+
+ /* jump start interrupt handler, if needed */
+ tcic_interrupt(0, NULL, NULL);
+
+ if (register_ss_entry(sockets, &tcic_service) != 0) {
+ printk(KERN_NOTICE "tcic: register_ss_entry() failed\n");
+ release_region(tcic_base, 16);
+ if (cs_irq != 0)
+ free_irq(cs_irq, NULL);
+ return -ENODEV;
+ }
+
+ return 0;
+
+} /* init_tcic */
+
+/*====================================================================*/
+
+static void __exit exit_tcic(void)
+{
+ u_long flags;
+ unregister_ss_entry(&tcic_service);
+ save_flags(flags);
+ cli();
+ if (cs_irq != 0) {
+ tcic_aux_setw(TCIC_AUX_SYSCFG, TCIC_SYSCFG_AUTOBUSY|0x0a00);
+ free_irq(cs_irq, NULL);
+ }
+ if (tcic_timer_pending)
+ del_timer(&poll_timer);
+ restore_flags(flags);
+ release_region(tcic_base, 16);
+} /* exit_tcic */
+
+/*====================================================================*/
+
+static void tcic_interrupt(int irq, void *dev, struct pt_regs *regs)
+{
+ int i, quick = 0;
+ u_char latch, sstat;
+ u_short psock;
+ u_int events;
+ static volatile int active = 0;
+
+ if (active) {
+ printk(KERN_NOTICE "tcic: reentered interrupt handler!\n");
+ return;
+ } else
+ active = 1;
+
+ DEBUG(2, "tcic: tcic_interrupt()\n");
+
+ for (i = 0; i < sockets; i++) {
+ psock = socket_table[i].psock;
+ tcic_setl(TCIC_ADDR, (psock << TCIC_ADDR_SS_SHFT)
+ | TCIC_ADDR_INDREG | TCIC_SCF1(psock));
+ sstat = tcic_getb(TCIC_SSTAT);
+ latch = sstat ^ socket_table[psock].last_sstat;
+ socket_table[i].last_sstat = sstat;
+ if (tcic_getb(TCIC_ICSR) & TCIC_ICSR_CDCHG) {
+ tcic_setb(TCIC_ICSR, TCIC_ICSR_CLEAR);
+ quick = 1;
+ }
+ if ((latch == 0) || (socket_table[psock].handler == NULL))
+ continue;
+ events = (latch & TCIC_SSTAT_CD) ? SS_DETECT : 0;
+ events |= (latch & TCIC_SSTAT_WP) ? SS_WRPROT : 0;
+ if (tcic_getw(TCIC_DATA) & TCIC_SCF1_IOSTS) {
+ events |= (latch & TCIC_SSTAT_LBAT1) ? SS_STSCHG : 0;
+ } else {
+ events |= (latch & TCIC_SSTAT_RDY) ? SS_READY : 0;
+ events |= (latch & TCIC_SSTAT_LBAT1) ? SS_BATDEAD : 0;
+ events |= (latch & TCIC_SSTAT_LBAT2) ? SS_BATWARN : 0;
+ }
+ if (events)
+ socket_table[i].handler(socket_table[i].info, events);
+ }
+
+ /* Schedule next poll, if needed */
+ if (((cs_irq == 0) || quick) && (!tcic_timer_pending)) {
+ poll_timer.expires = jiffies + (quick ? poll_quick : poll_interval);
+ add_timer(&poll_timer);
+ tcic_timer_pending = 1;
+ }
+ active = 0;
+
+ DEBUG(2, "tcic: interrupt done\n");
+
+} /* tcic_interrupt */
+
+static void tcic_timer(u_long data)
+{
+ DEBUG(2, "tcic: tcic_timer()\n");
+ tcic_timer_pending = 0;
+ tcic_interrupt(0, NULL, NULL);
+} /* tcic_timer */
+
+/*====================================================================*/
+
+static int tcic_register_callback(u_short lsock, ss_callback_t *call)
+{
+ if (call == NULL) {
+ socket_table[lsock].handler = NULL;
+ MOD_DEC_USE_COUNT;
+ } else {
+ MOD_INC_USE_COUNT;
+ socket_table[lsock].handler = call->handler;
+ socket_table[lsock].info = call->info;
+ }
+ return 0;
+} /* tcic_register_callback */
+
+/*====================================================================*/
+
+static int tcic_get_status(u_short lsock, u_int *value)
+{
+ u_short psock = socket_table[lsock].psock;
+ u_char reg;
+
+ tcic_setl(TCIC_ADDR, (psock << TCIC_ADDR_SS_SHFT)
+ | TCIC_ADDR_INDREG | TCIC_SCF1(psock));
+ reg = tcic_getb(TCIC_SSTAT);
+ *value = (reg & TCIC_SSTAT_CD) ? SS_DETECT : 0;
+ *value |= (reg & TCIC_SSTAT_WP) ? SS_WRPROT : 0;
+ if (tcic_getw(TCIC_DATA) & TCIC_SCF1_IOSTS) {
+ *value |= (reg & TCIC_SSTAT_LBAT1) ? SS_STSCHG : 0;
+ } else {
+ *value |= (reg & TCIC_SSTAT_RDY) ? SS_READY : 0;
+ *value |= (reg & TCIC_SSTAT_LBAT1) ? SS_BATDEAD : 0;
+ *value |= (reg & TCIC_SSTAT_LBAT2) ? SS_BATWARN : 0;
+ }
+ reg = tcic_getb(TCIC_PWR);
+ if (reg & (TCIC_PWR_VCC(psock)|TCIC_PWR_VPP(psock)))
+ *value |= SS_POWERON;
+ DEBUG(1, "tcic: GetStatus(%d) = %#2.2x\n", lsock, *value);
+ return 0;
+} /* tcic_get_status */
+
+/*====================================================================*/
+
+static int tcic_inquire_socket(u_short lsock, socket_cap_t *cap)
+{
+ *cap = tcic_cap;
+ return 0;
+} /* tcic_inquire_socket */
+
+/*====================================================================*/
+
+static int tcic_get_socket(u_short lsock, socket_state_t *state)
+{
+ u_short psock = socket_table[lsock].psock;
+ u_char reg;
+ u_short scf1, scf2;
+
+ tcic_setl(TCIC_ADDR, (psock << TCIC_ADDR_SS_SHFT)
+ | TCIC_ADDR_INDREG | TCIC_SCF1(psock));
+ scf1 = tcic_getw(TCIC_DATA);
+ state->flags = (scf1 & TCIC_SCF1_IOSTS) ? SS_IOCARD : 0;
+ state->flags |= (scf1 & TCIC_SCF1_DMA_MASK) ? SS_DMA_MODE : 0;
+ state->flags |= (scf1 & TCIC_SCF1_SPKR) ? SS_SPKR_ENA : 0;
+ if (tcic_getb(TCIC_SCTRL) & TCIC_SCTRL_ENA)
+ state->flags |= SS_OUTPUT_ENA;
+ state->io_irq = scf1 & TCIC_SCF1_IRQ_MASK;
+ if (state->io_irq == 1) state->io_irq = 11;
+
+ reg = tcic_getb(TCIC_PWR);
+ state->Vcc = state->Vpp = 0;
+ if (reg & TCIC_PWR_VCC(psock)) {
+ if (reg & TCIC_PWR_VPP(psock))
+ state->Vcc = 50;
+ else
+ state->Vcc = state->Vpp = 50;
+ } else {
+ if (reg & TCIC_PWR_VPP(psock)) {
+ state->Vcc = 50;
+ state->Vpp = 120;
+ }
+ }
+ reg = tcic_aux_getb(TCIC_AUX_ILOCK);
+ state->flags |= (reg & TCIC_ILOCK_CRESET) ? SS_RESET : 0;
+
+ /* Card status change interrupt mask */
+ tcic_setw(TCIC_ADDR, TCIC_SCF2(psock));
+ scf2 = tcic_getw(TCIC_DATA);
+ state->csc_mask = (scf2 & TCIC_SCF2_MCD) ? 0 : SS_DETECT;
+ if (state->flags & SS_IOCARD) {
+ state->csc_mask |= (scf2 & TCIC_SCF2_MLBAT1) ? 0 : SS_STSCHG;
+ } else {
+ state->csc_mask |= (scf2 & TCIC_SCF2_MLBAT1) ? 0 : SS_BATDEAD;
+ state->csc_mask |= (scf2 & TCIC_SCF2_MLBAT2) ? 0 : SS_BATWARN;
+ state->csc_mask |= (scf2 & TCIC_SCF2_MRDY) ? 0 : SS_READY;
+ }
+
+ DEBUG(1, "tcic: GetSocket(%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);
+ return 0;
+} /* tcic_get_socket */
+
+/*====================================================================*/
+
+static int tcic_set_socket(u_short lsock, socket_state_t *state)
+{
+ u_short psock = socket_table[lsock].psock;
+ u_char reg;
+ u_short scf1, scf2;
+
+ DEBUG(1, "tcic: 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);
+ tcic_setw(TCIC_ADDR+2, (psock << TCIC_SS_SHFT) | TCIC_ADR2_INDREG);
+
+ reg = tcic_getb(TCIC_PWR);
+ reg &= ~(TCIC_PWR_VCC(psock) | TCIC_PWR_VPP(psock));
+
+ if (state->Vcc == 50) {
+ switch (state->Vpp) {
+ case 0: reg |= TCIC_PWR_VCC(psock) | TCIC_PWR_VPP(psock); break;
+ case 50: reg |= TCIC_PWR_VCC(psock); break;
+ case 120: reg |= TCIC_PWR_VPP(psock); break;
+ default: return -EINVAL;
+ }
+ } else if (state->Vcc != 0)
+ return -EINVAL;
+
+ if (reg != tcic_getb(TCIC_PWR))
+ tcic_setb(TCIC_PWR, reg);
+
+ reg = TCIC_ILOCK_HOLD_CCLK | TCIC_ILOCK_CWAIT;
+ if (state->flags & SS_OUTPUT_ENA) {
+ tcic_setb(TCIC_SCTRL, TCIC_SCTRL_ENA);
+ reg |= TCIC_ILOCK_CRESENA;
+ } else
+ tcic_setb(TCIC_SCTRL, 0);
+ if (state->flags & SS_RESET)
+ reg |= TCIC_ILOCK_CRESET;
+ tcic_aux_setb(TCIC_AUX_ILOCK, reg);
+
+ tcic_setw(TCIC_ADDR, TCIC_SCF1(psock));
+ scf1 = TCIC_SCF1_FINPACK;
+ scf1 |= TCIC_IRQ(state->io_irq);
+ if (state->flags & SS_IOCARD) {
+ scf1 |= TCIC_SCF1_IOSTS;
+ if (state->flags & SS_SPKR_ENA)
+ scf1 |= TCIC_SCF1_SPKR;
+ if (state->flags & SS_DMA_MODE)
+ scf1 |= TCIC_SCF1_DREQ2 << TCIC_SCF1_DMA_SHIFT;
+ }
+ tcic_setw(TCIC_DATA, scf1);
+
+ /* Some general setup stuff, and configure status interrupt */
+ reg = TCIC_WAIT_ASYNC | TCIC_WAIT_SENSE | to_cycles(250);
+ tcic_aux_setb(TCIC_AUX_WCTL, reg);
+ tcic_aux_setw(TCIC_AUX_SYSCFG, TCIC_SYSCFG_AUTOBUSY|0x0a00|
+ TCIC_IRQ(cs_irq));
+
+ /* Card status change interrupt mask */
+ tcic_setw(TCIC_ADDR, TCIC_SCF2(psock));
+ scf2 = TCIC_SCF2_MALL;
+ if (state->csc_mask & SS_DETECT) scf2 &= ~TCIC_SCF2_MCD;
+ if (state->flags & SS_IOCARD) {
+ if (state->csc_mask & SS_STSCHG) reg &= ~TCIC_SCF2_MLBAT1;
+ } else {
+ if (state->csc_mask & SS_BATDEAD) reg &= ~TCIC_SCF2_MLBAT1;
+ if (state->csc_mask & SS_BATWARN) reg &= ~TCIC_SCF2_MLBAT2;
+ if (state->csc_mask & SS_READY) reg &= ~TCIC_SCF2_MRDY;
+ }
+ tcic_setw(TCIC_DATA, scf2);
+ /* For the ISA bus, the irq should be active-high totem-pole */
+ tcic_setb(TCIC_IENA, TCIC_IENA_CDCHG | TCIC_IENA_CFG_HIGH);
+
+ return 0;
+} /* tcic_set_socket */
+
+/*====================================================================*/
+
+static int tcic_get_io_map(u_short lsock, struct pccard_io_map *io)
+{
+ u_short psock = socket_table[lsock].psock;
+ u_short base, ioctl;
+ u_int addr;
+
+ if (io->map > 1) return -EINVAL;
+ tcic_setw(TCIC_ADDR+2, TCIC_ADR2_INDREG | (psock << TCIC_SS_SHFT));
+ addr = TCIC_IWIN(psock, io->map);
+ tcic_setw(TCIC_ADDR, addr + TCIC_IBASE_X);
+ base = tcic_getw(TCIC_DATA);
+ tcic_setw(TCIC_ADDR, addr + TCIC_ICTL_X);
+ ioctl = tcic_getw(TCIC_DATA);
+
+ if (ioctl & TCIC_ICTL_TINY)
+ io->start = io->stop = base;
+ else {
+ io->start = base & (base-1);
+ io->stop = io->start + (base ^ (base-1));
+ }
+ io->speed = to_ns(ioctl & TCIC_ICTL_WSCNT_MASK);
+ io->flags = (ioctl & TCIC_ICTL_ENA) ? MAP_ACTIVE : 0;
+ switch (ioctl & TCIC_ICTL_BW_MASK) {
+ case TCIC_ICTL_BW_DYN:
+ io->flags |= MAP_AUTOSZ; break;
+ case TCIC_ICTL_BW_16:
+ io->flags |= MAP_16BIT; break;
+ default:
+ break;
+ }
+ DEBUG(1, "tcic: 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;
+} /* tcic_get_io_map */
+
+/*====================================================================*/
+
+static int tcic_set_io_map(u_short lsock, struct pccard_io_map *io)
+{
+ u_short psock = socket_table[lsock].psock;
+ u_int addr;
+ u_short base, len, ioctl;
+
+ DEBUG(1, "tcic: SetIOMap(%d, %d, %#2.2x, %d ns, "
+ "%#4.4x-%#4.4x)\n", lsock, io->map, io->flags,
+ io->speed, io->start, io->stop);
+ if ((io->map > 1) || (io->start > 0xffff) || (io->stop > 0xffff) ||
+ (io->stop < io->start)) return -EINVAL;
+ tcic_setw(TCIC_ADDR+2, TCIC_ADR2_INDREG | (psock << TCIC_SS_SHFT));
+ addr = TCIC_IWIN(psock, io->map);
+
+ base = io->start; len = io->stop - io->start;
+ /* Check to see that len+1 is power of two, etc */
+ if ((len & (len+1)) || (base & len)) return -EINVAL;
+ base |= (len+1)>>1;
+ tcic_setw(TCIC_ADDR, addr + TCIC_IBASE_X);
+ tcic_setw(TCIC_DATA, base);
+
+ ioctl = (psock << TCIC_ICTL_SS_SHFT);
+ ioctl |= (len == 0) ? TCIC_ICTL_TINY : 0;
+ ioctl |= (io->flags & MAP_ACTIVE) ? TCIC_ICTL_ENA : 0;
+ ioctl |= to_cycles(io->speed) & TCIC_ICTL_WSCNT_MASK;
+ if (!(io->flags & MAP_AUTOSZ)) {
+ ioctl |= TCIC_ICTL_QUIET;
+ ioctl |= (io->flags & MAP_16BIT) ? TCIC_ICTL_BW_16 : TCIC_ICTL_BW_8;
+ }
+ tcic_setw(TCIC_ADDR, addr + TCIC_ICTL_X);
+ tcic_setw(TCIC_DATA, ioctl);
+
+ return 0;
+} /* tcic_set_io_map */
+
+/*====================================================================*/
+
+static int tcic_get_mem_map(u_short lsock, struct pccard_mem_map *mem)
+{
+ u_short psock = socket_table[lsock].psock;
+ u_short addr, ctl;
+ u_long base, mmap;
+
+ if (mem->map > 3) return -EINVAL;
+ tcic_setw(TCIC_ADDR+2, TCIC_ADR2_INDREG | (psock << TCIC_SS_SHFT));
+ addr = TCIC_MWIN(psock, mem->map);
+
+ tcic_setw(TCIC_ADDR, addr + TCIC_MBASE_X);
+ base = tcic_getw(TCIC_DATA);
+ if (base & TCIC_MBASE_4K_BIT) {
+ mem->sys_start = base & TCIC_MBASE_HA_MASK;
+ mem->sys_stop = mem->sys_start;
+ } else {
+ base &= TCIC_MBASE_HA_MASK;
+ mem->sys_start = (base & (base-1));
+ mem->sys_stop = mem->sys_start + (base ^ (base-1));
+ }
+ mem->sys_start = mem->sys_start << TCIC_MBASE_HA_SHFT;
+ mem->sys_stop = (mem->sys_stop << TCIC_MBASE_HA_SHFT) + 0x0fff;
+
+ tcic_setw(TCIC_ADDR, addr + TCIC_MMAP_X);
+ mmap = tcic_getw(TCIC_DATA);
+ mem->flags = (mmap & TCIC_MMAP_REG) ? MAP_ATTRIB : 0;
+ mmap &= TCIC_MMAP_CA_MASK;
+ mem->card_start = mem->sys_start + (mmap << TCIC_MMAP_CA_SHFT);
+ mem->card_start &= 0x3ffffff;
+
+ tcic_setw(TCIC_ADDR, addr + TCIC_MCTL_X);
+ ctl = tcic_getw(TCIC_DATA);
+ mem->flags |= (ctl & TCIC_MCTL_ENA) ? MAP_ACTIVE : 0;
+ mem->flags |= (ctl & TCIC_MCTL_B8) ? 0 : MAP_16BIT;
+ mem->flags |= (ctl & TCIC_MCTL_WP) ? MAP_WRPROT : 0;
+ mem->speed = to_ns(ctl & TCIC_MCTL_WSCNT_MASK);
+
+ DEBUG(1, "tcic: GetMemMap(%d, %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;
+} /* tcic_get_mem_map */
+
+/*====================================================================*/
+
+static int tcic_set_mem_map(u_short lsock, struct pccard_mem_map *mem)
+{
+ u_short psock = socket_table[lsock].psock;
+ u_short addr, ctl;
+ u_long base, len, mmap;
+
+ DEBUG(1, "tcic: SetMemMap(%d, %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);
+ if ((mem->map > 3) || (mem->card_start > 0x3ffffff) ||
+ (mem->sys_start > 0xffffff) || (mem->sys_stop > 0xffffff) ||
+ (mem->sys_start > mem->sys_stop) || (mem->speed > 1000))
+ return -EINVAL;
+ tcic_setw(TCIC_ADDR+2, TCIC_ADR2_INDREG | (psock << TCIC_SS_SHFT));
+ addr = TCIC_MWIN(psock, mem->map);
+
+ base = mem->sys_start; len = mem->sys_stop - mem->sys_start;
+ if ((len & (len+1)) || (base & len)) return -EINVAL;
+ if (len == 0x0fff)
+ base = (base >> TCIC_MBASE_HA_SHFT) | TCIC_MBASE_4K_BIT;
+ else
+ base = (base | (len+1)>>1) >> TCIC_MBASE_HA_SHFT;
+ tcic_setw(TCIC_ADDR, addr + TCIC_MBASE_X);
+ tcic_setw(TCIC_DATA, base);
+
+ mmap = mem->card_start - mem->sys_start;
+ mmap = (mmap >> TCIC_MMAP_CA_SHFT) & TCIC_MMAP_CA_MASK;
+ if (mem->flags & MAP_ATTRIB) mmap |= TCIC_MMAP_REG;
+ tcic_setw(TCIC_ADDR, addr + TCIC_MMAP_X);
+ tcic_setw(TCIC_DATA, mmap);
+
+ ctl = TCIC_MCTL_QUIET | (psock << TCIC_MCTL_SS_SHFT);
+ ctl |= to_cycles(mem->speed) & TCIC_MCTL_WSCNT_MASK;
+ ctl |= (mem->flags & MAP_16BIT) ? 0 : TCIC_MCTL_B8;
+ ctl |= (mem->flags & MAP_WRPROT) ? TCIC_MCTL_WP : 0;
+ ctl |= (mem->flags & MAP_ACTIVE) ? TCIC_MCTL_ENA : 0;
+ tcic_setw(TCIC_ADDR, addr + TCIC_MCTL_X);
+ tcic_setw(TCIC_DATA, ctl);
+
+ return 0;
+} /* tcic_set_mem_map */
+
+/*====================================================================*/
+
+typedef int (*subfn_t)(u_short, void *);
+
+static subfn_t service_table[] = {
+ (subfn_t)&tcic_register_callback,
+ (subfn_t)&tcic_inquire_socket,
+ (subfn_t)&tcic_get_status,
+ (subfn_t)&tcic_get_socket,
+ (subfn_t)&tcic_set_socket,
+ (subfn_t)&tcic_get_io_map,
+ (subfn_t)&tcic_set_io_map,
+ (subfn_t)&tcic_get_mem_map,
+ (subfn_t)&tcic_set_mem_map,
+};
+
+#define NFUNC (sizeof(service_table)/sizeof(subfn_t))
+
+static int tcic_service(u_int lsock, u_int cmd, void *arg)
+{
+ int err;
+
+ DEBUG(2, "tcic_service(%d, %d, 0x%p)\n", lsock, cmd, arg);
+
+ if (cmd < NFUNC)
+ err = service_table[cmd](lsock, arg);
+ else
+ err = -EINVAL;
+
+ return err;
+} /* tcic_service */
+
+/*====================================================================*/
+
+module_init(init_tcic);
+module_exit(exit_tcic);
diff --git a/drivers/pcmcia/tcic.h b/drivers/pcmcia/tcic.h
new file mode 100644
index 000000000..e33f79531
--- /dev/null
+++ b/drivers/pcmcia/tcic.h
@@ -0,0 +1,266 @@
+/*
+ * tcic.h 1.12 1999/08/28 04:01:47
+ *
+ * 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 David A. Hinds
+ * <dhinds@hyper.stanford.edu>. Portions created by David A. Hinds
+ * are Copyright (C) 1999 David A. Hinds. 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.
+ */
+
+#ifndef _LINUX_TCIC_H
+#define _LINUX_TCIC_H
+
+#define TCIC_BASE 0x240
+
+/* offsets of registers from TCIC_BASE */
+#define TCIC_DATA 0x00
+#define TCIC_ADDR 0x02
+#define TCIC_SCTRL 0x06
+#define TCIC_SSTAT 0x07
+#define TCIC_MODE 0x08
+#define TCIC_PWR 0x09
+#define TCIC_EDC 0x0A
+#define TCIC_ICSR 0x0C
+#define TCIC_IENA 0x0D
+#define TCIC_AUX 0x0E
+
+#define TCIC_SS_SHFT 12
+#define TCIC_SS_MASK 0x7000
+
+/* Flags for TCIC_ADDR */
+#define TCIC_ADR2_REG 0x8000
+#define TCIC_ADR2_INDREG 0x0800
+
+#define TCIC_ADDR_REG 0x80000000
+#define TCIC_ADDR_SS_SHFT (TCIC_SS_SHFT+16)
+#define TCIC_ADDR_SS_MASK (TCIC_SS_MASK<<16)
+#define TCIC_ADDR_INDREG 0x08000000
+#define TCIC_ADDR_IO 0x04000000
+#define TCIC_ADDR_MASK 0x03ffffff
+
+/* Flags for TCIC_SCTRL */
+#define TCIC_SCTRL_ENA 0x01
+#define TCIC_SCTRL_INCMODE 0x18
+#define TCIC_SCTRL_INCMODE_HOLD 0x00
+#define TCIC_SCTRL_INCMODE_WORD 0x08
+#define TCIC_SCTRL_INCMODE_REG 0x10
+#define TCIC_SCTRL_INCMODE_AUTO 0x18
+#define TCIC_SCTRL_EDCSUM 0x20
+#define TCIC_SCTRL_RESET 0x80
+
+/* Flags for TCIC_SSTAT */
+#define TCIC_SSTAT_6US 0x01
+#define TCIC_SSTAT_10US 0x02
+#define TCIC_SSTAT_PROGTIME 0x04
+#define TCIC_SSTAT_LBAT1 0x08
+#define TCIC_SSTAT_LBAT2 0x10
+#define TCIC_SSTAT_RDY 0x20 /* Inverted */
+#define TCIC_SSTAT_WP 0x40
+#define TCIC_SSTAT_CD 0x80 /* Card detect */
+
+/* Flags for TCIC_MODE */
+#define TCIC_MODE_PGMMASK 0x1f
+#define TCIC_MODE_NORMAL 0x00
+#define TCIC_MODE_PGMWR 0x01
+#define TCIC_MODE_PGMRD 0x02
+#define TCIC_MODE_PGMCE 0x04
+#define TCIC_MODE_PGMDBW 0x08
+#define TCIC_MODE_PGMWORD 0x10
+#define TCIC_MODE_AUXSEL_MASK 0xe0
+
+/* Registers accessed through TCIC_AUX, by setting TCIC_MODE */
+#define TCIC_AUX_TCTL (0<<5)
+#define TCIC_AUX_PCTL (1<<5)
+#define TCIC_AUX_WCTL (2<<5)
+#define TCIC_AUX_EXTERN (3<<5)
+#define TCIC_AUX_PDATA (4<<5)
+#define TCIC_AUX_SYSCFG (5<<5)
+#define TCIC_AUX_ILOCK (6<<5)
+#define TCIC_AUX_TEST (7<<5)
+
+/* Flags for TCIC_PWR */
+#define TCIC_PWR_VCC(sock) (0x01<<(sock))
+#define TCIC_PWR_VCC_MASK 0x03
+#define TCIC_PWR_VPP(sock) (0x08<<(sock))
+#define TCIC_PWR_VPP_MASK 0x18
+#define TCIC_PWR_CLIMENA 0x40
+#define TCIC_PWR_CLIMSTAT 0x80
+
+/* Flags for TCIC_ICSR */
+#define TCIC_ICSR_CLEAR 0x01
+#define TCIC_ICSR_SET 0x02
+#define TCIC_ICSR_JAM (TCIC_ICSR_CLEAR|TCIC_ICSR_SET)
+#define TCIC_ICSR_STOPCPU 0x04
+#define TCIC_ICSR_ILOCK 0x08
+#define TCIC_ICSR_PROGTIME 0x10
+#define TCIC_ICSR_ERR 0x20
+#define TCIC_ICSR_CDCHG 0x40
+#define TCIC_ICSR_IOCHK 0x80
+
+/* Flags for TCIC_IENA */
+#define TCIC_IENA_CFG_MASK 0x03
+#define TCIC_IENA_CFG_OFF 0x00 /* disabled */
+#define TCIC_IENA_CFG_OD 0x01 /* active low, open drain */
+#define TCIC_IENA_CFG_LOW 0x02 /* active low, totem pole */
+#define TCIC_IENA_CFG_HIGH 0x03 /* active high, totem pole */
+#define TCIC_IENA_ILOCK 0x08
+#define TCIC_IENA_PROGTIME 0x10
+#define TCIC_IENA_ERR 0x20 /* overcurrent or iochk */
+#define TCIC_IENA_CDCHG 0x40
+
+/* Flags for TCIC_AUX_WCTL */
+#define TCIC_WAIT_COUNT_MASK 0x001f
+#define TCIC_WAIT_ASYNC 0x0020
+#define TCIC_WAIT_SENSE 0x0040
+#define TCIC_WAIT_SRC 0x0080
+#define TCIC_WCTL_WR 0x0100
+#define TCIC_WCTL_RD 0x0200
+#define TCIC_WCTL_CE 0x0400
+#define TCIC_WCTL_LLBAT1 0x0800
+#define TCIC_WCTL_LLBAT2 0x1000
+#define TCIC_WCTL_LRDY 0x2000
+#define TCIC_WCTL_LWP 0x4000
+#define TCIC_WCTL_LCD 0x8000
+
+/* Flags for TCIC_AUX_SYSCFG */
+#define TCIC_SYSCFG_IRQ_MASK 0x000f
+#define TCIC_SYSCFG_MCSFULL 0x0010
+#define TCIC_SYSCFG_IO1723 0x0020
+#define TCIC_SYSCFG_MCSXB 0x0040
+#define TCIC_SYSCFG_ICSXB 0x0080
+#define TCIC_SYSCFG_NOPDN 0x0100
+#define TCIC_SYSCFG_MPSEL_SHFT 9
+#define TCIC_SYSCFG_MPSEL_MASK 0x0e00
+#define TCIC_SYSCFG_MPSENSE 0x2000
+#define TCIC_SYSCFG_AUTOBUSY 0x4000
+#define TCIC_SYSCFG_ACC 0x8000
+
+#define TCIC_ILOCK_OUT 0x01
+#define TCIC_ILOCK_SENSE 0x02
+#define TCIC_ILOCK_CRESET 0x04
+#define TCIC_ILOCK_CRESENA 0x08
+#define TCIC_ILOCK_CWAIT 0x10
+#define TCIC_ILOCK_CWAITSNS 0x20
+#define TCIC_ILOCK_HOLD_MASK 0xc0
+#define TCIC_ILOCK_HOLD_CCLK 0xc0
+
+#define TCIC_ILOCKTEST_ID_SH 8
+#define TCIC_ILOCKTEST_ID_MASK 0x7f00
+#define TCIC_ILOCKTEST_MCIC_1 0x8000
+
+#define TCIC_ID_DB86082 0x02
+#define TCIC_ID_DB86082A 0x03
+#define TCIC_ID_DB86084 0x04
+#define TCIC_ID_DB86084A 0x08
+#define TCIC_ID_DB86072 0x15
+#define TCIC_ID_DB86184 0x14
+#define TCIC_ID_DB86082B 0x17
+
+#define TCIC_TEST_DIAG 0x8000
+
+/*
+ * Indirectly addressed registers
+ */
+
+#define TCIC_SCF1(sock) ((sock)<<3)
+#define TCIC_SCF2(sock) (((sock)<<3)+2)
+
+/* Flags for SCF1 */
+#define TCIC_SCF1_IRQ_MASK 0x000f
+#define TCIC_SCF1_IRQ_OFF 0x0000
+#define TCIC_SCF1_IRQOC 0x0010
+#define TCIC_SCF1_PCVT 0x0020
+#define TCIC_SCF1_IRDY 0x0040
+#define TCIC_SCF1_ATA 0x0080
+#define TCIC_SCF1_DMA_SHIFT 8
+#define TCIC_SCF1_DMA_MASK 0x0700
+#define TCIC_SCF1_DMA_OFF 0
+#define TCIC_SCF1_DREQ2 2
+#define TCIC_SCF1_IOSTS 0x0800
+#define TCIC_SCF1_SPKR 0x1000
+#define TCIC_SCF1_FINPACK 0x2000
+#define TCIC_SCF1_DELWR 0x4000
+#define TCIC_SCF1_HD7IDE 0x8000
+
+/* Flags for SCF2 */
+#define TCIC_SCF2_RI 0x0001
+#define TCIC_SCF2_IDBR 0x0002
+#define TCIC_SCF2_MDBR 0x0004
+#define TCIC_SCF2_MLBAT1 0x0008
+#define TCIC_SCF2_MLBAT2 0x0010
+#define TCIC_SCF2_MRDY 0x0020
+#define TCIC_SCF2_MWP 0x0040
+#define TCIC_SCF2_MCD 0x0080
+#define TCIC_SCF2_MALL 0x00f8
+
+/* Indirect addresses for memory window registers */
+#define TCIC_MWIN(sock,map) (0x100+(((map)+((sock)<<2))<<3))
+#define TCIC_MBASE_X 2
+#define TCIC_MMAP_X 4
+#define TCIC_MCTL_X 6
+
+#define TCIC_MBASE_4K_BIT 0x4000
+#define TCIC_MBASE_HA_SHFT 12
+#define TCIC_MBASE_HA_MASK 0x0fff
+
+#define TCIC_MMAP_REG 0x8000
+#define TCIC_MMAP_CA_SHFT 12
+#define TCIC_MMAP_CA_MASK 0x3fff
+
+#define TCIC_MCTL_WSCNT_MASK 0x001f
+#define TCIC_MCTL_WCLK 0x0020
+#define TCIC_MCTL_WCLK_CCLK 0x0000
+#define TCIC_MCTL_WCLK_BCLK 0x0020
+#define TCIC_MCTL_QUIET 0x0040
+#define TCIC_MCTL_WP 0x0080
+#define TCIC_MCTL_ACC 0x0100
+#define TCIC_MCTL_KE 0x0200
+#define TCIC_MCTL_EDC 0x0400
+#define TCIC_MCTL_B8 0x0800
+#define TCIC_MCTL_SS_SHFT TCIC_SS_SHFT
+#define TCIC_MCTL_SS_MASK TCIC_SS_MASK
+#define TCIC_MCTL_ENA 0x8000
+
+/* Indirect addresses for I/O window registers */
+#define TCIC_IWIN(sock,map) (0x200+(((map)+((sock)<<1))<<2))
+#define TCIC_IBASE_X 0
+#define TCIC_ICTL_X 2
+
+#define TCIC_ICTL_WSCNT_MASK TCIC_MCTL_WSCNT_MASK
+#define TCIC_ICTL_QUIET TCIC_MCTL_QUIET
+#define TCIC_ICTL_1K 0x0080
+#define TCIC_ICTL_PASS16 0x0100
+#define TCIC_ICTL_ACC TCIC_MCTL_ACC
+#define TCIC_ICTL_TINY 0x0200
+#define TCIC_ICTL_B16 0x0400
+#define TCIC_ICTL_B8 TCIC_MCTL_B8
+#define TCIC_ICTL_BW_MASK (TCIC_ICTL_B16|TCIC_ICTL_B8)
+#define TCIC_ICTL_BW_DYN 0
+#define TCIC_ICTL_BW_8 TCIC_ICTL_B8
+#define TCIC_ICTL_BW_16 TCIC_ICTL_B16
+#define TCIC_ICTL_BW_ATA (TCIC_ICTL_B16|TCIC_ICTL_B8)
+#define TCIC_ICTL_SS_SHFT TCIC_SS_SHFT
+#define TCIC_ICTL_SS_MASK TCIC_SS_MASK
+#define TCIC_ICTL_ENA TCIC_MCTL_ENA
+
+#endif /* _LINUX_TCIC_H */
diff --git a/drivers/pcmcia/ti113x.h b/drivers/pcmcia/ti113x.h
new file mode 100644
index 000000000..758757d99
--- /dev/null
+++ b/drivers/pcmcia/ti113x.h
@@ -0,0 +1,180 @@
+/*
+ * ti113x.h 1.15 1999/09/03 16:43:35
+ *
+ * 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 David A. Hinds
+ * <dhinds@hyper.stanford.edu>. Portions created by David A. Hinds
+ * are Copyright (C) 1999 David A. Hinds. 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.
+ */
+
+#ifndef _LINUX_TI113X_H
+#define _LINUX_TI113X_H
+
+#ifndef PCI_VENDOR_ID_TI
+#define PCI_VENDOR_ID_TI 0x104c
+#endif
+
+#ifndef PCI_DEVICE_ID_TI_1130
+#define PCI_DEVICE_ID_TI_1130 0xac12
+#endif
+#ifndef PCI_DEVICE_ID_TI_1131
+#define PCI_DEVICE_ID_TI_1131 0xac15
+#endif
+#ifndef PCI_DEVICE_ID_TI_1031
+#define PCI_DEVICE_ID_TI_1031 0xac13
+#endif
+#ifndef PCI_DEVICE_ID_TI_1250A
+#define PCI_DEVICE_ID_TI_1250A 0xac16
+#endif
+#ifndef PCI_DEVICE_ID_TI_1220
+#define PCI_DEVICE_ID_TI_1220 0xac17
+#endif
+#ifndef PCI_DEVICE_ID_TI_1221
+#define PCI_DEVICE_ID_TI_1221 0xac19
+#endif
+#ifndef PCI_DEVICE_ID_TI_1210
+#define PCI_DEVICE_ID_TI_1210 0xac1a
+#endif
+#ifndef PCI_DEVICE_ID_TI_1450
+#define PCI_DEVICE_ID_TI_1450 0xac1b
+#endif
+#ifndef PCI_DEVICE_ID_TI_1225
+#define PCI_DEVICE_ID_TI_1225 0xac1c
+#endif
+#ifndef PCI_DEVICE_ID_TI_1251A
+#define PCI_DEVICE_ID_TI_1251A 0xac1d
+#endif
+#ifndef PCI_DEVICE_ID_TI_1211
+#define PCI_DEVICE_ID_TI_1211 0xac1e
+#endif
+#ifndef PCI_DEVICE_ID_TI_1251B
+#define PCI_DEVICE_ID_TI_1251B 0xac1f
+#endif
+#ifndef PCI_DEVICE_ID_TI_1420
+#define PCI_DEVICE_ID_TI_1420 0xac51
+#endif
+
+/* Register definitions for TI 113X PCI-to-CardBus bridges */
+
+/* System Control Register */
+#define TI113X_SYSTEM_CONTROL 0x0080 /* 32 bit */
+#define TI113X_SCR_SMIROUTE 0x04000000
+#define TI113X_SCR_SMISTATUS 0x02000000
+#define TI113X_SCR_SMIENB 0x01000000
+#define TI113X_SCR_VCCPROT 0x00200000
+#define TI113X_SCR_REDUCEZV 0x00100000
+#define TI113X_SCR_CDREQEN 0x00080000
+#define TI113X_SCR_CDMACHAN 0x00070000
+#define TI113X_SCR_SOCACTIVE 0x00002000
+#define TI113X_SCR_PWRSTREAM 0x00000800
+#define TI113X_SCR_DELAYUP 0x00000400
+#define TI113X_SCR_DELAYDOWN 0x00000200
+#define TI113X_SCR_INTERROGATE 0x00000100
+#define TI113X_SCR_CLKRUN_SEL 0x00000080
+#define TI113X_SCR_PWRSAVINGS 0x00000040
+#define TI113X_SCR_SUBSYSRW 0x00000020
+#define TI113X_SCR_CB_DPAR 0x00000010
+#define TI113X_SCR_CDMA_EN 0x00000008
+#define TI113X_SCR_ASYNC_IRQ 0x00000004
+#define TI113X_SCR_KEEPCLK 0x00000002
+#define TI113X_SCR_CLKRUN_ENA 0x00000001
+
+#define TI122X_SCR_SER_STEP 0xc0000000
+#define TI122X_SCR_INTRTIE 0x20000000
+#define TI122X_SCR_CBRSVD 0x00400000
+#define TI122X_SCR_MRBURSTDN 0x00008000
+#define TI122X_SCR_MRBURSTUP 0x00004000
+#define TI122X_SCR_RIMUX 0x00000001
+
+/* Multimedia Control Register */
+#define TI1250_MULTIMEDIA_CTL 0x0084 /* 8 bit */
+#define TI1250_MMC_ZVOUTEN 0x80
+#define TI1250_MMC_PORTSEL 0x40
+#define TI1250_MMC_ZVEN1 0x02
+#define TI1250_MMC_ZVEN0 0x01
+
+#define TI1250_GENERAL_STATUS 0x0085 /* 8 bit */
+#define TI1250_GPIO0_CONTROL 0x0088 /* 8 bit */
+#define TI1250_GPIO1_CONTROL 0x0089 /* 8 bit */
+#define TI1250_GPIO2_CONTROL 0x008a /* 8 bit */
+#define TI1250_GPIO3_CONTROL 0x008b /* 8 bit */
+#define TI122X_IRQMUX 0x008c /* 32 bit */
+
+/* Retry Status Register */
+#define TI113X_RETRY_STATUS 0x0090 /* 8 bit */
+#define TI113X_RSR_PCIRETRY 0x80
+#define TI113X_RSR_CBRETRY 0x40
+#define TI113X_RSR_TEXP_CBB 0x20
+#define TI113X_RSR_MEXP_CBB 0x10
+#define TI113X_RSR_TEXP_CBA 0x08
+#define TI113X_RSR_MEXP_CBA 0x04
+#define TI113X_RSR_TEXP_PCI 0x02
+#define TI113X_RSR_MEXP_PCI 0x01
+
+/* Card Control Register */
+#define TI113X_CARD_CONTROL 0x0091 /* 8 bit */
+#define TI113X_CCR_RIENB 0x80
+#define TI113X_CCR_ZVENABLE 0x40
+#define TI113X_CCR_PCI_IRQ_ENA 0x20
+#define TI113X_CCR_PCI_IREQ 0x10
+#define TI113X_CCR_PCI_CSC 0x08
+#define TI113X_CCR_SPKROUTEN 0x02
+#define TI113X_CCR_IFG 0x01
+
+#define TI1220_CCR_PORT_SEL 0x20
+#define TI122X_CCR_AUD2MUX 0x04
+
+/* Device Control Register */
+#define TI113X_DEVICE_CONTROL 0x0092 /* 8 bit */
+#define TI113X_DCR_5V_FORCE 0x40
+#define TI113X_DCR_3V_FORCE 0x20
+#define TI113X_DCR_IMODE_MASK 0x06
+#define TI113X_DCR_IMODE_ISA 0x02
+#define TI113X_DCR_IMODE_SERIAL 0x04
+
+#define TI12XX_DCR_IMODE_PCI_ONLY 0x00
+#define TI12XX_DCR_IMODE_ALL_SERIAL 0x06
+
+/* Buffer Control Register */
+#define TI113X_BUFFER_CONTROL 0x0093 /* 8 bit */
+#define TI113X_BCR_CB_READ_DEPTH 0x08
+#define TI113X_BCR_CB_WRITE_DEPTH 0x04
+#define TI113X_BCR_PCI_READ_DEPTH 0x02
+#define TI113X_BCR_PCI_WRITE_DEPTH 0x01
+
+/* Diagnostic Register */
+#define TI1250_DIAGNOSTIC 0x0093 /* 8 bit */
+#define TI1250_DIAG_TRUE_VALUE 0x80
+#define TI1250_DIAG_PCI_IREQ 0x40
+#define TI1250_DIAG_PCI_CSC 0x20
+#define TI1250_DIAG_ASYNC_CSC 0x01
+
+/* DMA Registers */
+#define TI113X_DMA_0 0x0094 /* 32 bit */
+#define TI113X_DMA_1 0x0098 /* 32 bit */
+
+/* ExCA IO offset registers */
+#define TI113X_IO_OFFSET(map) (0x36+((map)<<1))
+
+#endif /* _LINUX_TI113X_H */
+
diff --git a/drivers/pcmcia/topic.h b/drivers/pcmcia/topic.h
new file mode 100644
index 000000000..57860a023
--- /dev/null
+++ b/drivers/pcmcia/topic.h
@@ -0,0 +1,96 @@
+/*
+ * topic.h 1.8 1999/08/28 04:01:47
+ *
+ * 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 David A. Hinds
+ * <dhinds@hyper.stanford.edu>. Portions created by David A. Hinds
+ * are Copyright (C) 1999 David A. Hinds. 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.
+ * topic.h $Release$ 1999/08/28 04:01:47
+ */
+
+#ifndef _LINUX_TOPIC_H
+#define _LINUX_TOPIC_H
+
+#ifndef PCI_VENDOR_ID_TOSHIBA
+#define PCI_VENDOR_ID_TOSHIBA 0x1179
+#endif
+#ifndef PCI_DEVICE_ID_TOSHIBA_TOPIC95_A
+#define PCI_DEVICE_ID_TOSHIBA_TOPIC95_A 0x0603
+#endif
+#ifndef PCI_DEVICE_ID_TOSHIBA_TOPIC95_B
+#define PCI_DEVICE_ID_TOSHIBA_TOPIC95_B 0x060a
+#endif
+#ifndef PCI_DEVICE_ID_TOSHIBA_TOPIC97
+#define PCI_DEVICE_ID_TOSHIBA_TOPIC97 0x060f
+#endif
+
+/* Register definitions for Toshiba ToPIC95 controllers */
+
+#define TOPIC_SOCKET_CONTROL 0x0090 /* 32 bit */
+#define TOPIC_SCR_IRQSEL 0x00000001
+
+#define TOPIC_SLOT_CONTROL 0x00a0 /* 8 bit */
+#define TOPIC_SLOT_SLOTON 0x80
+#define TOPIC_SLOT_SLOTEN 0x40
+#define TOPIC_SLOT_ID_LOCK 0x20
+#define TOPIC_SLOT_ID_WP 0x10
+#define TOPIC_SLOT_PORT_MASK 0x0c
+#define TOPIC_SLOT_PORT_SHIFT 2
+#define TOPIC_SLOT_OFS_MASK 0x03
+
+#define TOPIC_CARD_CONTROL 0x00a1 /* 8 bit */
+#define TOPIC_CCR_INTB 0x20
+#define TOPIC_CCR_INTA 0x10
+#define TOPIC_CCR_CLOCK 0x0c
+#define TOPIC_CCR_PCICLK 0x0c
+#define TOPIC_CCR_PCICLK_2 0x08
+#define TOPIC_CCR_CCLK 0x04
+
+#define TOPIC97_INT_CONTROL 0x00a1 /* 8 bit */
+#define TOPIC97_ICR_INTB 0x20
+#define TOPIC97_ICR_INTA 0x10
+#define TOPIC97_ICR_STSIRQNP 0x04
+#define TOPIC97_ICR_IRQNP 0x02
+#define TOPIC97_ICR_IRQSEL 0x01
+
+#define TOPIC_CARD_DETECT 0x00a3 /* 8 bit */
+#define TOPIC_CDR_MODE_PC32 0x80
+#define TOPIC_CDR_VS1 0x04
+#define TOPIC_CDR_VS2 0x02
+#define TOPIC_CDR_SW_DETECT 0x01
+
+#define TOPIC_REGISTER_CONTROL 0x00a4 /* 32 bit */
+#define TOPIC_RCR_RESUME_RESET 0x80000000
+#define TOPIC_RCR_REMOVE_RESET 0x40000000
+#define TOPIC97_RCR_CLKRUN_ENA 0x20000000
+#define TOPIC97_RCR_TESTMODE 0x10000000
+#define TOPIC97_RCR_IOPLUP 0x08000000
+#define TOPIC_RCR_BUFOFF_PWROFF 0x02000000
+#define TOPIC_RCR_BUFOFF_SIGOFF 0x01000000
+#define TOPIC97_RCR_CB_DEV_MASK 0x0000f800
+#define TOPIC97_RCR_CB_DEV_SHIFT 11
+#define TOPIC97_RCR_RI_DISABLE 0x00000004
+#define TOPIC97_RCR_CAUDIO_OFF 0x00000002
+#define TOPIC_RCR_CAUDIO_INVERT 0x00000001
+
+#endif /* _LINUX_TOPIC_H */
diff --git a/drivers/pcmcia/vg468.h b/drivers/pcmcia/vg468.h
new file mode 100644
index 000000000..c19d53d15
--- /dev/null
+++ b/drivers/pcmcia/vg468.h
@@ -0,0 +1,106 @@
+/*
+ * vg468.h 1.10 1999/08/28 04:01:47
+ *
+ * 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 David A. Hinds
+ * <dhinds@hyper.stanford.edu>. Portions created by David A. Hinds
+ * are Copyright (C) 1999 David A. Hinds. 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.
+ */
+
+#ifndef _LINUX_VG468_H
+#define _LINUX_VG468_H
+
+/* Special bit in I365_IDENT used for Vadem chip detection */
+#define I365_IDENT_VADEM 0x08
+
+/* Special definitions in I365_POWER */
+#define VG468_VPP2_MASK 0x0c
+#define VG468_VPP2_5V 0x04
+#define VG468_VPP2_12V 0x08
+
+/* Unique Vadem registers */
+#define VG469_VSENSE 0x1f /* Card voltage sense */
+#define VG469_VSELECT 0x2f /* Card voltage select */
+#define VG468_CTL 0x38 /* Control register */
+#define VG468_TIMER 0x39 /* Timer control */
+#define VG468_MISC 0x3a /* Miscellaneous */
+#define VG468_GPIO_CFG 0x3b /* GPIO configuration */
+#define VG469_EXT_MODE 0x3c /* Extended mode register */
+#define VG468_SELECT 0x3d /* Programmable chip select */
+#define VG468_SELECT_CFG 0x3e /* Chip select configuration */
+#define VG468_ATA 0x3f /* ATA control */
+
+/* Flags for VG469_VSENSE */
+#define VG469_VSENSE_A_VS1 0x01
+#define VG469_VSENSE_A_VS2 0x02
+#define VG469_VSENSE_B_VS1 0x04
+#define VG469_VSENSE_B_VS2 0x08
+
+/* Flags for VG469_VSELECT */
+#define VG469_VSEL_VCC 0x03
+#define VG469_VSEL_5V 0x00
+#define VG469_VSEL_3V 0x03
+#define VG469_VSEL_MAX 0x0c
+#define VG469_VSEL_EXT_STAT 0x10
+#define VG469_VSEL_EXT_BUS 0x20
+#define VG469_VSEL_MIXED 0x40
+#define VG469_VSEL_ISA 0x80
+
+/* Flags for VG468_CTL */
+#define VG468_CTL_SLOW 0x01 /* 600ns memory timing */
+#define VG468_CTL_ASYNC 0x02 /* Asynchronous bus clocking */
+#define VG468_CTL_TSSI 0x08 /* Tri-state some outputs */
+#define VG468_CTL_DELAY 0x10 /* Card detect debounce */
+#define VG468_CTL_INPACK 0x20 /* Obey INPACK signal? */
+#define VG468_CTL_POLARITY 0x40 /* VCCEN polarity */
+#define VG468_CTL_COMPAT 0x80 /* Compatibility stuff */
+
+#define VG469_CTL_WS_COMPAT 0x04 /* Wait state compatibility */
+#define VG469_CTL_STRETCH 0x10 /* LED stretch */
+
+/* Flags for VG468_TIMER */
+#define VG468_TIMER_ZEROPWR 0x10 /* Zero power control */
+#define VG468_TIMER_SIGEN 0x20 /* Power up */
+#define VG468_TIMER_STATUS 0x40 /* Activity timer status */
+#define VG468_TIMER_RES 0x80 /* Timer resolution */
+#define VG468_TIMER_MASK 0x0f /* Activity timer timeout */
+
+/* Flags for VG468_MISC */
+#define VG468_MISC_GPIO 0x04 /* General-purpose IO */
+#define VG468_MISC_DMAWSB 0x08 /* DMA wait state control */
+#define VG469_MISC_LEDENA 0x10 /* LED enable */
+#define VG468_MISC_VADEMREV 0x40 /* Vadem revision control */
+#define VG468_MISC_UNLOCK 0x80 /* Unique register lock */
+
+/* Flags for VG469_EXT_MODE_A */
+#define VG469_MODE_VPPST 0x03 /* Vpp steering control */
+#define VG469_MODE_INT_SENSE 0x04 /* Internal voltage sense */
+#define VG469_MODE_CABLE 0x08
+#define VG469_MODE_COMPAT 0x10 /* i82365sl B or DF step */
+#define VG469_MODE_TEST 0x20
+#define VG469_MODE_RIO 0x40 /* Steer RIO to INTR? */
+
+/* Flags for VG469_EXT_MODE_B */
+#define VG469_MODE_B_3V 0x01 /* 3.3v for socket B */
+
+#endif /* _LINUX_VG468_H */
diff --git a/drivers/pcmcia/yenta.h b/drivers/pcmcia/yenta.h
new file mode 100644
index 000000000..5c09f2978
--- /dev/null
+++ b/drivers/pcmcia/yenta.h
@@ -0,0 +1,153 @@
+/*
+ * yenta.h 1.15 1999/08/28 04:01:47
+ *
+ * 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 David A. Hinds
+ * <dhinds@hyper.stanford.edu>. Portions created by David A. Hinds
+ * are Copyright (C) 1999 David A. Hinds. 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.
+ */
+
+#ifndef _LINUX_YENTA_H
+#define _LINUX_YENTA_H
+
+/* PCI Configuration Registers */
+
+#define PCI_STATUS_CAPLIST 0x10
+#define PCI_CB_CAPABILITY_POINTER 0x14 /* 8 bit */
+#define PCI_CAPABILITY_ID 0x00 /* 8 bit */
+#define PCI_CAPABILITY_PM 0x01
+#define PCI_NEXT_CAPABILITY 0x01 /* 8 bit */
+#define PCI_PM_CAPABILITIES 0x02 /* 16 bit */
+#define PCI_PMCAP_PME_D3COLD 0x8000
+#define PCI_PMCAP_PME_D3HOT 0x4000
+#define PCI_PMCAP_PME_D2 0x2000
+#define PCI_PMCAP_PME_D1 0x1000
+#define PCI_PMCAP_PME_D0 0x0800
+#define PCI_PMCAP_D2_CAP 0x0400
+#define PCI_PMCAP_D1_CAP 0x0200
+#define PCI_PMCAP_DYN_DATA 0x0100
+#define PCI_PMCAP_DSI 0x0020
+#define PCI_PMCAP_AUX_PWR 0x0010
+#define PCI_PMCAP_PMECLK 0x0008
+#define PCI_PMCAP_VERSION_MASK 0x0007
+#define PCI_PM_CONTROL_STATUS 0x04 /* 16 bit */
+#define PCI_PMCS_PME_STATUS 0x8000
+#define PCI_PMCS_DATASCALE_MASK 0x6000
+#define PCI_PMCS_DATASCALE_SHIFT 13
+#define PCI_PMCS_DATASEL_MASK 0x1e00
+#define PCI_PMCS_DATASEL_SHIFT 9
+#define PCI_PMCS_PME_ENABLE 0x0100
+#define PCI_PMCS_PWR_STATE_MASK 0x0003
+#define PCI_PMCS_PWR_STATE_D0 0x0000
+#define PCI_PMCS_PWR_STATE_D1 0x0001
+#define PCI_PMCS_PWR_STATE_D2 0x0002
+#define PCI_PMCS_PWR_STATE_D3 0x0003
+#define PCI_PM_BRIDGE_EXT 0x06 /* 8 bit */
+#define PCI_PM_DATA 0x07 /* 8 bit */
+
+#define CB_PRIMARY_BUS 0x18 /* 8 bit */
+#define CB_CARDBUS_BUS 0x19 /* 8 bit */
+#define CB_SUBORD_BUS 0x1a /* 8 bit */
+#define CB_LATENCY_TIMER 0x1b /* 8 bit */
+
+#define CB_MEM_BASE(m) (0x1c + 8*(m))
+#define CB_MEM_LIMIT(m) (0x20 + 8*(m))
+#define CB_IO_BASE(m) (0x2c + 8*(m))
+#define CB_IO_LIMIT(m) (0x30 + 8*(m))
+
+#define CB_BRIDGE_CONTROL 0x3e /* 16 bit */
+#define CB_BCR_PARITY_ENA 0x0001
+#define CB_BCR_SERR_ENA 0x0002
+#define CB_BCR_ISA_ENA 0x0004
+#define CB_BCR_VGA_ENA 0x0008
+#define CB_BCR_MABORT 0x0020
+#define CB_BCR_CB_RESET 0x0040
+#define CB_BCR_ISA_IRQ 0x0080
+#define CB_BCR_PREFETCH(m) (0x0100 << (m))
+#define CB_BCR_WRITE_POST 0x0400
+
+#define CB_LEGACY_MODE_BASE 0x44
+
+/* Memory mapped registers */
+
+#define CB_SOCKET_EVENT 0x0000
+#define CB_SE_CSTSCHG 0x00000001
+#define CB_SE_CCD1 0x00000002
+#define CB_SE_CCD2 0x00000004
+#define CB_SE_PWRCYCLE 0x00000008
+
+#define CB_SOCKET_MASK 0x0004
+#define CB_SM_CSTSCHG 0x00000001
+#define CB_SM_CCD 0x00000006
+#define CB_SM_PWRCYCLE 0x00000008
+
+#define CB_SOCKET_STATE 0x0008
+#define CB_SS_CSTSCHG 0x00000001
+#define CB_SS_CCD1 0x00000002
+#define CB_SS_CCD2 0x00000004
+#define CB_SS_PWRCYCLE 0x00000008
+#define CB_SS_16BIT 0x00000010
+#define CB_SS_32BIT 0x00000020
+#define CB_SS_CINT 0x00000040
+#define CB_SS_BADCARD 0x00000080
+#define CB_SS_DATALOST 0x00000100
+#define CB_SS_BADVCC 0x00000200
+#define CB_SS_5VCARD 0x00000400
+#define CB_SS_3VCARD 0x00000800
+#define CB_SS_XVCARD 0x00001000
+#define CB_SS_YVCARD 0x00002000
+#define CB_SS_5VSOCKET 0x10000000
+#define CB_SS_3VSOCKET 0x20000000
+#define CB_SS_XVSOCKET 0x40000000
+#define CB_SS_YVSOCKET 0x80000000
+
+#define CB_SOCKET_FORCE 0x000c
+#define CB_SF_CVSTEST 0x00004000
+
+#define CB_SOCKET_CONTROL 0x0010
+#define CB_SC_VPP_MASK 0x00000007
+#define CB_SC_VPP_OFF 0x00000000
+#define CB_SC_VPP_12V 0x00000001
+#define CB_SC_VPP_5V 0x00000002
+#define CB_SC_VPP_3V 0x00000003
+#define CB_SC_VPP_XV 0x00000004
+#define CB_SC_VPP_YV 0x00000005
+#define CB_SC_VCC_MASK 0x00000070
+#define CB_SC_VCC_OFF 0x00000000
+#define CB_SC_VCC_5V 0x00000020
+#define CB_SC_VCC_3V 0x00000030
+#define CB_SC_VCC_XV 0x00000040
+#define CB_SC_VCC_YV 0x00000050
+#define CB_SC_CCLK_STOP 0x00000080
+
+#define CB_SOCKET_POWER 0x0020
+#define CB_SP_CLK_CTRL 0x00000001
+#define CB_SP_CLK_CTRL_ENA 0x00010000
+#define CB_SP_CLK_MODE 0x01000000
+#define CB_SP_ACCESS 0x02000000
+
+/* Address bits 31..24 for memory windows for 16-bit cards,
+ accessable only by memory mapping the 16-bit register set */
+#define CB_MEM_PAGE(map) (0x40 + (map))
+
+#endif /* _LINUX_YENTA_H */