18c2ecf20Sopenharmony_ci/*====================================================================== 28c2ecf20Sopenharmony_ci 38c2ecf20Sopenharmony_ci Device driver for Databook TCIC-2 PCMCIA controller 48c2ecf20Sopenharmony_ci 58c2ecf20Sopenharmony_ci tcic.c 1.111 2000/02/15 04:13:12 68c2ecf20Sopenharmony_ci 78c2ecf20Sopenharmony_ci The contents of this file are subject to the Mozilla Public 88c2ecf20Sopenharmony_ci License Version 1.1 (the "License"); you may not use this file 98c2ecf20Sopenharmony_ci except in compliance with the License. You may obtain a copy of 108c2ecf20Sopenharmony_ci the License at http://www.mozilla.org/MPL/ 118c2ecf20Sopenharmony_ci 128c2ecf20Sopenharmony_ci Software distributed under the License is distributed on an "AS 138c2ecf20Sopenharmony_ci IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or 148c2ecf20Sopenharmony_ci implied. See the License for the specific language governing 158c2ecf20Sopenharmony_ci rights and limitations under the License. 168c2ecf20Sopenharmony_ci 178c2ecf20Sopenharmony_ci The initial developer of the original code is David A. Hinds 188c2ecf20Sopenharmony_ci <dahinds@users.sourceforge.net>. Portions created by David A. Hinds 198c2ecf20Sopenharmony_ci are Copyright (C) 1999 David A. Hinds. All Rights Reserved. 208c2ecf20Sopenharmony_ci 218c2ecf20Sopenharmony_ci Alternatively, the contents of this file may be used under the 228c2ecf20Sopenharmony_ci terms of the GNU General Public License version 2 (the "GPL"), in which 238c2ecf20Sopenharmony_ci case the provisions of the GPL are applicable instead of the 248c2ecf20Sopenharmony_ci above. If you wish to allow the use of your version of this file 258c2ecf20Sopenharmony_ci only under the terms of the GPL and not to allow others to use 268c2ecf20Sopenharmony_ci your version of this file under the MPL, indicate your decision 278c2ecf20Sopenharmony_ci by deleting the provisions above and replace them with the notice 288c2ecf20Sopenharmony_ci and other provisions required by the GPL. If you do not delete 298c2ecf20Sopenharmony_ci the provisions above, a recipient may use your version of this 308c2ecf20Sopenharmony_ci file under either the MPL or the GPL. 318c2ecf20Sopenharmony_ci 328c2ecf20Sopenharmony_ci======================================================================*/ 338c2ecf20Sopenharmony_ci 348c2ecf20Sopenharmony_ci#include <linux/module.h> 358c2ecf20Sopenharmony_ci#include <linux/moduleparam.h> 368c2ecf20Sopenharmony_ci#include <linux/init.h> 378c2ecf20Sopenharmony_ci#include <linux/types.h> 388c2ecf20Sopenharmony_ci#include <linux/fcntl.h> 398c2ecf20Sopenharmony_ci#include <linux/string.h> 408c2ecf20Sopenharmony_ci#include <linux/errno.h> 418c2ecf20Sopenharmony_ci#include <linux/interrupt.h> 428c2ecf20Sopenharmony_ci#include <linux/timer.h> 438c2ecf20Sopenharmony_ci#include <linux/ioport.h> 448c2ecf20Sopenharmony_ci#include <linux/delay.h> 458c2ecf20Sopenharmony_ci#include <linux/workqueue.h> 468c2ecf20Sopenharmony_ci#include <linux/platform_device.h> 478c2ecf20Sopenharmony_ci#include <linux/bitops.h> 488c2ecf20Sopenharmony_ci 498c2ecf20Sopenharmony_ci#include <asm/io.h> 508c2ecf20Sopenharmony_ci 518c2ecf20Sopenharmony_ci#include <pcmcia/ss.h> 528c2ecf20Sopenharmony_ci#include "tcic.h" 538c2ecf20Sopenharmony_ci 548c2ecf20Sopenharmony_ciMODULE_AUTHOR("David Hinds <dahinds@users.sourceforge.net>"); 558c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Databook TCIC-2 PCMCIA socket driver"); 568c2ecf20Sopenharmony_ciMODULE_LICENSE("Dual MPL/GPL"); 578c2ecf20Sopenharmony_ci 588c2ecf20Sopenharmony_ci/*====================================================================*/ 598c2ecf20Sopenharmony_ci 608c2ecf20Sopenharmony_ci/* Parameters that can be set with 'insmod' */ 618c2ecf20Sopenharmony_ci 628c2ecf20Sopenharmony_ci/* The base port address of the TCIC-2 chip */ 638c2ecf20Sopenharmony_cistatic unsigned long tcic_base = TCIC_BASE; 648c2ecf20Sopenharmony_ci 658c2ecf20Sopenharmony_ci/* Specify a socket number to ignore */ 668c2ecf20Sopenharmony_cistatic int ignore = -1; 678c2ecf20Sopenharmony_ci 688c2ecf20Sopenharmony_ci/* Probe for safe interrupts? */ 698c2ecf20Sopenharmony_cistatic int do_scan = 1; 708c2ecf20Sopenharmony_ci 718c2ecf20Sopenharmony_ci/* Bit map of interrupts to choose from */ 728c2ecf20Sopenharmony_cistatic u_int irq_mask = 0xffff; 738c2ecf20Sopenharmony_cistatic int irq_list[16]; 748c2ecf20Sopenharmony_cistatic unsigned int irq_list_count; 758c2ecf20Sopenharmony_ci 768c2ecf20Sopenharmony_ci/* The card status change interrupt -- 0 means autoselect */ 778c2ecf20Sopenharmony_cistatic int cs_irq; 788c2ecf20Sopenharmony_ci 798c2ecf20Sopenharmony_ci/* Poll status interval -- 0 means default to interrupt */ 808c2ecf20Sopenharmony_cistatic int poll_interval; 818c2ecf20Sopenharmony_ci 828c2ecf20Sopenharmony_ci/* Delay for card status double-checking */ 838c2ecf20Sopenharmony_cistatic int poll_quick = HZ/20; 848c2ecf20Sopenharmony_ci 858c2ecf20Sopenharmony_ci/* CCLK external clock time, in nanoseconds. 70 ns = 14.31818 MHz */ 868c2ecf20Sopenharmony_cistatic int cycle_time = 70; 878c2ecf20Sopenharmony_ci 888c2ecf20Sopenharmony_cimodule_param_hw(tcic_base, ulong, ioport, 0444); 898c2ecf20Sopenharmony_cimodule_param(ignore, int, 0444); 908c2ecf20Sopenharmony_cimodule_param(do_scan, int, 0444); 918c2ecf20Sopenharmony_cimodule_param_hw(irq_mask, int, other, 0444); 928c2ecf20Sopenharmony_cimodule_param_hw_array(irq_list, int, irq, &irq_list_count, 0444); 938c2ecf20Sopenharmony_cimodule_param_hw(cs_irq, int, irq, 0444); 948c2ecf20Sopenharmony_cimodule_param(poll_interval, int, 0444); 958c2ecf20Sopenharmony_cimodule_param(poll_quick, int, 0444); 968c2ecf20Sopenharmony_cimodule_param(cycle_time, int, 0444); 978c2ecf20Sopenharmony_ci 988c2ecf20Sopenharmony_ci/*====================================================================*/ 998c2ecf20Sopenharmony_ci 1008c2ecf20Sopenharmony_cistatic irqreturn_t tcic_interrupt(int irq, void *dev); 1018c2ecf20Sopenharmony_cistatic void tcic_timer(struct timer_list *unused); 1028c2ecf20Sopenharmony_cistatic struct pccard_operations tcic_operations; 1038c2ecf20Sopenharmony_ci 1048c2ecf20Sopenharmony_cistruct tcic_socket { 1058c2ecf20Sopenharmony_ci u_short psock; 1068c2ecf20Sopenharmony_ci u_char last_sstat; 1078c2ecf20Sopenharmony_ci u_char id; 1088c2ecf20Sopenharmony_ci struct pcmcia_socket socket; 1098c2ecf20Sopenharmony_ci}; 1108c2ecf20Sopenharmony_ci 1118c2ecf20Sopenharmony_cistatic struct timer_list poll_timer; 1128c2ecf20Sopenharmony_cistatic int tcic_timer_pending; 1138c2ecf20Sopenharmony_ci 1148c2ecf20Sopenharmony_cistatic int sockets; 1158c2ecf20Sopenharmony_cistatic struct tcic_socket socket_table[2]; 1168c2ecf20Sopenharmony_ci 1178c2ecf20Sopenharmony_ci/*====================================================================*/ 1188c2ecf20Sopenharmony_ci 1198c2ecf20Sopenharmony_ci/* Trick when selecting interrupts: the TCIC sktirq pin is supposed 1208c2ecf20Sopenharmony_ci to map to irq 11, but is coded as 0 or 1 in the irq registers. */ 1218c2ecf20Sopenharmony_ci#define TCIC_IRQ(x) ((x) ? (((x) == 11) ? 1 : (x)) : 15) 1228c2ecf20Sopenharmony_ci 1238c2ecf20Sopenharmony_ci#ifdef DEBUG_X 1248c2ecf20Sopenharmony_cistatic u_char tcic_getb(u_char reg) 1258c2ecf20Sopenharmony_ci{ 1268c2ecf20Sopenharmony_ci u_char val = inb(tcic_base+reg); 1278c2ecf20Sopenharmony_ci printk(KERN_DEBUG "tcic_getb(%#lx) = %#x\n", tcic_base+reg, val); 1288c2ecf20Sopenharmony_ci return val; 1298c2ecf20Sopenharmony_ci} 1308c2ecf20Sopenharmony_ci 1318c2ecf20Sopenharmony_cistatic u_short tcic_getw(u_char reg) 1328c2ecf20Sopenharmony_ci{ 1338c2ecf20Sopenharmony_ci u_short val = inw(tcic_base+reg); 1348c2ecf20Sopenharmony_ci printk(KERN_DEBUG "tcic_getw(%#lx) = %#x\n", tcic_base+reg, val); 1358c2ecf20Sopenharmony_ci return val; 1368c2ecf20Sopenharmony_ci} 1378c2ecf20Sopenharmony_ci 1388c2ecf20Sopenharmony_cistatic void tcic_setb(u_char reg, u_char data) 1398c2ecf20Sopenharmony_ci{ 1408c2ecf20Sopenharmony_ci printk(KERN_DEBUG "tcic_setb(%#lx, %#x)\n", tcic_base+reg, data); 1418c2ecf20Sopenharmony_ci outb(data, tcic_base+reg); 1428c2ecf20Sopenharmony_ci} 1438c2ecf20Sopenharmony_ci 1448c2ecf20Sopenharmony_cistatic void tcic_setw(u_char reg, u_short data) 1458c2ecf20Sopenharmony_ci{ 1468c2ecf20Sopenharmony_ci printk(KERN_DEBUG "tcic_setw(%#lx, %#x)\n", tcic_base+reg, data); 1478c2ecf20Sopenharmony_ci outw(data, tcic_base+reg); 1488c2ecf20Sopenharmony_ci} 1498c2ecf20Sopenharmony_ci#else 1508c2ecf20Sopenharmony_ci#define tcic_getb(reg) inb(tcic_base+reg) 1518c2ecf20Sopenharmony_ci#define tcic_getw(reg) inw(tcic_base+reg) 1528c2ecf20Sopenharmony_ci#define tcic_setb(reg, data) outb(data, tcic_base+reg) 1538c2ecf20Sopenharmony_ci#define tcic_setw(reg, data) outw(data, tcic_base+reg) 1548c2ecf20Sopenharmony_ci#endif 1558c2ecf20Sopenharmony_ci 1568c2ecf20Sopenharmony_cistatic void tcic_setl(u_char reg, u_int data) 1578c2ecf20Sopenharmony_ci{ 1588c2ecf20Sopenharmony_ci#ifdef DEBUG_X 1598c2ecf20Sopenharmony_ci printk(KERN_DEBUG "tcic_setl(%#x, %#lx)\n", tcic_base+reg, data); 1608c2ecf20Sopenharmony_ci#endif 1618c2ecf20Sopenharmony_ci outw(data & 0xffff, tcic_base+reg); 1628c2ecf20Sopenharmony_ci outw(data >> 16, tcic_base+reg+2); 1638c2ecf20Sopenharmony_ci} 1648c2ecf20Sopenharmony_ci 1658c2ecf20Sopenharmony_cistatic void tcic_aux_setb(u_short reg, u_char data) 1668c2ecf20Sopenharmony_ci{ 1678c2ecf20Sopenharmony_ci u_char mode = (tcic_getb(TCIC_MODE) & TCIC_MODE_PGMMASK) | reg; 1688c2ecf20Sopenharmony_ci tcic_setb(TCIC_MODE, mode); 1698c2ecf20Sopenharmony_ci tcic_setb(TCIC_AUX, data); 1708c2ecf20Sopenharmony_ci} 1718c2ecf20Sopenharmony_ci 1728c2ecf20Sopenharmony_cistatic u_short tcic_aux_getw(u_short reg) 1738c2ecf20Sopenharmony_ci{ 1748c2ecf20Sopenharmony_ci u_char mode = (tcic_getb(TCIC_MODE) & TCIC_MODE_PGMMASK) | reg; 1758c2ecf20Sopenharmony_ci tcic_setb(TCIC_MODE, mode); 1768c2ecf20Sopenharmony_ci return tcic_getw(TCIC_AUX); 1778c2ecf20Sopenharmony_ci} 1788c2ecf20Sopenharmony_ci 1798c2ecf20Sopenharmony_cistatic void tcic_aux_setw(u_short reg, u_short data) 1808c2ecf20Sopenharmony_ci{ 1818c2ecf20Sopenharmony_ci u_char mode = (tcic_getb(TCIC_MODE) & TCIC_MODE_PGMMASK) | reg; 1828c2ecf20Sopenharmony_ci tcic_setb(TCIC_MODE, mode); 1838c2ecf20Sopenharmony_ci tcic_setw(TCIC_AUX, data); 1848c2ecf20Sopenharmony_ci} 1858c2ecf20Sopenharmony_ci 1868c2ecf20Sopenharmony_ci/*====================================================================*/ 1878c2ecf20Sopenharmony_ci 1888c2ecf20Sopenharmony_ci/* Time conversion functions */ 1898c2ecf20Sopenharmony_ci 1908c2ecf20Sopenharmony_cistatic int to_cycles(int ns) 1918c2ecf20Sopenharmony_ci{ 1928c2ecf20Sopenharmony_ci if (ns < 14) 1938c2ecf20Sopenharmony_ci return 0; 1948c2ecf20Sopenharmony_ci else 1958c2ecf20Sopenharmony_ci return 2*(ns-14)/cycle_time; 1968c2ecf20Sopenharmony_ci} 1978c2ecf20Sopenharmony_ci 1988c2ecf20Sopenharmony_ci/*====================================================================*/ 1998c2ecf20Sopenharmony_ci 2008c2ecf20Sopenharmony_cistatic volatile u_int irq_hits; 2018c2ecf20Sopenharmony_ci 2028c2ecf20Sopenharmony_cistatic irqreturn_t __init tcic_irq_count(int irq, void *dev) 2038c2ecf20Sopenharmony_ci{ 2048c2ecf20Sopenharmony_ci irq_hits++; 2058c2ecf20Sopenharmony_ci return IRQ_HANDLED; 2068c2ecf20Sopenharmony_ci} 2078c2ecf20Sopenharmony_ci 2088c2ecf20Sopenharmony_cistatic u_int __init try_irq(int irq) 2098c2ecf20Sopenharmony_ci{ 2108c2ecf20Sopenharmony_ci u_short cfg; 2118c2ecf20Sopenharmony_ci 2128c2ecf20Sopenharmony_ci irq_hits = 0; 2138c2ecf20Sopenharmony_ci if (request_irq(irq, tcic_irq_count, 0, "irq scan", tcic_irq_count) != 0) 2148c2ecf20Sopenharmony_ci return -1; 2158c2ecf20Sopenharmony_ci mdelay(10); 2168c2ecf20Sopenharmony_ci if (irq_hits) { 2178c2ecf20Sopenharmony_ci free_irq(irq, tcic_irq_count); 2188c2ecf20Sopenharmony_ci return -1; 2198c2ecf20Sopenharmony_ci } 2208c2ecf20Sopenharmony_ci 2218c2ecf20Sopenharmony_ci /* Generate one interrupt */ 2228c2ecf20Sopenharmony_ci cfg = TCIC_SYSCFG_AUTOBUSY | 0x0a00; 2238c2ecf20Sopenharmony_ci tcic_aux_setw(TCIC_AUX_SYSCFG, cfg | TCIC_IRQ(irq)); 2248c2ecf20Sopenharmony_ci tcic_setb(TCIC_IENA, TCIC_IENA_ERR | TCIC_IENA_CFG_HIGH); 2258c2ecf20Sopenharmony_ci tcic_setb(TCIC_ICSR, TCIC_ICSR_ERR | TCIC_ICSR_JAM); 2268c2ecf20Sopenharmony_ci 2278c2ecf20Sopenharmony_ci udelay(1000); 2288c2ecf20Sopenharmony_ci free_irq(irq, tcic_irq_count); 2298c2ecf20Sopenharmony_ci 2308c2ecf20Sopenharmony_ci /* Turn off interrupts */ 2318c2ecf20Sopenharmony_ci tcic_setb(TCIC_IENA, TCIC_IENA_CFG_OFF); 2328c2ecf20Sopenharmony_ci while (tcic_getb(TCIC_ICSR)) 2338c2ecf20Sopenharmony_ci tcic_setb(TCIC_ICSR, TCIC_ICSR_JAM); 2348c2ecf20Sopenharmony_ci tcic_aux_setw(TCIC_AUX_SYSCFG, cfg); 2358c2ecf20Sopenharmony_ci 2368c2ecf20Sopenharmony_ci return (irq_hits != 1); 2378c2ecf20Sopenharmony_ci} 2388c2ecf20Sopenharmony_ci 2398c2ecf20Sopenharmony_cistatic u_int __init irq_scan(u_int mask0) 2408c2ecf20Sopenharmony_ci{ 2418c2ecf20Sopenharmony_ci u_int mask1; 2428c2ecf20Sopenharmony_ci int i; 2438c2ecf20Sopenharmony_ci 2448c2ecf20Sopenharmony_ci#ifdef __alpha__ 2458c2ecf20Sopenharmony_ci#define PIC 0x4d0 2468c2ecf20Sopenharmony_ci /* Don't probe level-triggered interrupts -- reserved for PCI */ 2478c2ecf20Sopenharmony_ci int level_mask = inb_p(PIC) | (inb_p(PIC+1) << 8); 2488c2ecf20Sopenharmony_ci if (level_mask) 2498c2ecf20Sopenharmony_ci mask0 &= ~level_mask; 2508c2ecf20Sopenharmony_ci#endif 2518c2ecf20Sopenharmony_ci 2528c2ecf20Sopenharmony_ci mask1 = 0; 2538c2ecf20Sopenharmony_ci if (do_scan) { 2548c2ecf20Sopenharmony_ci for (i = 0; i < 16; i++) 2558c2ecf20Sopenharmony_ci if ((mask0 & (1 << i)) && (try_irq(i) == 0)) 2568c2ecf20Sopenharmony_ci mask1 |= (1 << i); 2578c2ecf20Sopenharmony_ci for (i = 0; i < 16; i++) 2588c2ecf20Sopenharmony_ci if ((mask1 & (1 << i)) && (try_irq(i) != 0)) { 2598c2ecf20Sopenharmony_ci mask1 ^= (1 << i); 2608c2ecf20Sopenharmony_ci } 2618c2ecf20Sopenharmony_ci } 2628c2ecf20Sopenharmony_ci 2638c2ecf20Sopenharmony_ci if (mask1) { 2648c2ecf20Sopenharmony_ci printk("scanned"); 2658c2ecf20Sopenharmony_ci } else { 2668c2ecf20Sopenharmony_ci /* Fallback: just find interrupts that aren't in use */ 2678c2ecf20Sopenharmony_ci for (i = 0; i < 16; i++) 2688c2ecf20Sopenharmony_ci if ((mask0 & (1 << i)) && 2698c2ecf20Sopenharmony_ci (request_irq(i, tcic_irq_count, 0, "x", tcic_irq_count) == 0)) { 2708c2ecf20Sopenharmony_ci mask1 |= (1 << i); 2718c2ecf20Sopenharmony_ci free_irq(i, tcic_irq_count); 2728c2ecf20Sopenharmony_ci } 2738c2ecf20Sopenharmony_ci printk("default"); 2748c2ecf20Sopenharmony_ci } 2758c2ecf20Sopenharmony_ci 2768c2ecf20Sopenharmony_ci printk(") = "); 2778c2ecf20Sopenharmony_ci for (i = 0; i < 16; i++) 2788c2ecf20Sopenharmony_ci if (mask1 & (1<<i)) 2798c2ecf20Sopenharmony_ci printk("%s%d", ((mask1 & ((1<<i)-1)) ? "," : ""), i); 2808c2ecf20Sopenharmony_ci printk(" "); 2818c2ecf20Sopenharmony_ci 2828c2ecf20Sopenharmony_ci return mask1; 2838c2ecf20Sopenharmony_ci} 2848c2ecf20Sopenharmony_ci 2858c2ecf20Sopenharmony_ci/*====================================================================== 2868c2ecf20Sopenharmony_ci 2878c2ecf20Sopenharmony_ci See if a card is present, powered up, in IO mode, and already 2888c2ecf20Sopenharmony_ci bound to a (non-PCMCIA) Linux driver. 2898c2ecf20Sopenharmony_ci 2908c2ecf20Sopenharmony_ci We make an exception for cards that look like serial devices. 2918c2ecf20Sopenharmony_ci 2928c2ecf20Sopenharmony_ci======================================================================*/ 2938c2ecf20Sopenharmony_ci 2948c2ecf20Sopenharmony_cistatic int __init is_active(int s) 2958c2ecf20Sopenharmony_ci{ 2968c2ecf20Sopenharmony_ci u_short scf1, ioctl, base, num; 2978c2ecf20Sopenharmony_ci u_char pwr, sstat; 2988c2ecf20Sopenharmony_ci u_int addr; 2998c2ecf20Sopenharmony_ci 3008c2ecf20Sopenharmony_ci tcic_setl(TCIC_ADDR, (s << TCIC_ADDR_SS_SHFT) 3018c2ecf20Sopenharmony_ci | TCIC_ADDR_INDREG | TCIC_SCF1(s)); 3028c2ecf20Sopenharmony_ci scf1 = tcic_getw(TCIC_DATA); 3038c2ecf20Sopenharmony_ci pwr = tcic_getb(TCIC_PWR); 3048c2ecf20Sopenharmony_ci sstat = tcic_getb(TCIC_SSTAT); 3058c2ecf20Sopenharmony_ci addr = TCIC_IWIN(s, 0); 3068c2ecf20Sopenharmony_ci tcic_setw(TCIC_ADDR, addr + TCIC_IBASE_X); 3078c2ecf20Sopenharmony_ci base = tcic_getw(TCIC_DATA); 3088c2ecf20Sopenharmony_ci tcic_setw(TCIC_ADDR, addr + TCIC_ICTL_X); 3098c2ecf20Sopenharmony_ci ioctl = tcic_getw(TCIC_DATA); 3108c2ecf20Sopenharmony_ci 3118c2ecf20Sopenharmony_ci if (ioctl & TCIC_ICTL_TINY) 3128c2ecf20Sopenharmony_ci num = 1; 3138c2ecf20Sopenharmony_ci else { 3148c2ecf20Sopenharmony_ci num = (base ^ (base-1)); 3158c2ecf20Sopenharmony_ci base = base & (base-1); 3168c2ecf20Sopenharmony_ci } 3178c2ecf20Sopenharmony_ci 3188c2ecf20Sopenharmony_ci if ((sstat & TCIC_SSTAT_CD) && (pwr & TCIC_PWR_VCC(s)) && 3198c2ecf20Sopenharmony_ci (scf1 & TCIC_SCF1_IOSTS) && (ioctl & TCIC_ICTL_ENA) && 3208c2ecf20Sopenharmony_ci ((base & 0xfeef) != 0x02e8)) { 3218c2ecf20Sopenharmony_ci struct resource *res = request_region(base, num, "tcic-2"); 3228c2ecf20Sopenharmony_ci if (!res) /* region is busy */ 3238c2ecf20Sopenharmony_ci return 1; 3248c2ecf20Sopenharmony_ci release_region(base, num); 3258c2ecf20Sopenharmony_ci } 3268c2ecf20Sopenharmony_ci 3278c2ecf20Sopenharmony_ci return 0; 3288c2ecf20Sopenharmony_ci} 3298c2ecf20Sopenharmony_ci 3308c2ecf20Sopenharmony_ci/*====================================================================== 3318c2ecf20Sopenharmony_ci 3328c2ecf20Sopenharmony_ci This returns the revision code for the specified socket. 3338c2ecf20Sopenharmony_ci 3348c2ecf20Sopenharmony_ci======================================================================*/ 3358c2ecf20Sopenharmony_ci 3368c2ecf20Sopenharmony_cistatic int __init get_tcic_id(void) 3378c2ecf20Sopenharmony_ci{ 3388c2ecf20Sopenharmony_ci u_short id; 3398c2ecf20Sopenharmony_ci 3408c2ecf20Sopenharmony_ci tcic_aux_setw(TCIC_AUX_TEST, TCIC_TEST_DIAG); 3418c2ecf20Sopenharmony_ci id = tcic_aux_getw(TCIC_AUX_ILOCK); 3428c2ecf20Sopenharmony_ci id = (id & TCIC_ILOCKTEST_ID_MASK) >> TCIC_ILOCKTEST_ID_SH; 3438c2ecf20Sopenharmony_ci tcic_aux_setw(TCIC_AUX_TEST, 0); 3448c2ecf20Sopenharmony_ci return id; 3458c2ecf20Sopenharmony_ci} 3468c2ecf20Sopenharmony_ci 3478c2ecf20Sopenharmony_ci/*====================================================================*/ 3488c2ecf20Sopenharmony_ci 3498c2ecf20Sopenharmony_cistatic struct platform_driver tcic_driver = { 3508c2ecf20Sopenharmony_ci .driver = { 3518c2ecf20Sopenharmony_ci .name = "tcic-pcmcia", 3528c2ecf20Sopenharmony_ci }, 3538c2ecf20Sopenharmony_ci}; 3548c2ecf20Sopenharmony_ci 3558c2ecf20Sopenharmony_cistatic struct platform_device tcic_device = { 3568c2ecf20Sopenharmony_ci .name = "tcic-pcmcia", 3578c2ecf20Sopenharmony_ci .id = 0, 3588c2ecf20Sopenharmony_ci}; 3598c2ecf20Sopenharmony_ci 3608c2ecf20Sopenharmony_ci 3618c2ecf20Sopenharmony_cistatic int __init init_tcic(void) 3628c2ecf20Sopenharmony_ci{ 3638c2ecf20Sopenharmony_ci int i, sock, ret = 0; 3648c2ecf20Sopenharmony_ci u_int mask, scan; 3658c2ecf20Sopenharmony_ci 3668c2ecf20Sopenharmony_ci if (platform_driver_register(&tcic_driver)) 3678c2ecf20Sopenharmony_ci return -1; 3688c2ecf20Sopenharmony_ci 3698c2ecf20Sopenharmony_ci printk(KERN_INFO "Databook TCIC-2 PCMCIA probe: "); 3708c2ecf20Sopenharmony_ci sock = 0; 3718c2ecf20Sopenharmony_ci 3728c2ecf20Sopenharmony_ci if (!request_region(tcic_base, 16, "tcic-2")) { 3738c2ecf20Sopenharmony_ci printk("could not allocate ports,\n "); 3748c2ecf20Sopenharmony_ci platform_driver_unregister(&tcic_driver); 3758c2ecf20Sopenharmony_ci return -ENODEV; 3768c2ecf20Sopenharmony_ci } 3778c2ecf20Sopenharmony_ci else { 3788c2ecf20Sopenharmony_ci tcic_setw(TCIC_ADDR, 0); 3798c2ecf20Sopenharmony_ci if (tcic_getw(TCIC_ADDR) == 0) { 3808c2ecf20Sopenharmony_ci tcic_setw(TCIC_ADDR, 0xc3a5); 3818c2ecf20Sopenharmony_ci if (tcic_getw(TCIC_ADDR) == 0xc3a5) sock = 2; 3828c2ecf20Sopenharmony_ci } 3838c2ecf20Sopenharmony_ci if (sock == 0) { 3848c2ecf20Sopenharmony_ci /* See if resetting the controller does any good */ 3858c2ecf20Sopenharmony_ci tcic_setb(TCIC_SCTRL, TCIC_SCTRL_RESET); 3868c2ecf20Sopenharmony_ci tcic_setb(TCIC_SCTRL, 0); 3878c2ecf20Sopenharmony_ci tcic_setw(TCIC_ADDR, 0); 3888c2ecf20Sopenharmony_ci if (tcic_getw(TCIC_ADDR) == 0) { 3898c2ecf20Sopenharmony_ci tcic_setw(TCIC_ADDR, 0xc3a5); 3908c2ecf20Sopenharmony_ci if (tcic_getw(TCIC_ADDR) == 0xc3a5) sock = 2; 3918c2ecf20Sopenharmony_ci } 3928c2ecf20Sopenharmony_ci } 3938c2ecf20Sopenharmony_ci } 3948c2ecf20Sopenharmony_ci if (sock == 0) { 3958c2ecf20Sopenharmony_ci printk("not found.\n"); 3968c2ecf20Sopenharmony_ci release_region(tcic_base, 16); 3978c2ecf20Sopenharmony_ci platform_driver_unregister(&tcic_driver); 3988c2ecf20Sopenharmony_ci return -ENODEV; 3998c2ecf20Sopenharmony_ci } 4008c2ecf20Sopenharmony_ci 4018c2ecf20Sopenharmony_ci sockets = 0; 4028c2ecf20Sopenharmony_ci for (i = 0; i < sock; i++) { 4038c2ecf20Sopenharmony_ci if ((i == ignore) || is_active(i)) continue; 4048c2ecf20Sopenharmony_ci socket_table[sockets].psock = i; 4058c2ecf20Sopenharmony_ci socket_table[sockets].id = get_tcic_id(); 4068c2ecf20Sopenharmony_ci 4078c2ecf20Sopenharmony_ci socket_table[sockets].socket.owner = THIS_MODULE; 4088c2ecf20Sopenharmony_ci /* only 16-bit cards, memory windows must be size-aligned */ 4098c2ecf20Sopenharmony_ci /* No PCI or CardBus support */ 4108c2ecf20Sopenharmony_ci socket_table[sockets].socket.features = SS_CAP_PCCARD | SS_CAP_MEM_ALIGN; 4118c2ecf20Sopenharmony_ci /* irq 14, 11, 10, 7, 6, 5, 4, 3 */ 4128c2ecf20Sopenharmony_ci socket_table[sockets].socket.irq_mask = 0x4cf8; 4138c2ecf20Sopenharmony_ci /* 4K minimum window size */ 4148c2ecf20Sopenharmony_ci socket_table[sockets].socket.map_size = 0x1000; 4158c2ecf20Sopenharmony_ci sockets++; 4168c2ecf20Sopenharmony_ci } 4178c2ecf20Sopenharmony_ci 4188c2ecf20Sopenharmony_ci switch (socket_table[0].id) { 4198c2ecf20Sopenharmony_ci case TCIC_ID_DB86082: 4208c2ecf20Sopenharmony_ci printk("DB86082"); break; 4218c2ecf20Sopenharmony_ci case TCIC_ID_DB86082A: 4228c2ecf20Sopenharmony_ci printk("DB86082A"); break; 4238c2ecf20Sopenharmony_ci case TCIC_ID_DB86084: 4248c2ecf20Sopenharmony_ci printk("DB86084"); break; 4258c2ecf20Sopenharmony_ci case TCIC_ID_DB86084A: 4268c2ecf20Sopenharmony_ci printk("DB86084A"); break; 4278c2ecf20Sopenharmony_ci case TCIC_ID_DB86072: 4288c2ecf20Sopenharmony_ci printk("DB86072"); break; 4298c2ecf20Sopenharmony_ci case TCIC_ID_DB86184: 4308c2ecf20Sopenharmony_ci printk("DB86184"); break; 4318c2ecf20Sopenharmony_ci case TCIC_ID_DB86082B: 4328c2ecf20Sopenharmony_ci printk("DB86082B"); break; 4338c2ecf20Sopenharmony_ci default: 4348c2ecf20Sopenharmony_ci printk("Unknown ID 0x%02x", socket_table[0].id); 4358c2ecf20Sopenharmony_ci } 4368c2ecf20Sopenharmony_ci 4378c2ecf20Sopenharmony_ci /* Set up polling */ 4388c2ecf20Sopenharmony_ci timer_setup(&poll_timer, &tcic_timer, 0); 4398c2ecf20Sopenharmony_ci 4408c2ecf20Sopenharmony_ci /* Build interrupt mask */ 4418c2ecf20Sopenharmony_ci printk(KERN_CONT ", %d sockets\n", sockets); 4428c2ecf20Sopenharmony_ci printk(KERN_INFO " irq list ("); 4438c2ecf20Sopenharmony_ci if (irq_list_count == 0) 4448c2ecf20Sopenharmony_ci mask = irq_mask; 4458c2ecf20Sopenharmony_ci else 4468c2ecf20Sopenharmony_ci for (i = mask = 0; i < irq_list_count; i++) 4478c2ecf20Sopenharmony_ci mask |= (1<<irq_list[i]); 4488c2ecf20Sopenharmony_ci 4498c2ecf20Sopenharmony_ci /* irq 14, 11, 10, 7, 6, 5, 4, 3 */ 4508c2ecf20Sopenharmony_ci mask &= 0x4cf8; 4518c2ecf20Sopenharmony_ci /* Scan interrupts */ 4528c2ecf20Sopenharmony_ci mask = irq_scan(mask); 4538c2ecf20Sopenharmony_ci for (i=0;i<sockets;i++) 4548c2ecf20Sopenharmony_ci socket_table[i].socket.irq_mask = mask; 4558c2ecf20Sopenharmony_ci 4568c2ecf20Sopenharmony_ci /* Check for only two interrupts available */ 4578c2ecf20Sopenharmony_ci scan = (mask & (mask-1)); 4588c2ecf20Sopenharmony_ci if (((scan & (scan-1)) == 0) && (poll_interval == 0)) 4598c2ecf20Sopenharmony_ci poll_interval = HZ; 4608c2ecf20Sopenharmony_ci 4618c2ecf20Sopenharmony_ci if (poll_interval == 0) { 4628c2ecf20Sopenharmony_ci /* Avoid irq 12 unless it is explicitly requested */ 4638c2ecf20Sopenharmony_ci u_int cs_mask = mask & ((cs_irq) ? (1<<cs_irq) : ~(1<<12)); 4648c2ecf20Sopenharmony_ci for (i = 15; i > 0; i--) 4658c2ecf20Sopenharmony_ci if ((cs_mask & (1 << i)) && 4668c2ecf20Sopenharmony_ci (request_irq(i, tcic_interrupt, 0, "tcic", 4678c2ecf20Sopenharmony_ci tcic_interrupt) == 0)) 4688c2ecf20Sopenharmony_ci break; 4698c2ecf20Sopenharmony_ci cs_irq = i; 4708c2ecf20Sopenharmony_ci if (cs_irq == 0) poll_interval = HZ; 4718c2ecf20Sopenharmony_ci } 4728c2ecf20Sopenharmony_ci 4738c2ecf20Sopenharmony_ci if (socket_table[0].socket.irq_mask & (1 << 11)) 4748c2ecf20Sopenharmony_ci printk("sktirq is irq 11, "); 4758c2ecf20Sopenharmony_ci if (cs_irq != 0) 4768c2ecf20Sopenharmony_ci printk("status change on irq %d\n", cs_irq); 4778c2ecf20Sopenharmony_ci else 4788c2ecf20Sopenharmony_ci printk("polled status, interval = %d ms\n", 4798c2ecf20Sopenharmony_ci poll_interval * 1000 / HZ); 4808c2ecf20Sopenharmony_ci 4818c2ecf20Sopenharmony_ci for (i = 0; i < sockets; i++) { 4828c2ecf20Sopenharmony_ci tcic_setw(TCIC_ADDR+2, socket_table[i].psock << TCIC_SS_SHFT); 4838c2ecf20Sopenharmony_ci socket_table[i].last_sstat = tcic_getb(TCIC_SSTAT); 4848c2ecf20Sopenharmony_ci } 4858c2ecf20Sopenharmony_ci 4868c2ecf20Sopenharmony_ci /* jump start interrupt handler, if needed */ 4878c2ecf20Sopenharmony_ci tcic_interrupt(0, NULL); 4888c2ecf20Sopenharmony_ci 4898c2ecf20Sopenharmony_ci platform_device_register(&tcic_device); 4908c2ecf20Sopenharmony_ci 4918c2ecf20Sopenharmony_ci for (i = 0; i < sockets; i++) { 4928c2ecf20Sopenharmony_ci socket_table[i].socket.ops = &tcic_operations; 4938c2ecf20Sopenharmony_ci socket_table[i].socket.resource_ops = &pccard_nonstatic_ops; 4948c2ecf20Sopenharmony_ci socket_table[i].socket.dev.parent = &tcic_device.dev; 4958c2ecf20Sopenharmony_ci ret = pcmcia_register_socket(&socket_table[i].socket); 4968c2ecf20Sopenharmony_ci if (ret && i) 4978c2ecf20Sopenharmony_ci pcmcia_unregister_socket(&socket_table[0].socket); 4988c2ecf20Sopenharmony_ci } 4998c2ecf20Sopenharmony_ci 5008c2ecf20Sopenharmony_ci return ret; 5018c2ecf20Sopenharmony_ci 5028c2ecf20Sopenharmony_ci return 0; 5038c2ecf20Sopenharmony_ci 5048c2ecf20Sopenharmony_ci} /* init_tcic */ 5058c2ecf20Sopenharmony_ci 5068c2ecf20Sopenharmony_ci/*====================================================================*/ 5078c2ecf20Sopenharmony_ci 5088c2ecf20Sopenharmony_cistatic void __exit exit_tcic(void) 5098c2ecf20Sopenharmony_ci{ 5108c2ecf20Sopenharmony_ci int i; 5118c2ecf20Sopenharmony_ci 5128c2ecf20Sopenharmony_ci del_timer_sync(&poll_timer); 5138c2ecf20Sopenharmony_ci if (cs_irq != 0) { 5148c2ecf20Sopenharmony_ci tcic_aux_setw(TCIC_AUX_SYSCFG, TCIC_SYSCFG_AUTOBUSY|0x0a00); 5158c2ecf20Sopenharmony_ci free_irq(cs_irq, tcic_interrupt); 5168c2ecf20Sopenharmony_ci } 5178c2ecf20Sopenharmony_ci release_region(tcic_base, 16); 5188c2ecf20Sopenharmony_ci 5198c2ecf20Sopenharmony_ci for (i = 0; i < sockets; i++) { 5208c2ecf20Sopenharmony_ci pcmcia_unregister_socket(&socket_table[i].socket); 5218c2ecf20Sopenharmony_ci } 5228c2ecf20Sopenharmony_ci 5238c2ecf20Sopenharmony_ci platform_device_unregister(&tcic_device); 5248c2ecf20Sopenharmony_ci platform_driver_unregister(&tcic_driver); 5258c2ecf20Sopenharmony_ci} /* exit_tcic */ 5268c2ecf20Sopenharmony_ci 5278c2ecf20Sopenharmony_ci/*====================================================================*/ 5288c2ecf20Sopenharmony_ci 5298c2ecf20Sopenharmony_cistatic irqreturn_t tcic_interrupt(int irq, void *dev) 5308c2ecf20Sopenharmony_ci{ 5318c2ecf20Sopenharmony_ci int i, quick = 0; 5328c2ecf20Sopenharmony_ci u_char latch, sstat; 5338c2ecf20Sopenharmony_ci u_short psock; 5348c2ecf20Sopenharmony_ci u_int events; 5358c2ecf20Sopenharmony_ci static volatile int active = 0; 5368c2ecf20Sopenharmony_ci 5378c2ecf20Sopenharmony_ci if (active) { 5388c2ecf20Sopenharmony_ci printk(KERN_NOTICE "tcic: reentered interrupt handler!\n"); 5398c2ecf20Sopenharmony_ci return IRQ_NONE; 5408c2ecf20Sopenharmony_ci } else 5418c2ecf20Sopenharmony_ci active = 1; 5428c2ecf20Sopenharmony_ci 5438c2ecf20Sopenharmony_ci pr_debug("tcic_interrupt()\n"); 5448c2ecf20Sopenharmony_ci 5458c2ecf20Sopenharmony_ci for (i = 0; i < sockets; i++) { 5468c2ecf20Sopenharmony_ci psock = socket_table[i].psock; 5478c2ecf20Sopenharmony_ci tcic_setl(TCIC_ADDR, (psock << TCIC_ADDR_SS_SHFT) 5488c2ecf20Sopenharmony_ci | TCIC_ADDR_INDREG | TCIC_SCF1(psock)); 5498c2ecf20Sopenharmony_ci sstat = tcic_getb(TCIC_SSTAT); 5508c2ecf20Sopenharmony_ci latch = sstat ^ socket_table[psock].last_sstat; 5518c2ecf20Sopenharmony_ci socket_table[i].last_sstat = sstat; 5528c2ecf20Sopenharmony_ci if (tcic_getb(TCIC_ICSR) & TCIC_ICSR_CDCHG) { 5538c2ecf20Sopenharmony_ci tcic_setb(TCIC_ICSR, TCIC_ICSR_CLEAR); 5548c2ecf20Sopenharmony_ci quick = 1; 5558c2ecf20Sopenharmony_ci } 5568c2ecf20Sopenharmony_ci if (latch == 0) 5578c2ecf20Sopenharmony_ci continue; 5588c2ecf20Sopenharmony_ci events = (latch & TCIC_SSTAT_CD) ? SS_DETECT : 0; 5598c2ecf20Sopenharmony_ci events |= (latch & TCIC_SSTAT_WP) ? SS_WRPROT : 0; 5608c2ecf20Sopenharmony_ci if (tcic_getw(TCIC_DATA) & TCIC_SCF1_IOSTS) { 5618c2ecf20Sopenharmony_ci events |= (latch & TCIC_SSTAT_LBAT1) ? SS_STSCHG : 0; 5628c2ecf20Sopenharmony_ci } else { 5638c2ecf20Sopenharmony_ci events |= (latch & TCIC_SSTAT_RDY) ? SS_READY : 0; 5648c2ecf20Sopenharmony_ci events |= (latch & TCIC_SSTAT_LBAT1) ? SS_BATDEAD : 0; 5658c2ecf20Sopenharmony_ci events |= (latch & TCIC_SSTAT_LBAT2) ? SS_BATWARN : 0; 5668c2ecf20Sopenharmony_ci } 5678c2ecf20Sopenharmony_ci if (events) { 5688c2ecf20Sopenharmony_ci pcmcia_parse_events(&socket_table[i].socket, events); 5698c2ecf20Sopenharmony_ci } 5708c2ecf20Sopenharmony_ci } 5718c2ecf20Sopenharmony_ci 5728c2ecf20Sopenharmony_ci /* Schedule next poll, if needed */ 5738c2ecf20Sopenharmony_ci if (((cs_irq == 0) || quick) && (!tcic_timer_pending)) { 5748c2ecf20Sopenharmony_ci poll_timer.expires = jiffies + (quick ? poll_quick : poll_interval); 5758c2ecf20Sopenharmony_ci add_timer(&poll_timer); 5768c2ecf20Sopenharmony_ci tcic_timer_pending = 1; 5778c2ecf20Sopenharmony_ci } 5788c2ecf20Sopenharmony_ci active = 0; 5798c2ecf20Sopenharmony_ci 5808c2ecf20Sopenharmony_ci pr_debug("interrupt done\n"); 5818c2ecf20Sopenharmony_ci return IRQ_HANDLED; 5828c2ecf20Sopenharmony_ci} /* tcic_interrupt */ 5838c2ecf20Sopenharmony_ci 5848c2ecf20Sopenharmony_cistatic void tcic_timer(struct timer_list *unused) 5858c2ecf20Sopenharmony_ci{ 5868c2ecf20Sopenharmony_ci pr_debug("tcic_timer()\n"); 5878c2ecf20Sopenharmony_ci tcic_timer_pending = 0; 5888c2ecf20Sopenharmony_ci tcic_interrupt(0, NULL); 5898c2ecf20Sopenharmony_ci} /* tcic_timer */ 5908c2ecf20Sopenharmony_ci 5918c2ecf20Sopenharmony_ci/*====================================================================*/ 5928c2ecf20Sopenharmony_ci 5938c2ecf20Sopenharmony_cistatic int tcic_get_status(struct pcmcia_socket *sock, u_int *value) 5948c2ecf20Sopenharmony_ci{ 5958c2ecf20Sopenharmony_ci u_short psock = container_of(sock, struct tcic_socket, socket)->psock; 5968c2ecf20Sopenharmony_ci u_char reg; 5978c2ecf20Sopenharmony_ci 5988c2ecf20Sopenharmony_ci tcic_setl(TCIC_ADDR, (psock << TCIC_ADDR_SS_SHFT) 5998c2ecf20Sopenharmony_ci | TCIC_ADDR_INDREG | TCIC_SCF1(psock)); 6008c2ecf20Sopenharmony_ci reg = tcic_getb(TCIC_SSTAT); 6018c2ecf20Sopenharmony_ci *value = (reg & TCIC_SSTAT_CD) ? SS_DETECT : 0; 6028c2ecf20Sopenharmony_ci *value |= (reg & TCIC_SSTAT_WP) ? SS_WRPROT : 0; 6038c2ecf20Sopenharmony_ci if (tcic_getw(TCIC_DATA) & TCIC_SCF1_IOSTS) { 6048c2ecf20Sopenharmony_ci *value |= (reg & TCIC_SSTAT_LBAT1) ? SS_STSCHG : 0; 6058c2ecf20Sopenharmony_ci } else { 6068c2ecf20Sopenharmony_ci *value |= (reg & TCIC_SSTAT_RDY) ? SS_READY : 0; 6078c2ecf20Sopenharmony_ci *value |= (reg & TCIC_SSTAT_LBAT1) ? SS_BATDEAD : 0; 6088c2ecf20Sopenharmony_ci *value |= (reg & TCIC_SSTAT_LBAT2) ? SS_BATWARN : 0; 6098c2ecf20Sopenharmony_ci } 6108c2ecf20Sopenharmony_ci reg = tcic_getb(TCIC_PWR); 6118c2ecf20Sopenharmony_ci if (reg & (TCIC_PWR_VCC(psock)|TCIC_PWR_VPP(psock))) 6128c2ecf20Sopenharmony_ci *value |= SS_POWERON; 6138c2ecf20Sopenharmony_ci dev_dbg(&sock->dev, "GetStatus(%d) = %#2.2x\n", psock, *value); 6148c2ecf20Sopenharmony_ci return 0; 6158c2ecf20Sopenharmony_ci} /* tcic_get_status */ 6168c2ecf20Sopenharmony_ci 6178c2ecf20Sopenharmony_ci/*====================================================================*/ 6188c2ecf20Sopenharmony_ci 6198c2ecf20Sopenharmony_cistatic int tcic_set_socket(struct pcmcia_socket *sock, socket_state_t *state) 6208c2ecf20Sopenharmony_ci{ 6218c2ecf20Sopenharmony_ci u_short psock = container_of(sock, struct tcic_socket, socket)->psock; 6228c2ecf20Sopenharmony_ci u_char reg; 6238c2ecf20Sopenharmony_ci u_short scf1, scf2; 6248c2ecf20Sopenharmony_ci 6258c2ecf20Sopenharmony_ci dev_dbg(&sock->dev, "SetSocket(%d, flags %#3.3x, Vcc %d, Vpp %d, " 6268c2ecf20Sopenharmony_ci "io_irq %d, csc_mask %#2.2x)\n", psock, state->flags, 6278c2ecf20Sopenharmony_ci state->Vcc, state->Vpp, state->io_irq, state->csc_mask); 6288c2ecf20Sopenharmony_ci tcic_setw(TCIC_ADDR+2, (psock << TCIC_SS_SHFT) | TCIC_ADR2_INDREG); 6298c2ecf20Sopenharmony_ci 6308c2ecf20Sopenharmony_ci reg = tcic_getb(TCIC_PWR); 6318c2ecf20Sopenharmony_ci reg &= ~(TCIC_PWR_VCC(psock) | TCIC_PWR_VPP(psock)); 6328c2ecf20Sopenharmony_ci 6338c2ecf20Sopenharmony_ci if (state->Vcc == 50) { 6348c2ecf20Sopenharmony_ci switch (state->Vpp) { 6358c2ecf20Sopenharmony_ci case 0: reg |= TCIC_PWR_VCC(psock) | TCIC_PWR_VPP(psock); break; 6368c2ecf20Sopenharmony_ci case 50: reg |= TCIC_PWR_VCC(psock); break; 6378c2ecf20Sopenharmony_ci case 120: reg |= TCIC_PWR_VPP(psock); break; 6388c2ecf20Sopenharmony_ci default: return -EINVAL; 6398c2ecf20Sopenharmony_ci } 6408c2ecf20Sopenharmony_ci } else if (state->Vcc != 0) 6418c2ecf20Sopenharmony_ci return -EINVAL; 6428c2ecf20Sopenharmony_ci 6438c2ecf20Sopenharmony_ci if (reg != tcic_getb(TCIC_PWR)) 6448c2ecf20Sopenharmony_ci tcic_setb(TCIC_PWR, reg); 6458c2ecf20Sopenharmony_ci 6468c2ecf20Sopenharmony_ci reg = TCIC_ILOCK_HOLD_CCLK | TCIC_ILOCK_CWAIT; 6478c2ecf20Sopenharmony_ci if (state->flags & SS_OUTPUT_ENA) { 6488c2ecf20Sopenharmony_ci tcic_setb(TCIC_SCTRL, TCIC_SCTRL_ENA); 6498c2ecf20Sopenharmony_ci reg |= TCIC_ILOCK_CRESENA; 6508c2ecf20Sopenharmony_ci } else 6518c2ecf20Sopenharmony_ci tcic_setb(TCIC_SCTRL, 0); 6528c2ecf20Sopenharmony_ci if (state->flags & SS_RESET) 6538c2ecf20Sopenharmony_ci reg |= TCIC_ILOCK_CRESET; 6548c2ecf20Sopenharmony_ci tcic_aux_setb(TCIC_AUX_ILOCK, reg); 6558c2ecf20Sopenharmony_ci 6568c2ecf20Sopenharmony_ci tcic_setw(TCIC_ADDR, TCIC_SCF1(psock)); 6578c2ecf20Sopenharmony_ci scf1 = TCIC_SCF1_FINPACK; 6588c2ecf20Sopenharmony_ci scf1 |= TCIC_IRQ(state->io_irq); 6598c2ecf20Sopenharmony_ci if (state->flags & SS_IOCARD) { 6608c2ecf20Sopenharmony_ci scf1 |= TCIC_SCF1_IOSTS; 6618c2ecf20Sopenharmony_ci if (state->flags & SS_SPKR_ENA) 6628c2ecf20Sopenharmony_ci scf1 |= TCIC_SCF1_SPKR; 6638c2ecf20Sopenharmony_ci if (state->flags & SS_DMA_MODE) 6648c2ecf20Sopenharmony_ci scf1 |= TCIC_SCF1_DREQ2 << TCIC_SCF1_DMA_SHIFT; 6658c2ecf20Sopenharmony_ci } 6668c2ecf20Sopenharmony_ci tcic_setw(TCIC_DATA, scf1); 6678c2ecf20Sopenharmony_ci 6688c2ecf20Sopenharmony_ci /* Some general setup stuff, and configure status interrupt */ 6698c2ecf20Sopenharmony_ci reg = TCIC_WAIT_ASYNC | TCIC_WAIT_SENSE | to_cycles(250); 6708c2ecf20Sopenharmony_ci tcic_aux_setb(TCIC_AUX_WCTL, reg); 6718c2ecf20Sopenharmony_ci tcic_aux_setw(TCIC_AUX_SYSCFG, TCIC_SYSCFG_AUTOBUSY|0x0a00| 6728c2ecf20Sopenharmony_ci TCIC_IRQ(cs_irq)); 6738c2ecf20Sopenharmony_ci 6748c2ecf20Sopenharmony_ci /* Card status change interrupt mask */ 6758c2ecf20Sopenharmony_ci tcic_setw(TCIC_ADDR, TCIC_SCF2(psock)); 6768c2ecf20Sopenharmony_ci scf2 = TCIC_SCF2_MALL; 6778c2ecf20Sopenharmony_ci if (state->csc_mask & SS_DETECT) scf2 &= ~TCIC_SCF2_MCD; 6788c2ecf20Sopenharmony_ci if (state->flags & SS_IOCARD) { 6798c2ecf20Sopenharmony_ci if (state->csc_mask & SS_STSCHG) reg &= ~TCIC_SCF2_MLBAT1; 6808c2ecf20Sopenharmony_ci } else { 6818c2ecf20Sopenharmony_ci if (state->csc_mask & SS_BATDEAD) reg &= ~TCIC_SCF2_MLBAT1; 6828c2ecf20Sopenharmony_ci if (state->csc_mask & SS_BATWARN) reg &= ~TCIC_SCF2_MLBAT2; 6838c2ecf20Sopenharmony_ci if (state->csc_mask & SS_READY) reg &= ~TCIC_SCF2_MRDY; 6848c2ecf20Sopenharmony_ci } 6858c2ecf20Sopenharmony_ci tcic_setw(TCIC_DATA, scf2); 6868c2ecf20Sopenharmony_ci /* For the ISA bus, the irq should be active-high totem-pole */ 6878c2ecf20Sopenharmony_ci tcic_setb(TCIC_IENA, TCIC_IENA_CDCHG | TCIC_IENA_CFG_HIGH); 6888c2ecf20Sopenharmony_ci 6898c2ecf20Sopenharmony_ci return 0; 6908c2ecf20Sopenharmony_ci} /* tcic_set_socket */ 6918c2ecf20Sopenharmony_ci 6928c2ecf20Sopenharmony_ci/*====================================================================*/ 6938c2ecf20Sopenharmony_ci 6948c2ecf20Sopenharmony_cistatic int tcic_set_io_map(struct pcmcia_socket *sock, struct pccard_io_map *io) 6958c2ecf20Sopenharmony_ci{ 6968c2ecf20Sopenharmony_ci u_short psock = container_of(sock, struct tcic_socket, socket)->psock; 6978c2ecf20Sopenharmony_ci u_int addr; 6988c2ecf20Sopenharmony_ci u_short base, len, ioctl; 6998c2ecf20Sopenharmony_ci 7008c2ecf20Sopenharmony_ci dev_dbg(&sock->dev, "SetIOMap(%d, %d, %#2.2x, %d ns, " 7018c2ecf20Sopenharmony_ci "%#llx-%#llx)\n", psock, io->map, io->flags, io->speed, 7028c2ecf20Sopenharmony_ci (unsigned long long)io->start, (unsigned long long)io->stop); 7038c2ecf20Sopenharmony_ci if ((io->map > 1) || (io->start > 0xffff) || (io->stop > 0xffff) || 7048c2ecf20Sopenharmony_ci (io->stop < io->start)) return -EINVAL; 7058c2ecf20Sopenharmony_ci tcic_setw(TCIC_ADDR+2, TCIC_ADR2_INDREG | (psock << TCIC_SS_SHFT)); 7068c2ecf20Sopenharmony_ci addr = TCIC_IWIN(psock, io->map); 7078c2ecf20Sopenharmony_ci 7088c2ecf20Sopenharmony_ci base = io->start; len = io->stop - io->start; 7098c2ecf20Sopenharmony_ci /* Check to see that len+1 is power of two, etc */ 7108c2ecf20Sopenharmony_ci if ((len & (len+1)) || (base & len)) return -EINVAL; 7118c2ecf20Sopenharmony_ci base |= (len+1)>>1; 7128c2ecf20Sopenharmony_ci tcic_setw(TCIC_ADDR, addr + TCIC_IBASE_X); 7138c2ecf20Sopenharmony_ci tcic_setw(TCIC_DATA, base); 7148c2ecf20Sopenharmony_ci 7158c2ecf20Sopenharmony_ci ioctl = (psock << TCIC_ICTL_SS_SHFT); 7168c2ecf20Sopenharmony_ci ioctl |= (len == 0) ? TCIC_ICTL_TINY : 0; 7178c2ecf20Sopenharmony_ci ioctl |= (io->flags & MAP_ACTIVE) ? TCIC_ICTL_ENA : 0; 7188c2ecf20Sopenharmony_ci ioctl |= to_cycles(io->speed) & TCIC_ICTL_WSCNT_MASK; 7198c2ecf20Sopenharmony_ci if (!(io->flags & MAP_AUTOSZ)) { 7208c2ecf20Sopenharmony_ci ioctl |= TCIC_ICTL_QUIET; 7218c2ecf20Sopenharmony_ci ioctl |= (io->flags & MAP_16BIT) ? TCIC_ICTL_BW_16 : TCIC_ICTL_BW_8; 7228c2ecf20Sopenharmony_ci } 7238c2ecf20Sopenharmony_ci tcic_setw(TCIC_ADDR, addr + TCIC_ICTL_X); 7248c2ecf20Sopenharmony_ci tcic_setw(TCIC_DATA, ioctl); 7258c2ecf20Sopenharmony_ci 7268c2ecf20Sopenharmony_ci return 0; 7278c2ecf20Sopenharmony_ci} /* tcic_set_io_map */ 7288c2ecf20Sopenharmony_ci 7298c2ecf20Sopenharmony_ci/*====================================================================*/ 7308c2ecf20Sopenharmony_ci 7318c2ecf20Sopenharmony_cistatic int tcic_set_mem_map(struct pcmcia_socket *sock, struct pccard_mem_map *mem) 7328c2ecf20Sopenharmony_ci{ 7338c2ecf20Sopenharmony_ci u_short psock = container_of(sock, struct tcic_socket, socket)->psock; 7348c2ecf20Sopenharmony_ci u_short addr, ctl; 7358c2ecf20Sopenharmony_ci u_long base, len, mmap; 7368c2ecf20Sopenharmony_ci 7378c2ecf20Sopenharmony_ci dev_dbg(&sock->dev, "SetMemMap(%d, %d, %#2.2x, %d ns, " 7388c2ecf20Sopenharmony_ci "%#llx-%#llx, %#x)\n", psock, mem->map, mem->flags, 7398c2ecf20Sopenharmony_ci mem->speed, (unsigned long long)mem->res->start, 7408c2ecf20Sopenharmony_ci (unsigned long long)mem->res->end, mem->card_start); 7418c2ecf20Sopenharmony_ci if ((mem->map > 3) || (mem->card_start > 0x3ffffff) || 7428c2ecf20Sopenharmony_ci (mem->res->start > 0xffffff) || (mem->res->end > 0xffffff) || 7438c2ecf20Sopenharmony_ci (mem->res->start > mem->res->end) || (mem->speed > 1000)) 7448c2ecf20Sopenharmony_ci return -EINVAL; 7458c2ecf20Sopenharmony_ci tcic_setw(TCIC_ADDR+2, TCIC_ADR2_INDREG | (psock << TCIC_SS_SHFT)); 7468c2ecf20Sopenharmony_ci addr = TCIC_MWIN(psock, mem->map); 7478c2ecf20Sopenharmony_ci 7488c2ecf20Sopenharmony_ci base = mem->res->start; len = mem->res->end - mem->res->start; 7498c2ecf20Sopenharmony_ci if ((len & (len+1)) || (base & len)) return -EINVAL; 7508c2ecf20Sopenharmony_ci if (len == 0x0fff) 7518c2ecf20Sopenharmony_ci base = (base >> TCIC_MBASE_HA_SHFT) | TCIC_MBASE_4K_BIT; 7528c2ecf20Sopenharmony_ci else 7538c2ecf20Sopenharmony_ci base = (base | (len+1)>>1) >> TCIC_MBASE_HA_SHFT; 7548c2ecf20Sopenharmony_ci tcic_setw(TCIC_ADDR, addr + TCIC_MBASE_X); 7558c2ecf20Sopenharmony_ci tcic_setw(TCIC_DATA, base); 7568c2ecf20Sopenharmony_ci 7578c2ecf20Sopenharmony_ci mmap = mem->card_start - mem->res->start; 7588c2ecf20Sopenharmony_ci mmap = (mmap >> TCIC_MMAP_CA_SHFT) & TCIC_MMAP_CA_MASK; 7598c2ecf20Sopenharmony_ci if (mem->flags & MAP_ATTRIB) mmap |= TCIC_MMAP_REG; 7608c2ecf20Sopenharmony_ci tcic_setw(TCIC_ADDR, addr + TCIC_MMAP_X); 7618c2ecf20Sopenharmony_ci tcic_setw(TCIC_DATA, mmap); 7628c2ecf20Sopenharmony_ci 7638c2ecf20Sopenharmony_ci ctl = TCIC_MCTL_QUIET | (psock << TCIC_MCTL_SS_SHFT); 7648c2ecf20Sopenharmony_ci ctl |= to_cycles(mem->speed) & TCIC_MCTL_WSCNT_MASK; 7658c2ecf20Sopenharmony_ci ctl |= (mem->flags & MAP_16BIT) ? 0 : TCIC_MCTL_B8; 7668c2ecf20Sopenharmony_ci ctl |= (mem->flags & MAP_WRPROT) ? TCIC_MCTL_WP : 0; 7678c2ecf20Sopenharmony_ci ctl |= (mem->flags & MAP_ACTIVE) ? TCIC_MCTL_ENA : 0; 7688c2ecf20Sopenharmony_ci tcic_setw(TCIC_ADDR, addr + TCIC_MCTL_X); 7698c2ecf20Sopenharmony_ci tcic_setw(TCIC_DATA, ctl); 7708c2ecf20Sopenharmony_ci 7718c2ecf20Sopenharmony_ci return 0; 7728c2ecf20Sopenharmony_ci} /* tcic_set_mem_map */ 7738c2ecf20Sopenharmony_ci 7748c2ecf20Sopenharmony_ci/*====================================================================*/ 7758c2ecf20Sopenharmony_ci 7768c2ecf20Sopenharmony_cistatic int tcic_init(struct pcmcia_socket *s) 7778c2ecf20Sopenharmony_ci{ 7788c2ecf20Sopenharmony_ci int i; 7798c2ecf20Sopenharmony_ci struct resource res = { .start = 0, .end = 0x1000 }; 7808c2ecf20Sopenharmony_ci pccard_io_map io = { 0, 0, 0, 0, 1 }; 7818c2ecf20Sopenharmony_ci pccard_mem_map mem = { .res = &res, }; 7828c2ecf20Sopenharmony_ci 7838c2ecf20Sopenharmony_ci for (i = 0; i < 2; i++) { 7848c2ecf20Sopenharmony_ci io.map = i; 7858c2ecf20Sopenharmony_ci tcic_set_io_map(s, &io); 7868c2ecf20Sopenharmony_ci } 7878c2ecf20Sopenharmony_ci for (i = 0; i < 5; i++) { 7888c2ecf20Sopenharmony_ci mem.map = i; 7898c2ecf20Sopenharmony_ci tcic_set_mem_map(s, &mem); 7908c2ecf20Sopenharmony_ci } 7918c2ecf20Sopenharmony_ci return 0; 7928c2ecf20Sopenharmony_ci} 7938c2ecf20Sopenharmony_ci 7948c2ecf20Sopenharmony_cistatic struct pccard_operations tcic_operations = { 7958c2ecf20Sopenharmony_ci .init = tcic_init, 7968c2ecf20Sopenharmony_ci .get_status = tcic_get_status, 7978c2ecf20Sopenharmony_ci .set_socket = tcic_set_socket, 7988c2ecf20Sopenharmony_ci .set_io_map = tcic_set_io_map, 7998c2ecf20Sopenharmony_ci .set_mem_map = tcic_set_mem_map, 8008c2ecf20Sopenharmony_ci}; 8018c2ecf20Sopenharmony_ci 8028c2ecf20Sopenharmony_ci/*====================================================================*/ 8038c2ecf20Sopenharmony_ci 8048c2ecf20Sopenharmony_cimodule_init(init_tcic); 8058c2ecf20Sopenharmony_cimodule_exit(exit_tcic); 806