summaryrefslogtreecommitdiff
path: root/linux/pcmcia-cs/modules
diff options
context:
space:
mode:
Diffstat (limited to 'linux/pcmcia-cs/modules')
-rw-r--r--linux/pcmcia-cs/modules/bulkmem.c626
-rw-r--r--linux/pcmcia-cs/modules/cirrus.h188
-rw-r--r--linux/pcmcia-cs/modules/cistpl.c1502
-rw-r--r--linux/pcmcia-cs/modules/cs.c2399
-rw-r--r--linux/pcmcia-cs/modules/cs_internal.h300
-rw-r--r--linux/pcmcia-cs/modules/ds.c1004
-rw-r--r--linux/pcmcia-cs/modules/ene.h59
-rw-r--r--linux/pcmcia-cs/modules/i82365.c2570
-rw-r--r--linux/pcmcia-cs/modules/i82365.h154
-rw-r--r--linux/pcmcia-cs/modules/o2micro.h160
-rw-r--r--linux/pcmcia-cs/modules/pci_fixup.c674
-rw-r--r--linux/pcmcia-cs/modules/ricoh.h161
-rw-r--r--linux/pcmcia-cs/modules/rsrc_mgr.c877
-rw-r--r--linux/pcmcia-cs/modules/smc34c90.h58
-rw-r--r--linux/pcmcia-cs/modules/ti113x.h264
-rw-r--r--linux/pcmcia-cs/modules/topic.h123
-rw-r--r--linux/pcmcia-cs/modules/vg468.h112
-rw-r--r--linux/pcmcia-cs/modules/yenta.h156
18 files changed, 11387 insertions, 0 deletions
diff --git a/linux/pcmcia-cs/modules/bulkmem.c b/linux/pcmcia-cs/modules/bulkmem.c
new file mode 100644
index 0000000..558e6d9
--- /dev/null
+++ b/linux/pcmcia-cs/modules/bulkmem.c
@@ -0,0 +1,626 @@
+/*======================================================================
+
+ PCMCIA Bulk Memory Services
+
+ bulkmem.c 1.44 2002/06/29 06:23:09
+
+ 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
+ <dahinds@users.sourceforge.net>. 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 General 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/slab.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 (ret = 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);
+ mod_timer(&busy->timeout, jiffies + req.Timeout*HZ/1000);
+ 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(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;
+ init_timer(&busy->timeout);
+ 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);
+ if (!queue) return CS_OUT_OF_RESOURCE;
+ 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(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/linux/pcmcia-cs/modules/cirrus.h b/linux/pcmcia-cs/modules/cirrus.h
new file mode 100644
index 0000000..e3bb255
--- /dev/null
+++ b/linux/pcmcia-cs/modules/cirrus.h
@@ -0,0 +1,188 @@
+/*
+ * cirrus.h 1.11 2003/09/09 07:05:40
+ *
+ * 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
+ * <dahinds@users.sourceforge.net>. 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 General 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 */
+
+#define pd67_ext_get(s, r) \
+ (i365_set(s, PD67_EXT_INDEX, r), i365_get(s, PD67_EXT_DATA))
+#define pd67_ext_set(s, r, v) \
+ (i365_set(s, PD67_EXT_INDEX, r), i365_set(s, PD67_EXT_DATA, v))
+
+/* 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_EXT_CTL_2 0x0b
+#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_EXTERN_DATA */
+#define PD67_EXD_VS1(s) (0x01 << ((s)<<1))
+#define PD67_EXD_VS2(s) (0x02 << ((s)<<1))
+
+/* Fields in PD67_EXT_CTL_2 */
+#define PD67_EC2_GPSTB_TOTEM 0x04
+#define PD67_EC2_GPSTB_IOR 0x08
+#define PD67_EC2_GPSTB_IOW 0x10
+#define PD67_EC2_GPSTB_HIGH 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_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 */
+
+/* Data structure for tracking vendor-specific state */
+typedef struct cirrus_state_t {
+ u_char misc1; /* PD67_MISC_CTL_1 */
+ u_char misc2; /* PD67_MISC_CTL_2 */
+ u_char ectl1; /* PD67_EXT_CTL_1 */
+ u_char timer[6]; /* PD67_TIME_* */
+} cirrus_state_t;
+
+#define CIRRUS_PCIC_ID \
+ IS_PD6729, IS_PD6730, IS_PD6832
+
+#define CIRRUS_PCIC_INFO \
+ { "Cirrus PD6729", IS_CIRRUS|IS_PCI, ID(CIRRUS, 6729) }, \
+ { "Cirrus PD6730", IS_CIRRUS|IS_PCI, PCI_VENDOR_ID_CIRRUS, -1 }, \
+ { "Cirrus PD6832", IS_CIRRUS|IS_CARDBUS, ID(CIRRUS, 6832) }
+
+#endif /* _LINUX_CIRRUS_H */
diff --git a/linux/pcmcia-cs/modules/cistpl.c b/linux/pcmcia-cs/modules/cistpl.c
new file mode 100644
index 0000000..404b8e4
--- /dev/null
+++ b/linux/pcmcia-cs/modules/cistpl.c
@@ -0,0 +1,1502 @@
+/*======================================================================
+
+ PCMCIA Card Information Structure parser
+
+ cistpl.c 1.101 2003/12/15 03:58:03
+
+ 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
+ <dahinds@users.sourceforge.net>. 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 General 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/config.h>
+#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/slab.h>
+#include <linux/mm.h>
+#include <linux/sched.h>
+#include <linux/pci.h>
+#include <linux/ioport.h>
+#include <asm/io.h>
+#include <asm/byteorder.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/cisreg.h>
+#include <pcmcia/cistpl.h>
+#include "cs_internal.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
+
+/*====================================================================*/
+
+/* Parameters that can be set with 'insmod' */
+
+#define INT_MODULE_PARM(n, v) static int n = v; MODULE_PARM(n, "i")
+
+INT_MODULE_PARM(cis_width, 0); /* 16-bit CIS? */
+
+/*======================================================================
+
+ Low-level functions to read and write CIS memory. I think the
+ write routine is only useful for writing one-byte registers.
+
+======================================================================*/
+
+/* Bits in attr field */
+#define IS_ATTR 1
+#define IS_INDIRECT 8
+
+static int setup_cis_mem(socket_info_t *s);
+
+static void set_cis_map(socket_info_t *s, pccard_mem_map *mem)
+{
+ s->ss_entry(s->sock, SS_SetMemMap, mem);
+ if (s->cap.features & SS_CAP_STATIC_MAP) {
+ if (s->cis_virt)
+ bus_iounmap(s->cap.bus, s->cis_virt);
+ s->cis_virt = bus_ioremap(s->cap.bus, mem->sys_start,
+ s->cap.map_size);
+ }
+}
+
+int 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, *buf = ptr;
+
+ DEBUG(3, "cs: read_cis_mem(%d, %#x, %u)\n", attr, addr, len);
+ if (setup_cis_mem(s) != 0) {
+ memset(ptr, 0xff, len);
+ return -1;
+ }
+ mem->flags = MAP_ACTIVE | ((cis_width) ? MAP_16BIT : 0);
+
+ if (attr & IS_INDIRECT) {
+ /* Indirect accesses use a bunch of special registers at fixed
+ locations in common memory */
+ u_char flags = ICTRL0_COMMON|ICTRL0_AUTOINC|ICTRL0_BYTEGRAN;
+ if (attr & IS_ATTR) { addr *= 2; flags = ICTRL0_AUTOINC; }
+ mem->card_start = 0; mem->flags = MAP_ACTIVE;
+ set_cis_map(s, mem);
+ sys = s->cis_virt;
+ bus_writeb(s->cap.bus, flags, sys+CISREG_ICTRL0);
+ bus_writeb(s->cap.bus, addr & 0xff, sys+CISREG_IADDR0);
+ bus_writeb(s->cap.bus, (addr>>8) & 0xff, sys+CISREG_IADDR1);
+ bus_writeb(s->cap.bus, (addr>>16) & 0xff, sys+CISREG_IADDR2);
+ bus_writeb(s->cap.bus, (addr>>24) & 0xff, sys+CISREG_IADDR3);
+ for ( ; len > 0; len--, buf++)
+ *buf = bus_readb(s->cap.bus, sys+CISREG_IDATA0);
+ } else {
+ u_int inc = 1;
+ if (attr) { mem->flags |= MAP_ATTRIB; inc++; addr *= 2; }
+ sys += (addr & (s->cap.map_size-1));
+ mem->card_start = addr & ~(s->cap.map_size-1);
+ while (len) {
+ set_cis_map(s, mem);
+ sys = s->cis_virt + (addr & (s->cap.map_size-1));
+ for ( ; len > 0; len--, buf++, sys += inc) {
+ if (sys == s->cis_virt+s->cap.map_size) break;
+ *buf = bus_readb(s->cap.bus, sys);
+ }
+ mem->card_start += s->cap.map_size;
+ addr = 0;
+ }
+ }
+ DEBUG(3, "cs: %#2.2x %#2.2x %#2.2x %#2.2x ...\n",
+ *(u_char *)(ptr+0), *(u_char *)(ptr+1),
+ *(u_char *)(ptr+2), *(u_char *)(ptr+3));
+ return 0;
+}
+
+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, *buf = ptr;
+
+ DEBUG(3, "cs: write_cis_mem(%d, %#x, %u)\n", attr, addr, len);
+ if (setup_cis_mem(s) != 0) return;
+ mem->flags = MAP_ACTIVE | ((cis_width) ? MAP_16BIT : 0);
+
+ if (attr & IS_INDIRECT) {
+ /* Indirect accesses use a bunch of special registers at fixed
+ locations in common memory */
+ u_char flags = ICTRL0_COMMON|ICTRL0_AUTOINC|ICTRL0_BYTEGRAN;
+ if (attr & IS_ATTR) { addr *= 2; flags = ICTRL0_AUTOINC; }
+ mem->card_start = 0; mem->flags = MAP_ACTIVE;
+ set_cis_map(s, mem);
+ sys = s->cis_virt;
+ bus_writeb(s->cap.bus, flags, sys+CISREG_ICTRL0);
+ bus_writeb(s->cap.bus, addr & 0xff, sys+CISREG_IADDR0);
+ bus_writeb(s->cap.bus, (addr>>8) & 0xff, sys+CISREG_IADDR1);
+ bus_writeb(s->cap.bus, (addr>>16) & 0xff, sys+CISREG_IADDR2);
+ bus_writeb(s->cap.bus, (addr>>24) & 0xff, sys+CISREG_IADDR3);
+ for ( ; len > 0; len--, buf++)
+ bus_writeb(s->cap.bus, *buf, sys+CISREG_IDATA0);
+ } else {
+ int inc = 1;
+ if (attr & IS_ATTR) { mem->flags |= MAP_ATTRIB; inc++; addr *= 2; }
+ mem->card_start = addr & ~(s->cap.map_size-1);
+ while (len) {
+ set_cis_map(s, mem);
+ sys = s->cis_virt + (addr & (s->cap.map_size-1));
+ for ( ; len > 0; len--, buf++, sys += inc) {
+ if (sys == s->cis_virt+s->cap.map_size) break;
+ bus_writeb(s->cap.bus, *buf, sys);
+ }
+ mem->card_start += s->cap.map_size;
+ addr = 0;
+ }
+ }
+}
+
+/*======================================================================
+
+ 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));
+}
+
+static int setup_cis_mem(socket_info_t *s)
+{
+ if (!(s->cap.features & SS_CAP_STATIC_MAP) &&
+ (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,
+ s->cap.map_size, low, "card services")) {
+ printk(KERN_NOTICE "cs: unable to map card memory!\n");
+ return -1;
+ }
+ s->cis_mem.sys_stop = s->cis_mem.sys_start+s->cap.map_size-1;
+ 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);
+ if (!(s->cap.features & SS_CAP_STATIC_MAP))
+ 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;
+ s->cis_virt = NULL;
+ }
+}
+
+/*======================================================================
+
+ 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, ret;
+ 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)
+ ret = read_cb_mem(s, 0, attr, addr, len, ptr);
+ else
+#endif
+ ret = read_cis_mem(s, attr, addr, len, ptr);
+ /* Copy data into the cache, if there is room */
+ if ((ret == 0) && (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, *caddr;
+ int i;
+
+ buf = kmalloc(256, GFP_KERNEL);
+ if (buf == NULL)
+ return -1;
+ 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;
+ }
+ kfree(buf);
+ 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:4;
+ u_int has_link:1;
+ u_int mfc_fn:3;
+ u_int space:4;
+} 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_INDIRECT) ||
+ (link[0] == CISTPL_NO_LINK)) {
+ switch (link[0]) {
+ case CISTPL_LONGLINK_A:
+ HAS_LINK(tuple->Flags) = 1;
+ LINK_SPACE(tuple->Flags) = attr | IS_ATTR;
+ read_cis_cache(s, attr, ofs+2, 4, &tuple->LinkOffset);
+ break;
+ case CISTPL_LONGLINK_C:
+ HAS_LINK(tuple->Flags) = 1;
+ LINK_SPACE(tuple->Flags) = attr & ~IS_ATTR;
+ read_cis_cache(s, attr, ofs+2, 4, &tuple->LinkOffset);
+ break;
+ case CISTPL_INDIRECT:
+ HAS_LINK(tuple->Flags) = 1;
+ LINK_SPACE(tuple->Flags) = IS_ATTR | IS_INDIRECT;
+ tuple->LinkOffset = 0;
+ 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;
+ 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;
+}
+
+/*====================================================================*/
+
+static int parse_format(tuple_t *tuple, cistpl_format_t *fmt)
+{
+ u_char *p;
+
+ if (tuple->TupleDataLen < 10)
+ return CS_BAD_TUPLE;
+
+ p = tuple->TupleData;
+
+ fmt->type = p[0];
+ fmt->edc = p[1];
+ fmt->offset = le32_to_cpu(*(u_int *)(p+2));
+ fmt->length = le32_to_cpu(*(u_int *)(p+6));
+
+ 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_FORMAT:
+ case CISTPL_FORMAT_A:
+ ret = parse_format(tuple, &parse->format);
+ 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;
+ int ret;
+
+ buf = kmalloc(255, GFP_KERNEL);
+ if (buf == NULL)
+ return CS_OUT_OF_RESOURCE;
+ tuple.DesiredTuple = code;
+ tuple.Attributes = TUPLE_RETURN_COMMON;
+ ret = CardServices(GetFirstTuple, handle, &tuple, NULL);
+ if (ret != CS_SUCCESS) goto done;
+ tuple.TupleData = buf;
+ tuple.TupleOffset = 0;
+ tuple.TupleDataMax = 255;
+ ret = CardServices(GetTupleData, handle, &tuple, NULL);
+ if (ret != CS_SUCCESS) goto done;
+ ret = CardServices(ParseTuple, handle, &tuple, parse);
+done:
+ kfree(buf);
+ 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, dev_ok = 0, ident_ok = 0;
+
+ if (CHECK_HANDLE(handle))
+ return CS_BAD_HANDLE;
+ p = kmalloc(sizeof(*p), GFP_KERNEL);
+ if (p == NULL)
+ return CS_OUT_OF_RESOURCE;
+
+ info->Chains = reserved = 0;
+ tuple.DesiredTuple = RETURN_FIRST_TUPLE;
+ tuple.Attributes = TUPLE_RETURN_COMMON;
+ ret = get_first_tuple(handle, &tuple);
+ if (ret != CS_SUCCESS)
+ goto done;
+
+ /* First tuple should be DEVICE; we should really have either that
+ or a CFTABLE_ENTRY of some sort */
+ if ((tuple.TupleCode == CISTPL_DEVICE) ||
+ (read_tuple(handle, CISTPL_CFTABLE_ENTRY, p) == CS_SUCCESS) ||
+ (read_tuple(handle, CISTPL_CFTABLE_ENTRY_CB, p) == CS_SUCCESS))
+ dev_ok++;
+
+ /* All cards should have a MANFID tuple, and/or a VERS_1 or VERS_2
+ tuple, for card identification. Certain old D-Link and Linksys
+ cards have only a broken VERS_2 tuple; hence the bogus test. */
+ if ((read_tuple(handle, CISTPL_MANFID, p) == CS_SUCCESS) ||
+ (read_tuple(handle, CISTPL_VERS_1, p) == CS_SUCCESS) ||
+ (read_tuple(handle, CISTPL_VERS_2, p) != CS_NO_MORE_ITEMS))
+ ident_ok++;
+
+ if (!dev_ok && !ident_ok)
+ goto done;
+
+ 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) ||
+ ((!dev_ok || !ident_ok) && (info->Chains > 10)))
+ info->Chains = 0;
+
+done:
+ kfree(p);
+ return CS_SUCCESS;
+}
+
diff --git a/linux/pcmcia-cs/modules/cs.c b/linux/pcmcia-cs/modules/cs.c
new file mode 100644
index 0000000..99ee192
--- /dev/null
+++ b/linux/pcmcia-cs/modules/cs.c
@@ -0,0 +1,2399 @@
+/*======================================================================
+
+ PCMCIA Card Services -- core services
+
+ cs.c 1.287 2004/04/09 03:54:25
+
+ 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
+ <dahinds@users.sourceforge.net>. 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 General 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/slab.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/pm.h>
+#include <linux/pci.h>
+#include <linux/spinlock.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"
+
+#ifdef CONFIG_PCI
+#define PCI_OPT " [pci]"
+#else
+#define PCI_OPT ""
+#endif
+#ifdef CONFIG_CARDBUS
+#define CB_OPT " [cardbus]"
+#else
+#define CB_OPT ""
+#endif
+#ifdef CONFIG_PM
+#define PM_OPT " [apm]"
+#else
+#define PM_OPT ""
+#endif
+#ifdef CONFIG_PNP_BIOS
+#define PNP_OPT " [pnp]"
+#else
+#define PNP_OPT ""
+#endif
+#if !defined(CONFIG_CARDBUS) && !defined(CONFIG_PCI) && \
+ !defined(CONFIG_PM) && !defined(CONFIG_PNP_BIOS)
+#define OPTIONS " none"
+#else
+#define OPTIONS PCI_OPT CB_OPT PM_OPT PNP_OPT
+#endif
+
+static const char *release = "Linux PCMCIA Card Services " CS_RELEASE;
+#ifdef UTS_RELEASE
+static const char *kernel = "kernel build: " UTS_RELEASE " " UTS_VERSION;
+#endif
+static const char *options = "options: " OPTIONS;
+
+/*====================================================================*/
+
+/* Module parameters */
+
+MODULE_AUTHOR("David Hinds <dahinds@users.sourceforge.net>");
+MODULE_DESCRIPTION("Linux PCMCIA Card Services " CS_RELEASE
+ "\n options:" OPTIONS);
+MODULE_LICENSE("Dual MPL/GPL");
+
+#define INT_MODULE_PARM(n, v) static int n = v; MODULE_PARM(n, "i")
+
+INT_MODULE_PARM(setup_delay, HZ/20); /* ticks */
+INT_MODULE_PARM(resume_delay, HZ/5); /* ticks */
+INT_MODULE_PARM(shutdown_delay, HZ/40); /* ticks */
+INT_MODULE_PARM(vcc_settle, HZ*4/10); /* ticks */
+INT_MODULE_PARM(reset_time, 10); /* usecs */
+INT_MODULE_PARM(unreset_delay, HZ/10); /* ticks */
+INT_MODULE_PARM(unreset_check, HZ/10); /* ticks */
+INT_MODULE_PARM(unreset_limit, 50); /* unreset_check's */
+
+/* Access speed for attribute memory windows */
+INT_MODULE_PARM(cis_speed, 300); /* ns */
+
+/* Access speed for IO windows */
+INT_MODULE_PARM(io_speed, 0); /* ns */
+
+/* Optional features */
+#ifdef CONFIG_PM
+INT_MODULE_PARM(do_apm, 1);
+#endif
+#ifdef CONFIG_PNP_BIOS
+INT_MODULE_PARM(do_pnp, 1);
+#endif
+
+#ifdef PCMCIA_DEBUG
+int pc_debug=PCMCIA_DEBUG;
+MODULE_PARM(pc_debug, "i");
+static const char *version =
+"cs.c 1.287 2004/04/09 03:54:25 (David Hinds)";
+#endif
+
+/*====================================================================*/
+
+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 HAS_PROC_BUS
+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(HAS_PROC_BUS) && defined(PCMCIA_DEBUG)
+static 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);
+ if (!s) {
+ printk(KERN_NOTICE "cs: memory allocation failure!\n");
+ return (!ns);
+ }
+ 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;
+ spin_lock_init(&s->lock);
+
+ 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 HAS_PROC_BUS
+ if (proc_pccard) {
+ char name[3];
+ sprintf(name, "%02d", i);
+ s->proc = proc_mkdir(name, proc_pccard);
+ if (s->proc)
+ ss_entry(ns, SS_ProcSetup, s->proc);
+#ifdef PCMCIA_DEBUG
+ if (s->proc)
+ create_proc_read_entry("clients", 0, s->proc,
+ proc_read_clients, s);
+#endif
+ }
+#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;
+
+#ifdef HAS_PROC_BUS
+ for (i = 0; i < sockets; i++) {
+ s = socket_table[i];
+ if (s->ss_entry != ss_entry) continue;
+ 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
+
+ for (;;) {
+ for (i = 0; i < sockets; i++) {
+ s = socket_table[i];
+ if (s->ss_entry == ss_entry) break;
+ }
+ if (i == sockets)
+ break;
+ shutdown_socket(i);
+ release_cis_mem(s);
+ while (s->clients) {
+ client = s->clients;
+ s->clients = s->clients->next;
+ kfree(client);
+ }
+ 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->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
+ s->functions = 0;
+ 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_PENDING) {
+ /* Does the socket need more time? */
+ DEBUG(2, "cs: setup_socket(%ld): status pending\n", i);
+ if (++s->setup_timeout > 100) {
+ printk(KERN_NOTICE "cs: socket %ld voltage interrogation"
+ " timed out\n", i);
+ } else {
+ mod_timer(&s->setup, jiffies + HZ/10);
+ }
+ } else 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;
+ mod_timer(&s->setup, jiffies + vcc_settle);
+ } 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->setup_timeout = 0;
+ s->setup.function = &unreset_socket;
+ mod_timer(&s->setup, jiffies + unreset_delay);
+} /* 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);
+ if (s->reset_handle) {
+ 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->setup_timeout > unreset_limit) {
+ printk(KERN_NOTICE "cs: socket %ld timed out during"
+ " reset\n", i);
+ s->state &= ~EVENT_MASK;
+ } else {
+ mod_timer(&s->setup, jiffies + unreset_check);
+ }
+ }
+} /* 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;
+ }
+ mod_timer(&s->shutdown, jiffies + shutdown_delay);
+ 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;
+ s->setup_timeout = 0;
+ 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_PM
+#if (LINUX_VERSION_CODE < VERSION(2,3,43))
+static int handle_pm_event(apm_event_t rqst)
+#else
+static int handle_pm_event(struct pm_dev *dev, pm_request_t rqst,
+ void *data)
+#endif
+{
+ int i, stat;
+ socket_info_t *s;
+ static int down = 0;
+
+ /* <linux/pm.h> hides a hack so this works with old APM support */
+ switch (rqst) {
+ case PM_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 PM_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_pm_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, u_int lines, char *name)
+{
+ int i;
+ ioaddr_t try, align;
+
+ align = (*base) ? (lines ? 1<<lines : 0) : 1;
+ if (align && (align < num)) {
+ if (*base) {
+ DEBUG(0, "odd IO request: num %04x align %04x\n",
+ num, align);
+ align = 0;
+ } else
+ while (align && (align < num)) align <<= 1;
+ }
+ if (*base & ~(align-1)) {
+ DEBUG(0, "odd IO request: base %04x align %04x\n",
+ *base, align);
+ while (*base & ~(align-1)) align <<= 1;
+ }
+ /* Check for an already-allocated window that must conflict with
+ what was asked for. It is a hack because it does not catch all
+ potential conflicts, just the most obvious ones. */
+ for (i = 0; i < MAX_IO_WIN; i++)
+ if ((s->io[i].NumPorts != 0) &&
+ ((s->io[i].BasePort & (align-1)) == *base))
+ return 1;
+ for (i = 0; i < MAX_IO_WIN; i++) {
+ if (s->io[i].NumPorts == 0) {
+ if (find_io_region(base, num, align, 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, 0, 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, 0, 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 (!(s->state & SOCKET_PRESENT))
+ return CS_NO_CARD;
+ 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);
+ if (!client) return CS_OUT_OF_RESOURCE;
+ 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) {
+ spin_unlock_irqrestore(&s->lock, flags);
+ 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;
+ config->IntType = INT_CARDBUS;
+ /* This is a nasty hack */
+ pcibios_read_config_dword(s->cap.cardbus, 0, 0, &config->ConfigBase);
+ if (s->cb_config) {
+ config->Attributes = CONF_VALID_CLIENT;
+ 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_16;
+ 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 cs_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 | INT_ZOOMED_VIDEO))) {
+ 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;
+} /* cs_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_16)
+ 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,
+ config_req_t *req)
+{
+ socket_info_t *s;
+ pccard_io_map io = { 0, 0, 0, 0, 1 };
+ 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_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)
+ printk(KERN_DEBUG "cs: ignoring requested Vcc\n");
+ 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->IntType & INT_ZOOMED_VIDEO)
+ s->socket.flags |= SS_ZVCARD;
+ 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_IREQ_ENA;
+ if (req->Present & PRESENT_IOBASE_0)
+ c->Option |= COR_ADDR_DECODE;
+ }
+ 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);
+ mdelay(40);
+ }
+ 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) {
+ u_char b = c->io.BasePort1 & 0xff;
+ write_cis_mem(s, 1, (base + CISREG_IOBASE_0)>>1, 1, &b);
+ b = (c->io.BasePort1 >> 8) & 0xff;
+ write_cis_mem(s, 1, (base + CISREG_IOBASE_1)>>1, 1, &b);
+ }
+ if (req->Present & PRESENT_IOSIZE) {
+ u_char b = c->io.NumPorts1 + c->io.NumPorts2 - 1;
+ write_cis_mem(s, 1, (base + CISREG_IOSIZE)>>1, 1, &b);
+ }
+
+ /* 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, req->IOAddrLines,
+ handle->dev_info))
+ return CS_IN_USE;
+
+ if (req->NumPorts2) {
+ if (alloc_io_space(s, req->Attributes2, &req->BasePort2,
+ req->NumPorts2, req->IOAddrLines,
+ 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 ret = CS_IN_USE, irq = 0;
+
+ 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;
+
+#ifdef CONFIG_ISA
+ if (s->irq.AssignedIRQ != 0) {
+ /* If the interrupt is already assigned, it must match */
+ irq = s->irq.AssignedIRQ;
+ if (req->IRQInfo1 & IRQ_INFO2_VALID) {
+ u_int 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 {
+ if (req->IRQInfo1 & IRQ_INFO2_VALID) {
+ u_int try, mask = req->IRQInfo2 & s->cap.irq_mask;
+ for (try = 0; try < 2; try++) {
+ for (irq = 0; irq < 16; 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) {
+ if (!s->cap.pci_irq)
+ return ret;
+ irq = s->cap.pci_irq;
+ }
+
+ 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;
+ u_long align;
+ 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;
+
+ /* Window size defaults to smallest available */
+ if (req->Size == 0)
+ req->Size = s->cap.map_size;
+ align = (((s->cap.features & SS_CAP_MEM_ALIGN) ||
+ (req->Attributes & WIN_STRICT_ALIGN)) ?
+ req->Size : s->cap.map_size);
+ if (req->Size & (s->cap.map_size-1))
+ return CS_BAD_SIZE;
+ if ((req->Base && (s->cap.features & SS_CAP_STATIC_MAP)) ||
+ (req->Base & (align-1)))
+ return CS_BAD_BASE;
+ if (req->Base)
+ align = 0;
+
+ /* Allocate system memory window */
+ for (w = 0; w < MAX_WIN; w++)
+ if (!(s->state & SOCKET_WIN_REQ(w))) break;
+ if (w == MAX_WIN)
+ return CS_OUT_OF_RESOURCE;
+
+ 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 (!(s->cap.features & SS_CAP_STATIC_MAP) &&
+ find_mem_region(&win->base, win->size, align,
+ (req->Attributes & WIN_MAP_BELOW_1MB) ||
+ !(s->cap.features & SS_CAP_PAGE_REGS),
+ (*handle)->dev_info))
+ return CS_IN_USE;
+ (*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_16)
+ win->ctl.flags |= MAP_16BIT;
+ if (req->Attributes & WIN_USE_WAIT)
+ win->ctl.flags |= MAP_USE_WAIT;
+ win->ctl.sys_start = win->base;
+ win->ctl.sys_stop = win->base + win->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 */
+ req->Base = win->ctl.sys_start;
+ *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 > 2) {
+ 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 cs_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
+
+======================================================================*/
+
+#include <linux/pci.h>
+
+#if (LINUX_VERSION_CODE <= VERSION(2,1,17))
+
+#undef CONFIG_MODVERSIONS
+static struct symbol_table cs_symtab = {
+#include <linux/symtab_begin.h>
+#undef X
+#define X(sym) { (void *)&sym, SYMBOL_NAME_STR(sym) }
+ X(register_ss_entry),
+ X(unregister_ss_entry),
+ X(CardServices),
+ X(MTDHelperEntry),
+#ifdef HAS_PROC_BUS
+ X(proc_pccard),
+#endif
+#ifndef HAVE_MEMRESERVE
+ X(request_mem_region),
+ X(release_mem_region),
+#endif
+#ifdef CONFIG_PNP_BIOS
+ X(check_pnp_irq),
+#endif
+#ifdef CONFIG_PCI
+ X(pci_irq_mask),
+ X(pci_devices),
+ X(pci_root),
+ X(pci_find_slot),
+ X(pci_find_class),
+ X(pci_enable_device),
+ X(pci_set_power_state),
+#endif
+#include <linux/symtab_end.h>
+};
+
+#else
+
+EXPORT_SYMBOL(register_ss_entry);
+EXPORT_SYMBOL(unregister_ss_entry);
+EXPORT_SYMBOL(CardServices);
+EXPORT_SYMBOL(MTDHelperEntry);
+#ifdef HAS_PROC_BUS
+EXPORT_SYMBOL(proc_pccard);
+#endif
+#ifndef HAVE_MEMRESERVE
+EXPORT_SYMBOL(request_mem_region);
+EXPORT_SYMBOL(release_mem_region);
+#endif
+#ifdef CONFIG_PNP_BIOS
+EXPORT_SYMBOL(check_pnp_irq);
+#endif
+#ifdef CONFIG_PCI
+EXPORT_SYMBOL(pci_irq_mask);
+#if (LINUX_VERSION_CODE < VERSION(2,3,24))
+EXPORT_SYMBOL(pci_enable_device);
+EXPORT_SYMBOL(pci_set_power_state);
+#endif
+#endif
+
+#endif
+
+static int __init init_pcmcia_cs(void)
+{
+ printk(KERN_INFO "%s\n", release);
+#ifdef UTS_RELEASE
+ printk(KERN_INFO " %s\n", kernel);
+#endif
+ printk(KERN_INFO " %s\n", options);
+ DEBUG(0, "%s\n", version);
+#ifdef CONFIG_PM
+ if (do_apm)
+ pm_register(PM_SYS_DEV, PM_SYS_PCMCIA, handle_pm_event);
+#endif
+#ifdef CONFIG_PCI
+ pci_fixup_init();
+#endif
+#ifdef CONFIG_PNP_BIOS
+ if (do_pnp) {
+ pnp_bios_init();
+ pnp_proc_init();
+ pnp_rsrc_init();
+ }
+#endif
+ register_symtab(&cs_symtab);
+#ifdef HAS_PROC_BUS
+ proc_pccard = proc_mkdir("pccard", proc_bus);
+#ifdef CONFIG_PNP_BIOS
+ if (proc_pccard) {
+ create_proc_read_entry("ioport", 0, proc_pccard,
+ proc_read_io, NULL);
+ create_proc_read_entry("irq", 0, proc_pccard,
+ proc_read_irq, NULL);
+ }
+#endif
+#ifndef HAVE_MEMRESERVE
+ if (proc_pccard)
+ create_proc_read_entry("memory", 0, proc_pccard,
+ proc_read_mem, NULL);
+#endif
+#endif
+ return 0;
+}
+
+static void __exit exit_pcmcia_cs(void)
+{
+ printk(KERN_INFO "unloading PCMCIA Card Services\n");
+#ifdef HAS_PROC_BUS
+ if (proc_pccard) {
+#ifdef CONFIG_PNP_BIOS
+ remove_proc_entry("ioport", proc_pccard);
+ remove_proc_entry("irq", proc_pccard);
+#endif
+#ifndef HAVE_MEMRESERVE
+ remove_proc_entry("memory", proc_pccard);
+#endif
+ remove_proc_entry("pccard", proc_bus);
+ }
+#endif
+#ifdef CONFIG_PM
+ if (do_apm)
+ pm_unregister_all(handle_pm_event);
+#endif
+#ifdef CONFIG_PCI
+ pci_fixup_done();
+#endif
+#ifdef CONFIG_PNP_BIOS
+ if (do_pnp) {
+ pnp_proc_done();
+ pnp_rsrc_done();
+ }
+#endif
+ release_resource_db();
+}
+
+module_init(init_pcmcia_cs);
+module_exit(exit_pcmcia_cs);
diff --git a/linux/pcmcia-cs/modules/cs_internal.h b/linux/pcmcia-cs/modules/cs_internal.h
new file mode 100644
index 0000000..c4d4d45
--- /dev/null
+++ b/linux/pcmcia-cs/modules/cs_internal.h
@@ -0,0 +1,300 @@
+/*
+ * cs_internal.h 1.58 2004/04/25 17:58:22
+ *
+ * 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
+ * <dahinds@users.sourceforge.net>. 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 General 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_CS_INTERNAL_H
+#define _LINUX_CS_INTERNAL_H
+
+#include <linux/config.h>
+#include <linux/spinlock.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 {
+#ifdef USE_SPIN_LOCKS
+ spinlock_t lock;
+#endif
+ 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 setup_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 HAS_PROC_BUS
+ 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);
+int read_cb_mem(socket_info_t *s, u_char fn, int space,
+ u_int addr, u_int len, void *ptr);
+void cb_release_cis_mem(socket_info_t *s);
+
+/* In cistpl.c */
+int 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);
+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, ioaddr_t align,
+ char *name);
+int find_mem_region(u_long *base, u_long num, u_long align,
+ int force_low, char *name);
+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);
+int check_pnp_irq(int n);
+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);
+
+/* in pci_fixup */
+void pci_fixup_init(void);
+void pci_fixup_done(void);
+
+#define MAX_SOCK 8
+extern socket_t sockets;
+extern socket_info_t *socket_table[MAX_SOCK];
+
+#ifdef HAS_PROC_BUS
+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/linux/pcmcia-cs/modules/ds.c b/linux/pcmcia-cs/modules/ds.c
new file mode 100644
index 0000000..b6eb10e
--- /dev/null
+++ b/linux/pcmcia-cs/modules/ds.c
@@ -0,0 +1,1004 @@
+/*======================================================================
+
+ PC Card Driver Services
+
+ ds.c 1.115 2002/10/12 19:03:44
+
+ 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
+ <dahinds@users.sourceforge.net>. 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 General 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/slab.h>
+#include <linux/mm.h>
+#include <linux/fcntl.h>
+#include <linux/sched.h>
+#include <linux/timer.h>
+#include <linux/ioctl.h>
+#include <linux/proc_fs.h>
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,23))
+#include <linux/poll.h>
+#endif
+
+#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>
+
+/*====================================================================*/
+
+/* Module parameters */
+
+MODULE_AUTHOR("David Hinds <dahinds@users.sourceforge.net>");
+MODULE_DESCRIPTION("PCMCIA Driver Services " CS_RELEASE);
+MODULE_LICENSE("Dual MPL/GPL");
+
+#define INT_MODULE_PARM(n, v) static int n = v; MODULE_PARM(n, "i")
+
+#ifdef PCMCIA_DEBUG
+INT_MODULE_PARM(pc_debug, PCMCIA_DEBUG);
+#define DEBUG(n, args...) if (pc_debug>(n)) printk(KERN_DEBUG args)
+static const char *version =
+"ds.c 1.115 2002/10/12 19:03:44 (David Hinds)";
+#else
+#define DEBUG(n, args...)
+#endif
+
+/*====================================================================*/
+
+typedef struct driver_info_t {
+ dev_info_t dev_info;
+ int use_count, status;
+ 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;
+ u_char function;
+ 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;
+
+extern struct proc_dir_entry *proc_pccard;
+
+/* We use this to distinguish in-kernel from modular drivers */
+static int init_status = 1;
+
+/*====================================================================*/
+
+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);
+ if (!driver) return -ENOMEM;
+ strncpy(driver->dev_info, (char *)dev_info, DEV_NAME_LEN);
+ driver->use_count = 0;
+ driver->status = init_status;
+ 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 -ENODEV;
+
+ 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 */
+
+/*====================================================================*/
+
+#ifdef HAS_PROC_BUS
+static int proc_read_drivers(char *buf, char **start, off_t pos,
+ int count, int *eof, void *data)
+{
+ driver_info_t *d;
+ char *p = buf;
+ for (d = root_driver; d; d = d->next)
+ p += sprintf(p, "%-24.24s %d %d\n", d->dev_info,
+ d->status, d->use_count);
+ return (p - buf);
+}
+#endif
+
+/*======================================================================
+
+ 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);
+ if (!driver) return -ENOMEM;
+ 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) &&
+ (bind_info->function == b->function))
+ break;
+ if (b != NULL) {
+ bind_info->instance = b->instance;
+ return -EBUSY;
+ }
+ b = kmalloc(sizeof(socket_bind_t), GFP_KERNEL);
+ if (!b)
+ return -ENOMEM;
+
+ 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);
+ kfree(b);
+ return -ENODEV;
+ }
+
+ /* Add binding to list for this socket */
+ driver->use_count++;
+ b->driver = driver;
+ b->function = bind_info->function;
+ 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) &&
+ (b->function == bind_info->function))
+ 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) &&
+ ((*b)->function == bind_info->function))
+ 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(c->driver);
+ }
+ }
+ *b = c->next;
+ kfree(c);
+
+ 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);
+ if (!user) {
+ MOD_DEC_USE_COUNT;
+ return -ENOMEM;
+ }
+ 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 FS_RELEASE_T 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 (FS_RELEASE_T)0;
+ s = &socket_table[i];
+ user = file->private_data;
+ if (CHECK_USER(user))
+ return (FS_RELEASE_T)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 (FS_RELEASE_T)0;
+ *link = user->next;
+ user->user_magic = 0;
+ kfree(user);
+
+ MOD_DEC_USE_COUNT;
+ return (FS_RELEASE_T)0;
+} /* ds_release */
+
+/*====================================================================*/
+
+static ssize_t ds_read FOPS(struct inode *inode,
+ struct file *file, char *buf,
+ size_t count, loff_t *ppos)
+{
+ socket_t i = MINOR(F_INODE(file)->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 FOPS(struct inode *inode,
+ struct file *file, const char *buf,
+ size_t count, loff_t *ppos)
+{
+ socket_t i = MINOR(F_INODE(file)->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 */
+
+/*====================================================================*/
+
+#if (LINUX_VERSION_CODE < VERSION(2,1,23))
+
+static int ds_select(struct inode *inode, struct file *file,
+ int sel_type, select_table *wait)
+{
+ socket_t i = MINOR(inode->i_rdev);
+ socket_info_t *s;
+ user_info_t *user;
+
+ DEBUG(2, "ds_select(socket %d)\n", i);
+
+ if ((i >= sockets) || (sockets == 0))
+ return -ENODEV;
+ s = &socket_table[i];
+ user = file->private_data;
+ if (CHECK_USER(user))
+ return -EIO;
+ if (sel_type != SEL_IN)
+ return 0;
+ if (!queue_empty(user))
+ return 1;
+ select_wait(&s->queue, wait);
+ return 0;
+} /* ds_select */
+
+#else
+
+static u_int ds_poll(struct file *file, poll_table *wait)
+{
+ socket_t i = MINOR(F_INODE(file)->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 */
+
+#endif
+
+/*====================================================================*/
+
+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) && !capable(CAP_SYS_ADMIN))
+ 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) && !capable(CAP_SYS_ADMIN))
+ 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 (!capable(CAP_SYS_ADMIN)) 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 (!capable(CAP_SYS_ADMIN)) 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 = {
+ open: ds_open,
+ release: ds_release,
+ ioctl: ds_ioctl,
+ read: ds_read,
+ write: ds_write,
+#if (LINUX_VERSION_CODE < VERSION(2,1,23))
+ select: ds_select
+#else
+ poll: ds_poll
+#endif
+};
+
+#if (LINUX_VERSION_CODE <= VERSION(2,1,17))
+
+#undef CONFIG_MODVERSIONS
+static struct symbol_table ds_symtab = {
+#include <linux/symtab_begin.h>
+#undef X
+#define X(sym) { (void *)&sym, SYMBOL_NAME_STR(sym) }
+ X(register_pccard_driver),
+ X(unregister_pccard_driver),
+#include <linux/symtab_end.h>
+};
+
+#else
+
+EXPORT_SYMBOL(register_pccard_driver);
+EXPORT_SYMBOL(unregister_pccard_driver);
+
+#endif
+
+/*====================================================================*/
+
+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 -EINVAL;
+ }
+ 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);
+ if (!socket_table) return -1;
+ 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;
+ init_timer(&s->removal);
+ 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;
+ register_symtab(&ds_symtab);
+
+#ifdef HAS_PROC_BUS
+ if (proc_pccard)
+ create_proc_read_entry("drivers", 0, proc_pccard,
+ proc_read_drivers, NULL);
+ init_status = 0;
+#endif
+ return 0;
+}
+
+#ifdef MODULE
+
+int __init init_module(void)
+{
+ return init_pcmcia_ds();
+}
+
+void __exit cleanup_module(void)
+{
+ int i;
+#ifdef HAS_PROC_BUS
+ if (proc_pccard)
+ remove_proc_entry("drivers", proc_pccard);
+#endif
+ 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/linux/pcmcia-cs/modules/ene.h b/linux/pcmcia-cs/modules/ene.h
new file mode 100644
index 0000000..6b9b18b
--- /dev/null
+++ b/linux/pcmcia-cs/modules/ene.h
@@ -0,0 +1,59 @@
+/*
+ * ene.h 1.2 2001/08/24 12:15:33
+ *
+ * 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
+ * <dahinds@users.sourceforge.net>. 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 General 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_ENE_H
+#define _LINUX_ENE_H
+
+#ifndef PCI_VENDOR_ID_ENE
+#define PCI_VENDOR_ID_ENE 0x1524
+#endif
+
+#ifndef PCI_DEVICE_ID_ENE_1211
+#define PCI_DEVICE_ID_ENE_1211 0x1211
+#endif
+#ifndef PCI_DEVICE_ID_ENE_1225
+#define PCI_DEVICE_ID_ENE_1225 0x1225
+#endif
+#ifndef PCI_DEVICE_ID_ENE_1410
+#define PCI_DEVICE_ID_ENE_1410 0x1410
+#endif
+#ifndef PCI_DEVICE_ID_ENE_1420
+#define PCI_DEVICE_ID_ENE_1420 0x1420
+#endif
+
+#define ENE_PCIC_ID \
+ IS_ENE1211, IS_ENE1225, IS_ENE1410, IS_ENE1420
+
+#define ENE_PCIC_INFO \
+ { "ENE 1211", IS_TI|IS_CARDBUS, ID(ENE, 1211) }, \
+ { "ENE 1225", IS_TI|IS_CARDBUS, ID(ENE, 1225) }, \
+ { "ENE 1410", IS_TI|IS_CARDBUS, ID(ENE, 1410) }, \
+ { "ENE 1420", IS_TI|IS_CARDBUS, ID(ENE, 1420) }
+
+#endif /* _LINUX_ENE_H */
diff --git a/linux/pcmcia-cs/modules/i82365.c b/linux/pcmcia-cs/modules/i82365.c
new file mode 100644
index 0000000..fc1c782
--- /dev/null
+++ b/linux/pcmcia-cs/modules/i82365.c
@@ -0,0 +1,2570 @@
+/*======================================================================
+
+ Device driver for Intel 82365 and compatible PC Card controllers,
+ and Yenta-compatible PCI-to-CardBus controllers.
+
+ i82365.c 1.358 2003/09/13 17:34:01
+
+ 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
+ <dahinds@users.sourceforge.net>. 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 General 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/slab.h>
+#include <linux/pci.h>
+#include <linux/ioport.h>
+#include <linux/delay.h>
+#include <linux/proc_fs.h>
+#include <linux/spinlock.h>
+#include <asm/irq.h>
+#include <asm/io.h>
+#include <asm/bitops.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"
+#include "ene.h"
+
+/*====================================================================*/
+
+/* Module parameters */
+
+MODULE_AUTHOR("David Hinds <dahinds@users.sourceforge.net>");
+MODULE_DESCRIPTION("Intel ExCA/Yenta PCMCIA socket driver");
+MODULE_LICENSE("Dual MPL/GPL");
+
+#define INT_MODULE_PARM(n, v) static int n = v; MODULE_PARM(n, "i")
+
+/* General options */
+INT_MODULE_PARM(poll_interval, 0); /* in ticks, 0 means never */
+INT_MODULE_PARM(cycle_time, 120); /* in ns, 120 ns = 8.33 MHz */
+INT_MODULE_PARM(do_scan, 1); /* Probe free interrupts? */
+
+/* Cirrus options */
+INT_MODULE_PARM(has_dma, -1);
+INT_MODULE_PARM(has_led, -1);
+INT_MODULE_PARM(has_ring, -1);
+INT_MODULE_PARM(has_vsense, 0);
+INT_MODULE_PARM(dynamic_mode, 0);
+INT_MODULE_PARM(freq_bypass, -1);
+INT_MODULE_PARM(setup_time, -1);
+INT_MODULE_PARM(cmd_time, -1);
+INT_MODULE_PARM(recov_time, -1);
+
+#ifdef CONFIG_ISA
+INT_MODULE_PARM(i365_base, 0x3e0); /* IO address for probes */
+INT_MODULE_PARM(extra_sockets, 0); /* Probe at i365_base+2? */
+INT_MODULE_PARM(ignore, -1); /* Ignore this socket # */
+INT_MODULE_PARM(cs_irq, 0); /* card status irq */
+INT_MODULE_PARM(irq_mask, 0xffff); /* bit map of irq's to use */
+static int irq_list[16] = { -1 };
+MODULE_PARM(irq_list, "1-16i");
+INT_MODULE_PARM(async_clock, -1); /* Vadem specific */
+INT_MODULE_PARM(cable_mode, -1);
+INT_MODULE_PARM(wakeup, 0); /* Cirrus specific */
+#endif
+
+#ifdef CONFIG_PCI
+static int pci_irq_list[8] = { 0 }; /* PCI interrupt assignments */
+MODULE_PARM(pci_irq_list, "1-8i");
+INT_MODULE_PARM(do_pci_probe, 1); /* Scan for PCI bridges? */
+INT_MODULE_PARM(fast_pci, -1);
+INT_MODULE_PARM(cb_write_post, -1);
+INT_MODULE_PARM(irq_mode, -1); /* Override BIOS routing? */
+INT_MODULE_PARM(hold_time, -1); /* Ricoh specific */
+INT_MODULE_PARM(p2cclk, -1); /* TI specific */
+#endif
+
+#if defined(CONFIG_ISA) && defined(CONFIG_PCI)
+INT_MODULE_PARM(pci_csc, 1); /* PCI card status irqs? */
+INT_MODULE_PARM(pci_int, 1); /* PCI IO card irqs? */
+#elif defined(CONFIG_ISA) && !defined(CONFIG_PCI)
+#define pci_csc 0
+#define pci_int 0
+#elif !defined(CONFIG_ISA) && defined(CONFIG_PCI)
+#define pci_csc 0
+#define pci_int 1 /* We must use PCI irq's */
+#else
+#error "No bus architectures defined!"
+#endif
+
+#ifdef PCMCIA_DEBUG
+INT_MODULE_PARM(pc_debug, PCMCIA_DEBUG);
+#define DEBUG(n, args...) if (pc_debug>(n)) printk(KERN_DEBUG args)
+static const char *version =
+"i82365.c 1.358 2003/09/13 17:34:01 (David Hinds)";
+#else
+#define DEBUG(n, args...) do { } while (0)
+#endif
+
+/*====================================================================*/
+
+typedef struct socket_info_t {
+ u_short type, flags;
+ socket_cap_t cap;
+ ioaddr_t ioaddr;
+ u_short psock;
+ u_char cs_irq, intr;
+ void (*handler)(void *info, u_int events);
+ void *info;
+#ifdef HAS_PROC_BUS
+ struct proc_dir_entry *proc;
+#endif
+ u_char pci_irq_code;
+#ifdef CONFIG_PCI
+ u_short vendor, device;
+ u_char revision, bus, devfn;
+ u_short bcr;
+ u_char pci_lat, cb_lat, sub_bus, cache;
+ u_int cb_phys;
+ char *cb_virt;
+#endif
+ union {
+ cirrus_state_t cirrus;
+ vg46x_state_t vg46x;
+ o2micro_state_t o2micro;
+ ti113x_state_t ti113x;
+ ricoh_state_t ricoh;
+ topic_state_t topic;
+ } state;
+} socket_info_t;
+
+/* Where we keep track of our sockets... */
+static int sockets = 0;
+static socket_info_t socket[8] = {
+ { 0, }, /* ... */
+};
+
+#ifdef CONFIG_ISA
+static int grab_irq;
+#ifdef USE_SPIN_LOCKS
+static spinlock_t isa_lock = SPIN_LOCK_UNLOCKED;
+#endif
+#define ISA_LOCK(s, f) \
+ if (!((s)->flags & IS_CARDBUS)) spin_lock_irqsave(&isa_lock, f)
+#define ISA_UNLOCK(n, f) \
+ if (!((s)->flags & IS_CARDBUS)) spin_unlock_irqrestore(&isa_lock, f)
+#else
+#define ISA_LOCK(n, f) do { } while (0)
+#define ISA_UNLOCK(n, f) do { } while (0)
+#endif
+
+static void pcic_interrupt_wrapper(u_long data);
+static struct timer_list poll_timer = {
+ function: pcic_interrupt_wrapper
+};
+
+#define flip(v,b,f) (v = ((f)<0) ? v : ((f) ? ((v)|(b)) : ((v)&(~b))))
+
+/*====================================================================*/
+
+/* Some PCI shortcuts */
+
+#ifdef CONFIG_PCI
+static int pci_readb(socket_info_t *s, int r, u_char *v)
+{ return pcibios_read_config_byte(s->bus, s->devfn, r, v); }
+static int pci_writeb(socket_info_t *s, int r, u_char v)
+{ return pcibios_write_config_byte(s->bus, s->devfn, r, v); }
+static int pci_readw(socket_info_t *s, int r, u_short *v)
+{ return pcibios_read_config_word(s->bus, s->devfn, r, v); }
+static int pci_writew(socket_info_t *s, int r, u_short v)
+{ return pcibios_write_config_word(s->bus, s->devfn, r, v); }
+static int pci_readl(socket_info_t *s, int r, u_int *v)
+{ return pcibios_read_config_dword(s->bus, s->devfn, r, v); }
+static int pci_writel(socket_info_t *s, int r, u_int v)
+{ return pcibios_write_config_dword(s->bus, s->devfn, r, v); }
+#endif
+
+#define cb_readb(s, r) readb((s)->cb_virt + (r))
+#define cb_readl(s, r) readl((s)->cb_virt + (r))
+#define cb_writeb(s, r, v) writeb(v, (s)->cb_virt + (r))
+#define cb_writel(s, r, v) writel(v, (s)->cb_virt + (r))
+
+/*====================================================================*/
+
+/* 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_I82092AA, IS_OM82C092G, CIRRUS_PCIC_ID, O2MICRO_PCIC_ID,
+ RICOH_PCIC_ID, SMC_PCIC_ID, TI_PCIC_ID, ENE_PCIC_ID,
+ TOPIC_PCIC_ID, 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_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;
+
+#define ID(a,b) PCI_VENDOR_ID_##a,PCI_DEVICE_ID_##a##_##b
+
+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 },
+#endif
+#ifdef CONFIG_PCI
+ { "Intel 82092AA", IS_PCI, ID(INTEL, 82092AA_0) },
+ { "Omega Micro 82C092G", IS_PCI, ID(OMEGA, 82C092G) },
+ CIRRUS_PCIC_INFO, O2MICRO_PCIC_INFO, RICOH_PCIC_INFO,
+ SMC_PCIC_INFO, TI_PCIC_INFO, ENE_PCIC_INFO, TOPIC_PCIC_INFO,
+ { "Unknown", IS_PCI|IS_UNKNOWN, 0, 0 },
+ { "Unknown", IS_CARDBUS|IS_UNKNOWN, 0, 0 }
+#endif
+};
+
+#define PCIC_COUNT (sizeof(pcic)/sizeof(pcic_t))
+
+/*====================================================================*/
+
+static u_char i365_get(socket_info_t *s, u_short reg)
+{
+#ifdef CONFIG_PCI
+ if (s->cb_virt)
+ return cb_readb(s, 0x0800 + reg);
+#endif
+ outb(I365_REG(s->psock, reg), s->ioaddr);
+ return inb(s->ioaddr+1);
+}
+
+static void i365_set(socket_info_t *s, u_short reg, u_char data)
+{
+#ifdef CONFIG_PCI
+ if (s->cb_virt) {
+ cb_writeb(s, 0x0800 + reg, data);
+ return;
+ }
+#endif
+ outb(I365_REG(s->psock, reg), s->ioaddr);
+ outb(data, s->ioaddr+1);
+}
+
+static void i365_bset(socket_info_t *s, u_short reg, u_char mask)
+{
+ i365_set(s, reg, i365_get(s, reg) | mask);
+}
+
+static void i365_bclr(socket_info_t *s, u_short reg, u_char mask)
+{
+ i365_set(s, reg, i365_get(s, reg) & ~mask);
+}
+
+static void i365_bflip(socket_info_t *s, u_short reg, u_char mask, int b)
+{
+ u_char d = i365_get(s, reg);
+ i365_set(s, reg, (b) ? (d | mask) : (d & ~mask));
+}
+
+static u_short i365_get_pair(socket_info_t *s, u_short reg)
+{
+ return (i365_get(s, reg) + (i365_get(s, reg+1) << 8));
+}
+
+static void i365_set_pair(socket_info_t *s, u_short reg, u_short data)
+{
+ i365_set(s, reg, data & 0xff);
+ i365_set(s, 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.
+
+======================================================================*/
+
+#ifdef CONFIG_PCI
+
+static int __init get_pci_irq(socket_info_t *s)
+{
+ u8 irq = pci_irq_list[s - socket];
+ if (!irq)
+ irq = pci_find_slot(s->bus, s->devfn)->irq;
+ if (irq >= NR_IRQS) irq = 0;
+ s->cap.pci_irq = irq;
+ return irq;
+}
+
+#endif
+
+static void __init cirrus_get_state(socket_info_t *s)
+{
+ cirrus_state_t *p = &s->state.cirrus;
+ int i;
+
+ 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);
+ if (s->flags & IS_PCI)
+ p->ectl1 = pd67_ext_get(s, PD67_EXT_CTL_1);
+ for (i = 0; i < 6; i++)
+ p->timer[i] = i365_get(s, PD67_TIME_SETUP(0)+i);
+}
+
+static void cirrus_set_state(socket_info_t *s)
+{
+ cirrus_state_t *p = &s->state.cirrus;
+ u_char misc;
+ int i;
+
+ 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);
+ if (s->flags & IS_PCI)
+ pd67_ext_set(s, PD67_EXT_CTL_1, p->ectl1);
+ else if (has_vsense) {
+ socket_info_t *t = (s->psock) ? s : s+1;
+ pd67_ext_set(t, PD67_EXT_CTL_2, PD67_EC2_GPSTB_IOR);
+ }
+ 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(socket_info_t *s, int pcsc, int pint)
+{
+ flip(s->bcr, PD6832_BCR_MGMT_IRQ_ENA, !pcsc);
+ return 0;
+}
+#endif
+
+static u_int __init cirrus_set_opts(socket_info_t *s, char *buf)
+{
+ cirrus_state_t *p = &s->state.cirrus;
+ u_int mask = 0xffff;
+
+ p->misc1 |= PD67_MC1_SPKR_ENA;
+ 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 (!(s->flags & (IS_PCI|IS_CARDBUS))) {
+ flip(p->misc2, PD67_MC2_FREQ_BYPASS, freq_bypass);
+ if (p->misc2 & PD67_MC2_FREQ_BYPASS)
+ strcat(buf, " [freq bypass]");
+ 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;
+ }
+#ifdef CONFIG_PCI
+ } else {
+ p->misc1 &= ~PD67_MC1_MEDIA_ENA;
+ p->misc1 &= ~(PD67_MC1_PULSE_MGMT | PD67_MC1_PULSE_IRQ);
+ p->ectl1 &= ~(PD67_EC1_INV_MGMT_IRQ | PD67_EC1_INV_CARD_IRQ);
+ flip(p->misc2, PD67_MC2_FAST_PCI, fast_pci);
+ if (p->misc2 & PD67_MC2_IRQ15_RI)
+ mask &= (s->type == IS_PD6730) ? ~0x0400 : ~0x8000;
+ if ((s->flags & IS_PCI) && (irq_mode == 1) && get_pci_irq(s)) {
+ /* Configure PD6729 bridge for PCI interrupts */
+ p->ectl1 |= PD67_EC1_INV_MGMT_IRQ | PD67_EC1_INV_CARD_IRQ;
+ s->pci_irq_code = 3; /* PCI INTA = "irq 3" */
+ buf += strlen(buf);
+ sprintf(buf, " [pci irq %d]", s->cap.pci_irq);
+ mask = 0;
+ }
+#endif
+ }
+#ifdef CONFIG_ISA
+ if (s->type != IS_VT83C469)
+#endif
+ {
+ 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 __init vg46x_get_state(socket_info_t *s)
+{
+ vg46x_state_t *p = &s->state.vg46x;
+ p->ctl = i365_get(s, VG468_CTL);
+ if (s->type == IS_VG469)
+ p->ema = i365_get(s, VG469_EXT_MODE);
+}
+
+static void vg46x_set_state(socket_info_t *s)
+{
+ vg46x_state_t *p = &s->state.vg46x;
+ i365_set(s, VG468_CTL, p->ctl);
+ if (s->type == IS_VG469)
+ i365_set(s, VG469_EXT_MODE, p->ema);
+}
+
+static u_int __init vg46x_set_opts(socket_info_t *s, char *buf)
+{
+ vg46x_state_t *p = &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 (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 __init ti113x_get_state(socket_info_t *s)
+{
+ ti113x_state_t *p = &s->state.ti113x;
+ pci_readl(s, TI113X_SYSTEM_CONTROL, &p->sysctl);
+ pci_readb(s, TI113X_CARD_CONTROL, &p->cardctl);
+ pci_readb(s, TI113X_DEVICE_CONTROL, &p->devctl);
+ pci_readb(s, TI1250_DIAGNOSTIC, &p->diag);
+ pci_readl(s, TI12XX_IRQMUX, &p->irqmux);
+}
+
+static void ti113x_set_state(socket_info_t *s)
+{
+ ti113x_state_t *p = &s->state.ti113x;
+ pci_writel(s, TI113X_SYSTEM_CONTROL, p->sysctl);
+ pci_writeb(s, TI113X_CARD_CONTROL, p->cardctl);
+ pci_writeb(s, TI113X_DEVICE_CONTROL, p->devctl);
+ pci_writeb(s, TI1250_MULTIMEDIA_CTL, 0);
+ pci_writeb(s, TI1250_DIAGNOSTIC, p->diag);
+ pci_writel(s, TI12XX_IRQMUX, p->irqmux);
+ i365_set_pair(s, TI113X_IO_OFFSET(0), 0);
+ i365_set_pair(s, TI113X_IO_OFFSET(1), 0);
+}
+
+static int ti113x_set_irq_mode(socket_info_t *s, int pcsc, int pint)
+{
+ ti113x_state_t *p = &s->state.ti113x;
+ s->intr = (pcsc) ? I365_INTR_ENA : 0;
+ if (s->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 (s->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(socket_info_t *s, char *buf)
+{
+ ti113x_state_t *p = &s->state.ti113x;
+ u_int mask = 0xffff;
+ int old = (s->type <= IS_TI1131);
+
+ flip(p->cardctl, TI113X_CCR_RIENB, has_ring);
+ p->cardctl &= ~TI113X_CCR_ZVENABLE;
+ p->cardctl |= TI113X_CCR_SPKROUTEN;
+ if (!old) flip(p->sysctl, TI122X_SCR_P2CCLK, p2cclk);
+ switch (irq_mode) {
+ case 0:
+ p->devctl &= ~TI113X_DCR_IMODE_MASK;
+ p->irqmux = (p->irqmux & ~0x0f) | 0x02; /* route INTA */
+ if (!(p->sysctl & TI122X_SCR_INTRTIE))
+ p->irqmux = (p->irqmux & ~0xf0) | 0x20; /* route INTB */
+ break;
+ 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:
+ /* Feeble fallback: if PCI-only but no PCI irq, try ISA */
+ if (((p->devctl & TI113X_DCR_IMODE_MASK) == 0) &&
+ (s->cap.pci_irq == 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;
+ }
+ }
+ 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.
+
+ The interrupt test doesn't seem to be reliable with Ricoh
+ bridges. It seems to depend on what type of card is in the
+ socket, and on the history of that socket, in some way that
+ doesn't show up in the current socket state.
+
+======================================================================*/
+
+#ifdef CONFIG_PCI
+
+static void __init ricoh_get_state(socket_info_t *s)
+{
+ ricoh_state_t *p = &s->state.ricoh;
+ pci_readw(s, RL5C4XX_CONFIG, &p->config);
+ pci_readw(s, RL5C4XX_MISC, &p->misc);
+ pci_readw(s, RL5C4XX_16BIT_CTL, &p->ctl);
+ pci_readw(s, RL5C4XX_16BIT_IO_0, &p->io);
+ pci_readw(s, RL5C4XX_16BIT_MEM_0, &p->mem);
+}
+
+static void ricoh_set_state(socket_info_t *s)
+{
+ ricoh_state_t *p = &s->state.ricoh;
+ pci_writew(s, RL5C4XX_CONFIG, p->config);
+ pci_writew(s, RL5C4XX_MISC, p->misc);
+ pci_writew(s, RL5C4XX_16BIT_CTL, p->ctl);
+ pci_writew(s, RL5C4XX_16BIT_IO_0, p->io);
+ pci_writew(s, RL5C4XX_16BIT_MEM_0, p->mem);
+}
+
+static u_int __init ricoh_set_opts(socket_info_t *s, char *buf)
+{
+ ricoh_state_t *p = &s->state.ricoh;
+ u_int mask = 0xffff;
+ int old = (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;
+ else
+ p->config |= RL5C4XX_CONFIG_PREFETCH;
+
+ 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 (irq_mode == 0) {
+ mask = 0;
+ p->misc &= ~RL5C47X_MISC_SRIRQ_ENA;
+ sprintf(buf, " [pci only]");
+ buf += strlen(buf);
+ } else 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 __init o2micro_get_state(socket_info_t *s)
+{
+ o2micro_state_t *p = &s->state.o2micro;
+ if ((s->revision == 0x34) || (s->revision == 0x62) ||
+ (s->type == IS_OZ6812)) {
+ 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 (s->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(socket_info_t *s)
+{
+ o2micro_state_t *p = &s->state.o2micro;
+ if ((s->revision == 0x34) || (s->revision == 0x62) ||
+ (s->type == IS_OZ6812)) {
+ 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 (s->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(socket_info_t *s, char *buf)
+{
+ o2micro_state_t *p = &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 (s->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;
+ if (s->revision == 0x34)
+ p->mode_c = 0x20;
+ } else {
+ if (p->mode_b & O2_MODE_B_IRQ15_RI) mask &= ~0x8000;
+ }
+ if (p->mode_b & O2_MODE_B_IRQ15_RI)
+ strcat(buf, " [ring]");
+ if (irq_mode != -1)
+ p->mode_d = irq_mode;
+ if (p->mode_d & O2_MODE_D_ISA_IRQ) {
+ strcat(buf, " [pci+isa]");
+ } else {
+ switch (p->mode_d & O2_MODE_D_IRQ_MODE) {
+ case O2_MODE_D_IRQ_PCPCI:
+ strcat(buf, " [pc/pci]"); break;
+ case O2_MODE_D_IRQ_PCIWAY:
+ strcat(buf, " [pci/way]"); break;
+ case O2_MODE_D_IRQ_PCI:
+ strcat(buf, " [pci only]"); mask = 0; break;
+ }
+ }
+ if (s->flags & IS_CARDBUS) {
+ if (p->mode_d & O2_MODE_D_W97_IRQ)
+ strcat(buf, " [win97]");
+ }
+ 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 __init topic_get_state(socket_info_t *s)
+{
+ topic_state_t *p = &s->state.topic;
+ pci_readb(s, TOPIC_SLOT_CONTROL, &p->slot);
+ pci_readb(s, TOPIC_CARD_CONTROL, &p->ccr);
+ pci_readb(s, TOPIC_CARD_DETECT, &p->cdr);
+ pci_readl(s, TOPIC_REGISTER_CONTROL, &p->rcr);
+ p->fcr = i365_get(s, TOPIC_FUNCTION_CONTROL);
+}
+
+static void topic_set_state(socket_info_t *s)
+{
+ topic_state_t *p = &s->state.topic;
+ u_int state;
+ pci_writeb(s, TOPIC_SLOT_CONTROL, p->slot);
+ pci_writeb(s, TOPIC_CARD_CONTROL, p->ccr);
+ pci_writeb(s, TOPIC_CARD_DETECT, p->cdr);
+ pci_writel(s, TOPIC_REGISTER_CONTROL, p->rcr);
+ i365_set(s, TOPIC_FUNCTION_CONTROL, p->fcr);
+ state = cb_readl(s, CB_SOCKET_STATE);
+ if (!(state & CB_SS_32BIT))
+ cb_writel(s, CB_SOCKET_CONTROL, 0);
+ if (!(state & CB_SS_VSENSE))
+ cb_writel(s, CB_SOCKET_FORCE, CB_SF_CVSTEST);
+}
+
+static u_int __init topic_set_opts(socket_info_t *s, char *buf)
+{
+ topic_state_t *p = &s->state.topic;
+
+ p->slot |= TOPIC_SLOT_SLOTON|TOPIC_SLOT_SLOTEN;
+ p->slot &= ~TOPIC_SLOT_ID_LOCK;
+ p->cdr |= TOPIC_CDR_MODE_PC32;
+ p->cdr &= ~(TOPIC_CDR_SW_DETECT);
+ p->ccr |= TOPIC97_ICR_IRQSEL;
+ p->fcr |= TOPIC_FCR_3V_ENA;
+ 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
+
+======================================================================*/
+
+/* Default settings for PCI command configuration register */
+#define CMD_DFLT (PCI_COMMAND_IO|PCI_COMMAND_MEMORY| \
+ PCI_COMMAND_MASTER|PCI_COMMAND_WAIT)
+
+#ifdef CONFIG_PCI
+
+static void __init cb_get_state(socket_info_t *s)
+{
+ pci_readb(s, PCI_CACHE_LINE_SIZE, &s->cache);
+ pci_readb(s, PCI_LATENCY_TIMER, &s->pci_lat);
+ pci_readb(s, CB_LATENCY_TIMER, &s->cb_lat);
+ pci_readb(s, CB_CARDBUS_BUS, &s->cap.cardbus);
+ pci_readb(s, CB_SUBORD_BUS, &s->sub_bus);
+ pci_readw(s, CB_BRIDGE_CONTROL, &s->bcr);
+ get_pci_irq(s);
+}
+
+static void cb_set_state(socket_info_t *s)
+{
+ pci_set_power_state(pci_find_slot(s->bus, s->devfn), 0);
+ pci_writel(s, CB_LEGACY_MODE_BASE, 0);
+ pci_writel(s, PCI_BASE_ADDRESS_0, s->cb_phys);
+ pci_writew(s, PCI_COMMAND, CMD_DFLT);
+ pci_writeb(s, PCI_CACHE_LINE_SIZE, s->cache);
+ pci_writeb(s, PCI_LATENCY_TIMER, s->pci_lat);
+ pci_writeb(s, CB_LATENCY_TIMER, s->cb_lat);
+ pci_writeb(s, CB_CARDBUS_BUS, s->cap.cardbus);
+ pci_writeb(s, CB_SUBORD_BUS, s->sub_bus);
+ pci_writew(s, CB_BRIDGE_CONTROL, s->bcr);
+}
+
+static int cb_get_irq_mode(socket_info_t *s)
+{
+ return (!(s->bcr & CB_BCR_ISA_IRQ));
+}
+
+static int cb_set_irq_mode(socket_info_t *s, int pcsc, int pint)
+{
+ flip(s->bcr, CB_BCR_ISA_IRQ, !(pint));
+ if (s->flags & IS_CIRRUS)
+ return cirrus_set_irq_mode(s, pcsc, pint);
+ else if (s->flags & IS_TI)
+ return ti113x_set_irq_mode(s, pcsc, pint);
+ /* By default, assume that we can't do ISA status irqs */
+ return (!pcsc);
+}
+
+static void __init cb_set_opts(socket_info_t *s, char *buf)
+{
+ s->bcr |= CB_BCR_WRITE_POST;
+ /* some TI1130's seem to exhibit problems with write posting */
+ if (((s->type == IS_TI1130) && (s->revision == 4) &&
+ (cb_write_post < 0)) || (cb_write_post == 0))
+ s->bcr &= ~CB_BCR_WRITE_POST;
+ if (s->cache == 0) s->cache = 8;
+ if (s->pci_lat == 0) s->pci_lat = 0xa8;
+ if (s->cb_lat == 0) s->cb_lat = 0xb0;
+ if (s->cap.pci_irq == 0)
+ strcat(buf, " [no pci irq]");
+ else
+ sprintf(buf, " [pci irq %d]", s->cap.pci_irq);
+ buf += strlen(buf);
+ if (!(s->flags & IS_TOPIC))
+ s->cap.features |= SS_CAP_PAGE_REGS;
+ sprintf(buf, " [lat %d/%d] [bus %d/%d]",
+ s->pci_lat, s->cb_lat, s->cap.cardbus, s->sub_bus);
+}
+
+#endif
+
+/*======================================================================
+
+ Power control for Cardbus controllers: used both for 16-bit and
+ Cardbus cards.
+
+======================================================================*/
+
+#ifdef CONFIG_PCI
+
+static void cb_get_power(socket_info_t *s, socket_state_t *state)
+{
+ u_int reg = cb_readl(s, 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_VPP_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 int cb_set_power(socket_info_t *s, socket_state_t *state)
+{
+ u_int reg = 0;
+ /* restart card voltage detection if it seems appropriate */
+ if ((state->Vcc == 0) && (state->Vpp == 0) &&
+ !(cb_readl(s, CB_SOCKET_STATE) & CB_SS_VSENSE))
+ cb_writel(s, CB_SOCKET_FORCE, CB_SF_CVSTEST);
+ switch (state->Vcc) {
+ case 0: reg = 0; break;
+ case 33: reg = CB_SC_VCC_3V; break;
+ case 50: reg = CB_SC_VCC_5V; break;
+ default: return -EINVAL;
+ }
+ switch (state->Vpp) {
+ case 0: break;
+ case 33: reg |= CB_SC_VPP_3V; break;
+ case 50: reg |= CB_SC_VPP_5V; break;
+ case 120: reg |= CB_SC_VPP_12V; break;
+ default: return -EINVAL;
+ }
+ if (reg != cb_readl(s, CB_SOCKET_CONTROL))
+ cb_writel(s, CB_SOCKET_CONTROL, reg);
+ return 0;
+}
+
+#endif
+
+/*======================================================================
+
+ Generic routines to get and set controller options
+
+======================================================================*/
+
+static void __init get_bridge_state(socket_info_t *s)
+{
+ if (s->flags & IS_CIRRUS)
+ cirrus_get_state(s);
+#ifdef CONFIG_ISA
+ else if (s->flags & IS_VADEM)
+ vg46x_get_state(s);
+#endif
+#ifdef CONFIG_PCI
+ else if (s->flags & IS_O2MICRO)
+ o2micro_get_state(s);
+ else if (s->flags & IS_TI)
+ ti113x_get_state(s);
+ else if (s->flags & IS_RICOH)
+ ricoh_get_state(s);
+ else if (s->flags & IS_TOPIC)
+ topic_get_state(s);
+ if (s->flags & IS_CARDBUS)
+ cb_get_state(s);
+#endif
+}
+
+static void set_bridge_state(socket_info_t *s)
+{
+#ifdef CONFIG_PCI
+ if (s->flags & IS_CARDBUS)
+ cb_set_state(s);
+#endif
+ if (s->flags & IS_CIRRUS) {
+ cirrus_set_state(s);
+ } else {
+ i365_set(s, I365_GBLCTL, 0x00);
+ i365_set(s, I365_GENCTL, 0x00);
+ /* Trouble: changes timing of memory operations */
+ /* i365_bset(s, I365_ADDRWIN, I365_ADDR_MEMCS16); */
+ }
+ i365_bflip(s, I365_INTCTL, I365_INTR_ENA, s->intr);
+#ifdef CONFIG_ISA
+ if (s->flags & IS_VADEM)
+ vg46x_set_state(s);
+#endif
+#ifdef CONFIG_PCI
+ if (s->flags & IS_O2MICRO)
+ o2micro_set_state(s);
+ else if (s->flags & IS_TI)
+ ti113x_set_state(s);
+ else if (s->flags & IS_RICOH)
+ ricoh_set_state(s);
+ else if (s->flags & IS_TOPIC)
+ topic_set_state(s);
+#endif
+}
+
+static u_int __init set_bridge_opts(socket_info_t *s, u_short ns)
+{
+ u_short i;
+ u_int m = 0xffff;
+ char buf[128];
+
+ for (i = 0; i < ns; i++) {
+ if (s[i].flags & IS_ALIVE) {
+ printk(KERN_INFO " host opts [%d]: already alive!\n", i);
+ continue;
+ }
+ buf[0] = '\0';
+ get_bridge_state(s+i);
+ if (s[i].flags & IS_CIRRUS)
+ m = cirrus_set_opts(s+i, buf);
+#ifdef CONFIG_ISA
+ else if (s[i].flags & IS_VADEM)
+ m = vg46x_set_opts(s+i, buf);
+#endif
+#ifdef CONFIG_PCI
+ else if (s[i].flags & IS_O2MICRO)
+ m = o2micro_set_opts(s+i, buf);
+ else if (s[i].flags & IS_TI)
+ m = ti113x_set_opts(s+i, buf);
+ else if (s[i].flags & IS_RICOH)
+ m = ricoh_set_opts(s+i, buf);
+ else if (s[i].flags & IS_TOPIC)
+ m = topic_set_opts(s+i, buf);
+ if (s[i].flags & IS_CARDBUS)
+ cb_set_opts(s+i, buf+strlen(buf));
+#endif
+ set_bridge_state(s+i);
+ printk(KERN_INFO " host opts [%d]:%s\n", i,
+ (*buf) ? buf : " none");
+ }
+#ifdef CONFIG_PCI
+ m &= ~pci_irq_mask;
+#endif
+ return m;
+}
+
+/*======================================================================
+
+ Interrupt testing code, for ISA and PCI interrupts
+
+======================================================================*/
+
+static volatile u_int irq_hits, irq_shared;
+static volatile socket_info_t *irq_sock;
+
+static void irq_count(int irq, void *dev, struct pt_regs *regs)
+{
+ irq_hits++;
+ DEBUG(2, "-> hit on irq %d\n", irq);
+ if (!irq_shared && (irq_hits > 100)) {
+ printk(KERN_INFO " PCI irq %d seems to be wedged!\n", irq);
+ disable_irq(irq);
+ return;
+ }
+#ifdef CONFIG_PCI
+ if (irq_sock->flags & IS_CARDBUS) {
+ cb_writel(irq_sock, CB_SOCKET_EVENT, -1);
+ } else
+#endif
+ i365_get((socket_info_t *)irq_sock, I365_CSC);
+ return;
+}
+
+static u_int __init test_irq(socket_info_t *s, int irq, int pci)
+{
+ u_char csc = (pci) ? 0 : irq;
+
+#ifdef CONFIG_PNP_BIOS
+ extern int check_pnp_irq(int);
+ if (!pci && check_pnp_irq(irq)) return 1;
+#endif
+
+ DEBUG(2, " testing %s irq %d\n", pci ? "PCI" : "ISA", irq);
+ irq_sock = s; irq_shared = irq_hits = 0;
+ if (request_irq(irq, irq_count, 0, "scan", socket)) {
+ irq_shared++;
+ if (!pci || request_irq(irq, irq_count, SA_SHIRQ, "scan", socket))
+ return 1;
+ }
+ irq_hits = 0;
+ __set_current_state(TASK_UNINTERRUPTIBLE);
+ schedule_timeout(HZ/100);
+ if (irq_hits && !irq_shared) {
+ free_irq(irq, socket);
+ DEBUG(2, " spurious hit!\n");
+ return 1;
+ }
+
+ /* Generate one interrupt */
+#ifdef CONFIG_PCI
+ if (s->flags & IS_CARDBUS) {
+ cb_writel(s, CB_SOCKET_EVENT, -1);
+ i365_set(s, I365_CSCINT, I365_CSC_STSCHG | (csc << 4));
+ cb_writel(s, CB_SOCKET_EVENT, -1);
+ cb_writel(s, CB_SOCKET_MASK, CB_SM_CSTSCHG);
+ cb_writel(s, CB_SOCKET_FORCE, CB_SE_CSTSCHG);
+ mdelay(1);
+ cb_writel(s, CB_SOCKET_EVENT, -1);
+ cb_writel(s, CB_SOCKET_MASK, 0);
+ } else
+#endif
+ {
+ i365_set(s, I365_CSCINT, I365_CSC_DETECT | (csc << 4));
+ i365_bset(s, I365_GENCTL, I365_CTL_SW_IRQ);
+ mdelay(1);
+ }
+
+ free_irq(irq, socket);
+
+ /* mask all interrupts */
+ i365_set(s, I365_CSCINT, 0);
+ DEBUG(2, " hits = %d\n", irq_hits);
+
+ return pci ? (irq_hits == 0) : (irq_hits != 1);
+}
+
+#ifdef CONFIG_ISA
+static int _check_irq(int irq, int flags)
+{
+#ifdef CONFIG_PNP_BIOS
+ extern int check_pnp_irq(int);
+ if ((flags != SA_SHIRQ) && check_pnp_irq(irq))
+ return -1;
+#endif
+ if (request_irq(irq, irq_count, flags, "x", irq_count) != 0)
+ return -1;
+ free_irq(irq, irq_count);
+ return 0;
+}
+
+static u_int __init isa_scan(socket_info_t *s, u_int mask0)
+{
+ u_int mask1 = 0;
+ int i;
+
+#ifdef CONFIG_PCI
+ /* Only scan if we can select ISA csc irq's */
+ if (!(s->flags & IS_CARDBUS) || (cb_set_irq_mode(s, 0, 0) == 0))
+#endif
+ if (do_scan) {
+ set_bridge_state(s);
+ i365_set(s, I365_CSCINT, 0);
+ for (i = 0; i < 16; i++)
+ if ((mask0 & (1 << i)) && (test_irq(s, i, 0) == 0))
+ mask1 |= (1 << i);
+ for (i = 0; i < 16; i++)
+ if ((mask1 & (1 << i)) && (test_irq(s, i, 0) != 0))
+ mask1 ^= (1 << i);
+ }
+
+ printk(KERN_INFO " ISA irqs (");
+ /* we trust TI bridges to do this right */
+ if (mask1 || (s->flags & IS_TI)) {
+ 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 int __init pci_scan(socket_info_t *s)
+{
+ int ret;
+ if ((s->flags & IS_RICOH) || !(s->flags & IS_CARDBUS) || !do_scan) {
+ /* for PCI-to-PCMCIA bridges, just check for wedged irq */
+ irq_sock = s; irq_hits = 0;
+ if (request_irq(s->cap.pci_irq, irq_count, 0, "scan", socket))
+ return 1;
+ udelay(50);
+ free_irq(s->cap.pci_irq, socket);
+ return (!irq_hits);
+ }
+ cb_set_irq_mode(s, 1, 0);
+ set_bridge_state(s);
+ i365_set(s, I365_CSCINT, 0);
+ ret = ((test_irq(s, s->cap.pci_irq, 1) == 0) &&
+ (test_irq(s, s->cap.pci_irq, 1) == 0));
+ if (!ret)
+ printk(KERN_INFO " PCI irq %d test failed\n",
+ s->cap.pci_irq);
+ return ret;
+}
+#endif /* CONFIG_PCI */
+
+/*====================================================================*/
+
+#ifdef CONFIG_ISA
+
+static int __init isa_identify(u_short port, u_short sock)
+{
+ socket_info_t *s = socket+sockets;
+ u_char val;
+ int type = -1;
+
+ /* Use the next free entry in the socket table */
+ s->ioaddr = port;
+ s->psock = sock;
+
+ /* Wake up a sleepy Cirrus controller */
+ if (wakeup) {
+ i365_bclr(s, PD67_MISC_CTL_2, PD67_MC2_SUSPEND);
+ /* Pause at least 50 ms */
+ mdelay(50);
+ }
+
+ if ((val = i365_get(s, 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(s, VG468_MISC, VG468_MISC_VADEMREV);
+ val = i365_get(s, I365_IDENT);
+ if (val & I365_IDENT_VADEM) {
+ i365_bclr(s, VG468_MISC, VG468_MISC_VADEMREV);
+ type = ((val & 7) >= 4) ? IS_VG469 : IS_VG468;
+ }
+
+ /* Check for Ricoh chips */
+ val = i365_get(s, RF5C_CHIP_ID);
+ if ((val == RF5C_CHIP_RF5C296) || (val == RF5C_CHIP_RF5C396))
+ type = IS_RF5Cx96;
+
+ /* Check for Cirrus CL-PD67xx chips */
+ i365_set(s, PD67_CHIP_INFO, 0);
+ val = i365_get(s, PD67_CHIP_INFO);
+ if ((val & PD67_INFO_CHIP_ID) == PD67_INFO_CHIP_ID) {
+ val = i365_get(s, PD67_CHIP_INFO);
+ if ((val & PD67_INFO_CHIP_ID) == 0) {
+ type = (val & PD67_INFO_SLOTS) ? IS_PD672X : IS_PD6710;
+ i365_set(s, PD67_EXT_INDEX, 0xe5);
+ if (i365_get(s, PD67_EXT_INDEX) != 0xe5)
+ type = IS_VT83C469;
+ }
+ }
+ return type;
+} /* isa_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(socket_info_t *s)
+{
+ u_char stat;
+ u_short start, stop;
+
+ stat = i365_get(s, I365_STATUS);
+ start = i365_get_pair(s, I365_IO(0)+I365_W_START);
+ stop = i365_get_pair(s, I365_IO(0)+I365_W_STOP);
+ if ((stop - start < 0x40) && (stop - start >= 0x07) &&
+ ((start & 0xfeef) != 0x02e8) && (start >= 0x100) &&
+ (stat & I365_CS_DETECT) && (stat & I365_CS_POWERON) &&
+ (i365_get(s, I365_INTCTL) & I365_PC_IOCARD) &&
+ (i365_get(s, I365_ADDRWIN) & I365_ENA_IO(0)) &&
+ (check_region(start, stop-start+1) != 0))
+ return 1;
+ else
+ return 0;
+}
+
+/*====================================================================*/
+
+static void __init add_socket(u_int port, int psock, int type)
+{
+ socket_info_t *s = socket+sockets;
+ s->ioaddr = port;
+ s->psock = psock;
+ s->type = type;
+ s->flags = pcic[type].flags;
+ if (is_alive(s))
+ s->flags |= IS_ALIVE;
+ sockets++;
+}
+
+static void __init add_pcic(int ns, int type)
+{
+ u_int mask = 0, i;
+ int use_pci = 0, isa_irq = 0;
+ socket_info_t *s = &socket[sockets-ns];
+
+ if (s->ioaddr > 0) request_region(s->ioaddr, 2, "i82365");
+
+ printk(KERN_INFO " %s", pcic[type].name);
+#ifdef CONFIG_PCI
+ if (s->flags & IS_UNKNOWN)
+ printk(" [%04x %04x]", s->vendor, s->device);
+ printk(" rev %02x", s->revision);
+ if (s->flags & IS_CARDBUS)
+ printk(" PCI-to-CardBus at slot %02x:%02x, mem %#08x\n",
+ s->bus, PCI_SLOT(s->devfn), s->cb_phys);
+ else if (s->flags & IS_PCI)
+ printk(" PCI-to-PCMCIA at slot %02x:%02x, port %#x\n",
+ s->bus, PCI_SLOT(s->devfn), s->ioaddr);
+ else
+#endif
+ printk(" ISA-to-PCMCIA at port %#x ofs 0x%02x\n",
+ s->ioaddr, s->psock*0x40);
+
+#ifdef CONFIG_ISA
+ if (irq_list[0] == -1)
+ mask = irq_mask;
+ else
+ for (i = mask = 0; i < 16; i++)
+ mask |= (1<<irq_list[i]);
+#endif
+ /* Set host options, build basic interrupt mask */
+ mask &= I365_ISA_IRQ_MASK & set_bridge_opts(s, ns);
+
+#ifdef CONFIG_PCI
+ /* Can we use PCI interrupts for card status changes? */
+ if (pci_csc || pci_int) {
+ for (i = 0; i < ns; i++)
+ if (!s[i].cap.pci_irq || !pci_scan(&s[i])) break;
+ use_pci = (i == ns);
+ }
+#endif
+#ifdef CONFIG_ISA
+ /* Scan, report ISA card interrupts */
+ if (mask)
+ mask = isa_scan(s, mask);
+#endif
+
+#ifdef CONFIG_PCI
+ if (!mask)
+ printk(KERN_INFO " %s card interrupts,",
+ (use_pci && pci_int) ? "PCI" : "*NO*");
+ if (use_pci && pci_csc)
+ printk(" PCI status changes\n");
+#endif
+
+#ifdef CONFIG_ISA
+ /* Poll if only two sensible interrupts available */
+ if (!(use_pci && pci_csc) && !poll_interval) {
+ u_int tmp = (mask & 0xff20);
+ tmp = tmp & (tmp-1);
+ if ((tmp & (tmp-1)) == 0)
+ poll_interval = HZ;
+ }
+ /* Only try an ISA cs_irq if this is the first controller */
+ if (!(use_pci && pci_csc) && !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 (isa_irq = 15; isa_irq > 0; isa_irq--)
+ if (cs_mask & (1 << isa_irq)) break;
+ if (isa_irq) {
+ grab_irq = 1;
+ cs_irq = isa_irq;
+ printk(" status change on irq %d\n", isa_irq);
+ }
+ }
+#endif
+
+ if (!(use_pci && pci_csc) && !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++) {
+ s[i].cap.features |= SS_CAP_PCCARD;
+ s[i].cap.map_size = 0x1000;
+ s[i].cap.irq_mask = mask;
+ if (!use_pci)
+ s[i].cap.pci_irq = 0;
+ s[i].cs_irq = isa_irq;
+#ifdef CONFIG_PCI
+ if (s[i].flags & IS_CARDBUS) {
+ s[i].cap.features |= SS_CAP_CARDBUS;
+ cb_set_irq_mode(s+i, pci_csc && s[i].cap.pci_irq, 0);
+ }
+#endif
+ }
+
+} /* add_pcic */
+
+/*====================================================================*/
+
+#ifdef CONFIG_PCI
+
+static int __init pci_lookup(u_int class, struct pci_dev **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_short v, u_short d)
+{
+ socket_info_t *s = &socket[sockets];
+ u_int addr, ns;
+
+ pci_enable_device(pci_find_slot(s->bus, s->devfn));
+ pci_writew(s, PCI_COMMAND, CMD_DFLT);
+
+ if (type == PCIC_COUNT) type = IS_UNK_PCI;
+ pci_readl(s, PCI_BASE_ADDRESS_0, &addr);
+ addr &= ~0x1;
+ for (ns = 0; ns < ((type == IS_I82092AA) ? 4 : 2); ns++) {
+ s[ns].bus = s->bus; s[ns].devfn = s->devfn;
+ s[ns].vendor = v; s[ns].device = d;
+ add_socket(addr, ns, type);
+ }
+ add_pcic(ns, type);
+}
+
+static int check_cb_mapping(socket_info_t *s)
+{
+ u_int state = cb_readl(s, CB_SOCKET_STATE) >> 16;
+ /* A few sanity checks to validate the bridge mapping */
+ if ((cb_readb(s, 0x800+I365_IDENT) & 0x70) ||
+ (cb_readb(s, 0x800+I365_CSC) && cb_readb(s, 0x800+I365_CSC) &&
+ cb_readb(s, 0x800+I365_CSC)) || cb_readl(s, CB_SOCKET_FORCE) ||
+ ((state & ~0x3000) || !(state & 0x3000)))
+ return 1;
+ return 0;
+}
+
+static void __init add_cb_bridge(int type, u_short v, u_short d0)
+{
+ socket_info_t *s = &socket[sockets];
+ u_char bus = s->bus, devfn = s->devfn;
+ u_short d, ns;
+ u_char a, 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(s, PCI_HEADER_TYPE, &a);
+ pci_readb(s, PCI_CLASS_REVISION, &r);
+ max = (a & 0x80) ? 8 : 1;
+ for (ns = 0; ns < max; ns++, s++, devfn++) {
+ s->bus = bus; s->devfn = devfn;
+ if (pci_readw(s, PCI_DEVICE_ID, &d) || (d != d0))
+ break;
+ s->vendor = v; s->device = d; s->revision = r;
+
+ pci_enable_device(pci_find_slot(bus, devfn));
+ pci_set_power_state(pci_find_slot(bus, devfn), 0);
+ pci_writew(s, PCI_COMMAND, CMD_DFLT);
+
+ /* Set up CardBus register mapping */
+ pci_writel(s, CB_LEGACY_MODE_BASE, 0);
+ pci_readl(s, PCI_BASE_ADDRESS_0, &s->cb_phys);
+ if (s->cb_phys == 0) {
+ printk("\n" KERN_NOTICE " Bridge register mapping failed:"
+ " check cb_mem_base setting\n");
+ break;
+ }
+ s->cb_virt = ioremap(s->cb_phys, 0x1000);
+ if (check_cb_mapping(s) != 0) {
+ printk("\n" KERN_NOTICE " Bad bridge mapping at "
+ "0x%08x!\n", s->cb_phys);
+ break;
+ }
+
+ request_mem_region(s->cb_phys, 0x1000, "i82365");
+ add_socket(0, 0, type);
+ }
+ if (ns == 0) return;
+
+ add_pcic(ns, type);
+
+ /* Look up PCI bus bridge structures if needed */
+ s -= ns;
+ for (a = 0; a < ns; a++) {
+ struct pci_dev *self = pci_find_slot(bus, s[a].devfn);
+#if (LINUX_VERSION_CODE >= VERSION(2,3,40))
+ s[a].cap.cb_bus = self->subordinate;
+#else
+ struct pci_bus *child;
+ for (child = self->bus->children; child; child = child->next)
+ if (child->number == s[a].cap.cardbus) break;
+ s[a].cap.cb_bus = child;
+#endif
+ }
+}
+
+static void __init pci_probe(u_int class)
+{
+ socket_info_t *s = &socket[sockets];
+ u_short i, v, d;
+ struct pci_dev *id;
+
+ id = 0;
+ while (pci_lookup(class, &id, &s->bus, &s->devfn) == 0) {
+ if (PCI_FUNC(s->devfn) != 0) continue;
+ pci_readw(s, PCI_VENDOR_ID, &v);
+ pci_readw(s, PCI_DEVICE_ID, &d);
+ for (i = 0; i < PCIC_COUNT; i++)
+ if ((pcic[i].vendor == v) && (pcic[i].device == d)) break;
+ /* The "ToPIC95-A" is unusable as a CardBus bridge */
+ if (i == IS_TOPIC95_A)
+ continue;
+ if (((i < PCIC_COUNT) && (pcic[i].flags & IS_CARDBUS)) ||
+ (class == PCI_CLASS_BRIDGE_CARDBUS))
+ add_cb_bridge(i, v, d);
+ else
+ add_pci_bridge(i, v, d);
+ s = &socket[sockets];
+ }
+}
+
+#endif
+
+/*====================================================================*/
+
+#ifdef CONFIG_ISA
+
+static void __init isa_probe(ioaddr_t base)
+{
+ int i, j, sock, k, ns, id;
+ ioaddr_t port;
+
+ if (check_region(base, 2) != 0) {
+ if (sockets == 0)
+ printk("port conflict at %#x\n", base);
+ return;
+ }
+
+ id = isa_identify(base, 0);
+ if ((id == IS_I82365DF) && (isa_identify(base, 1) != id)) {
+ for (i = 0; i < 4; i++) {
+ if (i == ignore) continue;
+ port = base + ((i & 1) << 2) + ((i & 2) << 1);
+ sock = (i & 1) << 1;
+ if (isa_identify(port, sock) == IS_I82365DF) {
+ add_socket(port, sock, IS_VLSI);
+ add_pcic(1, IS_VLSI);
+ }
+ }
+ } else {
+ for (i = 0; i < 4; i += 2) {
+ port = base + 2*(i>>2);
+ sock = (i & 3);
+ id = isa_identify(port, sock);
+ if (id < 0) continue;
+
+ for (j = ns = 0; j < 2; j++) {
+ /* Does the socket exist? */
+ if ((ignore == i+j) || (isa_identify(port, sock+j) < 0))
+ continue;
+ /* Check for bad socket decode */
+ for (k = 0; k <= sockets; k++)
+ i365_set(socket+k, I365_MEM(0)+I365_W_OFF, k);
+ for (k = 0; k <= sockets; k++)
+ if (i365_get(socket+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
+
+/*======================================================================
+
+ The card status event handler. This may either be interrupt
+ driven or polled. It monitors mainly for card insert and eject
+ events; there are various other kinds of events that can be
+ monitored (ready/busy, status change, etc), but they are almost
+ never used.
+
+======================================================================*/
+
+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(2, "i82365: pcic_interrupt(%d)\n", irq);
+
+ for (j = 0; j < 20; j++) {
+ active = 0;
+ for (i = 0; i < sockets; i++) {
+ socket_info_t *s = &socket[i];
+ if ((s->cs_irq != irq) && (s->cap.pci_irq != irq))
+ continue;
+ ISA_LOCK(s, flags);
+ csc = i365_get(s, I365_CSC);
+#ifdef CONFIG_PCI
+ if ((s->flags & IS_CARDBUS) &&
+ (cb_readl(s, CB_SOCKET_EVENT) & CB_SE_CCD)) {
+ cb_writel(s, CB_SOCKET_EVENT, CB_SE_CCD);
+ csc |= I365_CSC_DETECT;
+ }
+#endif
+ if ((csc == 0) || (!s->handler) ||
+ (i365_get(s, I365_IDENT) & 0x70)) {
+ ISA_UNLOCK(s, flags);
+ continue;
+ }
+ events = (csc & I365_CSC_DETECT) ? SS_DETECT : 0;
+ if (i365_get(s, 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;
+ }
+ ISA_UNLOCK(s, flags);
+ DEBUG(1, "i82365: socket %d event 0x%04x\n", i, events);
+ if (events)
+ s->handler(s->info, events);
+ active |= events;
+ }
+ if (!active) break;
+ }
+ if (j == 20)
+ printk(KERN_NOTICE "i82365: infinite loop in interrupt "
+ "handler: active = 0x%04x\n", active);
+
+ DEBUG(2, "i82365: interrupt done\n");
+} /* pcic_interrupt */
+
+static void pcic_interrupt_wrapper(u_long data)
+{
+ pcic_interrupt(0, NULL, NULL);
+ poll_timer.expires = jiffies + poll_interval;
+ add_timer(&poll_timer);
+}
+
+/*====================================================================*/
+
+static int pcic_register_callback(socket_info_t *s, ss_callback_t *call)
+{
+ if (call == NULL) {
+ s->handler = NULL;
+ MOD_DEC_USE_COUNT;
+ } else {
+ MOD_INC_USE_COUNT;
+ s->handler = call->handler;
+ s->info = call->info;
+ }
+ return 0;
+} /* pcic_register_callback */
+
+/*====================================================================*/
+
+static int pcic_inquire_socket(socket_info_t *s, socket_cap_t *cap)
+{
+ *cap = s->cap;
+ return 0;
+}
+
+/*====================================================================*/
+
+static int i365_get_status(socket_info_t *s, u_int *value)
+{
+ u_int status;
+
+ status = i365_get(s, I365_STATUS);
+ *value = ((status & I365_CS_DETECT) == I365_CS_DETECT)
+ ? SS_DETECT : 0;
+ if (i365_get(s, 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 (s->flags & IS_CARDBUS) {
+ status = cb_readl(s, CB_SOCKET_STATE);
+ *value |= (status & CB_SS_32BIT) ? SS_CARDBUS : 0;
+ *value |= (status & CB_SS_3VCARD) ? SS_3VCARD : 0;
+ *value |= (status & CB_SS_XVCARD) ? SS_XVCARD : 0;
+ *value |= (status & CB_SS_VSENSE) ? 0 : SS_PENDING;
+ } else if (s->flags & IS_O2MICRO) {
+ status = i365_get(s, O2_MODE_B);
+ *value |= (status & O2_MODE_B_VS1) ? 0 : SS_3VCARD;
+ *value |= (status & O2_MODE_B_VS2) ? 0 : SS_XVCARD;
+ }
+#endif
+ if ((s->flags & IS_CIRRUS) &&
+ ((s->flags & IS_PCI) || has_vsense)) {
+ socket_info_t *t = (s->psock) ? s : s+1;
+ status = pd67_ext_get(t, PD67_EXTERN_DATA);
+ *value |= (status & PD67_EXD_VS1(s->psock)) ? 0 : SS_3VCARD;
+ *value |= (status & PD67_EXD_VS2(s->psock)) ? 0 : SS_XVCARD;
+ }
+#ifdef CONFIG_ISA
+ if (s->type == IS_VG469) {
+ status = i365_get(s, VG469_VSENSE);
+ if (s->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
+ /* For now, ignore cards with unsupported voltage keys */
+ if (*value & SS_XVCARD)
+ *value &= ~(SS_DETECT|SS_3VCARD|SS_XVCARD);
+ DEBUG(1, "i82365: GetStatus(%d) = %#4.4x\n", s-socket, *value);
+ return 0;
+} /* i365_get_status */
+
+/*====================================================================*/
+
+static int i365_get_socket(socket_info_t *s, socket_state_t *state)
+{
+ u_char reg, vcc, vpp;
+
+ reg = i365_get(s, 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 ((s->flags & IS_CARDBUS) && !(s->flags & IS_TOPIC)) {
+ cb_get_power(s, state);
+ } else
+#endif
+ {
+ if ((s->flags & IS_CIRRUS) && (reg & I365_VCC_5V)) {
+ state->Vcc = (i365_get(s, PD67_MISC_CTL_1) &
+ PD67_MC1_VCC_3V) ? 33 : 50;
+ } else if ((s->flags & IS_VG_PWR) && (reg & I365_VCC_5V)) {
+ state->Vcc = (i365_get(s, VG469_VSELECT) &
+ VG469_VSEL_VCC) ? 33 : 50;
+ } else if ((s->flags & IS_DF_PWR) || (s->flags & IS_TOPIC)) {
+ if (vcc == I365_VCC_3V) state->Vcc = 33;
+ if (vcc == I365_VCC_5V) state->Vcc = 50;
+ } else {
+ if (reg & I365_VCC_5V) state->Vcc = 50;
+ }
+ if (vpp == I365_VPP1_5V)
+ state->Vpp = (s->flags & IS_DF_PWR) ? 50 : state->Vcc;
+ if (vpp == I365_VPP1_12V) state->Vpp = 120;
+ }
+
+ /* IO card, RESET flags, IO interrupt */
+ reg = i365_get(s, I365_INTCTL);
+ state->flags |= (reg & I365_PC_RESET) ? 0 : SS_RESET;
+ state->flags |= (reg & I365_PC_IOCARD) ? SS_IOCARD : 0;
+#ifdef CONFIG_PCI
+ if (cb_get_irq_mode(s) != 0)
+ state->io_irq = s->cap.pci_irq;
+ else
+#endif
+ state->io_irq = reg & I365_IRQ_MASK;
+
+ /* Card status change mask */
+ reg = i365_get(s, 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(2, "i82365: GetSocket(%d) = flags %#3.3x, Vcc %d, Vpp %d, "
+ "io_irq %d, csc_mask %#2.2x\n", s-socket, state->flags,
+ state->Vcc, state->Vpp, state->io_irq, state->csc_mask);
+ return 0;
+} /* i365_get_socket */
+
+/*====================================================================*/
+
+static int i365_set_socket(socket_info_t *s, socket_state_t *state)
+{
+ u_char reg;
+
+ DEBUG(2, "i82365: SetSocket(%d, flags %#3.3x, Vcc %d, Vpp %d, "
+ "io_irq %d, csc_mask %#2.2x)\n", s-socket, state->flags,
+ state->Vcc, state->Vpp, state->io_irq, state->csc_mask);
+
+ /* First set global controller options */
+#ifdef CONFIG_PCI
+ if (s->cap.pci_irq)
+ cb_set_irq_mode(s, pci_csc, (s->cap.pci_irq == state->io_irq));
+ s->bcr &= ~CB_BCR_CB_RESET;
+#endif
+ set_bridge_state(s);
+
+ /* IO card, RESET flag, IO interrupt */
+ reg = s->intr | ((state->io_irq == s->cap.pci_irq) ?
+ s->pci_irq_code : state->io_irq);
+ reg |= (state->flags & SS_RESET) ? 0 : I365_PC_RESET;
+ reg |= (state->flags & SS_IOCARD) ? I365_PC_IOCARD : 0;
+ i365_set(s, 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 ((s->flags & IS_CARDBUS) && !(s->flags & IS_TOPIC)) {
+ cb_set_power(s, state);
+ reg |= i365_get(s, I365_POWER) & (I365_VCC_MASK|I365_VPP1_MASK);
+ } else
+#endif
+ {
+ int new = s->flags & (IS_TOPIC|IS_CIRRUS|IS_VG_PWR|IS_DF_PWR);
+ int vcc3 = (state->Vcc == 33), df = (s->flags & IS_DF_PWR);
+
+ if (state->Vcc == 50) {
+ reg |= I365_VCC_5V;
+ } else if (new && vcc3) {
+ reg |= ((s->flags & (IS_TOPIC|IS_DF_PWR)) ?
+ I365_VCC_3V : I365_VCC_5V);
+ } else if (state->Vcc)
+ return -EINVAL;
+ if (s->flags & IS_CIRRUS)
+ i365_bflip(s, PD67_MISC_CTL_1, PD67_MC1_VCC_3V, vcc3);
+ if (s->flags & IS_VG_PWR)
+ i365_bflip(s, VG469_VSELECT, VG469_VSEL_VCC, vcc3);
+
+ if (state->Vpp == 120) {
+ reg |= I365_VPP1_12V | (new ? 0 : I365_VPP2_12V);
+ } else if (state->Vpp == (df ? 50 : state->Vcc)) {
+ reg |= I365_VPP1_5V | (new ? 0 : I365_VPP2_5V);
+ } else if (state->Vpp)
+ return -EINVAL;
+ }
+
+ if (reg != i365_get(s, I365_POWER))
+ i365_set(s, I365_POWER, reg);
+
+ /* Card status change interrupt mask */
+ reg = (s->cap.pci_irq ? s->pci_irq_code : s->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(s, I365_CSCINT, reg);
+ i365_get(s, I365_CSC);
+#ifdef CONFIG_PCI
+ if (s->flags & IS_CARDBUS) {
+ if (s->cs_irq || (pci_csc && s->cap.pci_irq))
+ cb_writel(s, CB_SOCKET_MASK, CB_SM_CCD);
+ cb_writel(s, CB_SOCKET_EVENT, -1);
+ }
+#endif
+
+ return 0;
+} /* i365_set_socket */
+
+/*====================================================================*/
+
+static int i365_get_io_map(socket_info_t *s, struct pccard_io_map *io)
+{
+ u_char map, ioctl, addr;
+
+ map = io->map;
+ if (map > 1) return -EINVAL;
+ io->start = i365_get_pair(s, I365_IO(map)+I365_W_START);
+ io->stop = i365_get_pair(s, I365_IO(map)+I365_W_STOP);
+ ioctl = i365_get(s, I365_IOCTL);
+ addr = i365_get(s, I365_ADDRWIN);
+ io->speed = (ioctl & I365_IOCTL_WAIT(map)) ? cycle_time : 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(3, "i82365: GetIOMap(%d, %d) = %#2.2x, %d ns, %#4.4x-%#4.4x\n",
+ s-socket, map, io->flags, io->speed, io->start, io->stop);
+ return 0;
+} /* i365_get_io_map */
+
+/*====================================================================*/
+
+static int i365_set_io_map(socket_info_t *s, struct pccard_io_map *io)
+{
+ u_char map, ioctl;
+
+ DEBUG(3, "i82365: SetIOMap(%d, %d, %#2.2x, %d ns, %#4.4x-%#4.4x)\n",
+ s-socket, 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(s, I365_ADDRWIN) & I365_ENA_IO(map))
+ i365_bclr(s, I365_ADDRWIN, I365_ENA_IO(map));
+ i365_set_pair(s, I365_IO(map)+I365_W_START, io->start);
+ i365_set_pair(s, I365_IO(map)+I365_W_STOP, io->stop);
+ ioctl = i365_get(s, 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(s, I365_IOCTL, ioctl);
+ /* Turn on the window if necessary */
+ if (io->flags & MAP_ACTIVE)
+ i365_bset(s, I365_ADDRWIN, I365_ENA_IO(map));
+ return 0;
+} /* i365_set_io_map */
+
+/*====================================================================*/
+
+static int i365_get_mem_map(socket_info_t *s, struct pccard_mem_map *mem)
+{
+ u_short base, i;
+ u_char map, addr;
+
+ map = mem->map;
+ if (map > 4) return -EINVAL;
+ addr = i365_get(s, I365_ADDRWIN);
+ mem->flags = (addr & I365_ENA_MEM(map)) ? MAP_ACTIVE : 0;
+ base = I365_MEM(map);
+
+ i = i365_get_pair(s, 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(s, base+I365_W_STOP);
+ mem->speed = (i & I365_MEM_WS0) ? 1 : 0;
+ mem->speed += (i & I365_MEM_WS1) ? 2 : 0;
+ mem->speed *= cycle_time;
+ mem->sys_stop = ((u_long)(i & 0x0fff) << 12) + 0x0fff;
+
+ i = i365_get_pair(s, 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 (s->type == IS_PD6729) {
+ addr = pd67_ext_get(s, PD67_MEM_PAGE(map)) << 24;
+ mem->sys_stop += addr; mem->sys_start += addr;
+ } else if (s->flags & IS_CARDBUS) {
+ addr = i365_get(s, CB_MEM_PAGE(map)) << 24;
+ mem->sys_stop += addr; mem->sys_start += addr;
+ }
+#endif
+
+ DEBUG(3, "i82365: GetMemMap(%d, %d) = %#2.2x, %d ns, %#5.5lx-%#5."
+ "5lx, %#5.5x\n", s-socket, 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(socket_info_t *s, struct pccard_mem_map *mem)
+{
+ u_short base, i;
+ u_char map;
+
+ DEBUG(3, "i82365: SetMemMap(%d, %d, %#2.2x, %d ns, %#5.5lx-%#5.5"
+ "lx, %#5.5x)\n", s-socket, 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 (!(s->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(s, I365_ADDRWIN) & I365_ENA_MEM(map))
+ i365_bclr(s, I365_ADDRWIN, I365_ENA_MEM(map));
+
+#ifdef CONFIG_PCI
+ /* Take care of high byte, for PCI controllers */
+ if (s->type == IS_PD6729) {
+ pd67_ext_set(s, PD67_MEM_PAGE(map), (mem->sys_start >> 24));
+ } else if (s->flags & IS_CARDBUS)
+ i365_set(s, 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(s, base+I365_W_START, i);
+
+ i = (mem->sys_stop >> 12) & 0x0fff;
+ switch (mem->speed / cycle_time) {
+ 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(s, 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(s, base+I365_W_OFF, i);
+
+ /* Turn on the window if necessary */
+ if (mem->flags & MAP_ACTIVE)
+ i365_bset(s, I365_ADDRWIN, I365_ENA_MEM(map));
+ return 0;
+} /* i365_set_mem_map */
+
+/*======================================================================
+
+ The few things that are strictly for Cardbus cards goes here.
+
+======================================================================*/
+
+#ifdef CONFIG_CARDBUS
+
+static int cb_get_status(socket_info_t *s, u_int *value)
+{
+ u_int state = cb_readl(s, CB_SOCKET_STATE);
+ *value = (state & CB_SS_32BIT) ? SS_CARDBUS : 0;
+ *value |= (state & CB_SS_CCD) ? 0 : SS_DETECT;
+ *value |= (state & CB_SS_CSTSCHG) ? SS_STSCHG : 0;
+ *value |= (state & CB_SS_PWRCYCLE) ? (SS_POWERON|SS_READY) : 0;
+ *value |= (state & CB_SS_3VCARD) ? SS_3VCARD : 0;
+ *value |= (state & CB_SS_XVCARD) ? SS_XVCARD : 0;
+ *value |= (state & CB_SS_VSENSE) ? 0 : SS_PENDING;
+ DEBUG(1, "yenta: GetStatus(%d) = %#4.4x\n", s-socket, *value);
+ return 0;
+} /* cb_get_status */
+
+static int cb_get_socket(socket_info_t *s, socket_state_t *state)
+{
+ u_short bcr;
+
+ cb_get_power(s, state);
+ pci_readw(s, CB_BRIDGE_CONTROL, &bcr);
+ state->flags |= (bcr & CB_BCR_CB_RESET) ? SS_RESET : 0;
+ if (cb_get_irq_mode(s) != 0)
+ state->io_irq = s->cap.pci_irq;
+ else
+ state->io_irq = i365_get(s, I365_INTCTL) & I365_IRQ_MASK;
+ DEBUG(2, "yenta: GetSocket(%d) = flags %#3.3x, Vcc %d, Vpp %d"
+ ", io_irq %d, csc_mask %#2.2x\n", s-socket, state->flags,
+ state->Vcc, state->Vpp, state->io_irq, state->csc_mask);
+ return 0;
+} /* cb_get_socket */
+
+static int cb_set_socket(socket_info_t *s, socket_state_t *state)
+{
+ u_int reg;
+
+ DEBUG(2, "yenta: SetSocket(%d, flags %#3.3x, Vcc %d, Vpp %d, "
+ "io_irq %d, csc_mask %#2.2x)\n", s-socket, 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(s, 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_bridge_state(s);
+
+ cb_set_power(s, state);
+
+ /* Handle IO interrupt using ISA routing */
+ reg = s->intr;
+ if (state->io_irq != s->cap.pci_irq) reg |= state->io_irq;
+ i365_set(s, I365_INTCTL, reg);
+
+ /* Handle CSC mask */
+ if (!s->cs_irq && (!pci_csc || !s->cap.pci_irq))
+ return 0;
+ reg = (s->cs_irq << 4);
+ if (state->csc_mask & SS_DETECT) reg |= I365_CSC_DETECT;
+ i365_set(s, I365_CSCINT, reg);
+ i365_get(s, I365_CSC);
+ cb_writel(s, CB_SOCKET_MASK, CB_SM_CCD);
+ cb_writel(s, CB_SOCKET_EVENT, -1);
+
+ return 0;
+} /* cb_set_socket */
+
+static int cb_get_bridge(socket_info_t *s, struct cb_bridge_map *m)
+{
+ u_char map = m->map;
+
+ if (map > 1) return -EINVAL;
+ m->flags &= MAP_IOSPACE;
+ map += (m->flags & MAP_IOSPACE) ? 2 : 0;
+ pci_readl(s, CB_MEM_BASE(map), &m->start);
+ pci_readl(s, 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, CB_BRIDGE_CONTROL, &bcr);
+ m->flags |= (bcr & CB_BCR_PREFETCH(map)) ? MAP_PREFETCH : 0;
+ }
+ DEBUG(3, "yenta: GetBridge(%d, %d) = %#2.2x, %#4.4x-%#4.4x\n",
+ s-socket, map, m->flags, m->start, m->stop);
+ return 0;
+}
+
+static int cb_set_bridge(socket_info_t *s, struct cb_bridge_map *m)
+{
+ u_char map;
+
+ DEBUG(3, "yenta: SetBridge(%d, %d, %#2.2x, %#4.4x-%#4.4x)\n",
+ s-socket, 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 {
+ if ((m->start & 0x0fff) || ((m->stop & 0x0fff) != 0x0fff))
+ return -EINVAL;
+ s->bcr &= ~CB_BCR_PREFETCH(map);
+ s->bcr |= (m->flags & MAP_PREFETCH) ? CB_BCR_PREFETCH(map) : 0;
+ pci_writew(s, CB_BRIDGE_CONTROL, s->bcr);
+ }
+ if (m->flags & MAP_ACTIVE) {
+ pci_writel(s, CB_MEM_BASE(map), m->start);
+ pci_writel(s, CB_MEM_LIMIT(map), m->stop);
+ } else {
+ pci_writel(s, CB_MEM_LIMIT(map), 0);
+ pci_writel(s, CB_MEM_BASE(map), 0);
+ }
+ return 0;
+}
+
+#endif /* CONFIG_CARDBUS */
+
+/*======================================================================
+
+ Routines for accessing socket information and register dumps via
+ /proc/bus/pccard/...
+
+======================================================================*/
+
+#ifdef HAS_PROC_BUS
+
+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)
+{
+ socket_info_t *s = data;
+ char *p = buf;
+ int i, top;
+
+#ifdef CONFIG_ISA
+ u_long flags = 0;
+#endif
+ ISA_LOCK(s, flags);
+ top = 0x40;
+ if (s->flags & IS_CARDBUS)
+ top = (s->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(s,i), i365_get(s,i+1),
+ i365_get(s,i+2), i365_get(s,i+3),
+ ((i % 16) == 12) ? "\n" : " ");
+ }
+ ISA_UNLOCK(s, flags);
+ 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;
+ char *p = buf;
+ u_int a, b, c, d;
+ int i;
+
+ for (i = 0; i < 0xc0; i += 0x10) {
+ pci_readl(s, i, &a);
+ pci_readl(s, i+4, &b);
+ pci_readl(s, i+8, &c);
+ pci_readl(s, i+12, &d);
+ p += sprintf(p, "%08x %08x %08x %08x\n", a, b, c, d);
+ }
+ return (p - buf);
+}
+
+static int proc_read_cardbus(char *buf, char **start, off_t pos,
+ int count, int *eof, void *data)
+{
+ socket_info_t *s = data;
+ char *p = buf;
+ int i, top;
+
+ top = (s->flags & IS_O2MICRO) ? 0x30 : 0x20;
+ for (i = 0; i < top; i += 0x10)
+ p += sprintf(p, "%08x %08x %08x %08x\n",
+ cb_readl(s,i+0x00), cb_readl(s,i+0x04),
+ cb_readl(s,i+0x08), cb_readl(s,i+0x0c));
+ return (p - buf);
+}
+#endif
+
+static void pcic_proc_setup(socket_info_t *s, struct proc_dir_entry *base)
+{
+ create_proc_read_entry("info", 0, base, proc_read_info, s);
+ create_proc_read_entry("exca", 0, base, proc_read_exca, s);
+#ifdef CONFIG_PCI
+ if (s->flags & (IS_PCI|IS_CARDBUS))
+ create_proc_read_entry("pci", 0, base, proc_read_pci, s);
+ if (s->flags & IS_CARDBUS)
+ create_proc_read_entry("cardbus", 0, base, proc_read_cardbus, s);
+#endif
+ s->proc = base;
+}
+
+static void pcic_proc_remove(socket_info_t *s)
+{
+ struct proc_dir_entry *base = s->proc;
+ if (base == NULL) return;
+ remove_proc_entry("info", base);
+ remove_proc_entry("exca", base);
+#ifdef CONFIG_PCI
+ if (s->flags & (IS_PCI|IS_CARDBUS))
+ remove_proc_entry("pci", base);
+ if (s->flags & IS_CARDBUS)
+ remove_proc_entry("cardbus", base);
+#endif
+}
+
+#endif /* HAS_PROC_BUS */
+
+/*====================================================================*/
+
+typedef int (*subfn_t)(socket_info_t *, 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 HAS_PROC_BUS
+ (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)
+{
+ socket_info_t *s = &socket[sock];
+ subfn_t fn;
+ int ret;
+#ifdef CONFIG_ISA
+ u_long flags = 0;
+#endif
+
+ if (cmd >= NFUNC)
+ return -EINVAL;
+
+ if (s->flags & IS_ALIVE) {
+ if (cmd == SS_GetStatus)
+ *(u_int *)arg = 0;
+ return -EINVAL;
+ }
+
+ fn = pcic_service_table[cmd];
+#ifdef CONFIG_CARDBUS
+ if ((s->flags & IS_CARDBUS) &&
+ (cb_readl(s, 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
+
+ ISA_LOCK(s, flags);
+ ret = (fn == NULL) ? -EINVAL : fn(s, arg);
+ ISA_UNLOCK(s, flags);
+ return ret;
+} /* pcic_service */
+
+/*====================================================================*/
+
+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 -EINVAL;
+ }
+ DEBUG(0, "%s\n", version);
+
+#ifdef CONFIG_PCI
+ if (pcic[IS_UNK_CARDBUS].flags != (IS_CARDBUS|IS_UNKNOWN)) {
+ printk(KERN_NOTICE "i82365: bad pcic_id enumeration!\n");
+ return -EINVAL;
+ }
+#endif
+
+ printk(KERN_INFO "Intel ISA/PCI/CardBus PCIC probe:\n");
+ sockets = 0;
+
+#ifdef CONFIG_PCI
+ if (do_pci_probe && pcibios_present()) {
+ pci_probe(PCI_CLASS_BRIDGE_CARDBUS);
+ pci_probe(PCI_CLASS_BRIDGE_PCMCIA);
+ }
+#endif
+
+#ifdef CONFIG_ISA
+ isa_probe(i365_base);
+ if (!sockets || extra_sockets)
+ isa_probe(i365_base+2);
+#endif
+
+ if (sockets == 0) {
+ printk(KERN_INFO " no bridges found.\n");
+ return -ENODEV;
+ }
+
+ /* Set up interrupt handler(s) */
+#ifdef CONFIG_ISA
+ if (grab_irq != 0)
+ request_irq(cs_irq, pcic_interrupt, 0, "i82365", socket);
+#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", socket);
+ 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.expires = jiffies + poll_interval;
+ add_timer(&poll_timer);
+ }
+
+ return 0;
+
+} /* init_i82365 */
+
+static void __exit exit_i82365(void)
+{
+ int i;
+#ifdef HAS_PROC_BUS
+ for (i = 0; i < sockets; i++)
+ pcic_proc_remove(&socket[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, socket);
+#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, socket);
+ mask |= (1<<irq);
+ }
+ }
+#endif
+ for (i = 0; i < sockets; i++) {
+ socket_info_t *s = &socket[i];
+ /* Turn off all interrupt sources! */
+ i365_set(s, I365_CSCINT, 0);
+#ifdef CONFIG_PCI
+ if (s->flags & IS_CARDBUS)
+ cb_writel(s, CB_SOCKET_MASK, 0);
+ if (s->cb_virt) {
+ iounmap(s->cb_virt);
+ release_mem_region(s->cb_phys, 0x1000);
+ } else
+#endif
+ release_region(s->ioaddr, 2);
+ }
+} /* exit_i82365 */
+
+module_init(init_i82365);
+module_exit(exit_i82365);
diff --git a/linux/pcmcia-cs/modules/i82365.h b/linux/pcmcia-cs/modules/i82365.h
new file mode 100644
index 0000000..27ee583
--- /dev/null
+++ b/linux/pcmcia-cs/modules/i82365.h
@@ -0,0 +1,154 @@
+/*
+ * i82365.h 1.21 2001/08/24 12:15:33
+ *
+ * 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
+ * <dahinds@users.sourceforge.net>. 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 General 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_ADDR_MEMCS16 0x20
+#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))
+
+/* Default ISA interrupt mask */
+#define I365_ISA_IRQ_MASK 0xdeb8 /* irq's 3-5,7,9-12,14,15 */
+
+/* Device ID's for PCI-to-PCMCIA bridges */
+
+#ifndef PCI_VENDOR_ID_INTEL
+#define PCI_VENDOR_ID_INTEL 0x8086
+#endif
+#ifndef PCI_DEVICE_ID_INTEL_82092AA_0
+#define PCI_DEVICE_ID_INTEL_82092AA_0 0x1221
+#endif
+#ifndef PCI_VENDOR_ID_OMEGA
+#define PCI_VENDOR_ID_OMEGA 0x119b
+#endif
+#ifndef PCI_DEVICE_ID_OMEGA_82C092G
+#define PCI_DEVICE_ID_OMEGA_82C092G 0x1221
+#endif
+
+#endif /* _LINUX_I82365_H */
diff --git a/linux/pcmcia-cs/modules/o2micro.h b/linux/pcmcia-cs/modules/o2micro.h
new file mode 100644
index 0000000..fd15234
--- /dev/null
+++ b/linux/pcmcia-cs/modules/o2micro.h
@@ -0,0 +1,160 @@
+/*
+ * o2micro.h 1.20 2002/03/03 14:16:57
+ *
+ * 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
+ * <dahinds@users.sourceforge.net>. 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 General 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
+#ifndef PCI_DEVICE_ID_O2_6812
+#define PCI_DEVICE_ID_O2_6812 0x6872
+#endif
+#ifndef PCI_DEVICE_ID_O2_6922
+#define PCI_DEVICE_ID_O2_6922 0x6825
+#endif
+#ifndef PCI_DEVICE_ID_O2_6933
+#define PCI_DEVICE_ID_O2_6933 0x6933
+#endif
+#ifndef PCI_DEVICE_ID_O2_6912
+#define PCI_DEVICE_ID_O2_6912 0x6972
+#endif
+
+/* Additional PCI configuration registers */
+
+#define O2_MUX_CONTROL 0x90 /* 32 bit */
+#define O2_MUX_RING_OUT 0x0000000f
+#define O2_MUX_SKTB_ACTV 0x000000f0
+#define O2_MUX_SCTA_ACTV_ENA 0x00000100
+#define O2_MUX_SCTB_ACTV_ENA 0x00000200
+#define O2_MUX_SER_IRQ_ROUTE 0x0000e000
+#define O2_MUX_SER_PCI 0x00010000
+
+#define O2_MUX_SKTA_TURBO 0x000c0000 /* for 6833, 6860 */
+#define O2_MUX_SKTB_TURBO 0x00300000
+#define O2_MUX_AUX_VCC_3V 0x00400000
+#define O2_MUX_PCI_VCC_5V 0x00800000
+#define O2_MUX_PME_MUX 0x0f000000
+
+/* Additional ExCA registers */
+
+#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_PWR_MASK 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_C_IREQ_SEL 0x30
+#define O2_MODE_C_MGMT_SEL 0xc0
+
+#define O2_MODE_D 0x3b
+#define O2_MODE_D_IRQ_MODE 0x03
+#define O2_MODE_D_IRQ_PCPCI 0x00
+#define O2_MODE_D_IRQ_PCIWAY 0x02
+#define O2_MODE_D_IRQ_PCI 0x03
+#define O2_MODE_D_PCI_CLKRUN 0x04
+#define O2_MODE_D_CB_CLKRUN 0x08
+#define O2_MODE_D_SKT_ACTV 0x20
+#define O2_MODE_D_PCI_FIFO 0x40 /* for OZ6729, OZ6730 */
+#define O2_MODE_D_W97_IRQ 0x40
+#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
+
+/* Data structure for tracking vendor-specific state */
+typedef struct o2micro_state_t {
+ u_char mode_a; /* O2_MODE_A */
+ u_char mode_b; /* O2_MODE_B */
+ u_char mode_c; /* O2_MODE_C */
+ u_char mode_d; /* O2_MODE_D */
+ u_char mhpg; /* O2_MHPG_DMA */
+ u_char fifo; /* O2_FIFO_ENA */
+ u_char mode_e; /* O2_MODE_E */
+} o2micro_state_t;
+
+#define O2MICRO_PCIC_ID \
+ IS_OZ6729, IS_OZ6730, IS_OZ6832, IS_OZ6836, IS_OZ6812, \
+ IS_OZ6922, IS_OZ6933, IS_OZ6912
+
+#define O2MICRO_PCIC_INFO \
+ { "O2Micro OZ6729", IS_O2MICRO|IS_PCI|IS_VG_PWR, ID(O2, 6729) }, \
+ { "O2Micro OZ6730", IS_O2MICRO|IS_PCI|IS_VG_PWR, ID(O2, 6730) }, \
+ { "O2Micro OZ6832/33", IS_O2MICRO|IS_CARDBUS, ID(O2, 6832) }, \
+ { "O2Micro OZ6836/60", IS_O2MICRO|IS_CARDBUS, ID(O2, 6836) }, \
+ { "O2Micro OZ6812", IS_O2MICRO|IS_CARDBUS, ID(O2, 6812) }, \
+ { "O2Micro OZ6922", IS_O2MICRO|IS_CARDBUS, ID(O2, 6922) }, \
+ { "O2Micro OZ6933", IS_O2MICRO|IS_CARDBUS, ID(O2, 6933) }, \
+ { "O2Micro OZ6912", IS_O2MICRO|IS_CARDBUS, ID(O2, 6912) }
+
+#endif /* _LINUX_O2MICRO_H */
diff --git a/linux/pcmcia-cs/modules/pci_fixup.c b/linux/pcmcia-cs/modules/pci_fixup.c
new file mode 100644
index 0000000..b892f63
--- /dev/null
+++ b/linux/pcmcia-cs/modules/pci_fixup.c
@@ -0,0 +1,674 @@
+/*======================================================================
+
+ Kernel fixups for PCI device support
+
+ pci_fixup.c 1.33 2002/10/12 19:02:59
+
+ PCI bus fixups: various bits of code that don't really belong in
+ the PCMCIA subsystem, but may or may not be available from the
+ kernel, depending on kernel version. The basic idea is to make
+ 2.0.* and 2.2.* kernels look like they have the 2.3.* features.
+
+======================================================================*/
+
+#define __NO_VERSION__
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/pci.h>
+#include <asm/io.h>
+
+/* We use these for setting up CardBus bridges */
+#include "yenta.h"
+#include "i82365.h"
+
+#define VERSION KERNEL_VERSION
+#if (LINUX_VERSION_CODE < VERSION(2,3,24))
+
+/* Default memory base addresses for CardBus controllers */
+static u_int cb_mem_base[] = { 0x0, 0x68000000, 0xf8000000 };
+MODULE_PARM(cb_mem_base, "i");
+
+/* PCI bus number overrides for CardBus controllers */
+#define INT_MODULE_PARM(n, v) static int n = v; MODULE_PARM(n, "i")
+INT_MODULE_PARM(cb_bus_base, 0);
+INT_MODULE_PARM(cb_bus_step, 2);
+INT_MODULE_PARM(cb_pci_irq, 0);
+
+#endif
+
+/* (exported) mask of interrupts reserved for PCI devices */
+u32 pci_irq_mask = 0;
+
+/*======================================================================
+
+ Basic PCI services missing from older kernels: device lookup, etc
+
+======================================================================*/
+
+#if (LINUX_VERSION_CODE < VERSION(2,1,0))
+struct pci_dev *pci_devices = NULL;
+struct pci_bus pci_root = {
+ parent: NULL,
+ children: NULL,
+ next: NULL,
+ self: NULL,
+ devices: NULL,
+ number: 0
+};
+#endif
+
+#if (LINUX_VERSION_CODE < VERSION(2,1,93))
+
+struct pci_dev *pci_find_slot(u_int bus, u_int devfn)
+{
+ struct pci_dev *dev;
+ for (dev = pci_devices; dev; dev = dev->next)
+ if ((dev->devfn == devfn) && (bus == dev->bus->number))
+ return dev;
+#if (LINUX_VERSION_CODE > VERSION(2,1,0))
+ return NULL;
+#else
+ {
+ struct pci_bus *b;
+ u8 hdr;
+ u32 id, class;
+
+ if (pcibios_read_config_byte(bus, devfn & ~7, PCI_HEADER_TYPE, &hdr))
+ return NULL;
+ if (PCI_FUNC(devfn) && !(hdr & 0x80))
+ return NULL;
+ pcibios_read_config_dword(bus, devfn, PCI_VENDOR_ID, &id);
+ if ((id == 0) || (id == 0xffffffff))
+ return NULL;
+ dev = kmalloc(sizeof *dev, GFP_ATOMIC);
+ if (!dev)
+ return NULL;
+ memset(dev, 0, sizeof *dev);
+ dev->devfn = devfn;
+ pcibios_read_config_byte(bus, devfn, PCI_INTERRUPT_LINE, &dev->irq);
+ dev->vendor = id & 0xffff;
+ dev->device = id >> 16;
+ pcibios_read_config_dword(bus, devfn, PCI_CLASS_REVISION, &class);
+ if (dev->irq == 255)
+ dev->irq = 0;
+ dev->class = class >> 8;
+ for (b = &pci_root; b; b = b->next)
+ if (b->number == bus) break;
+ if (!b) {
+ b = kmalloc(sizeof *b, GFP_ATOMIC);
+ if (!b) {
+ kfree(dev);
+ return NULL;
+ }
+ memset(b, 0, sizeof *b);
+ b->number = bus;
+ b->next = pci_root.next;
+ pci_root.next = b;
+ }
+ dev->bus = b;
+ return dev;
+ }
+#endif
+}
+
+struct pci_dev *pci_find_class(u_int class, struct pci_dev *from)
+{
+ static u16 index = 0;
+ u8 bus, devfn;
+ if (from == NULL)
+ index = 0;
+ if (pcibios_find_class(class, index++, &bus, &devfn) == 0)
+ return pci_find_slot(bus, devfn);
+ else
+ return NULL;
+}
+
+#endif /* (LINUX_VERSION_CODE < VERSION(2,1,93)) */
+
+/*======================================================================
+
+ PCI Interrupt Routing Table parser
+
+ This only needs to be done once per boot: we scan the BIOS for
+ the routing table, and then look for devices that have interrupt
+ assignments that the kernel doesn't know about. If we find any,
+ we update their pci_dev structures and write the PCI interrupt
+ line registers.
+
+======================================================================*/
+
+#if (LINUX_VERSION_CODE < VERSION(2,3,24)) && defined(__i386__)
+
+#pragma pack(1)
+
+struct slot_entry {
+ u8 bus, devfn;
+ struct pirq_pin {
+ u8 link;
+ u16 irq_map;
+ } pin[4];
+ u8 slot;
+ u8 reserved;
+};
+
+struct routing_table {
+ u32 signature;
+ u8 minor, major;
+ u16 size;
+ u8 bus, devfn;
+ u16 pci_mask;
+ u32 compat;
+ u32 miniport;
+ u8 reserved[11];
+ u8 checksum;
+ struct slot_entry entry[0];
+};
+
+#pragma pack()
+
+/*
+ The meaning of the link bytes in the routing table is vendor
+ specific. We need code to get and set the routing information.
+*/
+
+static u8 pIIx_link(struct pci_dev *router, u8 link)
+{
+ u8 pirq;
+ /* link should be 0x60, 0x61, 0x62, 0x63 */
+ pci_read_config_byte(router, link, &pirq);
+ return (pirq < 16) ? pirq : 0;
+}
+
+static void pIIx_init(struct pci_dev *router, u8 link, u8 irq)
+{
+ pci_write_config_byte(router, link, irq);
+}
+
+static u8 via_link(struct pci_dev *router, u8 link)
+{
+ u8 pirq = 0;
+ /* link should be 1, 2, 3, 5 */
+ if (link < 6)
+ pci_read_config_byte(router, 0x55 + (link>>1), &pirq);
+ return (link & 1) ? (pirq >> 4) : (pirq & 15);
+}
+
+static void via_init(struct pci_dev *router, u8 link, u8 irq)
+{
+ u8 pirq;
+ pci_read_config_byte(router, 0x55 + (link>>1), &pirq);
+ pirq &= (link & 1) ? 0x0f : 0xf0;
+ pirq |= (link & 1) ? (irq << 4) : (irq & 15);
+ pci_write_config_byte(router, 0x55 + (link>>1), pirq);
+}
+
+static u8 opti_link(struct pci_dev *router, u8 link)
+{
+ u8 pirq = 0;
+ /* link should be 0x02, 0x12, 0x22, 0x32 */
+ if ((link & 0xcf) == 0x02)
+ pci_read_config_byte(router, 0xb8 + (link >> 5), &pirq);
+ return (link & 0x10) ? (pirq >> 4) : (pirq & 15);
+}
+
+static void opti_init(struct pci_dev *router, u8 link, u8 irq)
+{
+ u8 pirq;
+ pci_read_config_byte(router, 0xb8 + (link >> 5), &pirq);
+ pirq &= (link & 0x10) ? 0x0f : 0xf0;
+ pirq |= (link & 0x10) ? (irq << 4) : (irq & 15);
+ pci_write_config_byte(router, 0xb8 + (link >> 5), pirq);
+}
+
+static u8 ali_link(struct pci_dev *router, u8 link)
+{
+ /* No, you're not dreaming */
+ static const u8 map[] =
+ { 0, 9, 3, 10, 4, 5, 7, 6, 1, 11, 0, 12, 0, 14, 0, 15 };
+ u8 pirq;
+ /* link should be 0x01..0x08 */
+ pci_read_config_byte(router, 0x48 + ((link-1)>>1), &pirq);
+ return (link & 1) ? map[pirq&15] : map[pirq>>4];
+}
+
+static void ali_init(struct pci_dev *router, u8 link, u8 irq)
+{
+ /* Inverse of map in ali_link */
+ static const u8 map[] =
+ { 0, 8, 0, 2, 4, 5, 7, 6, 0, 1, 3, 9, 11, 0, 13, 15 };
+ u8 pirq;
+ pci_read_config_byte(router, 0x48 + ((link-1)>>1), &pirq);
+ pirq &= (link & 1) ? 0x0f : 0xf0;
+ pirq |= (link & 1) ? (map[irq] << 4) : (map[irq] & 15);
+ pci_write_config_byte(router, 0x48 + ((link-1)>>1), pirq);
+}
+
+static u8 cyrix_link(struct pci_dev *router, u8 link)
+{
+ u8 pirq;
+ /* link should be 1, 2, 3, 4 */
+ link--;
+ pci_read_config_byte(router, 0x5c + (link>>1), &pirq);
+ return ((link & 1) ? pirq >> 4 : pirq & 15);
+}
+
+static void cyrix_init(struct pci_dev *router, u8 link, u8 irq)
+{
+ u8 pirq;
+ link--;
+ pci_read_config_byte(router, 0x5c + (link>>1), &pirq);
+ pirq &= (link & 1) ? 0x0f : 0xf0;
+ pirq |= (link & 1) ? (irq << 4) : (irq & 15);
+ pci_write_config_byte(router, 0x5c + (link>>1), pirq);
+}
+
+/*
+ A table of all the PCI interrupt routers for which we know how to
+ interpret the link bytes.
+*/
+
+#ifndef PCI_DEVICE_ID_INTEL_82371FB_0
+#define PCI_DEVICE_ID_INTEL_82371FB_0 0x122e
+#endif
+#ifndef PCI_DEVICE_ID_INTEL_82371SB_0
+#define PCI_DEVICE_ID_INTEL_82371SB_0 0x7000
+#endif
+#ifndef PCI_DEVICE_ID_INTEL_82371AB_0
+#define PCI_DEVICE_ID_INTEL_82371AB_0 0x7110
+#endif
+#ifndef PCI_DEVICE_ID_INTEL_82443MX_1
+#define PCI_DEVICE_ID_INTEL_82443MX_1 0x7198
+#endif
+#ifndef PCI_DEVICE_ID_INTEL_82443MX_1
+#define PCI_DEVICE_ID_INTEL_82443MX_1 0x7198
+#endif
+#ifndef PCI_DEVICE_ID_INTEL_82801AA_0
+#define PCI_DEVICE_ID_INTEL_82801AA_0 0x2410
+#endif
+#ifndef PCI_DEVICE_ID_INTEL_82801AB_0
+#define PCI_DEVICE_ID_INTEL_82801AB_0 0x2420
+#endif
+#ifndef PCI_DEVICE_ID_INTEL_82801BA_0
+#define PCI_DEVICE_ID_INTEL_82801BA_0 0x2440
+#endif
+#ifndef PCI_DEVICE_ID_INTEL_82801BAM_0
+#define PCI_DEVICE_ID_INTEL_82801BAM_0 0x244c
+#endif
+#ifndef PCI_DEVICE_ID_VIA_82C586_0
+#define PCI_DEVICE_ID_VIA_82C586_0 0x0586
+#endif
+#ifndef PCI_DEVICE_ID_VIA_82C596
+#define PCI_DEVICE_ID_VIA_82C596 0x0596
+#endif
+#ifndef PCI_DEVICE_ID_VIA_82C686
+#define PCI_DEVICE_ID_VIA_82C686 0x0686
+#endif
+#ifndef PCI_DEVICE_ID_SI
+#define PCI_DEVICE_ID_SI 0x1039
+#endif
+#ifndef PCI_DEVICE_ID_SI_503
+#define PCI_DEVICE_ID_SI_503 0x0008
+#endif
+#ifndef PCI_DEVICE_ID_SI_496
+#define PCI_DEVICE_ID_SI_496 0x0496
+#endif
+
+#define ID(a,b) PCI_VENDOR_ID_##a,PCI_DEVICE_ID_##a##_##b
+
+struct router {
+ u16 vendor, device;
+ u8 (*xlate)(struct pci_dev *, u8);
+ void (*init)(struct pci_dev *, u8, u8);
+} router_table[] = {
+ { ID(INTEL, 82371FB_0), &pIIx_link, &pIIx_init },
+ { ID(INTEL, 82371SB_0), &pIIx_link, &pIIx_init },
+ { ID(INTEL, 82371AB_0), &pIIx_link, &pIIx_init },
+ { ID(INTEL, 82443MX_1), &pIIx_link, &pIIx_init },
+ { ID(INTEL, 82801AA_0), &pIIx_link, &pIIx_init },
+ { ID(INTEL, 82801AB_0), &pIIx_link, &pIIx_init },
+ { ID(INTEL, 82801BA_0), &pIIx_link, &pIIx_init },
+ { ID(INTEL, 82801BAM_0), &pIIx_link, &pIIx_init },
+ { ID(VIA, 82C586_0), &via_link, &via_init },
+ { ID(VIA, 82C596), &via_link, &via_init },
+ { ID(VIA, 82C686), &via_link, &via_init },
+ { ID(OPTI, 82C700), &opti_link, &opti_init },
+ { ID(AL, M1533), &ali_link, &ali_init },
+ { ID(SI, 503), &pIIx_link, &pIIx_init },
+ { ID(SI, 496), &pIIx_link, &pIIx_init },
+ { ID(CYRIX, 5530_LEGACY), &cyrix_link, &cyrix_init }
+};
+#define ROUTER_COUNT (sizeof(router_table)/sizeof(router_table[0]))
+
+/* Global variables for current interrupt routing table */
+static struct routing_table *pirq = NULL;
+static struct pci_dev *router_dev = NULL;
+static struct router *router_info = NULL;
+
+#ifndef __va
+#define __va(x) (x)
+#endif
+
+static void scan_pirq_table(void)
+{
+ struct routing_table *r;
+ struct pci_dev *router, *dev;
+ u8 pin, fn, *p;
+ int i, j;
+ struct slot_entry *e;
+
+ /* Scan the BIOS for the routing table signature */
+ for (p = (u8 *)__va(0xf0000); p < (u8 *)__va(0xfffff); p += 16)
+ if ((p[0] == '$') && (p[1] == 'P') &&
+ (p[2] == 'I') && (p[3] == 'R')) break;
+ if (p >= (u8 *)__va(0xfffff))
+ return;
+
+ pirq = r = (struct routing_table *)p;
+ printk(KERN_INFO "PCI routing table version %d.%d at %#06x\n",
+ r->major, r->minor, (u32)r & 0xfffff);
+ for (i = j = 0; i < 16; i++)
+ j += (r->pci_mask >> i) & 1;
+ if (j > 4)
+ printk(KERN_NOTICE " bogus PCI irq mask %#04x!\n",
+ r->pci_mask);
+ else
+ pci_irq_mask |= r->pci_mask;
+
+ router_dev = router = pci_find_slot(r->bus, r->devfn);
+ if (router) {
+ for (i = 0; i < ROUTER_COUNT; i++) {
+ if ((router->vendor == router_table[i].vendor) &&
+ (router->device == router_table[i].device))
+ break;
+ if (((r->compat & 0xffff) == router_table[i].vendor) &&
+ ((r->compat >> 16) == router_table[i].device))
+ break;
+ }
+ if (i == ROUTER_COUNT)
+ printk(KERN_INFO " unknown PCI interrupt router %04x:%04x\n",
+ router->vendor, router->device);
+ else
+ router_info = &router_table[i];
+ }
+
+ for (e = r->entry; (u8 *)e < p+r->size; e++) {
+ for (fn = 0; fn < 8; fn++) {
+ dev = pci_find_slot(e->bus, e->devfn | fn);
+ if ((dev == NULL) || (dev->irq != 0)) continue;
+ pci_read_config_byte(dev, PCI_INTERRUPT_PIN, &pin);
+ if ((pin == 0) || (pin == 255)) continue;
+ if (router_info) {
+ dev->irq = router_info->xlate(router, e->pin[pin-1].link);
+ } else {
+ /* Fallback: see if only one irq possible */
+ int map = e->pin[pin-1].irq_map;
+ if (map && (!(map & (map-1))))
+ dev->irq = ffs(map)-1;
+ }
+ if (dev->irq) {
+ printk(KERN_INFO " %02x:%02x.%1x -> irq %d\n",
+ e->bus, PCI_SLOT(dev->devfn),
+ PCI_FUNC(dev->devfn), dev->irq);
+ pci_write_config_byte(dev, PCI_INTERRUPT_LINE,
+ dev->irq);
+ }
+ }
+ }
+}
+
+#endif /* (LINUX_VERSION_CODE < VERSION(2,3,24)) && defined(__i386__) */
+
+/*======================================================================
+
+ PCI device enabler
+
+ This is not at all generic... it is mostly a hack to correctly
+ configure CardBus bridges.
+
+======================================================================*/
+
+#if (LINUX_VERSION_CODE < VERSION(2,3,24))
+
+static int check_cb_mapping(u_int phys)
+{
+ /* A few sanity checks to validate the bridge mapping */
+ char *virt = ioremap(phys, 0x1000);
+ int ret = ((readb(virt+0x800+I365_IDENT) & 0x70) ||
+ (readb(virt+0x800+I365_CSC) &&
+ readb(virt+0x800+I365_CSC) &&
+ readb(virt+0x800+I365_CSC)));
+ int state = readl(virt+CB_SOCKET_STATE) >> 16;
+ ret |= (state & ~0x3000) || !(state & 0x3000);
+ ret |= readl(virt+CB_SOCKET_FORCE);
+ iounmap(virt);
+ return ret;
+}
+
+static void setup_cb_bridge(struct pci_dev *dev)
+{
+ u8 bus, sub;
+ u32 phys;
+ int i;
+
+ /* This is nasty, but where else can we put it? */
+ if (PCI_FUNC(dev->devfn) == 0) {
+ struct pci_dev *sib;
+ sib = pci_find_slot(dev->bus->number, dev->devfn+1);
+ if (sib) {
+ u8 a, b;
+ u32 c, d;
+ /* Check for bad PCI bus numbering */
+ pci_read_config_byte(dev, CB_CARDBUS_BUS, &a);
+ pci_read_config_byte(sib, CB_CARDBUS_BUS, &b);
+ if (a == b) {
+ pci_write_config_byte(dev, CB_CARDBUS_BUS, 0);
+ pci_write_config_byte(sib, CB_CARDBUS_BUS, 0);
+ }
+ /* check for bad register mapping */
+ pci_read_config_dword(dev, PCI_BASE_ADDRESS_0, &c);
+ pci_read_config_dword(sib, PCI_BASE_ADDRESS_0, &d);
+ if ((c != 0) && (c == d)) {
+ pci_write_config_dword(dev, PCI_BASE_ADDRESS_0, 0);
+ pci_write_config_dword(sib, PCI_BASE_ADDRESS_0, 0);
+ }
+ }
+ }
+
+ /* Assign PCI bus numbers, if needed */
+ pci_read_config_byte(dev, CB_CARDBUS_BUS, &bus);
+ pci_read_config_byte(dev, CB_SUBORD_BUS, &sub);
+ if ((cb_bus_base > 0) || (bus == 0)) {
+ if (cb_bus_base <= 0) cb_bus_base = 0x20;
+ bus = cb_bus_base;
+ sub = cb_bus_base+cb_bus_step;
+ cb_bus_base += cb_bus_step+1;
+ pci_write_config_byte(dev, CB_CARDBUS_BUS, bus);
+ pci_write_config_byte(dev, CB_SUBORD_BUS, sub);
+ }
+
+ /* Create pci_bus structure for the CardBus, if needed */
+ {
+ struct pci_bus *child, *parent = dev->bus;
+ for (child = parent->children; child; child = child->next)
+ if (child->number == bus) break;
+ if (!child) {
+ child = kmalloc(sizeof(struct pci_bus), GFP_KERNEL);
+ memset(child, 0, sizeof(struct pci_bus));
+ child->self = dev;
+ child->primary = bus;
+ child->number = child->secondary = bus;
+ child->subordinate = sub;
+ child->parent = parent;
+#if (LINUX_VERSION_CODE >= VERSION(2,3,15))
+ child->ops = parent->ops;
+#endif
+ child->next = parent->children;
+ parent->children = child;
+ }
+ }
+
+ /* Map the CardBus bridge registers, if needed */
+ pci_write_config_dword(dev, CB_LEGACY_MODE_BASE, 0);
+ pci_read_config_dword(dev, PCI_BASE_ADDRESS_0, &phys);
+ if ((phys == 0) || (cb_mem_base[0] != 0)) {
+ /* Make sure the bridge is awake so we can test it */
+ pci_set_power_state(dev, 0);
+ for (i = 0; i < sizeof(cb_mem_base)/sizeof(u_int); i++) {
+ phys = cb_mem_base[i];
+ if (phys == 0) continue;
+ pci_write_config_dword(dev, PCI_BASE_ADDRESS_0, phys);
+ if ((i == 0) || (check_cb_mapping(phys) == 0)) break;
+ }
+ if (i == sizeof(cb_mem_base)/sizeof(u_int)) {
+ pci_write_config_dword(dev, PCI_BASE_ADDRESS_0, 0);
+ } else {
+ cb_mem_base[0] = cb_mem_base[i] + 0x1000;
+ }
+ }
+}
+
+#ifdef __i386__
+
+static u8 pirq_init(struct pci_dev *router, struct pirq_pin *pin)
+{
+ u16 map = pin->irq_map;
+ u8 irq = 0;
+ if (pirq->pci_mask)
+ map &= pirq->pci_mask;
+ if (cb_pci_irq)
+ map = 1<<cb_pci_irq;
+ /* Be conservative: only init irq if the mask is unambiguous */
+ if (map && (!(map & (map-1)))) {
+ irq = ffs(map)-1;
+ router_info->init(router, pin->link, irq);
+ pci_irq_mask |= (1<<irq);
+ }
+ return irq;
+}
+
+static void setup_cb_bridge_irq(struct pci_dev *dev)
+{
+ struct slot_entry *e;
+ u8 pin;
+ u32 phys;
+ char *virt;
+
+ pci_read_config_byte(dev, PCI_INTERRUPT_PIN, &pin);
+ pci_read_config_dword(dev, PCI_BASE_ADDRESS_0, &phys);
+ if (!pin || !phys)
+ return;
+ virt = ioremap(phys, 0x1000);
+ if (virt) {
+ /* Disable any pending interrupt sources */
+ writel(0, virt+CB_SOCKET_MASK);
+ writel(-1, virt+CB_SOCKET_EVENT);
+ iounmap(virt);
+ }
+ for (e = pirq->entry; (u8 *)e < (u8 *)pirq + pirq->size; e++) {
+ if ((e->bus != dev->bus->number) ||
+ (e->devfn != (dev->devfn & ~7)))
+ continue;
+ dev->irq = pirq_init(router_dev, &e->pin[pin-1]);
+ pci_write_config_byte(dev, PCI_INTERRUPT_LINE, dev->irq);
+ break;
+ }
+}
+
+#endif
+
+int pci_enable_device(struct pci_dev *dev)
+{
+ if ((dev->class >> 8) == PCI_CLASS_BRIDGE_CARDBUS) {
+ setup_cb_bridge(dev);
+ }
+#ifdef __i386__
+ /* In certain cases, if the interrupt can be deduced, but was
+ unrouted when the pirq table was scanned, we'll try to set it
+ up now. */
+ if (!dev->irq && pirq && (router_info) &&
+ ((dev->class >> 8) == PCI_CLASS_BRIDGE_CARDBUS)) {
+ setup_cb_bridge_irq(dev);
+ }
+#endif
+ return 0;
+}
+
+int pci_set_power_state(struct pci_dev *dev, int state)
+{
+ u16 tmp, cmd;
+ u32 base, bus;
+ u8 a, b, pmcs;
+ pci_read_config_byte(dev, PCI_STATUS, &a);
+ if (a & PCI_STATUS_CAPLIST) {
+ pci_read_config_byte(dev, PCI_CB_CAPABILITY_POINTER, &b);
+ while (b != 0) {
+ pci_read_config_byte(dev, b+PCI_CAPABILITY_ID, &a);
+ if (a == PCI_CAPABILITY_PM) {
+ pmcs = b + PCI_PM_CONTROL_STATUS;
+ /* Make sure we're in D0 state */
+ pci_read_config_word(dev, pmcs, &tmp);
+ if (!(tmp & PCI_PMCS_PWR_STATE_MASK)) break;
+ pci_read_config_dword(dev, PCI_BASE_ADDRESS_0, &base);
+ pci_read_config_dword(dev, CB_PRIMARY_BUS, &bus);
+ pci_read_config_word(dev, PCI_COMMAND, &cmd);
+ pci_write_config_word(dev, pmcs, PCI_PMCS_PWR_STATE_D0);
+ pci_write_config_dword(dev, PCI_BASE_ADDRESS_0, base);
+ pci_write_config_dword(dev, CB_PRIMARY_BUS, bus);
+ pci_write_config_word(dev, PCI_COMMAND, cmd);
+ break;
+ }
+ pci_read_config_byte(dev, b+PCI_NEXT_CAPABILITY, &b);
+ }
+ }
+ return 0;
+}
+
+#endif /* (LINUX_VERSION_CODE < VERSION(2,3,24)) */
+
+/*======================================================================
+
+ General setup and cleanup entry points
+
+======================================================================*/
+
+void pci_fixup_init(void)
+{
+ struct pci_dev *p;
+
+#if (LINUX_VERSION_CODE < VERSION(2,3,24)) && defined(__i386__)
+ scan_pirq_table();
+ pci_for_each_dev(p)
+ if (((p->class >> 8) == PCI_CLASS_BRIDGE_CARDBUS) &&
+ (p->irq == 0)) break;
+ if (p && !pirq)
+ printk(KERN_INFO "No PCI interrupt routing table!\n");
+ if (!pirq && cb_pci_irq)
+ printk(KERN_INFO "cb_pci_irq will be ignored.\n");
+#endif
+
+ pci_for_each_dev(p)
+ pci_irq_mask |= (1<<p->irq);
+
+#ifdef __alpha__
+#define PIC 0x4d0
+ pci_irq_mask |= inb(PIC) | (inb(PIC+1) << 8);
+#endif
+}
+
+void pci_fixup_done(void)
+{
+#if (LINUX_VERSION_CODE < VERSION(2,1,0))
+ struct pci_dev *d, *dn;
+ struct pci_bus *b, *bn;
+ for (d = pci_devices; d; d = dn) {
+ dn = d->next;
+ kfree(d);
+ }
+ for (b = pci_root.next; b; b = bn) {
+ bn = b->next;
+ kfree(b);
+ }
+#endif
+}
diff --git a/linux/pcmcia-cs/modules/ricoh.h b/linux/pcmcia-cs/modules/ricoh.h
new file mode 100644
index 0000000..de62f8b
--- /dev/null
+++ b/linux/pcmcia-cs/modules/ricoh.h
@@ -0,0 +1,161 @@
+/*
+ * ricoh.h 1.16 2002/08/13 15:17:14
+ *
+ * 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
+ * <dahinds@users.sourceforge.net>. 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 General 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_RL5C477
+#define PCI_DEVICE_ID_RICOH_RL5C477 0x0477
+#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
+
+/* Bridge Configuration Register */
+#define RL5C4XX_CONFIG 0x80 /* 16 bit */
+#define RL5C4XX_CONFIG_IO_1_MODE 0x0200
+#define RL5C4XX_CONFIG_IO_0_MODE 0x0100
+#define RL5C4XX_CONFIG_PREFETCH 0x0001
+
+/* Misc Control Register */
+#define RL5C4XX_MISC 0x82 /* 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 0x84 /* 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 0x88 /* 16 bit */
+#define RL5C4XX_16BIT_MEM_0 0x8a /* 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
+
+/* Data structure for tracking vendor-specific state */
+typedef struct ricoh_state_t {
+ u_short config; /* RL5C4XX_CONFIG */
+ u_short misc; /* RL5C4XX_MISC */
+ u_short ctl; /* RL5C4XX_16BIT_CTL */
+ u_short io; /* RL5C4XX_16BIT_IO_0 */
+ u_short mem; /* RL5C4XX_16BIT_MEM_0 */
+} ricoh_state_t;
+
+#define RICOH_PCIC_ID \
+ IS_RL5C465, IS_RL5C466, IS_RL5C475, IS_RL5C476, IS_RL5C477, IS_RL5C478
+
+#define RICOH_PCIC_INFO \
+ { "Ricoh RL5C465", IS_RICOH|IS_CARDBUS, ID(RICOH, RL5C465) }, \
+ { "Ricoh RL5C466", IS_RICOH|IS_CARDBUS, ID(RICOH, RL5C466) }, \
+ { "Ricoh RL5C475", IS_RICOH|IS_CARDBUS, ID(RICOH, RL5C475) }, \
+ { "Ricoh RL5C476", IS_RICOH|IS_CARDBUS, ID(RICOH, RL5C476) }, \
+ { "Ricoh RL5C477", IS_RICOH|IS_CARDBUS, ID(RICOH, RL5C477) }, \
+ { "Ricoh RL5C478", IS_RICOH|IS_CARDBUS, ID(RICOH, RL5C478) }
+
+#endif /* _LINUX_RICOH_H */
diff --git a/linux/pcmcia-cs/modules/rsrc_mgr.c b/linux/pcmcia-cs/modules/rsrc_mgr.c
new file mode 100644
index 0000000..2becb2f
--- /dev/null
+++ b/linux/pcmcia-cs/modules/rsrc_mgr.c
@@ -0,0 +1,877 @@
+/*======================================================================
+
+ Resource management routines
+
+ rsrc_mgr.c 1.94 2003/12/12 17:12:53
+
+ 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
+ <dahinds@users.sourceforge.net>. 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 General 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/config.h>
+#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/slab.h>
+#include <linux/ioport.h>
+#include <linux/timer.h>
+#include <linux/spinlock.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"
+
+/*====================================================================*/
+
+/* Parameters that can be set with 'insmod' */
+
+#define INT_MODULE_PARM(n, v) static int n = v; MODULE_PARM(n, "i")
+
+INT_MODULE_PARM(probe_mem, 1); /* memory probe? */
+#ifdef CONFIG_ISA
+INT_MODULE_PARM(probe_io, 1); /* IO port probe? */
+INT_MODULE_PARM(mem_limit, 0x10000);
+#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 ISA IRQ assignments */
+static irq_info_t irq_table[16] = { { 0, 0, 0 }, /* etc */ };
+
+#endif
+
+/*======================================================================
+
+ Linux resource management extensions
+
+======================================================================*/
+
+#ifndef CONFIG_PNP_BIOS
+#define check_io_region(b,n) (0)
+#endif
+
+#if defined(CONFIG_PNP_BIOS) || !defined(HAVE_MEMRESERVE)
+
+#ifdef USE_SPIN_LOCKS
+static spinlock_t rsrc_lock = SPIN_LOCK_UNLOCKED;
+#endif
+
+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 */
+#ifdef CONFIG_PNP_BIOS
+static resource_entry_t io_list = { 0, 0, NULL, NULL };
+#endif
+#ifndef HAVE_MEMRESERVE
+static resource_entry_t mem_list = { 0, 0, NULL, NULL };
+#endif
+
+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);
+ if (!entry) return -ENOMEM;
+ 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;
+}
+
+#ifdef CONFIG_PNP_BIOS
+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 HAS_PROC_BUS
+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
+#endif
+
+#ifndef HAVE_MEMRESERVE
+int check_mem_region(u_long base, u_long num)
+{
+ return check_my_resource(&mem_list, base, num);
+}
+void request_mem_region(u_long base, u_long num, char *name)
+{
+ register_my_resource(&mem_list, base, num, name);
+}
+void release_mem_region(u_long base, u_long num)
+{
+ release_my_resource(&mem_list, base, num);
+}
+#ifdef HAS_PROC_BUS
+int proc_read_mem(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 = mem_list.next; r; r = r->next)
+ p += sprintf(p, "%08lx-%08lx : %s\n", r->base,
+ r->base+r->num-1, r->name);
+ spin_unlock_irqrestore(&rsrc_lock, flags);
+ return (p - buf);
+}
+#endif
+#endif
+
+#endif /* defined(CONFIG_PNP_BIOS) || !defined(HAVE_MEMRESERVE) */
+
+/*======================================================================
+
+ 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);
+ if (!q) return CS_OUT_OF_RESOURCE;
+ q->base = base; q->num = num;
+ q->next = p->next; p->next = q;
+ return CS_SUCCESS;
+}
+
+/*====================================================================*/
+
+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);
+ if (!p) return CS_OUT_OF_RESOURCE;
+ 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 CS_SUCCESS;
+}
+
+/*======================================================================
+
+ 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);
+ if (!b) {
+ printk(KERN_INFO " kmalloc failed!\n");
+ return;
+ }
+ 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");
+}
+
+static int io_scan; /* = 0 */
+
+static void invalidate_io(void)
+{
+ io_scan = 0;
+}
+
+static void validate_io(void)
+{
+ resource_map_t *m, *n;
+ if (!probe_io || io_scan++)
+ return;
+ for (m = io_db.next; m != &io_db; m = n) {
+ n = m->next;
+ do_io_probe(m->base, m->num);
+ }
+}
+
+#else /* CONFIG_ISA */
+
+#define validate_io() do { } while (0)
+#define invalidate_io() do { } while (0)
+
+#endif /* CONFIG_ISA */
+
+/*======================================================================
+
+ 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 = j = 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);
+}
+
+static int hi_scan, lo_scan; /* = 0 */
+
+static void invalidate_mem(void)
+{
+ hi_scan = lo_scan = 0;
+}
+
+void validate_mem(int (*is_valid)(u_long), int (*do_cksum)(u_long),
+ int force_low)
+{
+ resource_map_t *m, mm;
+ static u_char order[] = { 0xd0, 0xe0, 0xc0, 0xf0 };
+ u_long b, i, ok = 0;
+
+ if (!probe_mem) return;
+ /* We do up to four passes through the list */
+ if (!force_low) {
+ if (hi_scan++ || (inv_probe(is_valid, do_cksum, mem_db.next) > 0))
+ return;
+ printk(KERN_NOTICE "cs: warning: no high memory space "
+ "available!\n");
+ }
+ if (lo_scan++) return;
+ for (m = mem_db.next; m != &mem_db; m = mm.next) {
+ mm = *m;
+ /* Only probe < 1 MB */
+ if (mm.base >= 0x100000) continue;
+ if ((mm.base | mm.num) & 0xffff) {
+ ok += do_mem_probe(mm.base, mm.num, is_valid, do_cksum);
+ continue;
+ }
+ /* Special probe for 64K-aligned block */
+ for (i = 0; i < 4; i++) {
+ b = order[i] << 12;
+ if ((b >= mm.base) && (b+0x10000 <= mm.base+mm.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 */
+
+#define invalidate_mem() do { } while (0)
+
+void validate_mem(int (*is_valid)(u_long), int (*do_cksum)(u_long),
+ int force_low)
+{
+ resource_map_t *m, *n;
+ static int done = 0;
+
+ if (!probe_mem || done++)
+ return;
+ for (m = mem_db.next; m != &mem_db; m = n)
+ n = 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.
+
+ The 'align' field should reflect the number of bits of address
+ that need to be preserved from the initial value of *base. It
+ should be a power of two, greater than or equal to 'num'. A value
+ of 0 means that all bits of *base are significant. *base should
+ also be strictly less than 'align'.
+
+======================================================================*/
+
+int find_io_region(ioaddr_t *base, ioaddr_t num, ioaddr_t align,
+ char *name)
+{
+ ioaddr_t try;
+ resource_map_t *m;
+
+ validate_io();
+ for (m = io_db.next; m != &io_db; m = m->next) {
+ try = (m->base & ~(align-1)) + *base;
+ for (try = (try >= m->base) ? try : try+align;
+ (try >= m->base) && (try+num <= m->base+m->num);
+ try += align) {
+ if ((check_region(try, num) == 0) &&
+ (check_io_region(try, num) == 0)) {
+ *base = try;
+ if (name) request_region(try, num, name);
+ return 0;
+ }
+ if (!align) break;
+ }
+ }
+ return -1;
+}
+
+int find_mem_region(u_long *base, u_long num, u_long align,
+ int force_low, char *name)
+{
+ u_long try;
+ resource_map_t *m;
+
+ 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;
+ try = (m->base & ~(align-1)) + *base;
+ for (try = (try >= m->base) ? try : try+align;
+ (try >= m->base) && (try+num <= m->base+m->num);
+ try += align) {
+ if (check_mem_region(try, num) == 0) {
+ if (name) request_mem_region(try, num, name);
+ *base = try;
+ return 0;
+ }
+ if (!align) break;
+ }
+ }
+ if (force_low) break;
+ force_low++;
+ }
+ return -1;
+}
+
+/*======================================================================
+
+ 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;
+}
+
+#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, ret;
+
+ base = adj->resource.memory.Base;
+ num = adj->resource.memory.Size;
+ if ((num == 0) || (base+num-1 < base))
+ return CS_BAD_SIZE;
+
+ ret = CS_SUCCESS;
+ switch (adj->Action) {
+ case ADD_MANAGED_RESOURCE:
+ ret = add_interval(&mem_db, base, num);
+ break;
+ case REMOVE_MANAGED_RESOURCE:
+ ret = sub_interval(&mem_db, base, num);
+ if (ret == CS_SUCCESS) {
+ invalidate_mem();
+ 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:
+ ret = CS_UNSUPPORTED_FUNCTION;
+ }
+
+ return ret;
+}
+
+/*====================================================================*/
+
+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;
+ break;
+ case REMOVE_MANAGED_RESOURCE:
+ sub_interval(&io_db, base, num);
+ invalidate_io();
+ break;
+ default:
+ return CS_UNSUPPORTED_FUNCTION;
+ break;
+ }
+
+ return CS_SUCCESS;
+}
+
+/*====================================================================*/
+
+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;
+}
+
+/*====================================================================*/
+
+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;
+}
+
+/*====================================================================*/
+
+void release_resource_db(void)
+{
+ resource_map_t *p, *q;
+#if defined(CONFIG_PNP_BIOS) || !defined(HAVE_MEMRESERVE)
+ resource_entry_t *u, *v;
+#endif
+
+ 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);
+ }
+#ifdef CONFIG_PNP_BIOS
+ for (u = io_list.next; u; u = v) {
+ v = u->next;
+ kfree(u);
+ }
+#endif
+#ifndef HAVE_MEMRESERVE
+ for (u = mem_list.next; u; u = v) {
+ v = u->next;
+ kfree(u);
+ }
+#endif
+}
diff --git a/linux/pcmcia-cs/modules/smc34c90.h b/linux/pcmcia-cs/modules/smc34c90.h
new file mode 100644
index 0000000..0f3ddc0
--- /dev/null
+++ b/linux/pcmcia-cs/modules/smc34c90.h
@@ -0,0 +1,58 @@
+/*
+ * smc34c90.h 1.10 2001/08/24 12:15:34
+ *
+ * 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
+ * <dahinds@users.sourceforge.net>. 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 General 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
+
+#define SMC_PCIC_ID \
+ IS_SMC34C90
+
+#define SMC_PCIC_INFO \
+ { "SMC 34C90", IS_CARDBUS, ID(SMC, 34C90) }
+
+#endif /* _LINUX_SMC34C90_H */
diff --git a/linux/pcmcia-cs/modules/ti113x.h b/linux/pcmcia-cs/modules/ti113x.h
new file mode 100644
index 0000000..c224d7a
--- /dev/null
+++ b/linux/pcmcia-cs/modules/ti113x.h
@@ -0,0 +1,264 @@
+/*
+ * ti113x.h 1.32 2003/02/13 06:28:09
+ *
+ * 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
+ * <dahinds@users.sourceforge.net>. 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 General 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_1031
+#define PCI_DEVICE_ID_TI_1031 0xac13
+#endif
+#ifndef PCI_DEVICE_ID_TI_1131
+#define PCI_DEVICE_ID_TI_1131 0xac15
+#endif
+#ifndef PCI_DEVICE_ID_TI_1210
+#define PCI_DEVICE_ID_TI_1210 0xac1a
+#endif
+#ifndef PCI_DEVICE_ID_TI_1211
+#define PCI_DEVICE_ID_TI_1211 0xac1e
+#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_1250A
+#define PCI_DEVICE_ID_TI_1250A 0xac16
+#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_1251B
+#define PCI_DEVICE_ID_TI_1251B 0xac1f
+#endif
+#ifndef PCI_DEVICE_ID_TI_1410
+#define PCI_DEVICE_ID_TI_1410 0xac50
+#endif
+#ifndef PCI_DEVICE_ID_TI_1420
+#define PCI_DEVICE_ID_TI_1420 0xac51
+#endif
+#ifndef PCI_DEVICE_ID_TI_1450
+#define PCI_DEVICE_ID_TI_1450 0xac1b
+#endif
+#ifndef PCI_DEVICE_ID_TI_1451
+#define PCI_DEVICE_ID_TI_1451 0xac52
+#endif
+#ifndef PCI_DEVICE_ID_TI_1510
+#define PCI_DEVICE_ID_TI_1510 0xac56
+#endif
+#ifndef PCI_DEVICE_ID_TI_1520
+#define PCI_DEVICE_ID_TI_1520 0xac55
+#endif
+#ifndef PCI_DEVICE_ID_TI_1620
+#define PCI_DEVICE_ID_TI_1620 0xac54
+#endif
+#ifndef PCI_DEVICE_ID_TI_4410
+#define PCI_DEVICE_ID_TI_4410 0xac41
+#endif
+#ifndef PCI_DEVICE_ID_TI_4450
+#define PCI_DEVICE_ID_TI_4450 0xac40
+#endif
+#ifndef PCI_DEVICE_ID_TI_4451
+#define PCI_DEVICE_ID_TI_4451 0xac42
+#endif
+#ifndef PCI_DEVICE_ID_TI_4510
+#define PCI_DEVICE_ID_TI_4510 0xac44
+#endif
+#ifndef PCI_DEVICE_ID_TI_4520
+#define PCI_DEVICE_ID_TI_4520 0xac46
+#endif
+#ifndef PCI_DEVICE_ID_TI_7410
+#define PCI_DEVICE_ID_TI_7410 0xac49
+#endif
+#ifndef PCI_DEVICE_ID_TI_7510
+#define PCI_DEVICE_ID_TI_7510 0xac47
+#endif
+#ifndef PCI_DEVICE_ID_TI_7610
+#define PCI_DEVICE_ID_TI_7610 0xac48
+#endif
+
+/* Register definitions for TI 113X PCI-to-CardBus bridges */
+
+/* System Control Register */
+#define TI113X_SYSTEM_CONTROL 0x80 /* 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_P2CCLK 0x08000000
+#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 0x84 /* 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 0x85 /* 8 bit */
+#define TI1250_GPIO0_CONTROL 0x88 /* 8 bit */
+#define TI1250_GPIO1_CONTROL 0x89 /* 8 bit */
+#define TI1250_GPIO2_CONTROL 0x8a /* 8 bit */
+#define TI1250_GPIO3_CONTROL 0x8b /* 8 bit */
+#define TI12XX_IRQMUX 0x8c /* 32 bit */
+
+/* Retry Status Register */
+#define TI113X_RETRY_STATUS 0x90 /* 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 0x91 /* 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 0x92 /* 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 0x93 /* 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 0x93 /* 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 0x94 /* 32 bit */
+#define TI113X_DMA_1 0x98 /* 32 bit */
+
+/* ExCA IO offset registers */
+#define TI113X_IO_OFFSET(map) (0x36+((map)<<1))
+
+/* Data structure for tracking vendor-specific state */
+typedef struct ti113x_state_t {
+ u32 sysctl; /* TI113X_SYSTEM_CONTROL */
+ u8 cardctl; /* TI113X_CARD_CONTROL */
+ u8 devctl; /* TI113X_DEVICE_CONTROL */
+ u8 diag; /* TI1250_DIAGNOSTIC */
+ u32 irqmux; /* TI12XX_IRQMUX */
+} ti113x_state_t;
+
+#define TI_PCIC_ID \
+ IS_TI1130, IS_TI1131, IS_TI1031, IS_TI1210, IS_TI1211, \
+ IS_TI1220, IS_TI1221, IS_TI1225, IS_TI1250A, IS_TI1251A, \
+ IS_TI1251B, IS_TI1410, IS_TI1420, IS_TI1450, IS_TI1451, \
+ IS_TI1510, IS_TI1520, IS_TI1620, IS_TI4410, IS_TI4450, \
+ IS_TI4451, IS_TI4510, IS_TI4520, IS_TI7410, IS_TI7510, \
+ IS_TI7610
+
+#define TI_PCIC_INFO \
+ { "TI 1130", IS_TI|IS_CARDBUS, ID(TI, 1130) }, \
+ { "TI 1131", IS_TI|IS_CARDBUS, ID(TI, 1131) }, \
+ { "TI 1031", IS_TI|IS_CARDBUS, ID(TI, 1031) }, \
+ { "TI 1210", IS_TI|IS_CARDBUS, ID(TI, 1210) }, \
+ { "TI 1211", IS_TI|IS_CARDBUS, ID(TI, 1211) }, \
+ { "TI 1220", IS_TI|IS_CARDBUS, ID(TI, 1220) }, \
+ { "TI 1221", IS_TI|IS_CARDBUS, ID(TI, 1221) }, \
+ { "TI 1225", IS_TI|IS_CARDBUS, ID(TI, 1225) }, \
+ { "TI 1250A", IS_TI|IS_CARDBUS, ID(TI, 1250A) }, \
+ { "TI 1251A", IS_TI|IS_CARDBUS, ID(TI, 1251A) }, \
+ { "TI 1251B", IS_TI|IS_CARDBUS, ID(TI, 1251B) }, \
+ { "TI 1410", IS_TI|IS_CARDBUS, ID(TI, 1410) }, \
+ { "TI 1420", IS_TI|IS_CARDBUS, ID(TI, 1420) }, \
+ { "TI 1450", IS_TI|IS_CARDBUS, ID(TI, 1450) }, \
+ { "TI 1451", IS_TI|IS_CARDBUS, ID(TI, 1451) }, \
+ { "TI 1510", IS_TI|IS_CARDBUS, ID(TI, 1510) }, \
+ { "TI 1520", IS_TI|IS_CARDBUS, ID(TI, 1520) }, \
+ { "TI 1620", IS_TI|IS_CARDBUS, ID(TI, 1620) }, \
+ { "TI 4410", IS_TI|IS_CARDBUS, ID(TI, 4410) }, \
+ { "TI 4450", IS_TI|IS_CARDBUS, ID(TI, 4450) }, \
+ { "TI 4451", IS_TI|IS_CARDBUS, ID(TI, 4451) }, \
+ { "TI 4510", IS_TI|IS_CARDBUS, ID(TI, 4510) }, \
+ { "TI 4520", IS_TI|IS_CARDBUS, ID(TI, 4520) }, \
+ { "TI 7410", IS_TI|IS_CARDBUS, ID(TI, 7410) }, \
+ { "TI 7510", IS_TI|IS_CARDBUS, ID(TI, 7510) }, \
+ { "TI 7610", IS_TI|IS_CARDBUS, ID(TI, 7610) }
+
+#endif /* _LINUX_TI113X_H */
diff --git a/linux/pcmcia-cs/modules/topic.h b/linux/pcmcia-cs/modules/topic.h
new file mode 100644
index 0000000..88662c4
--- /dev/null
+++ b/linux/pcmcia-cs/modules/topic.h
@@ -0,0 +1,123 @@
+/*
+ * topic.h 1.15 2002/02/27 01:21:09
+ *
+ * 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
+ * <dahinds@users.sourceforge.net>. 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 General 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$ 2002/02/27 01:21:09
+ */
+
+#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
+#ifndef PCI_DEVICE_ID_TOSHIBA_TOPIC100
+#define PCI_DEVICE_ID_TOSHIBA_TOPIC100 0x0617
+#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
+
+#define TOPIC_FUNCTION_CONTROL 0x3e
+#define TOPIC_FCR_PWR_BUF_ENA 0x40
+#define TOPIC_FCR_CTR_ENA 0x08
+#define TOPIC_FCR_VS_ENA 0x02
+#define TOPIC_FCR_3V_ENA 0x01
+
+/* Data structure for tracking vendor-specific state */
+typedef struct topic_state_t {
+ u_char slot; /* TOPIC_SLOT_CONTROL */
+ u_char ccr; /* TOPIC_CARD_CONTROL */
+ u_char cdr; /* TOPIC_CARD_DETECT */
+ u_int rcr; /* TOPIC_REGISTER_CONTROL */
+ u_char fcr; /* TOPIC_FUNCTION_CONTROL */
+} topic_state_t;
+
+#define TOPIC_PCIC_ID \
+ IS_TOPIC95_A, IS_TOPIC95_B, IS_TOPIC97, IS_TOPIC100
+
+#define TOPIC_PCIC_INFO \
+ { "Toshiba ToPIC95-A", IS_CARDBUS|IS_TOPIC, ID(TOSHIBA, TOPIC95_A) }, \
+ { "Toshiba ToPIC95-B", IS_CARDBUS|IS_TOPIC, ID(TOSHIBA, TOPIC95_B) }, \
+ { "Toshiba ToPIC97", IS_CARDBUS|IS_TOPIC, ID(TOSHIBA, TOPIC97) }, \
+ { "Toshiba ToPIC100", IS_CARDBUS|IS_TOPIC, ID(TOSHIBA, TOPIC100) }
+
+#endif /* _LINUX_TOPIC_H */
diff --git a/linux/pcmcia-cs/modules/vg468.h b/linux/pcmcia-cs/modules/vg468.h
new file mode 100644
index 0000000..93dc00b
--- /dev/null
+++ b/linux/pcmcia-cs/modules/vg468.h
@@ -0,0 +1,112 @@
+/*
+ * vg468.h 1.14 2001/08/24 12:15:34
+ *
+ * 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
+ * <dahinds@users.sourceforge.net>. 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 General 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 */
+
+/* Data structure for tracking vendor-specific state */
+typedef struct vg46x_state_t {
+ u_char ctl; /* VG468_CTL */
+ u_char ema; /* VG468_EXT_MODE_A */
+} vg46x_state_t;
+
+#endif /* _LINUX_VG468_H */
diff --git a/linux/pcmcia-cs/modules/yenta.h b/linux/pcmcia-cs/modules/yenta.h
new file mode 100644
index 0000000..525d8ec
--- /dev/null
+++ b/linux/pcmcia-cs/modules/yenta.h
@@ -0,0 +1,156 @@
+/*
+ * yenta.h 1.20 2001/08/24 12:15:34
+ *
+ * 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
+ * <dahinds@users.sourceforge.net>. 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 General 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_CCD 0x00000006
+#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_CCD 0x00000006
+#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_VSENSE 0x00003c86
+#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 */