diff options
Diffstat (limited to 'linux/pcmcia-cs/modules')
-rw-r--r-- | linux/pcmcia-cs/modules/bulkmem.c | 626 | ||||
-rw-r--r-- | linux/pcmcia-cs/modules/cirrus.h | 188 | ||||
-rw-r--r-- | linux/pcmcia-cs/modules/cistpl.c | 1502 | ||||
-rw-r--r-- | linux/pcmcia-cs/modules/cs.c | 2399 | ||||
-rw-r--r-- | linux/pcmcia-cs/modules/cs_internal.h | 300 | ||||
-rw-r--r-- | linux/pcmcia-cs/modules/ds.c | 1004 | ||||
-rw-r--r-- | linux/pcmcia-cs/modules/ene.h | 59 | ||||
-rw-r--r-- | linux/pcmcia-cs/modules/i82365.c | 2570 | ||||
-rw-r--r-- | linux/pcmcia-cs/modules/i82365.h | 154 | ||||
-rw-r--r-- | linux/pcmcia-cs/modules/o2micro.h | 160 | ||||
-rw-r--r-- | linux/pcmcia-cs/modules/pci_fixup.c | 674 | ||||
-rw-r--r-- | linux/pcmcia-cs/modules/ricoh.h | 161 | ||||
-rw-r--r-- | linux/pcmcia-cs/modules/rsrc_mgr.c | 877 | ||||
-rw-r--r-- | linux/pcmcia-cs/modules/smc34c90.h | 58 | ||||
-rw-r--r-- | linux/pcmcia-cs/modules/ti113x.h | 264 | ||||
-rw-r--r-- | linux/pcmcia-cs/modules/topic.h | 123 | ||||
-rw-r--r-- | linux/pcmcia-cs/modules/vg468.h | 112 | ||||
-rw-r--r-- | linux/pcmcia-cs/modules/yenta.h | 156 |
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, ®); + 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, ®); + 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 */ |