162306a36Sopenharmony_ci/*====================================================================== 262306a36Sopenharmony_ci 362306a36Sopenharmony_ci Device driver for Databook TCIC-2 PCMCIA controller 462306a36Sopenharmony_ci 562306a36Sopenharmony_ci tcic.c 1.111 2000/02/15 04:13:12 662306a36Sopenharmony_ci 762306a36Sopenharmony_ci The contents of this file are subject to the Mozilla Public 862306a36Sopenharmony_ci License Version 1.1 (the "License"); you may not use this file 962306a36Sopenharmony_ci except in compliance with the License. You may obtain a copy of 1062306a36Sopenharmony_ci the License at http://www.mozilla.org/MPL/ 1162306a36Sopenharmony_ci 1262306a36Sopenharmony_ci Software distributed under the License is distributed on an "AS 1362306a36Sopenharmony_ci IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or 1462306a36Sopenharmony_ci implied. See the License for the specific language governing 1562306a36Sopenharmony_ci rights and limitations under the License. 1662306a36Sopenharmony_ci 1762306a36Sopenharmony_ci The initial developer of the original code is David A. Hinds 1862306a36Sopenharmony_ci <dahinds@users.sourceforge.net>. Portions created by David A. Hinds 1962306a36Sopenharmony_ci are Copyright (C) 1999 David A. Hinds. All Rights Reserved. 2062306a36Sopenharmony_ci 2162306a36Sopenharmony_ci Alternatively, the contents of this file may be used under the 2262306a36Sopenharmony_ci terms of the GNU General Public License version 2 (the "GPL"), in which 2362306a36Sopenharmony_ci case the provisions of the GPL are applicable instead of the 2462306a36Sopenharmony_ci above. If you wish to allow the use of your version of this file 2562306a36Sopenharmony_ci only under the terms of the GPL and not to allow others to use 2662306a36Sopenharmony_ci your version of this file under the MPL, indicate your decision 2762306a36Sopenharmony_ci by deleting the provisions above and replace them with the notice 2862306a36Sopenharmony_ci and other provisions required by the GPL. If you do not delete 2962306a36Sopenharmony_ci the provisions above, a recipient may use your version of this 3062306a36Sopenharmony_ci file under either the MPL or the GPL. 3162306a36Sopenharmony_ci 3262306a36Sopenharmony_ci======================================================================*/ 3362306a36Sopenharmony_ci 3462306a36Sopenharmony_ci#include <linux/module.h> 3562306a36Sopenharmony_ci#include <linux/moduleparam.h> 3662306a36Sopenharmony_ci#include <linux/init.h> 3762306a36Sopenharmony_ci#include <linux/types.h> 3862306a36Sopenharmony_ci#include <linux/fcntl.h> 3962306a36Sopenharmony_ci#include <linux/string.h> 4062306a36Sopenharmony_ci#include <linux/errno.h> 4162306a36Sopenharmony_ci#include <linux/interrupt.h> 4262306a36Sopenharmony_ci#include <linux/timer.h> 4362306a36Sopenharmony_ci#include <linux/ioport.h> 4462306a36Sopenharmony_ci#include <linux/delay.h> 4562306a36Sopenharmony_ci#include <linux/workqueue.h> 4662306a36Sopenharmony_ci#include <linux/platform_device.h> 4762306a36Sopenharmony_ci#include <linux/bitops.h> 4862306a36Sopenharmony_ci 4962306a36Sopenharmony_ci#include <asm/io.h> 5062306a36Sopenharmony_ci 5162306a36Sopenharmony_ci#include <pcmcia/ss.h> 5262306a36Sopenharmony_ci#include "tcic.h" 5362306a36Sopenharmony_ci 5462306a36Sopenharmony_ciMODULE_AUTHOR("David Hinds <dahinds@users.sourceforge.net>"); 5562306a36Sopenharmony_ciMODULE_DESCRIPTION("Databook TCIC-2 PCMCIA socket driver"); 5662306a36Sopenharmony_ciMODULE_LICENSE("Dual MPL/GPL"); 5762306a36Sopenharmony_ci 5862306a36Sopenharmony_ci/*====================================================================*/ 5962306a36Sopenharmony_ci 6062306a36Sopenharmony_ci/* Parameters that can be set with 'insmod' */ 6162306a36Sopenharmony_ci 6262306a36Sopenharmony_ci/* The base port address of the TCIC-2 chip */ 6362306a36Sopenharmony_cistatic unsigned long tcic_base = TCIC_BASE; 6462306a36Sopenharmony_ci 6562306a36Sopenharmony_ci/* Specify a socket number to ignore */ 6662306a36Sopenharmony_cistatic int ignore = -1; 6762306a36Sopenharmony_ci 6862306a36Sopenharmony_ci/* Probe for safe interrupts? */ 6962306a36Sopenharmony_cistatic int do_scan = 1; 7062306a36Sopenharmony_ci 7162306a36Sopenharmony_ci/* Bit map of interrupts to choose from */ 7262306a36Sopenharmony_cistatic u_int irq_mask = 0xffff; 7362306a36Sopenharmony_cistatic int irq_list[16]; 7462306a36Sopenharmony_cistatic unsigned int irq_list_count; 7562306a36Sopenharmony_ci 7662306a36Sopenharmony_ci/* The card status change interrupt -- 0 means autoselect */ 7762306a36Sopenharmony_cistatic int cs_irq; 7862306a36Sopenharmony_ci 7962306a36Sopenharmony_ci/* Poll status interval -- 0 means default to interrupt */ 8062306a36Sopenharmony_cistatic int poll_interval; 8162306a36Sopenharmony_ci 8262306a36Sopenharmony_ci/* Delay for card status double-checking */ 8362306a36Sopenharmony_cistatic int poll_quick = HZ/20; 8462306a36Sopenharmony_ci 8562306a36Sopenharmony_ci/* CCLK external clock time, in nanoseconds. 70 ns = 14.31818 MHz */ 8662306a36Sopenharmony_cistatic int cycle_time = 70; 8762306a36Sopenharmony_ci 8862306a36Sopenharmony_cimodule_param_hw(tcic_base, ulong, ioport, 0444); 8962306a36Sopenharmony_cimodule_param(ignore, int, 0444); 9062306a36Sopenharmony_cimodule_param(do_scan, int, 0444); 9162306a36Sopenharmony_cimodule_param_hw(irq_mask, int, other, 0444); 9262306a36Sopenharmony_cimodule_param_hw_array(irq_list, int, irq, &irq_list_count, 0444); 9362306a36Sopenharmony_cimodule_param_hw(cs_irq, int, irq, 0444); 9462306a36Sopenharmony_cimodule_param(poll_interval, int, 0444); 9562306a36Sopenharmony_cimodule_param(poll_quick, int, 0444); 9662306a36Sopenharmony_cimodule_param(cycle_time, int, 0444); 9762306a36Sopenharmony_ci 9862306a36Sopenharmony_ci/*====================================================================*/ 9962306a36Sopenharmony_ci 10062306a36Sopenharmony_cistatic irqreturn_t tcic_interrupt(int irq, void *dev); 10162306a36Sopenharmony_cistatic void tcic_timer(struct timer_list *unused); 10262306a36Sopenharmony_cistatic struct pccard_operations tcic_operations; 10362306a36Sopenharmony_ci 10462306a36Sopenharmony_cistruct tcic_socket { 10562306a36Sopenharmony_ci u_short psock; 10662306a36Sopenharmony_ci u_char last_sstat; 10762306a36Sopenharmony_ci u_char id; 10862306a36Sopenharmony_ci struct pcmcia_socket socket; 10962306a36Sopenharmony_ci}; 11062306a36Sopenharmony_ci 11162306a36Sopenharmony_cistatic struct timer_list poll_timer; 11262306a36Sopenharmony_cistatic int tcic_timer_pending; 11362306a36Sopenharmony_ci 11462306a36Sopenharmony_cistatic int sockets; 11562306a36Sopenharmony_cistatic struct tcic_socket socket_table[2]; 11662306a36Sopenharmony_ci 11762306a36Sopenharmony_ci/*====================================================================*/ 11862306a36Sopenharmony_ci 11962306a36Sopenharmony_ci/* Trick when selecting interrupts: the TCIC sktirq pin is supposed 12062306a36Sopenharmony_ci to map to irq 11, but is coded as 0 or 1 in the irq registers. */ 12162306a36Sopenharmony_ci#define TCIC_IRQ(x) ((x) ? (((x) == 11) ? 1 : (x)) : 15) 12262306a36Sopenharmony_ci 12362306a36Sopenharmony_ci#ifdef DEBUG_X 12462306a36Sopenharmony_cistatic u_char tcic_getb(u_char reg) 12562306a36Sopenharmony_ci{ 12662306a36Sopenharmony_ci u_char val = inb(tcic_base+reg); 12762306a36Sopenharmony_ci printk(KERN_DEBUG "tcic_getb(%#lx) = %#x\n", tcic_base+reg, val); 12862306a36Sopenharmony_ci return val; 12962306a36Sopenharmony_ci} 13062306a36Sopenharmony_ci 13162306a36Sopenharmony_cistatic u_short tcic_getw(u_char reg) 13262306a36Sopenharmony_ci{ 13362306a36Sopenharmony_ci u_short val = inw(tcic_base+reg); 13462306a36Sopenharmony_ci printk(KERN_DEBUG "tcic_getw(%#lx) = %#x\n", tcic_base+reg, val); 13562306a36Sopenharmony_ci return val; 13662306a36Sopenharmony_ci} 13762306a36Sopenharmony_ci 13862306a36Sopenharmony_cistatic void tcic_setb(u_char reg, u_char data) 13962306a36Sopenharmony_ci{ 14062306a36Sopenharmony_ci printk(KERN_DEBUG "tcic_setb(%#lx, %#x)\n", tcic_base+reg, data); 14162306a36Sopenharmony_ci outb(data, tcic_base+reg); 14262306a36Sopenharmony_ci} 14362306a36Sopenharmony_ci 14462306a36Sopenharmony_cistatic void tcic_setw(u_char reg, u_short data) 14562306a36Sopenharmony_ci{ 14662306a36Sopenharmony_ci printk(KERN_DEBUG "tcic_setw(%#lx, %#x)\n", tcic_base+reg, data); 14762306a36Sopenharmony_ci outw(data, tcic_base+reg); 14862306a36Sopenharmony_ci} 14962306a36Sopenharmony_ci#else 15062306a36Sopenharmony_ci#define tcic_getb(reg) inb(tcic_base+reg) 15162306a36Sopenharmony_ci#define tcic_getw(reg) inw(tcic_base+reg) 15262306a36Sopenharmony_ci#define tcic_setb(reg, data) outb(data, tcic_base+reg) 15362306a36Sopenharmony_ci#define tcic_setw(reg, data) outw(data, tcic_base+reg) 15462306a36Sopenharmony_ci#endif 15562306a36Sopenharmony_ci 15662306a36Sopenharmony_cistatic void tcic_setl(u_char reg, u_int data) 15762306a36Sopenharmony_ci{ 15862306a36Sopenharmony_ci#ifdef DEBUG_X 15962306a36Sopenharmony_ci printk(KERN_DEBUG "tcic_setl(%#x, %#lx)\n", tcic_base+reg, data); 16062306a36Sopenharmony_ci#endif 16162306a36Sopenharmony_ci outw(data & 0xffff, tcic_base+reg); 16262306a36Sopenharmony_ci outw(data >> 16, tcic_base+reg+2); 16362306a36Sopenharmony_ci} 16462306a36Sopenharmony_ci 16562306a36Sopenharmony_cistatic void tcic_aux_setb(u_short reg, u_char data) 16662306a36Sopenharmony_ci{ 16762306a36Sopenharmony_ci u_char mode = (tcic_getb(TCIC_MODE) & TCIC_MODE_PGMMASK) | reg; 16862306a36Sopenharmony_ci tcic_setb(TCIC_MODE, mode); 16962306a36Sopenharmony_ci tcic_setb(TCIC_AUX, data); 17062306a36Sopenharmony_ci} 17162306a36Sopenharmony_ci 17262306a36Sopenharmony_cistatic u_short tcic_aux_getw(u_short reg) 17362306a36Sopenharmony_ci{ 17462306a36Sopenharmony_ci u_char mode = (tcic_getb(TCIC_MODE) & TCIC_MODE_PGMMASK) | reg; 17562306a36Sopenharmony_ci tcic_setb(TCIC_MODE, mode); 17662306a36Sopenharmony_ci return tcic_getw(TCIC_AUX); 17762306a36Sopenharmony_ci} 17862306a36Sopenharmony_ci 17962306a36Sopenharmony_cistatic void tcic_aux_setw(u_short reg, u_short data) 18062306a36Sopenharmony_ci{ 18162306a36Sopenharmony_ci u_char mode = (tcic_getb(TCIC_MODE) & TCIC_MODE_PGMMASK) | reg; 18262306a36Sopenharmony_ci tcic_setb(TCIC_MODE, mode); 18362306a36Sopenharmony_ci tcic_setw(TCIC_AUX, data); 18462306a36Sopenharmony_ci} 18562306a36Sopenharmony_ci 18662306a36Sopenharmony_ci/*====================================================================*/ 18762306a36Sopenharmony_ci 18862306a36Sopenharmony_ci/* Time conversion functions */ 18962306a36Sopenharmony_ci 19062306a36Sopenharmony_cistatic int to_cycles(int ns) 19162306a36Sopenharmony_ci{ 19262306a36Sopenharmony_ci if (ns < 14) 19362306a36Sopenharmony_ci return 0; 19462306a36Sopenharmony_ci else 19562306a36Sopenharmony_ci return 2*(ns-14)/cycle_time; 19662306a36Sopenharmony_ci} 19762306a36Sopenharmony_ci 19862306a36Sopenharmony_ci/*====================================================================*/ 19962306a36Sopenharmony_ci 20062306a36Sopenharmony_cistatic volatile u_int irq_hits; 20162306a36Sopenharmony_ci 20262306a36Sopenharmony_cistatic irqreturn_t __init tcic_irq_count(int irq, void *dev) 20362306a36Sopenharmony_ci{ 20462306a36Sopenharmony_ci irq_hits++; 20562306a36Sopenharmony_ci return IRQ_HANDLED; 20662306a36Sopenharmony_ci} 20762306a36Sopenharmony_ci 20862306a36Sopenharmony_cistatic u_int __init try_irq(int irq) 20962306a36Sopenharmony_ci{ 21062306a36Sopenharmony_ci u_short cfg; 21162306a36Sopenharmony_ci 21262306a36Sopenharmony_ci irq_hits = 0; 21362306a36Sopenharmony_ci if (request_irq(irq, tcic_irq_count, 0, "irq scan", tcic_irq_count) != 0) 21462306a36Sopenharmony_ci return -1; 21562306a36Sopenharmony_ci mdelay(10); 21662306a36Sopenharmony_ci if (irq_hits) { 21762306a36Sopenharmony_ci free_irq(irq, tcic_irq_count); 21862306a36Sopenharmony_ci return -1; 21962306a36Sopenharmony_ci } 22062306a36Sopenharmony_ci 22162306a36Sopenharmony_ci /* Generate one interrupt */ 22262306a36Sopenharmony_ci cfg = TCIC_SYSCFG_AUTOBUSY | 0x0a00; 22362306a36Sopenharmony_ci tcic_aux_setw(TCIC_AUX_SYSCFG, cfg | TCIC_IRQ(irq)); 22462306a36Sopenharmony_ci tcic_setb(TCIC_IENA, TCIC_IENA_ERR | TCIC_IENA_CFG_HIGH); 22562306a36Sopenharmony_ci tcic_setb(TCIC_ICSR, TCIC_ICSR_ERR | TCIC_ICSR_JAM); 22662306a36Sopenharmony_ci 22762306a36Sopenharmony_ci udelay(1000); 22862306a36Sopenharmony_ci free_irq(irq, tcic_irq_count); 22962306a36Sopenharmony_ci 23062306a36Sopenharmony_ci /* Turn off interrupts */ 23162306a36Sopenharmony_ci tcic_setb(TCIC_IENA, TCIC_IENA_CFG_OFF); 23262306a36Sopenharmony_ci while (tcic_getb(TCIC_ICSR)) 23362306a36Sopenharmony_ci tcic_setb(TCIC_ICSR, TCIC_ICSR_JAM); 23462306a36Sopenharmony_ci tcic_aux_setw(TCIC_AUX_SYSCFG, cfg); 23562306a36Sopenharmony_ci 23662306a36Sopenharmony_ci return (irq_hits != 1); 23762306a36Sopenharmony_ci} 23862306a36Sopenharmony_ci 23962306a36Sopenharmony_cistatic u_int __init irq_scan(u_int mask0) 24062306a36Sopenharmony_ci{ 24162306a36Sopenharmony_ci u_int mask1; 24262306a36Sopenharmony_ci int i; 24362306a36Sopenharmony_ci 24462306a36Sopenharmony_ci#ifdef __alpha__ 24562306a36Sopenharmony_ci#define PIC 0x4d0 24662306a36Sopenharmony_ci /* Don't probe level-triggered interrupts -- reserved for PCI */ 24762306a36Sopenharmony_ci int level_mask = inb_p(PIC) | (inb_p(PIC+1) << 8); 24862306a36Sopenharmony_ci if (level_mask) 24962306a36Sopenharmony_ci mask0 &= ~level_mask; 25062306a36Sopenharmony_ci#endif 25162306a36Sopenharmony_ci 25262306a36Sopenharmony_ci mask1 = 0; 25362306a36Sopenharmony_ci if (do_scan) { 25462306a36Sopenharmony_ci for (i = 0; i < 16; i++) 25562306a36Sopenharmony_ci if ((mask0 & (1 << i)) && (try_irq(i) == 0)) 25662306a36Sopenharmony_ci mask1 |= (1 << i); 25762306a36Sopenharmony_ci for (i = 0; i < 16; i++) 25862306a36Sopenharmony_ci if ((mask1 & (1 << i)) && (try_irq(i) != 0)) { 25962306a36Sopenharmony_ci mask1 ^= (1 << i); 26062306a36Sopenharmony_ci } 26162306a36Sopenharmony_ci } 26262306a36Sopenharmony_ci 26362306a36Sopenharmony_ci if (mask1) { 26462306a36Sopenharmony_ci printk("scanned"); 26562306a36Sopenharmony_ci } else { 26662306a36Sopenharmony_ci /* Fallback: just find interrupts that aren't in use */ 26762306a36Sopenharmony_ci for (i = 0; i < 16; i++) 26862306a36Sopenharmony_ci if ((mask0 & (1 << i)) && 26962306a36Sopenharmony_ci (request_irq(i, tcic_irq_count, 0, "x", tcic_irq_count) == 0)) { 27062306a36Sopenharmony_ci mask1 |= (1 << i); 27162306a36Sopenharmony_ci free_irq(i, tcic_irq_count); 27262306a36Sopenharmony_ci } 27362306a36Sopenharmony_ci printk("default"); 27462306a36Sopenharmony_ci } 27562306a36Sopenharmony_ci 27662306a36Sopenharmony_ci printk(") = "); 27762306a36Sopenharmony_ci for (i = 0; i < 16; i++) 27862306a36Sopenharmony_ci if (mask1 & (1<<i)) 27962306a36Sopenharmony_ci printk("%s%d", ((mask1 & ((1<<i)-1)) ? "," : ""), i); 28062306a36Sopenharmony_ci printk(" "); 28162306a36Sopenharmony_ci 28262306a36Sopenharmony_ci return mask1; 28362306a36Sopenharmony_ci} 28462306a36Sopenharmony_ci 28562306a36Sopenharmony_ci/*====================================================================== 28662306a36Sopenharmony_ci 28762306a36Sopenharmony_ci See if a card is present, powered up, in IO mode, and already 28862306a36Sopenharmony_ci bound to a (non-PCMCIA) Linux driver. 28962306a36Sopenharmony_ci 29062306a36Sopenharmony_ci We make an exception for cards that look like serial devices. 29162306a36Sopenharmony_ci 29262306a36Sopenharmony_ci======================================================================*/ 29362306a36Sopenharmony_ci 29462306a36Sopenharmony_cistatic int __init is_active(int s) 29562306a36Sopenharmony_ci{ 29662306a36Sopenharmony_ci u_short scf1, ioctl, base, num; 29762306a36Sopenharmony_ci u_char pwr, sstat; 29862306a36Sopenharmony_ci u_int addr; 29962306a36Sopenharmony_ci 30062306a36Sopenharmony_ci tcic_setl(TCIC_ADDR, (s << TCIC_ADDR_SS_SHFT) 30162306a36Sopenharmony_ci | TCIC_ADDR_INDREG | TCIC_SCF1(s)); 30262306a36Sopenharmony_ci scf1 = tcic_getw(TCIC_DATA); 30362306a36Sopenharmony_ci pwr = tcic_getb(TCIC_PWR); 30462306a36Sopenharmony_ci sstat = tcic_getb(TCIC_SSTAT); 30562306a36Sopenharmony_ci addr = TCIC_IWIN(s, 0); 30662306a36Sopenharmony_ci tcic_setw(TCIC_ADDR, addr + TCIC_IBASE_X); 30762306a36Sopenharmony_ci base = tcic_getw(TCIC_DATA); 30862306a36Sopenharmony_ci tcic_setw(TCIC_ADDR, addr + TCIC_ICTL_X); 30962306a36Sopenharmony_ci ioctl = tcic_getw(TCIC_DATA); 31062306a36Sopenharmony_ci 31162306a36Sopenharmony_ci if (ioctl & TCIC_ICTL_TINY) 31262306a36Sopenharmony_ci num = 1; 31362306a36Sopenharmony_ci else { 31462306a36Sopenharmony_ci num = (base ^ (base-1)); 31562306a36Sopenharmony_ci base = base & (base-1); 31662306a36Sopenharmony_ci } 31762306a36Sopenharmony_ci 31862306a36Sopenharmony_ci if ((sstat & TCIC_SSTAT_CD) && (pwr & TCIC_PWR_VCC(s)) && 31962306a36Sopenharmony_ci (scf1 & TCIC_SCF1_IOSTS) && (ioctl & TCIC_ICTL_ENA) && 32062306a36Sopenharmony_ci ((base & 0xfeef) != 0x02e8)) { 32162306a36Sopenharmony_ci struct resource *res = request_region(base, num, "tcic-2"); 32262306a36Sopenharmony_ci if (!res) /* region is busy */ 32362306a36Sopenharmony_ci return 1; 32462306a36Sopenharmony_ci release_region(base, num); 32562306a36Sopenharmony_ci } 32662306a36Sopenharmony_ci 32762306a36Sopenharmony_ci return 0; 32862306a36Sopenharmony_ci} 32962306a36Sopenharmony_ci 33062306a36Sopenharmony_ci/*====================================================================== 33162306a36Sopenharmony_ci 33262306a36Sopenharmony_ci This returns the revision code for the specified socket. 33362306a36Sopenharmony_ci 33462306a36Sopenharmony_ci======================================================================*/ 33562306a36Sopenharmony_ci 33662306a36Sopenharmony_cistatic int __init get_tcic_id(void) 33762306a36Sopenharmony_ci{ 33862306a36Sopenharmony_ci u_short id; 33962306a36Sopenharmony_ci 34062306a36Sopenharmony_ci tcic_aux_setw(TCIC_AUX_TEST, TCIC_TEST_DIAG); 34162306a36Sopenharmony_ci id = tcic_aux_getw(TCIC_AUX_ILOCK); 34262306a36Sopenharmony_ci id = (id & TCIC_ILOCKTEST_ID_MASK) >> TCIC_ILOCKTEST_ID_SH; 34362306a36Sopenharmony_ci tcic_aux_setw(TCIC_AUX_TEST, 0); 34462306a36Sopenharmony_ci return id; 34562306a36Sopenharmony_ci} 34662306a36Sopenharmony_ci 34762306a36Sopenharmony_ci/*====================================================================*/ 34862306a36Sopenharmony_ci 34962306a36Sopenharmony_cistatic struct platform_driver tcic_driver = { 35062306a36Sopenharmony_ci .driver = { 35162306a36Sopenharmony_ci .name = "tcic-pcmcia", 35262306a36Sopenharmony_ci }, 35362306a36Sopenharmony_ci}; 35462306a36Sopenharmony_ci 35562306a36Sopenharmony_cistatic struct platform_device tcic_device = { 35662306a36Sopenharmony_ci .name = "tcic-pcmcia", 35762306a36Sopenharmony_ci .id = 0, 35862306a36Sopenharmony_ci}; 35962306a36Sopenharmony_ci 36062306a36Sopenharmony_ci 36162306a36Sopenharmony_cistatic int __init init_tcic(void) 36262306a36Sopenharmony_ci{ 36362306a36Sopenharmony_ci int i, sock, ret = 0; 36462306a36Sopenharmony_ci u_int mask, scan; 36562306a36Sopenharmony_ci 36662306a36Sopenharmony_ci if (platform_driver_register(&tcic_driver)) 36762306a36Sopenharmony_ci return -1; 36862306a36Sopenharmony_ci 36962306a36Sopenharmony_ci printk(KERN_INFO "Databook TCIC-2 PCMCIA probe: "); 37062306a36Sopenharmony_ci sock = 0; 37162306a36Sopenharmony_ci 37262306a36Sopenharmony_ci if (!request_region(tcic_base, 16, "tcic-2")) { 37362306a36Sopenharmony_ci printk("could not allocate ports,\n "); 37462306a36Sopenharmony_ci platform_driver_unregister(&tcic_driver); 37562306a36Sopenharmony_ci return -ENODEV; 37662306a36Sopenharmony_ci } 37762306a36Sopenharmony_ci else { 37862306a36Sopenharmony_ci tcic_setw(TCIC_ADDR, 0); 37962306a36Sopenharmony_ci if (tcic_getw(TCIC_ADDR) == 0) { 38062306a36Sopenharmony_ci tcic_setw(TCIC_ADDR, 0xc3a5); 38162306a36Sopenharmony_ci if (tcic_getw(TCIC_ADDR) == 0xc3a5) sock = 2; 38262306a36Sopenharmony_ci } 38362306a36Sopenharmony_ci if (sock == 0) { 38462306a36Sopenharmony_ci /* See if resetting the controller does any good */ 38562306a36Sopenharmony_ci tcic_setb(TCIC_SCTRL, TCIC_SCTRL_RESET); 38662306a36Sopenharmony_ci tcic_setb(TCIC_SCTRL, 0); 38762306a36Sopenharmony_ci tcic_setw(TCIC_ADDR, 0); 38862306a36Sopenharmony_ci if (tcic_getw(TCIC_ADDR) == 0) { 38962306a36Sopenharmony_ci tcic_setw(TCIC_ADDR, 0xc3a5); 39062306a36Sopenharmony_ci if (tcic_getw(TCIC_ADDR) == 0xc3a5) sock = 2; 39162306a36Sopenharmony_ci } 39262306a36Sopenharmony_ci } 39362306a36Sopenharmony_ci } 39462306a36Sopenharmony_ci if (sock == 0) { 39562306a36Sopenharmony_ci printk("not found.\n"); 39662306a36Sopenharmony_ci release_region(tcic_base, 16); 39762306a36Sopenharmony_ci platform_driver_unregister(&tcic_driver); 39862306a36Sopenharmony_ci return -ENODEV; 39962306a36Sopenharmony_ci } 40062306a36Sopenharmony_ci 40162306a36Sopenharmony_ci sockets = 0; 40262306a36Sopenharmony_ci for (i = 0; i < sock; i++) { 40362306a36Sopenharmony_ci if ((i == ignore) || is_active(i)) continue; 40462306a36Sopenharmony_ci socket_table[sockets].psock = i; 40562306a36Sopenharmony_ci socket_table[sockets].id = get_tcic_id(); 40662306a36Sopenharmony_ci 40762306a36Sopenharmony_ci socket_table[sockets].socket.owner = THIS_MODULE; 40862306a36Sopenharmony_ci /* only 16-bit cards, memory windows must be size-aligned */ 40962306a36Sopenharmony_ci /* No PCI or CardBus support */ 41062306a36Sopenharmony_ci socket_table[sockets].socket.features = SS_CAP_PCCARD | SS_CAP_MEM_ALIGN; 41162306a36Sopenharmony_ci /* irq 14, 11, 10, 7, 6, 5, 4, 3 */ 41262306a36Sopenharmony_ci socket_table[sockets].socket.irq_mask = 0x4cf8; 41362306a36Sopenharmony_ci /* 4K minimum window size */ 41462306a36Sopenharmony_ci socket_table[sockets].socket.map_size = 0x1000; 41562306a36Sopenharmony_ci sockets++; 41662306a36Sopenharmony_ci } 41762306a36Sopenharmony_ci 41862306a36Sopenharmony_ci switch (socket_table[0].id) { 41962306a36Sopenharmony_ci case TCIC_ID_DB86082: 42062306a36Sopenharmony_ci printk("DB86082"); break; 42162306a36Sopenharmony_ci case TCIC_ID_DB86082A: 42262306a36Sopenharmony_ci printk("DB86082A"); break; 42362306a36Sopenharmony_ci case TCIC_ID_DB86084: 42462306a36Sopenharmony_ci printk("DB86084"); break; 42562306a36Sopenharmony_ci case TCIC_ID_DB86084A: 42662306a36Sopenharmony_ci printk("DB86084A"); break; 42762306a36Sopenharmony_ci case TCIC_ID_DB86072: 42862306a36Sopenharmony_ci printk("DB86072"); break; 42962306a36Sopenharmony_ci case TCIC_ID_DB86184: 43062306a36Sopenharmony_ci printk("DB86184"); break; 43162306a36Sopenharmony_ci case TCIC_ID_DB86082B: 43262306a36Sopenharmony_ci printk("DB86082B"); break; 43362306a36Sopenharmony_ci default: 43462306a36Sopenharmony_ci printk("Unknown ID 0x%02x", socket_table[0].id); 43562306a36Sopenharmony_ci } 43662306a36Sopenharmony_ci 43762306a36Sopenharmony_ci /* Set up polling */ 43862306a36Sopenharmony_ci timer_setup(&poll_timer, &tcic_timer, 0); 43962306a36Sopenharmony_ci 44062306a36Sopenharmony_ci /* Build interrupt mask */ 44162306a36Sopenharmony_ci printk(KERN_CONT ", %d sockets\n", sockets); 44262306a36Sopenharmony_ci printk(KERN_INFO " irq list ("); 44362306a36Sopenharmony_ci if (irq_list_count == 0) 44462306a36Sopenharmony_ci mask = irq_mask; 44562306a36Sopenharmony_ci else 44662306a36Sopenharmony_ci for (i = mask = 0; i < irq_list_count; i++) 44762306a36Sopenharmony_ci mask |= (1<<irq_list[i]); 44862306a36Sopenharmony_ci 44962306a36Sopenharmony_ci /* irq 14, 11, 10, 7, 6, 5, 4, 3 */ 45062306a36Sopenharmony_ci mask &= 0x4cf8; 45162306a36Sopenharmony_ci /* Scan interrupts */ 45262306a36Sopenharmony_ci mask = irq_scan(mask); 45362306a36Sopenharmony_ci for (i=0;i<sockets;i++) 45462306a36Sopenharmony_ci socket_table[i].socket.irq_mask = mask; 45562306a36Sopenharmony_ci 45662306a36Sopenharmony_ci /* Check for only two interrupts available */ 45762306a36Sopenharmony_ci scan = (mask & (mask-1)); 45862306a36Sopenharmony_ci if (((scan & (scan-1)) == 0) && (poll_interval == 0)) 45962306a36Sopenharmony_ci poll_interval = HZ; 46062306a36Sopenharmony_ci 46162306a36Sopenharmony_ci if (poll_interval == 0) { 46262306a36Sopenharmony_ci /* Avoid irq 12 unless it is explicitly requested */ 46362306a36Sopenharmony_ci u_int cs_mask = mask & ((cs_irq) ? (1<<cs_irq) : ~(1<<12)); 46462306a36Sopenharmony_ci for (i = 15; i > 0; i--) 46562306a36Sopenharmony_ci if ((cs_mask & (1 << i)) && 46662306a36Sopenharmony_ci (request_irq(i, tcic_interrupt, 0, "tcic", 46762306a36Sopenharmony_ci tcic_interrupt) == 0)) 46862306a36Sopenharmony_ci break; 46962306a36Sopenharmony_ci cs_irq = i; 47062306a36Sopenharmony_ci if (cs_irq == 0) poll_interval = HZ; 47162306a36Sopenharmony_ci } 47262306a36Sopenharmony_ci 47362306a36Sopenharmony_ci if (socket_table[0].socket.irq_mask & (1 << 11)) 47462306a36Sopenharmony_ci printk("sktirq is irq 11, "); 47562306a36Sopenharmony_ci if (cs_irq != 0) 47662306a36Sopenharmony_ci printk("status change on irq %d\n", cs_irq); 47762306a36Sopenharmony_ci else 47862306a36Sopenharmony_ci printk("polled status, interval = %d ms\n", 47962306a36Sopenharmony_ci poll_interval * 1000 / HZ); 48062306a36Sopenharmony_ci 48162306a36Sopenharmony_ci for (i = 0; i < sockets; i++) { 48262306a36Sopenharmony_ci tcic_setw(TCIC_ADDR+2, socket_table[i].psock << TCIC_SS_SHFT); 48362306a36Sopenharmony_ci socket_table[i].last_sstat = tcic_getb(TCIC_SSTAT); 48462306a36Sopenharmony_ci } 48562306a36Sopenharmony_ci 48662306a36Sopenharmony_ci /* jump start interrupt handler, if needed */ 48762306a36Sopenharmony_ci tcic_interrupt(0, NULL); 48862306a36Sopenharmony_ci 48962306a36Sopenharmony_ci platform_device_register(&tcic_device); 49062306a36Sopenharmony_ci 49162306a36Sopenharmony_ci for (i = 0; i < sockets; i++) { 49262306a36Sopenharmony_ci socket_table[i].socket.ops = &tcic_operations; 49362306a36Sopenharmony_ci socket_table[i].socket.resource_ops = &pccard_nonstatic_ops; 49462306a36Sopenharmony_ci socket_table[i].socket.dev.parent = &tcic_device.dev; 49562306a36Sopenharmony_ci ret = pcmcia_register_socket(&socket_table[i].socket); 49662306a36Sopenharmony_ci if (ret && i) 49762306a36Sopenharmony_ci pcmcia_unregister_socket(&socket_table[0].socket); 49862306a36Sopenharmony_ci } 49962306a36Sopenharmony_ci 50062306a36Sopenharmony_ci return ret; 50162306a36Sopenharmony_ci 50262306a36Sopenharmony_ci return 0; 50362306a36Sopenharmony_ci 50462306a36Sopenharmony_ci} /* init_tcic */ 50562306a36Sopenharmony_ci 50662306a36Sopenharmony_ci/*====================================================================*/ 50762306a36Sopenharmony_ci 50862306a36Sopenharmony_cistatic void __exit exit_tcic(void) 50962306a36Sopenharmony_ci{ 51062306a36Sopenharmony_ci int i; 51162306a36Sopenharmony_ci 51262306a36Sopenharmony_ci del_timer_sync(&poll_timer); 51362306a36Sopenharmony_ci if (cs_irq != 0) { 51462306a36Sopenharmony_ci tcic_aux_setw(TCIC_AUX_SYSCFG, TCIC_SYSCFG_AUTOBUSY|0x0a00); 51562306a36Sopenharmony_ci free_irq(cs_irq, tcic_interrupt); 51662306a36Sopenharmony_ci } 51762306a36Sopenharmony_ci release_region(tcic_base, 16); 51862306a36Sopenharmony_ci 51962306a36Sopenharmony_ci for (i = 0; i < sockets; i++) { 52062306a36Sopenharmony_ci pcmcia_unregister_socket(&socket_table[i].socket); 52162306a36Sopenharmony_ci } 52262306a36Sopenharmony_ci 52362306a36Sopenharmony_ci platform_device_unregister(&tcic_device); 52462306a36Sopenharmony_ci platform_driver_unregister(&tcic_driver); 52562306a36Sopenharmony_ci} /* exit_tcic */ 52662306a36Sopenharmony_ci 52762306a36Sopenharmony_ci/*====================================================================*/ 52862306a36Sopenharmony_ci 52962306a36Sopenharmony_cistatic irqreturn_t tcic_interrupt(int irq, void *dev) 53062306a36Sopenharmony_ci{ 53162306a36Sopenharmony_ci int i, quick = 0; 53262306a36Sopenharmony_ci u_char latch, sstat; 53362306a36Sopenharmony_ci u_short psock; 53462306a36Sopenharmony_ci u_int events; 53562306a36Sopenharmony_ci static volatile int active = 0; 53662306a36Sopenharmony_ci 53762306a36Sopenharmony_ci if (active) { 53862306a36Sopenharmony_ci printk(KERN_NOTICE "tcic: reentered interrupt handler!\n"); 53962306a36Sopenharmony_ci return IRQ_NONE; 54062306a36Sopenharmony_ci } else 54162306a36Sopenharmony_ci active = 1; 54262306a36Sopenharmony_ci 54362306a36Sopenharmony_ci pr_debug("tcic_interrupt()\n"); 54462306a36Sopenharmony_ci 54562306a36Sopenharmony_ci for (i = 0; i < sockets; i++) { 54662306a36Sopenharmony_ci psock = socket_table[i].psock; 54762306a36Sopenharmony_ci tcic_setl(TCIC_ADDR, (psock << TCIC_ADDR_SS_SHFT) 54862306a36Sopenharmony_ci | TCIC_ADDR_INDREG | TCIC_SCF1(psock)); 54962306a36Sopenharmony_ci sstat = tcic_getb(TCIC_SSTAT); 55062306a36Sopenharmony_ci latch = sstat ^ socket_table[psock].last_sstat; 55162306a36Sopenharmony_ci socket_table[i].last_sstat = sstat; 55262306a36Sopenharmony_ci if (tcic_getb(TCIC_ICSR) & TCIC_ICSR_CDCHG) { 55362306a36Sopenharmony_ci tcic_setb(TCIC_ICSR, TCIC_ICSR_CLEAR); 55462306a36Sopenharmony_ci quick = 1; 55562306a36Sopenharmony_ci } 55662306a36Sopenharmony_ci if (latch == 0) 55762306a36Sopenharmony_ci continue; 55862306a36Sopenharmony_ci events = (latch & TCIC_SSTAT_CD) ? SS_DETECT : 0; 55962306a36Sopenharmony_ci events |= (latch & TCIC_SSTAT_WP) ? SS_WRPROT : 0; 56062306a36Sopenharmony_ci if (tcic_getw(TCIC_DATA) & TCIC_SCF1_IOSTS) { 56162306a36Sopenharmony_ci events |= (latch & TCIC_SSTAT_LBAT1) ? SS_STSCHG : 0; 56262306a36Sopenharmony_ci } else { 56362306a36Sopenharmony_ci events |= (latch & TCIC_SSTAT_RDY) ? SS_READY : 0; 56462306a36Sopenharmony_ci events |= (latch & TCIC_SSTAT_LBAT1) ? SS_BATDEAD : 0; 56562306a36Sopenharmony_ci events |= (latch & TCIC_SSTAT_LBAT2) ? SS_BATWARN : 0; 56662306a36Sopenharmony_ci } 56762306a36Sopenharmony_ci if (events) { 56862306a36Sopenharmony_ci pcmcia_parse_events(&socket_table[i].socket, events); 56962306a36Sopenharmony_ci } 57062306a36Sopenharmony_ci } 57162306a36Sopenharmony_ci 57262306a36Sopenharmony_ci /* Schedule next poll, if needed */ 57362306a36Sopenharmony_ci if (((cs_irq == 0) || quick) && (!tcic_timer_pending)) { 57462306a36Sopenharmony_ci poll_timer.expires = jiffies + (quick ? poll_quick : poll_interval); 57562306a36Sopenharmony_ci add_timer(&poll_timer); 57662306a36Sopenharmony_ci tcic_timer_pending = 1; 57762306a36Sopenharmony_ci } 57862306a36Sopenharmony_ci active = 0; 57962306a36Sopenharmony_ci 58062306a36Sopenharmony_ci pr_debug("interrupt done\n"); 58162306a36Sopenharmony_ci return IRQ_HANDLED; 58262306a36Sopenharmony_ci} /* tcic_interrupt */ 58362306a36Sopenharmony_ci 58462306a36Sopenharmony_cistatic void tcic_timer(struct timer_list *unused) 58562306a36Sopenharmony_ci{ 58662306a36Sopenharmony_ci pr_debug("tcic_timer()\n"); 58762306a36Sopenharmony_ci tcic_timer_pending = 0; 58862306a36Sopenharmony_ci tcic_interrupt(0, NULL); 58962306a36Sopenharmony_ci} /* tcic_timer */ 59062306a36Sopenharmony_ci 59162306a36Sopenharmony_ci/*====================================================================*/ 59262306a36Sopenharmony_ci 59362306a36Sopenharmony_cistatic int tcic_get_status(struct pcmcia_socket *sock, u_int *value) 59462306a36Sopenharmony_ci{ 59562306a36Sopenharmony_ci u_short psock = container_of(sock, struct tcic_socket, socket)->psock; 59662306a36Sopenharmony_ci u_char reg; 59762306a36Sopenharmony_ci 59862306a36Sopenharmony_ci tcic_setl(TCIC_ADDR, (psock << TCIC_ADDR_SS_SHFT) 59962306a36Sopenharmony_ci | TCIC_ADDR_INDREG | TCIC_SCF1(psock)); 60062306a36Sopenharmony_ci reg = tcic_getb(TCIC_SSTAT); 60162306a36Sopenharmony_ci *value = (reg & TCIC_SSTAT_CD) ? SS_DETECT : 0; 60262306a36Sopenharmony_ci *value |= (reg & TCIC_SSTAT_WP) ? SS_WRPROT : 0; 60362306a36Sopenharmony_ci if (tcic_getw(TCIC_DATA) & TCIC_SCF1_IOSTS) { 60462306a36Sopenharmony_ci *value |= (reg & TCIC_SSTAT_LBAT1) ? SS_STSCHG : 0; 60562306a36Sopenharmony_ci } else { 60662306a36Sopenharmony_ci *value |= (reg & TCIC_SSTAT_RDY) ? SS_READY : 0; 60762306a36Sopenharmony_ci *value |= (reg & TCIC_SSTAT_LBAT1) ? SS_BATDEAD : 0; 60862306a36Sopenharmony_ci *value |= (reg & TCIC_SSTAT_LBAT2) ? SS_BATWARN : 0; 60962306a36Sopenharmony_ci } 61062306a36Sopenharmony_ci reg = tcic_getb(TCIC_PWR); 61162306a36Sopenharmony_ci if (reg & (TCIC_PWR_VCC(psock)|TCIC_PWR_VPP(psock))) 61262306a36Sopenharmony_ci *value |= SS_POWERON; 61362306a36Sopenharmony_ci dev_dbg(&sock->dev, "GetStatus(%d) = %#2.2x\n", psock, *value); 61462306a36Sopenharmony_ci return 0; 61562306a36Sopenharmony_ci} /* tcic_get_status */ 61662306a36Sopenharmony_ci 61762306a36Sopenharmony_ci/*====================================================================*/ 61862306a36Sopenharmony_ci 61962306a36Sopenharmony_cistatic int tcic_set_socket(struct pcmcia_socket *sock, socket_state_t *state) 62062306a36Sopenharmony_ci{ 62162306a36Sopenharmony_ci u_short psock = container_of(sock, struct tcic_socket, socket)->psock; 62262306a36Sopenharmony_ci u_char reg; 62362306a36Sopenharmony_ci u_short scf1, scf2; 62462306a36Sopenharmony_ci 62562306a36Sopenharmony_ci dev_dbg(&sock->dev, "SetSocket(%d, flags %#3.3x, Vcc %d, Vpp %d, " 62662306a36Sopenharmony_ci "io_irq %d, csc_mask %#2.2x)\n", psock, state->flags, 62762306a36Sopenharmony_ci state->Vcc, state->Vpp, state->io_irq, state->csc_mask); 62862306a36Sopenharmony_ci tcic_setw(TCIC_ADDR+2, (psock << TCIC_SS_SHFT) | TCIC_ADR2_INDREG); 62962306a36Sopenharmony_ci 63062306a36Sopenharmony_ci reg = tcic_getb(TCIC_PWR); 63162306a36Sopenharmony_ci reg &= ~(TCIC_PWR_VCC(psock) | TCIC_PWR_VPP(psock)); 63262306a36Sopenharmony_ci 63362306a36Sopenharmony_ci if (state->Vcc == 50) { 63462306a36Sopenharmony_ci switch (state->Vpp) { 63562306a36Sopenharmony_ci case 0: reg |= TCIC_PWR_VCC(psock) | TCIC_PWR_VPP(psock); break; 63662306a36Sopenharmony_ci case 50: reg |= TCIC_PWR_VCC(psock); break; 63762306a36Sopenharmony_ci case 120: reg |= TCIC_PWR_VPP(psock); break; 63862306a36Sopenharmony_ci default: return -EINVAL; 63962306a36Sopenharmony_ci } 64062306a36Sopenharmony_ci } else if (state->Vcc != 0) 64162306a36Sopenharmony_ci return -EINVAL; 64262306a36Sopenharmony_ci 64362306a36Sopenharmony_ci if (reg != tcic_getb(TCIC_PWR)) 64462306a36Sopenharmony_ci tcic_setb(TCIC_PWR, reg); 64562306a36Sopenharmony_ci 64662306a36Sopenharmony_ci reg = TCIC_ILOCK_HOLD_CCLK | TCIC_ILOCK_CWAIT; 64762306a36Sopenharmony_ci if (state->flags & SS_OUTPUT_ENA) { 64862306a36Sopenharmony_ci tcic_setb(TCIC_SCTRL, TCIC_SCTRL_ENA); 64962306a36Sopenharmony_ci reg |= TCIC_ILOCK_CRESENA; 65062306a36Sopenharmony_ci } else 65162306a36Sopenharmony_ci tcic_setb(TCIC_SCTRL, 0); 65262306a36Sopenharmony_ci if (state->flags & SS_RESET) 65362306a36Sopenharmony_ci reg |= TCIC_ILOCK_CRESET; 65462306a36Sopenharmony_ci tcic_aux_setb(TCIC_AUX_ILOCK, reg); 65562306a36Sopenharmony_ci 65662306a36Sopenharmony_ci tcic_setw(TCIC_ADDR, TCIC_SCF1(psock)); 65762306a36Sopenharmony_ci scf1 = TCIC_SCF1_FINPACK; 65862306a36Sopenharmony_ci scf1 |= TCIC_IRQ(state->io_irq); 65962306a36Sopenharmony_ci if (state->flags & SS_IOCARD) { 66062306a36Sopenharmony_ci scf1 |= TCIC_SCF1_IOSTS; 66162306a36Sopenharmony_ci if (state->flags & SS_SPKR_ENA) 66262306a36Sopenharmony_ci scf1 |= TCIC_SCF1_SPKR; 66362306a36Sopenharmony_ci if (state->flags & SS_DMA_MODE) 66462306a36Sopenharmony_ci scf1 |= TCIC_SCF1_DREQ2 << TCIC_SCF1_DMA_SHIFT; 66562306a36Sopenharmony_ci } 66662306a36Sopenharmony_ci tcic_setw(TCIC_DATA, scf1); 66762306a36Sopenharmony_ci 66862306a36Sopenharmony_ci /* Some general setup stuff, and configure status interrupt */ 66962306a36Sopenharmony_ci reg = TCIC_WAIT_ASYNC | TCIC_WAIT_SENSE | to_cycles(250); 67062306a36Sopenharmony_ci tcic_aux_setb(TCIC_AUX_WCTL, reg); 67162306a36Sopenharmony_ci tcic_aux_setw(TCIC_AUX_SYSCFG, TCIC_SYSCFG_AUTOBUSY|0x0a00| 67262306a36Sopenharmony_ci TCIC_IRQ(cs_irq)); 67362306a36Sopenharmony_ci 67462306a36Sopenharmony_ci /* Card status change interrupt mask */ 67562306a36Sopenharmony_ci tcic_setw(TCIC_ADDR, TCIC_SCF2(psock)); 67662306a36Sopenharmony_ci scf2 = TCIC_SCF2_MALL; 67762306a36Sopenharmony_ci if (state->csc_mask & SS_DETECT) scf2 &= ~TCIC_SCF2_MCD; 67862306a36Sopenharmony_ci if (state->flags & SS_IOCARD) { 67962306a36Sopenharmony_ci if (state->csc_mask & SS_STSCHG) reg &= ~TCIC_SCF2_MLBAT1; 68062306a36Sopenharmony_ci } else { 68162306a36Sopenharmony_ci if (state->csc_mask & SS_BATDEAD) reg &= ~TCIC_SCF2_MLBAT1; 68262306a36Sopenharmony_ci if (state->csc_mask & SS_BATWARN) reg &= ~TCIC_SCF2_MLBAT2; 68362306a36Sopenharmony_ci if (state->csc_mask & SS_READY) reg &= ~TCIC_SCF2_MRDY; 68462306a36Sopenharmony_ci } 68562306a36Sopenharmony_ci tcic_setw(TCIC_DATA, scf2); 68662306a36Sopenharmony_ci /* For the ISA bus, the irq should be active-high totem-pole */ 68762306a36Sopenharmony_ci tcic_setb(TCIC_IENA, TCIC_IENA_CDCHG | TCIC_IENA_CFG_HIGH); 68862306a36Sopenharmony_ci 68962306a36Sopenharmony_ci return 0; 69062306a36Sopenharmony_ci} /* tcic_set_socket */ 69162306a36Sopenharmony_ci 69262306a36Sopenharmony_ci/*====================================================================*/ 69362306a36Sopenharmony_ci 69462306a36Sopenharmony_cistatic int tcic_set_io_map(struct pcmcia_socket *sock, struct pccard_io_map *io) 69562306a36Sopenharmony_ci{ 69662306a36Sopenharmony_ci u_short psock = container_of(sock, struct tcic_socket, socket)->psock; 69762306a36Sopenharmony_ci u_int addr; 69862306a36Sopenharmony_ci u_short base, len, ioctl; 69962306a36Sopenharmony_ci 70062306a36Sopenharmony_ci dev_dbg(&sock->dev, "SetIOMap(%d, %d, %#2.2x, %d ns, " 70162306a36Sopenharmony_ci "%#llx-%#llx)\n", psock, io->map, io->flags, io->speed, 70262306a36Sopenharmony_ci (unsigned long long)io->start, (unsigned long long)io->stop); 70362306a36Sopenharmony_ci if ((io->map > 1) || (io->start > 0xffff) || (io->stop > 0xffff) || 70462306a36Sopenharmony_ci (io->stop < io->start)) return -EINVAL; 70562306a36Sopenharmony_ci tcic_setw(TCIC_ADDR+2, TCIC_ADR2_INDREG | (psock << TCIC_SS_SHFT)); 70662306a36Sopenharmony_ci addr = TCIC_IWIN(psock, io->map); 70762306a36Sopenharmony_ci 70862306a36Sopenharmony_ci base = io->start; len = io->stop - io->start; 70962306a36Sopenharmony_ci /* Check to see that len+1 is power of two, etc */ 71062306a36Sopenharmony_ci if ((len & (len+1)) || (base & len)) return -EINVAL; 71162306a36Sopenharmony_ci base |= (len+1)>>1; 71262306a36Sopenharmony_ci tcic_setw(TCIC_ADDR, addr + TCIC_IBASE_X); 71362306a36Sopenharmony_ci tcic_setw(TCIC_DATA, base); 71462306a36Sopenharmony_ci 71562306a36Sopenharmony_ci ioctl = (psock << TCIC_ICTL_SS_SHFT); 71662306a36Sopenharmony_ci ioctl |= (len == 0) ? TCIC_ICTL_TINY : 0; 71762306a36Sopenharmony_ci ioctl |= (io->flags & MAP_ACTIVE) ? TCIC_ICTL_ENA : 0; 71862306a36Sopenharmony_ci ioctl |= to_cycles(io->speed) & TCIC_ICTL_WSCNT_MASK; 71962306a36Sopenharmony_ci if (!(io->flags & MAP_AUTOSZ)) { 72062306a36Sopenharmony_ci ioctl |= TCIC_ICTL_QUIET; 72162306a36Sopenharmony_ci ioctl |= (io->flags & MAP_16BIT) ? TCIC_ICTL_BW_16 : TCIC_ICTL_BW_8; 72262306a36Sopenharmony_ci } 72362306a36Sopenharmony_ci tcic_setw(TCIC_ADDR, addr + TCIC_ICTL_X); 72462306a36Sopenharmony_ci tcic_setw(TCIC_DATA, ioctl); 72562306a36Sopenharmony_ci 72662306a36Sopenharmony_ci return 0; 72762306a36Sopenharmony_ci} /* tcic_set_io_map */ 72862306a36Sopenharmony_ci 72962306a36Sopenharmony_ci/*====================================================================*/ 73062306a36Sopenharmony_ci 73162306a36Sopenharmony_cistatic int tcic_set_mem_map(struct pcmcia_socket *sock, struct pccard_mem_map *mem) 73262306a36Sopenharmony_ci{ 73362306a36Sopenharmony_ci u_short psock = container_of(sock, struct tcic_socket, socket)->psock; 73462306a36Sopenharmony_ci u_short addr, ctl; 73562306a36Sopenharmony_ci u_long base, len, mmap; 73662306a36Sopenharmony_ci 73762306a36Sopenharmony_ci dev_dbg(&sock->dev, "SetMemMap(%d, %d, %#2.2x, %d ns, " 73862306a36Sopenharmony_ci "%#llx-%#llx, %#x)\n", psock, mem->map, mem->flags, 73962306a36Sopenharmony_ci mem->speed, (unsigned long long)mem->res->start, 74062306a36Sopenharmony_ci (unsigned long long)mem->res->end, mem->card_start); 74162306a36Sopenharmony_ci if ((mem->map > 3) || (mem->card_start > 0x3ffffff) || 74262306a36Sopenharmony_ci (mem->res->start > 0xffffff) || (mem->res->end > 0xffffff) || 74362306a36Sopenharmony_ci (mem->res->start > mem->res->end) || (mem->speed > 1000)) 74462306a36Sopenharmony_ci return -EINVAL; 74562306a36Sopenharmony_ci tcic_setw(TCIC_ADDR+2, TCIC_ADR2_INDREG | (psock << TCIC_SS_SHFT)); 74662306a36Sopenharmony_ci addr = TCIC_MWIN(psock, mem->map); 74762306a36Sopenharmony_ci 74862306a36Sopenharmony_ci base = mem->res->start; len = mem->res->end - mem->res->start; 74962306a36Sopenharmony_ci if ((len & (len+1)) || (base & len)) return -EINVAL; 75062306a36Sopenharmony_ci if (len == 0x0fff) 75162306a36Sopenharmony_ci base = (base >> TCIC_MBASE_HA_SHFT) | TCIC_MBASE_4K_BIT; 75262306a36Sopenharmony_ci else 75362306a36Sopenharmony_ci base = (base | (len+1)>>1) >> TCIC_MBASE_HA_SHFT; 75462306a36Sopenharmony_ci tcic_setw(TCIC_ADDR, addr + TCIC_MBASE_X); 75562306a36Sopenharmony_ci tcic_setw(TCIC_DATA, base); 75662306a36Sopenharmony_ci 75762306a36Sopenharmony_ci mmap = mem->card_start - mem->res->start; 75862306a36Sopenharmony_ci mmap = (mmap >> TCIC_MMAP_CA_SHFT) & TCIC_MMAP_CA_MASK; 75962306a36Sopenharmony_ci if (mem->flags & MAP_ATTRIB) mmap |= TCIC_MMAP_REG; 76062306a36Sopenharmony_ci tcic_setw(TCIC_ADDR, addr + TCIC_MMAP_X); 76162306a36Sopenharmony_ci tcic_setw(TCIC_DATA, mmap); 76262306a36Sopenharmony_ci 76362306a36Sopenharmony_ci ctl = TCIC_MCTL_QUIET | (psock << TCIC_MCTL_SS_SHFT); 76462306a36Sopenharmony_ci ctl |= to_cycles(mem->speed) & TCIC_MCTL_WSCNT_MASK; 76562306a36Sopenharmony_ci ctl |= (mem->flags & MAP_16BIT) ? 0 : TCIC_MCTL_B8; 76662306a36Sopenharmony_ci ctl |= (mem->flags & MAP_WRPROT) ? TCIC_MCTL_WP : 0; 76762306a36Sopenharmony_ci ctl |= (mem->flags & MAP_ACTIVE) ? TCIC_MCTL_ENA : 0; 76862306a36Sopenharmony_ci tcic_setw(TCIC_ADDR, addr + TCIC_MCTL_X); 76962306a36Sopenharmony_ci tcic_setw(TCIC_DATA, ctl); 77062306a36Sopenharmony_ci 77162306a36Sopenharmony_ci return 0; 77262306a36Sopenharmony_ci} /* tcic_set_mem_map */ 77362306a36Sopenharmony_ci 77462306a36Sopenharmony_ci/*====================================================================*/ 77562306a36Sopenharmony_ci 77662306a36Sopenharmony_cistatic int tcic_init(struct pcmcia_socket *s) 77762306a36Sopenharmony_ci{ 77862306a36Sopenharmony_ci int i; 77962306a36Sopenharmony_ci struct resource res = { .start = 0, .end = 0x1000 }; 78062306a36Sopenharmony_ci pccard_io_map io = { 0, 0, 0, 0, 1 }; 78162306a36Sopenharmony_ci pccard_mem_map mem = { .res = &res, }; 78262306a36Sopenharmony_ci 78362306a36Sopenharmony_ci for (i = 0; i < 2; i++) { 78462306a36Sopenharmony_ci io.map = i; 78562306a36Sopenharmony_ci tcic_set_io_map(s, &io); 78662306a36Sopenharmony_ci } 78762306a36Sopenharmony_ci for (i = 0; i < 5; i++) { 78862306a36Sopenharmony_ci mem.map = i; 78962306a36Sopenharmony_ci tcic_set_mem_map(s, &mem); 79062306a36Sopenharmony_ci } 79162306a36Sopenharmony_ci return 0; 79262306a36Sopenharmony_ci} 79362306a36Sopenharmony_ci 79462306a36Sopenharmony_cistatic struct pccard_operations tcic_operations = { 79562306a36Sopenharmony_ci .init = tcic_init, 79662306a36Sopenharmony_ci .get_status = tcic_get_status, 79762306a36Sopenharmony_ci .set_socket = tcic_set_socket, 79862306a36Sopenharmony_ci .set_io_map = tcic_set_io_map, 79962306a36Sopenharmony_ci .set_mem_map = tcic_set_mem_map, 80062306a36Sopenharmony_ci}; 80162306a36Sopenharmony_ci 80262306a36Sopenharmony_ci/*====================================================================*/ 80362306a36Sopenharmony_ci 80462306a36Sopenharmony_cimodule_init(init_tcic); 80562306a36Sopenharmony_cimodule_exit(exit_tcic); 806