162306a36Sopenharmony_ci/*====================================================================== 262306a36Sopenharmony_ci 362306a36Sopenharmony_ci Device driver for Intel 82365 and compatible PC Card controllers. 462306a36Sopenharmony_ci 562306a36Sopenharmony_ci i82365.c 1.265 1999/11/10 18:36:21 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/kernel.h> 4162306a36Sopenharmony_ci#include <linux/errno.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/interrupt.h> 4762306a36Sopenharmony_ci#include <linux/platform_device.h> 4862306a36Sopenharmony_ci#include <linux/bitops.h> 4962306a36Sopenharmony_ci#include <asm/irq.h> 5062306a36Sopenharmony_ci#include <asm/io.h> 5162306a36Sopenharmony_ci 5262306a36Sopenharmony_ci#include <pcmcia/ss.h> 5362306a36Sopenharmony_ci 5462306a36Sopenharmony_ci#include <linux/isapnp.h> 5562306a36Sopenharmony_ci 5662306a36Sopenharmony_ci/* ISA-bus controllers */ 5762306a36Sopenharmony_ci#include "i82365.h" 5862306a36Sopenharmony_ci#include "cirrus.h" 5962306a36Sopenharmony_ci#include "vg468.h" 6062306a36Sopenharmony_ci#include "ricoh.h" 6162306a36Sopenharmony_ci 6262306a36Sopenharmony_ci 6362306a36Sopenharmony_cistatic irqreturn_t i365_count_irq(int, void *); 6462306a36Sopenharmony_cistatic inline int _check_irq(int irq, int flags) 6562306a36Sopenharmony_ci{ 6662306a36Sopenharmony_ci if (request_irq(irq, i365_count_irq, flags, "x", i365_count_irq) != 0) 6762306a36Sopenharmony_ci return -1; 6862306a36Sopenharmony_ci free_irq(irq, i365_count_irq); 6962306a36Sopenharmony_ci return 0; 7062306a36Sopenharmony_ci} 7162306a36Sopenharmony_ci 7262306a36Sopenharmony_ci/*====================================================================*/ 7362306a36Sopenharmony_ci 7462306a36Sopenharmony_ci/* Parameters that can be set with 'insmod' */ 7562306a36Sopenharmony_ci 7662306a36Sopenharmony_ci/* Default base address for i82365sl and other ISA chips */ 7762306a36Sopenharmony_cistatic unsigned long i365_base = 0x3e0; 7862306a36Sopenharmony_ci/* Should we probe at 0x3e2 for an extra ISA controller? */ 7962306a36Sopenharmony_cistatic int extra_sockets = 0; 8062306a36Sopenharmony_ci/* Specify a socket number to ignore */ 8162306a36Sopenharmony_cistatic int ignore = -1; 8262306a36Sopenharmony_ci/* Bit map or list of interrupts to choose from */ 8362306a36Sopenharmony_cistatic u_int irq_mask = 0xffff; 8462306a36Sopenharmony_cistatic int irq_list[16]; 8562306a36Sopenharmony_cistatic unsigned int irq_list_count; 8662306a36Sopenharmony_ci/* The card status change interrupt -- 0 means autoselect */ 8762306a36Sopenharmony_cistatic int cs_irq = 0; 8862306a36Sopenharmony_ci 8962306a36Sopenharmony_ci/* Probe for safe interrupts? */ 9062306a36Sopenharmony_cistatic int do_scan = 1; 9162306a36Sopenharmony_ci/* Poll status interval -- 0 means default to interrupt */ 9262306a36Sopenharmony_cistatic int poll_interval = 0; 9362306a36Sopenharmony_ci/* External clock time, in nanoseconds. 120 ns = 8.33 MHz */ 9462306a36Sopenharmony_cistatic int cycle_time = 120; 9562306a36Sopenharmony_ci 9662306a36Sopenharmony_ci/* Cirrus options */ 9762306a36Sopenharmony_cistatic int has_dma = -1; 9862306a36Sopenharmony_cistatic int has_led = -1; 9962306a36Sopenharmony_cistatic int has_ring = -1; 10062306a36Sopenharmony_cistatic int dynamic_mode = 0; 10162306a36Sopenharmony_cistatic int freq_bypass = -1; 10262306a36Sopenharmony_cistatic int setup_time = -1; 10362306a36Sopenharmony_cistatic int cmd_time = -1; 10462306a36Sopenharmony_cistatic int recov_time = -1; 10562306a36Sopenharmony_ci 10662306a36Sopenharmony_ci/* Vadem options */ 10762306a36Sopenharmony_cistatic int async_clock = -1; 10862306a36Sopenharmony_cistatic int cable_mode = -1; 10962306a36Sopenharmony_cistatic int wakeup = 0; 11062306a36Sopenharmony_ci 11162306a36Sopenharmony_cimodule_param_hw(i365_base, ulong, ioport, 0444); 11262306a36Sopenharmony_cimodule_param(ignore, int, 0444); 11362306a36Sopenharmony_cimodule_param(extra_sockets, int, 0444); 11462306a36Sopenharmony_cimodule_param_hw(irq_mask, int, other, 0444); 11562306a36Sopenharmony_cimodule_param_hw_array(irq_list, int, irq, &irq_list_count, 0444); 11662306a36Sopenharmony_cimodule_param_hw(cs_irq, int, irq, 0444); 11762306a36Sopenharmony_cimodule_param(async_clock, int, 0444); 11862306a36Sopenharmony_cimodule_param(cable_mode, int, 0444); 11962306a36Sopenharmony_cimodule_param(wakeup, int, 0444); 12062306a36Sopenharmony_ci 12162306a36Sopenharmony_cimodule_param(do_scan, int, 0444); 12262306a36Sopenharmony_cimodule_param(poll_interval, int, 0444); 12362306a36Sopenharmony_cimodule_param(cycle_time, int, 0444); 12462306a36Sopenharmony_cimodule_param(has_dma, int, 0444); 12562306a36Sopenharmony_cimodule_param(has_led, int, 0444); 12662306a36Sopenharmony_cimodule_param(has_ring, int, 0444); 12762306a36Sopenharmony_cimodule_param(dynamic_mode, int, 0444); 12862306a36Sopenharmony_cimodule_param(freq_bypass, int, 0444); 12962306a36Sopenharmony_cimodule_param(setup_time, int, 0444); 13062306a36Sopenharmony_cimodule_param(cmd_time, int, 0444); 13162306a36Sopenharmony_cimodule_param(recov_time, int, 0444); 13262306a36Sopenharmony_ci 13362306a36Sopenharmony_ci/*====================================================================*/ 13462306a36Sopenharmony_ci 13562306a36Sopenharmony_cistruct cirrus_state { 13662306a36Sopenharmony_ci u_char misc1, misc2; 13762306a36Sopenharmony_ci u_char timer[6]; 13862306a36Sopenharmony_ci}; 13962306a36Sopenharmony_ci 14062306a36Sopenharmony_cistruct vg46x_state { 14162306a36Sopenharmony_ci u_char ctl, ema; 14262306a36Sopenharmony_ci}; 14362306a36Sopenharmony_ci 14462306a36Sopenharmony_cistruct i82365_socket { 14562306a36Sopenharmony_ci u_short type, flags; 14662306a36Sopenharmony_ci struct pcmcia_socket socket; 14762306a36Sopenharmony_ci unsigned int number; 14862306a36Sopenharmony_ci unsigned int ioaddr; 14962306a36Sopenharmony_ci u_short psock; 15062306a36Sopenharmony_ci u_char cs_irq, intr; 15162306a36Sopenharmony_ci union { 15262306a36Sopenharmony_ci struct cirrus_state cirrus; 15362306a36Sopenharmony_ci struct vg46x_state vg46x; 15462306a36Sopenharmony_ci } state; 15562306a36Sopenharmony_ci}; 15662306a36Sopenharmony_ci 15762306a36Sopenharmony_ci/* Where we keep track of our sockets... */ 15862306a36Sopenharmony_cistatic int sockets = 0; 15962306a36Sopenharmony_cistatic struct i82365_socket socket[8] = { 16062306a36Sopenharmony_ci { 0, }, /* ... */ 16162306a36Sopenharmony_ci}; 16262306a36Sopenharmony_ci 16362306a36Sopenharmony_ci/* Default ISA interrupt mask */ 16462306a36Sopenharmony_ci#define I365_MASK 0xdeb8 /* irq 15,14,12,11,10,9,7,5,4,3 */ 16562306a36Sopenharmony_ci 16662306a36Sopenharmony_cistatic int grab_irq; 16762306a36Sopenharmony_cistatic DEFINE_SPINLOCK(isa_lock); 16862306a36Sopenharmony_ci#define ISA_LOCK(n, f) spin_lock_irqsave(&isa_lock, f) 16962306a36Sopenharmony_ci#define ISA_UNLOCK(n, f) spin_unlock_irqrestore(&isa_lock, f) 17062306a36Sopenharmony_ci 17162306a36Sopenharmony_cistatic struct timer_list poll_timer; 17262306a36Sopenharmony_ci 17362306a36Sopenharmony_ci/*====================================================================*/ 17462306a36Sopenharmony_ci 17562306a36Sopenharmony_ci/* These definitions must match the pcic table! */ 17662306a36Sopenharmony_cienum pcic_id { 17762306a36Sopenharmony_ci IS_I82365A, IS_I82365B, IS_I82365DF, 17862306a36Sopenharmony_ci IS_IBM, IS_RF5Cx96, IS_VLSI, IS_VG468, IS_VG469, 17962306a36Sopenharmony_ci IS_PD6710, IS_PD672X, IS_VT83C469, 18062306a36Sopenharmony_ci}; 18162306a36Sopenharmony_ci 18262306a36Sopenharmony_ci/* Flags for classifying groups of controllers */ 18362306a36Sopenharmony_ci#define IS_VADEM 0x0001 18462306a36Sopenharmony_ci#define IS_CIRRUS 0x0002 18562306a36Sopenharmony_ci#define IS_VIA 0x0010 18662306a36Sopenharmony_ci#define IS_UNKNOWN 0x0400 18762306a36Sopenharmony_ci#define IS_VG_PWR 0x0800 18862306a36Sopenharmony_ci#define IS_DF_PWR 0x1000 18962306a36Sopenharmony_ci#define IS_REGISTERED 0x2000 19062306a36Sopenharmony_ci#define IS_ALIVE 0x8000 19162306a36Sopenharmony_ci 19262306a36Sopenharmony_cistruct pcic { 19362306a36Sopenharmony_ci char *name; 19462306a36Sopenharmony_ci u_short flags; 19562306a36Sopenharmony_ci}; 19662306a36Sopenharmony_ci 19762306a36Sopenharmony_cistatic struct pcic pcic[] = { 19862306a36Sopenharmony_ci { "Intel i82365sl A step", 0 }, 19962306a36Sopenharmony_ci { "Intel i82365sl B step", 0 }, 20062306a36Sopenharmony_ci { "Intel i82365sl DF", IS_DF_PWR }, 20162306a36Sopenharmony_ci { "IBM Clone", 0 }, 20262306a36Sopenharmony_ci { "Ricoh RF5C296/396", 0 }, 20362306a36Sopenharmony_ci { "VLSI 82C146", 0 }, 20462306a36Sopenharmony_ci { "Vadem VG-468", IS_VADEM }, 20562306a36Sopenharmony_ci { "Vadem VG-469", IS_VADEM|IS_VG_PWR }, 20662306a36Sopenharmony_ci { "Cirrus PD6710", IS_CIRRUS }, 20762306a36Sopenharmony_ci { "Cirrus PD672x", IS_CIRRUS }, 20862306a36Sopenharmony_ci { "VIA VT83C469", IS_CIRRUS|IS_VIA }, 20962306a36Sopenharmony_ci}; 21062306a36Sopenharmony_ci 21162306a36Sopenharmony_ci#define PCIC_COUNT ARRAY_SIZE(pcic) 21262306a36Sopenharmony_ci 21362306a36Sopenharmony_ci/*====================================================================*/ 21462306a36Sopenharmony_ci 21562306a36Sopenharmony_cistatic DEFINE_SPINLOCK(bus_lock); 21662306a36Sopenharmony_ci 21762306a36Sopenharmony_cistatic u_char i365_get(u_short sock, u_short reg) 21862306a36Sopenharmony_ci{ 21962306a36Sopenharmony_ci unsigned long flags; 22062306a36Sopenharmony_ci spin_lock_irqsave(&bus_lock,flags); 22162306a36Sopenharmony_ci { 22262306a36Sopenharmony_ci unsigned int port = socket[sock].ioaddr; 22362306a36Sopenharmony_ci u_char val; 22462306a36Sopenharmony_ci reg = I365_REG(socket[sock].psock, reg); 22562306a36Sopenharmony_ci outb(reg, port); val = inb(port+1); 22662306a36Sopenharmony_ci spin_unlock_irqrestore(&bus_lock,flags); 22762306a36Sopenharmony_ci return val; 22862306a36Sopenharmony_ci } 22962306a36Sopenharmony_ci} 23062306a36Sopenharmony_ci 23162306a36Sopenharmony_cistatic void i365_set(u_short sock, u_short reg, u_char data) 23262306a36Sopenharmony_ci{ 23362306a36Sopenharmony_ci unsigned long flags; 23462306a36Sopenharmony_ci spin_lock_irqsave(&bus_lock,flags); 23562306a36Sopenharmony_ci { 23662306a36Sopenharmony_ci unsigned int port = socket[sock].ioaddr; 23762306a36Sopenharmony_ci u_char val = I365_REG(socket[sock].psock, reg); 23862306a36Sopenharmony_ci outb(val, port); outb(data, port+1); 23962306a36Sopenharmony_ci spin_unlock_irqrestore(&bus_lock,flags); 24062306a36Sopenharmony_ci } 24162306a36Sopenharmony_ci} 24262306a36Sopenharmony_ci 24362306a36Sopenharmony_cistatic void i365_bset(u_short sock, u_short reg, u_char mask) 24462306a36Sopenharmony_ci{ 24562306a36Sopenharmony_ci u_char d = i365_get(sock, reg); 24662306a36Sopenharmony_ci d |= mask; 24762306a36Sopenharmony_ci i365_set(sock, reg, d); 24862306a36Sopenharmony_ci} 24962306a36Sopenharmony_ci 25062306a36Sopenharmony_cistatic void i365_bclr(u_short sock, u_short reg, u_char mask) 25162306a36Sopenharmony_ci{ 25262306a36Sopenharmony_ci u_char d = i365_get(sock, reg); 25362306a36Sopenharmony_ci d &= ~mask; 25462306a36Sopenharmony_ci i365_set(sock, reg, d); 25562306a36Sopenharmony_ci} 25662306a36Sopenharmony_ci 25762306a36Sopenharmony_cistatic void i365_bflip(u_short sock, u_short reg, u_char mask, int b) 25862306a36Sopenharmony_ci{ 25962306a36Sopenharmony_ci u_char d = i365_get(sock, reg); 26062306a36Sopenharmony_ci if (b) 26162306a36Sopenharmony_ci d |= mask; 26262306a36Sopenharmony_ci else 26362306a36Sopenharmony_ci d &= ~mask; 26462306a36Sopenharmony_ci i365_set(sock, reg, d); 26562306a36Sopenharmony_ci} 26662306a36Sopenharmony_ci 26762306a36Sopenharmony_cistatic u_short i365_get_pair(u_short sock, u_short reg) 26862306a36Sopenharmony_ci{ 26962306a36Sopenharmony_ci u_short a, b; 27062306a36Sopenharmony_ci a = i365_get(sock, reg); 27162306a36Sopenharmony_ci b = i365_get(sock, reg+1); 27262306a36Sopenharmony_ci return (a + (b<<8)); 27362306a36Sopenharmony_ci} 27462306a36Sopenharmony_ci 27562306a36Sopenharmony_cistatic void i365_set_pair(u_short sock, u_short reg, u_short data) 27662306a36Sopenharmony_ci{ 27762306a36Sopenharmony_ci i365_set(sock, reg, data & 0xff); 27862306a36Sopenharmony_ci i365_set(sock, reg+1, data >> 8); 27962306a36Sopenharmony_ci} 28062306a36Sopenharmony_ci 28162306a36Sopenharmony_ci/*====================================================================== 28262306a36Sopenharmony_ci 28362306a36Sopenharmony_ci Code to save and restore global state information for Cirrus 28462306a36Sopenharmony_ci PD67xx controllers, and to set and report global configuration 28562306a36Sopenharmony_ci options. 28662306a36Sopenharmony_ci 28762306a36Sopenharmony_ci The VIA controllers also use these routines, as they are mostly 28862306a36Sopenharmony_ci Cirrus lookalikes, without the timing registers. 28962306a36Sopenharmony_ci 29062306a36Sopenharmony_ci======================================================================*/ 29162306a36Sopenharmony_ci 29262306a36Sopenharmony_ci#define flip(v,b,f) (v = ((f)<0) ? v : ((f) ? ((v)|(b)) : ((v)&(~b)))) 29362306a36Sopenharmony_ci 29462306a36Sopenharmony_cistatic void cirrus_get_state(u_short s) 29562306a36Sopenharmony_ci{ 29662306a36Sopenharmony_ci int i; 29762306a36Sopenharmony_ci struct cirrus_state *p = &socket[s].state.cirrus; 29862306a36Sopenharmony_ci p->misc1 = i365_get(s, PD67_MISC_CTL_1); 29962306a36Sopenharmony_ci p->misc1 &= (PD67_MC1_MEDIA_ENA | PD67_MC1_INPACK_ENA); 30062306a36Sopenharmony_ci p->misc2 = i365_get(s, PD67_MISC_CTL_2); 30162306a36Sopenharmony_ci for (i = 0; i < 6; i++) 30262306a36Sopenharmony_ci p->timer[i] = i365_get(s, PD67_TIME_SETUP(0)+i); 30362306a36Sopenharmony_ci} 30462306a36Sopenharmony_ci 30562306a36Sopenharmony_cistatic void cirrus_set_state(u_short s) 30662306a36Sopenharmony_ci{ 30762306a36Sopenharmony_ci int i; 30862306a36Sopenharmony_ci u_char misc; 30962306a36Sopenharmony_ci struct cirrus_state *p = &socket[s].state.cirrus; 31062306a36Sopenharmony_ci 31162306a36Sopenharmony_ci misc = i365_get(s, PD67_MISC_CTL_2); 31262306a36Sopenharmony_ci i365_set(s, PD67_MISC_CTL_2, p->misc2); 31362306a36Sopenharmony_ci if (misc & PD67_MC2_SUSPEND) mdelay(50); 31462306a36Sopenharmony_ci misc = i365_get(s, PD67_MISC_CTL_1); 31562306a36Sopenharmony_ci misc &= ~(PD67_MC1_MEDIA_ENA | PD67_MC1_INPACK_ENA); 31662306a36Sopenharmony_ci i365_set(s, PD67_MISC_CTL_1, misc | p->misc1); 31762306a36Sopenharmony_ci for (i = 0; i < 6; i++) 31862306a36Sopenharmony_ci i365_set(s, PD67_TIME_SETUP(0)+i, p->timer[i]); 31962306a36Sopenharmony_ci} 32062306a36Sopenharmony_ci 32162306a36Sopenharmony_cistatic u_int __init cirrus_set_opts(u_short s, char *buf) 32262306a36Sopenharmony_ci{ 32362306a36Sopenharmony_ci struct i82365_socket *t = &socket[s]; 32462306a36Sopenharmony_ci struct cirrus_state *p = &socket[s].state.cirrus; 32562306a36Sopenharmony_ci u_int mask = 0xffff; 32662306a36Sopenharmony_ci 32762306a36Sopenharmony_ci if (has_ring == -1) has_ring = 1; 32862306a36Sopenharmony_ci flip(p->misc2, PD67_MC2_IRQ15_RI, has_ring); 32962306a36Sopenharmony_ci flip(p->misc2, PD67_MC2_DYNAMIC_MODE, dynamic_mode); 33062306a36Sopenharmony_ci flip(p->misc2, PD67_MC2_FREQ_BYPASS, freq_bypass); 33162306a36Sopenharmony_ci if (p->misc2 & PD67_MC2_IRQ15_RI) 33262306a36Sopenharmony_ci strcat(buf, " [ring]"); 33362306a36Sopenharmony_ci if (p->misc2 & PD67_MC2_DYNAMIC_MODE) 33462306a36Sopenharmony_ci strcat(buf, " [dyn mode]"); 33562306a36Sopenharmony_ci if (p->misc2 & PD67_MC2_FREQ_BYPASS) 33662306a36Sopenharmony_ci strcat(buf, " [freq bypass]"); 33762306a36Sopenharmony_ci if (p->misc1 & PD67_MC1_INPACK_ENA) 33862306a36Sopenharmony_ci strcat(buf, " [inpack]"); 33962306a36Sopenharmony_ci if (p->misc2 & PD67_MC2_IRQ15_RI) 34062306a36Sopenharmony_ci mask &= ~0x8000; 34162306a36Sopenharmony_ci if (has_led > 0) { 34262306a36Sopenharmony_ci strcat(buf, " [led]"); 34362306a36Sopenharmony_ci mask &= ~0x1000; 34462306a36Sopenharmony_ci } 34562306a36Sopenharmony_ci if (has_dma > 0) { 34662306a36Sopenharmony_ci strcat(buf, " [dma]"); 34762306a36Sopenharmony_ci mask &= ~0x0600; 34862306a36Sopenharmony_ci } 34962306a36Sopenharmony_ci if (!(t->flags & IS_VIA)) { 35062306a36Sopenharmony_ci if (setup_time >= 0) 35162306a36Sopenharmony_ci p->timer[0] = p->timer[3] = setup_time; 35262306a36Sopenharmony_ci if (cmd_time > 0) { 35362306a36Sopenharmony_ci p->timer[1] = cmd_time; 35462306a36Sopenharmony_ci p->timer[4] = cmd_time*2+4; 35562306a36Sopenharmony_ci } 35662306a36Sopenharmony_ci if (p->timer[1] == 0) { 35762306a36Sopenharmony_ci p->timer[1] = 6; p->timer[4] = 16; 35862306a36Sopenharmony_ci if (p->timer[0] == 0) 35962306a36Sopenharmony_ci p->timer[0] = p->timer[3] = 1; 36062306a36Sopenharmony_ci } 36162306a36Sopenharmony_ci if (recov_time >= 0) 36262306a36Sopenharmony_ci p->timer[2] = p->timer[5] = recov_time; 36362306a36Sopenharmony_ci buf += strlen(buf); 36462306a36Sopenharmony_ci sprintf(buf, " [%d/%d/%d] [%d/%d/%d]", p->timer[0], p->timer[1], 36562306a36Sopenharmony_ci p->timer[2], p->timer[3], p->timer[4], p->timer[5]); 36662306a36Sopenharmony_ci } 36762306a36Sopenharmony_ci return mask; 36862306a36Sopenharmony_ci} 36962306a36Sopenharmony_ci 37062306a36Sopenharmony_ci/*====================================================================== 37162306a36Sopenharmony_ci 37262306a36Sopenharmony_ci Code to save and restore global state information for Vadem VG468 37362306a36Sopenharmony_ci and VG469 controllers, and to set and report global configuration 37462306a36Sopenharmony_ci options. 37562306a36Sopenharmony_ci 37662306a36Sopenharmony_ci======================================================================*/ 37762306a36Sopenharmony_ci 37862306a36Sopenharmony_cistatic void vg46x_get_state(u_short s) 37962306a36Sopenharmony_ci{ 38062306a36Sopenharmony_ci struct vg46x_state *p = &socket[s].state.vg46x; 38162306a36Sopenharmony_ci p->ctl = i365_get(s, VG468_CTL); 38262306a36Sopenharmony_ci if (socket[s].type == IS_VG469) 38362306a36Sopenharmony_ci p->ema = i365_get(s, VG469_EXT_MODE); 38462306a36Sopenharmony_ci} 38562306a36Sopenharmony_ci 38662306a36Sopenharmony_cistatic void vg46x_set_state(u_short s) 38762306a36Sopenharmony_ci{ 38862306a36Sopenharmony_ci struct vg46x_state *p = &socket[s].state.vg46x; 38962306a36Sopenharmony_ci i365_set(s, VG468_CTL, p->ctl); 39062306a36Sopenharmony_ci if (socket[s].type == IS_VG469) 39162306a36Sopenharmony_ci i365_set(s, VG469_EXT_MODE, p->ema); 39262306a36Sopenharmony_ci} 39362306a36Sopenharmony_ci 39462306a36Sopenharmony_cistatic u_int __init vg46x_set_opts(u_short s, char *buf) 39562306a36Sopenharmony_ci{ 39662306a36Sopenharmony_ci struct vg46x_state *p = &socket[s].state.vg46x; 39762306a36Sopenharmony_ci 39862306a36Sopenharmony_ci flip(p->ctl, VG468_CTL_ASYNC, async_clock); 39962306a36Sopenharmony_ci flip(p->ema, VG469_MODE_CABLE, cable_mode); 40062306a36Sopenharmony_ci if (p->ctl & VG468_CTL_ASYNC) 40162306a36Sopenharmony_ci strcat(buf, " [async]"); 40262306a36Sopenharmony_ci if (p->ctl & VG468_CTL_INPACK) 40362306a36Sopenharmony_ci strcat(buf, " [inpack]"); 40462306a36Sopenharmony_ci if (socket[s].type == IS_VG469) { 40562306a36Sopenharmony_ci u_char vsel = i365_get(s, VG469_VSELECT); 40662306a36Sopenharmony_ci if (vsel & VG469_VSEL_EXT_STAT) { 40762306a36Sopenharmony_ci strcat(buf, " [ext mode]"); 40862306a36Sopenharmony_ci if (vsel & VG469_VSEL_EXT_BUS) 40962306a36Sopenharmony_ci strcat(buf, " [isa buf]"); 41062306a36Sopenharmony_ci } 41162306a36Sopenharmony_ci if (p->ema & VG469_MODE_CABLE) 41262306a36Sopenharmony_ci strcat(buf, " [cable]"); 41362306a36Sopenharmony_ci if (p->ema & VG469_MODE_COMPAT) 41462306a36Sopenharmony_ci strcat(buf, " [c step]"); 41562306a36Sopenharmony_ci } 41662306a36Sopenharmony_ci return 0xffff; 41762306a36Sopenharmony_ci} 41862306a36Sopenharmony_ci 41962306a36Sopenharmony_ci/*====================================================================== 42062306a36Sopenharmony_ci 42162306a36Sopenharmony_ci Generic routines to get and set controller options 42262306a36Sopenharmony_ci 42362306a36Sopenharmony_ci======================================================================*/ 42462306a36Sopenharmony_ci 42562306a36Sopenharmony_cistatic void get_bridge_state(u_short s) 42662306a36Sopenharmony_ci{ 42762306a36Sopenharmony_ci struct i82365_socket *t = &socket[s]; 42862306a36Sopenharmony_ci if (t->flags & IS_CIRRUS) 42962306a36Sopenharmony_ci cirrus_get_state(s); 43062306a36Sopenharmony_ci else if (t->flags & IS_VADEM) 43162306a36Sopenharmony_ci vg46x_get_state(s); 43262306a36Sopenharmony_ci} 43362306a36Sopenharmony_ci 43462306a36Sopenharmony_cistatic void set_bridge_state(u_short s) 43562306a36Sopenharmony_ci{ 43662306a36Sopenharmony_ci struct i82365_socket *t = &socket[s]; 43762306a36Sopenharmony_ci if (t->flags & IS_CIRRUS) 43862306a36Sopenharmony_ci cirrus_set_state(s); 43962306a36Sopenharmony_ci else { 44062306a36Sopenharmony_ci i365_set(s, I365_GBLCTL, 0x00); 44162306a36Sopenharmony_ci i365_set(s, I365_GENCTL, 0x00); 44262306a36Sopenharmony_ci } 44362306a36Sopenharmony_ci i365_bflip(s, I365_INTCTL, I365_INTR_ENA, t->intr); 44462306a36Sopenharmony_ci if (t->flags & IS_VADEM) 44562306a36Sopenharmony_ci vg46x_set_state(s); 44662306a36Sopenharmony_ci} 44762306a36Sopenharmony_ci 44862306a36Sopenharmony_cistatic u_int __init set_bridge_opts(u_short s, u_short ns) 44962306a36Sopenharmony_ci{ 45062306a36Sopenharmony_ci u_short i; 45162306a36Sopenharmony_ci u_int m = 0xffff; 45262306a36Sopenharmony_ci char buf[128]; 45362306a36Sopenharmony_ci 45462306a36Sopenharmony_ci for (i = s; i < s+ns; i++) { 45562306a36Sopenharmony_ci if (socket[i].flags & IS_ALIVE) { 45662306a36Sopenharmony_ci printk(KERN_INFO " host opts [%d]: already alive!\n", i); 45762306a36Sopenharmony_ci continue; 45862306a36Sopenharmony_ci } 45962306a36Sopenharmony_ci buf[0] = '\0'; 46062306a36Sopenharmony_ci get_bridge_state(i); 46162306a36Sopenharmony_ci if (socket[i].flags & IS_CIRRUS) 46262306a36Sopenharmony_ci m = cirrus_set_opts(i, buf); 46362306a36Sopenharmony_ci else if (socket[i].flags & IS_VADEM) 46462306a36Sopenharmony_ci m = vg46x_set_opts(i, buf); 46562306a36Sopenharmony_ci set_bridge_state(i); 46662306a36Sopenharmony_ci printk(KERN_INFO " host opts [%d]:%s\n", i, 46762306a36Sopenharmony_ci (*buf) ? buf : " none"); 46862306a36Sopenharmony_ci } 46962306a36Sopenharmony_ci return m; 47062306a36Sopenharmony_ci} 47162306a36Sopenharmony_ci 47262306a36Sopenharmony_ci/*====================================================================== 47362306a36Sopenharmony_ci 47462306a36Sopenharmony_ci Interrupt testing code, for ISA and PCI interrupts 47562306a36Sopenharmony_ci 47662306a36Sopenharmony_ci======================================================================*/ 47762306a36Sopenharmony_ci 47862306a36Sopenharmony_cistatic volatile u_int irq_hits; 47962306a36Sopenharmony_cistatic u_short irq_sock; 48062306a36Sopenharmony_ci 48162306a36Sopenharmony_cistatic irqreturn_t i365_count_irq(int irq, void *dev) 48262306a36Sopenharmony_ci{ 48362306a36Sopenharmony_ci i365_get(irq_sock, I365_CSC); 48462306a36Sopenharmony_ci irq_hits++; 48562306a36Sopenharmony_ci pr_debug("i82365: -> hit on irq %d\n", irq); 48662306a36Sopenharmony_ci return IRQ_HANDLED; 48762306a36Sopenharmony_ci} 48862306a36Sopenharmony_ci 48962306a36Sopenharmony_cistatic u_int __init test_irq(u_short sock, int irq) 49062306a36Sopenharmony_ci{ 49162306a36Sopenharmony_ci pr_debug("i82365: testing ISA irq %d\n", irq); 49262306a36Sopenharmony_ci if (request_irq(irq, i365_count_irq, IRQF_PROBE_SHARED, "scan", 49362306a36Sopenharmony_ci i365_count_irq) != 0) 49462306a36Sopenharmony_ci return 1; 49562306a36Sopenharmony_ci irq_hits = 0; irq_sock = sock; 49662306a36Sopenharmony_ci msleep(10); 49762306a36Sopenharmony_ci if (irq_hits) { 49862306a36Sopenharmony_ci free_irq(irq, i365_count_irq); 49962306a36Sopenharmony_ci pr_debug("i82365: spurious hit!\n"); 50062306a36Sopenharmony_ci return 1; 50162306a36Sopenharmony_ci } 50262306a36Sopenharmony_ci 50362306a36Sopenharmony_ci /* Generate one interrupt */ 50462306a36Sopenharmony_ci i365_set(sock, I365_CSCINT, I365_CSC_DETECT | (irq << 4)); 50562306a36Sopenharmony_ci i365_bset(sock, I365_GENCTL, I365_CTL_SW_IRQ); 50662306a36Sopenharmony_ci udelay(1000); 50762306a36Sopenharmony_ci 50862306a36Sopenharmony_ci free_irq(irq, i365_count_irq); 50962306a36Sopenharmony_ci 51062306a36Sopenharmony_ci /* mask all interrupts */ 51162306a36Sopenharmony_ci i365_set(sock, I365_CSCINT, 0); 51262306a36Sopenharmony_ci pr_debug("i82365: hits = %d\n", irq_hits); 51362306a36Sopenharmony_ci 51462306a36Sopenharmony_ci return (irq_hits != 1); 51562306a36Sopenharmony_ci} 51662306a36Sopenharmony_ci 51762306a36Sopenharmony_cistatic u_int __init isa_scan(u_short sock, u_int mask0) 51862306a36Sopenharmony_ci{ 51962306a36Sopenharmony_ci u_int mask1 = 0; 52062306a36Sopenharmony_ci int i; 52162306a36Sopenharmony_ci 52262306a36Sopenharmony_ci#ifdef __alpha__ 52362306a36Sopenharmony_ci#define PIC 0x4d0 52462306a36Sopenharmony_ci /* Don't probe level-triggered interrupts -- reserved for PCI */ 52562306a36Sopenharmony_ci mask0 &= ~(inb(PIC) | (inb(PIC+1) << 8)); 52662306a36Sopenharmony_ci#endif 52762306a36Sopenharmony_ci 52862306a36Sopenharmony_ci if (do_scan) { 52962306a36Sopenharmony_ci set_bridge_state(sock); 53062306a36Sopenharmony_ci i365_set(sock, I365_CSCINT, 0); 53162306a36Sopenharmony_ci for (i = 0; i < 16; i++) 53262306a36Sopenharmony_ci if ((mask0 & (1 << i)) && (test_irq(sock, i) == 0)) 53362306a36Sopenharmony_ci mask1 |= (1 << i); 53462306a36Sopenharmony_ci for (i = 0; i < 16; i++) 53562306a36Sopenharmony_ci if ((mask1 & (1 << i)) && (test_irq(sock, i) != 0)) 53662306a36Sopenharmony_ci mask1 ^= (1 << i); 53762306a36Sopenharmony_ci } 53862306a36Sopenharmony_ci 53962306a36Sopenharmony_ci printk(KERN_INFO " ISA irqs ("); 54062306a36Sopenharmony_ci if (mask1) { 54162306a36Sopenharmony_ci printk("scanned"); 54262306a36Sopenharmony_ci } else { 54362306a36Sopenharmony_ci /* Fallback: just find interrupts that aren't in use */ 54462306a36Sopenharmony_ci for (i = 0; i < 16; i++) 54562306a36Sopenharmony_ci if ((mask0 & (1 << i)) && (_check_irq(i, IRQF_PROBE_SHARED) == 0)) 54662306a36Sopenharmony_ci mask1 |= (1 << i); 54762306a36Sopenharmony_ci printk("default"); 54862306a36Sopenharmony_ci /* If scan failed, default to polled status */ 54962306a36Sopenharmony_ci if (!cs_irq && (poll_interval == 0)) poll_interval = HZ; 55062306a36Sopenharmony_ci } 55162306a36Sopenharmony_ci printk(") = "); 55262306a36Sopenharmony_ci 55362306a36Sopenharmony_ci for (i = 0; i < 16; i++) 55462306a36Sopenharmony_ci if (mask1 & (1<<i)) 55562306a36Sopenharmony_ci printk("%s%d", ((mask1 & ((1<<i)-1)) ? "," : ""), i); 55662306a36Sopenharmony_ci if (mask1 == 0) printk("none!"); 55762306a36Sopenharmony_ci 55862306a36Sopenharmony_ci return mask1; 55962306a36Sopenharmony_ci} 56062306a36Sopenharmony_ci 56162306a36Sopenharmony_ci/*====================================================================*/ 56262306a36Sopenharmony_ci 56362306a36Sopenharmony_ci/* Time conversion functions */ 56462306a36Sopenharmony_ci 56562306a36Sopenharmony_cistatic int to_cycles(int ns) 56662306a36Sopenharmony_ci{ 56762306a36Sopenharmony_ci return ns/cycle_time; 56862306a36Sopenharmony_ci} 56962306a36Sopenharmony_ci 57062306a36Sopenharmony_ci/*====================================================================*/ 57162306a36Sopenharmony_ci 57262306a36Sopenharmony_cistatic int __init identify(unsigned int port, u_short sock) 57362306a36Sopenharmony_ci{ 57462306a36Sopenharmony_ci u_char val; 57562306a36Sopenharmony_ci int type = -1; 57662306a36Sopenharmony_ci 57762306a36Sopenharmony_ci /* Use the next free entry in the socket table */ 57862306a36Sopenharmony_ci socket[sockets].ioaddr = port; 57962306a36Sopenharmony_ci socket[sockets].psock = sock; 58062306a36Sopenharmony_ci 58162306a36Sopenharmony_ci /* Wake up a sleepy Cirrus controller */ 58262306a36Sopenharmony_ci if (wakeup) { 58362306a36Sopenharmony_ci i365_bclr(sockets, PD67_MISC_CTL_2, PD67_MC2_SUSPEND); 58462306a36Sopenharmony_ci /* Pause at least 50 ms */ 58562306a36Sopenharmony_ci mdelay(50); 58662306a36Sopenharmony_ci } 58762306a36Sopenharmony_ci 58862306a36Sopenharmony_ci if ((val = i365_get(sockets, I365_IDENT)) & 0x70) 58962306a36Sopenharmony_ci return -1; 59062306a36Sopenharmony_ci switch (val) { 59162306a36Sopenharmony_ci case 0x82: 59262306a36Sopenharmony_ci type = IS_I82365A; break; 59362306a36Sopenharmony_ci case 0x83: 59462306a36Sopenharmony_ci type = IS_I82365B; break; 59562306a36Sopenharmony_ci case 0x84: 59662306a36Sopenharmony_ci type = IS_I82365DF; break; 59762306a36Sopenharmony_ci case 0x88: case 0x89: case 0x8a: 59862306a36Sopenharmony_ci type = IS_IBM; break; 59962306a36Sopenharmony_ci } 60062306a36Sopenharmony_ci 60162306a36Sopenharmony_ci /* Check for Vadem VG-468 chips */ 60262306a36Sopenharmony_ci outb(0x0e, port); 60362306a36Sopenharmony_ci outb(0x37, port); 60462306a36Sopenharmony_ci i365_bset(sockets, VG468_MISC, VG468_MISC_VADEMREV); 60562306a36Sopenharmony_ci val = i365_get(sockets, I365_IDENT); 60662306a36Sopenharmony_ci if (val & I365_IDENT_VADEM) { 60762306a36Sopenharmony_ci i365_bclr(sockets, VG468_MISC, VG468_MISC_VADEMREV); 60862306a36Sopenharmony_ci type = ((val & 7) >= 4) ? IS_VG469 : IS_VG468; 60962306a36Sopenharmony_ci } 61062306a36Sopenharmony_ci 61162306a36Sopenharmony_ci /* Check for Ricoh chips */ 61262306a36Sopenharmony_ci val = i365_get(sockets, RF5C_CHIP_ID); 61362306a36Sopenharmony_ci if ((val == RF5C_CHIP_RF5C296) || (val == RF5C_CHIP_RF5C396)) 61462306a36Sopenharmony_ci type = IS_RF5Cx96; 61562306a36Sopenharmony_ci 61662306a36Sopenharmony_ci /* Check for Cirrus CL-PD67xx chips */ 61762306a36Sopenharmony_ci i365_set(sockets, PD67_CHIP_INFO, 0); 61862306a36Sopenharmony_ci val = i365_get(sockets, PD67_CHIP_INFO); 61962306a36Sopenharmony_ci if ((val & PD67_INFO_CHIP_ID) == PD67_INFO_CHIP_ID) { 62062306a36Sopenharmony_ci val = i365_get(sockets, PD67_CHIP_INFO); 62162306a36Sopenharmony_ci if ((val & PD67_INFO_CHIP_ID) == 0) { 62262306a36Sopenharmony_ci type = (val & PD67_INFO_SLOTS) ? IS_PD672X : IS_PD6710; 62362306a36Sopenharmony_ci i365_set(sockets, PD67_EXT_INDEX, 0xe5); 62462306a36Sopenharmony_ci if (i365_get(sockets, PD67_EXT_INDEX) != 0xe5) 62562306a36Sopenharmony_ci type = IS_VT83C469; 62662306a36Sopenharmony_ci } 62762306a36Sopenharmony_ci } 62862306a36Sopenharmony_ci return type; 62962306a36Sopenharmony_ci} /* identify */ 63062306a36Sopenharmony_ci 63162306a36Sopenharmony_ci/*====================================================================== 63262306a36Sopenharmony_ci 63362306a36Sopenharmony_ci See if a card is present, powered up, in IO mode, and already 63462306a36Sopenharmony_ci bound to a (non PC Card) Linux driver. We leave these alone. 63562306a36Sopenharmony_ci 63662306a36Sopenharmony_ci We make an exception for cards that seem to be serial devices. 63762306a36Sopenharmony_ci 63862306a36Sopenharmony_ci======================================================================*/ 63962306a36Sopenharmony_ci 64062306a36Sopenharmony_cistatic int __init is_alive(u_short sock) 64162306a36Sopenharmony_ci{ 64262306a36Sopenharmony_ci u_char stat; 64362306a36Sopenharmony_ci unsigned int start, stop; 64462306a36Sopenharmony_ci 64562306a36Sopenharmony_ci stat = i365_get(sock, I365_STATUS); 64662306a36Sopenharmony_ci start = i365_get_pair(sock, I365_IO(0)+I365_W_START); 64762306a36Sopenharmony_ci stop = i365_get_pair(sock, I365_IO(0)+I365_W_STOP); 64862306a36Sopenharmony_ci if ((stat & I365_CS_DETECT) && (stat & I365_CS_POWERON) && 64962306a36Sopenharmony_ci (i365_get(sock, I365_INTCTL) & I365_PC_IOCARD) && 65062306a36Sopenharmony_ci (i365_get(sock, I365_ADDRWIN) & I365_ENA_IO(0)) && 65162306a36Sopenharmony_ci ((start & 0xfeef) != 0x02e8)) { 65262306a36Sopenharmony_ci if (!request_region(start, stop-start+1, "i82365")) 65362306a36Sopenharmony_ci return 1; 65462306a36Sopenharmony_ci release_region(start, stop-start+1); 65562306a36Sopenharmony_ci } 65662306a36Sopenharmony_ci 65762306a36Sopenharmony_ci return 0; 65862306a36Sopenharmony_ci} 65962306a36Sopenharmony_ci 66062306a36Sopenharmony_ci/*====================================================================*/ 66162306a36Sopenharmony_ci 66262306a36Sopenharmony_cistatic void __init add_socket(unsigned int port, int psock, int type) 66362306a36Sopenharmony_ci{ 66462306a36Sopenharmony_ci socket[sockets].ioaddr = port; 66562306a36Sopenharmony_ci socket[sockets].psock = psock; 66662306a36Sopenharmony_ci socket[sockets].type = type; 66762306a36Sopenharmony_ci socket[sockets].flags = pcic[type].flags; 66862306a36Sopenharmony_ci if (is_alive(sockets)) 66962306a36Sopenharmony_ci socket[sockets].flags |= IS_ALIVE; 67062306a36Sopenharmony_ci sockets++; 67162306a36Sopenharmony_ci} 67262306a36Sopenharmony_ci 67362306a36Sopenharmony_cistatic void __init add_pcic(int ns, int type) 67462306a36Sopenharmony_ci{ 67562306a36Sopenharmony_ci u_int mask = 0, i, base; 67662306a36Sopenharmony_ci int isa_irq = 0; 67762306a36Sopenharmony_ci struct i82365_socket *t = &socket[sockets-ns]; 67862306a36Sopenharmony_ci 67962306a36Sopenharmony_ci base = sockets-ns; 68062306a36Sopenharmony_ci if (base == 0) printk("\n"); 68162306a36Sopenharmony_ci printk(KERN_INFO " %s", pcic[type].name); 68262306a36Sopenharmony_ci printk(" ISA-to-PCMCIA at port %#x ofs 0x%02x", 68362306a36Sopenharmony_ci t->ioaddr, t->psock*0x40); 68462306a36Sopenharmony_ci printk(", %d socket%s\n", ns, ((ns > 1) ? "s" : "")); 68562306a36Sopenharmony_ci 68662306a36Sopenharmony_ci /* Set host options, build basic interrupt mask */ 68762306a36Sopenharmony_ci if (irq_list_count == 0) 68862306a36Sopenharmony_ci mask = irq_mask; 68962306a36Sopenharmony_ci else 69062306a36Sopenharmony_ci for (i = mask = 0; i < irq_list_count; i++) 69162306a36Sopenharmony_ci mask |= (1<<irq_list[i]); 69262306a36Sopenharmony_ci mask &= I365_MASK & set_bridge_opts(base, ns); 69362306a36Sopenharmony_ci /* Scan for ISA interrupts */ 69462306a36Sopenharmony_ci mask = isa_scan(base, mask); 69562306a36Sopenharmony_ci 69662306a36Sopenharmony_ci /* Poll if only two interrupts available */ 69762306a36Sopenharmony_ci if (!poll_interval) { 69862306a36Sopenharmony_ci u_int tmp = (mask & 0xff20); 69962306a36Sopenharmony_ci tmp = tmp & (tmp-1); 70062306a36Sopenharmony_ci if ((tmp & (tmp-1)) == 0) 70162306a36Sopenharmony_ci poll_interval = HZ; 70262306a36Sopenharmony_ci } 70362306a36Sopenharmony_ci /* Only try an ISA cs_irq if this is the first controller */ 70462306a36Sopenharmony_ci if (!grab_irq && (cs_irq || !poll_interval)) { 70562306a36Sopenharmony_ci /* Avoid irq 12 unless it is explicitly requested */ 70662306a36Sopenharmony_ci u_int cs_mask = mask & ((cs_irq) ? (1<<cs_irq) : ~(1<<12)); 70762306a36Sopenharmony_ci for (cs_irq = 15; cs_irq > 0; cs_irq--) 70862306a36Sopenharmony_ci if ((cs_mask & (1 << cs_irq)) && 70962306a36Sopenharmony_ci (_check_irq(cs_irq, IRQF_PROBE_SHARED) == 0)) 71062306a36Sopenharmony_ci break; 71162306a36Sopenharmony_ci if (cs_irq) { 71262306a36Sopenharmony_ci grab_irq = 1; 71362306a36Sopenharmony_ci isa_irq = cs_irq; 71462306a36Sopenharmony_ci printk(" status change on irq %d\n", cs_irq); 71562306a36Sopenharmony_ci } 71662306a36Sopenharmony_ci } 71762306a36Sopenharmony_ci 71862306a36Sopenharmony_ci if (!isa_irq) { 71962306a36Sopenharmony_ci if (poll_interval == 0) 72062306a36Sopenharmony_ci poll_interval = HZ; 72162306a36Sopenharmony_ci printk(" polling interval = %d ms\n", 72262306a36Sopenharmony_ci poll_interval * 1000 / HZ); 72362306a36Sopenharmony_ci 72462306a36Sopenharmony_ci } 72562306a36Sopenharmony_ci 72662306a36Sopenharmony_ci /* Update socket interrupt information, capabilities */ 72762306a36Sopenharmony_ci for (i = 0; i < ns; i++) { 72862306a36Sopenharmony_ci t[i].socket.features |= SS_CAP_PCCARD; 72962306a36Sopenharmony_ci t[i].socket.map_size = 0x1000; 73062306a36Sopenharmony_ci t[i].socket.irq_mask = mask; 73162306a36Sopenharmony_ci t[i].cs_irq = isa_irq; 73262306a36Sopenharmony_ci } 73362306a36Sopenharmony_ci 73462306a36Sopenharmony_ci} /* add_pcic */ 73562306a36Sopenharmony_ci 73662306a36Sopenharmony_ci/*====================================================================*/ 73762306a36Sopenharmony_ci 73862306a36Sopenharmony_ci#ifdef CONFIG_PNP 73962306a36Sopenharmony_cistatic struct isapnp_device_id id_table[] __initdata = { 74062306a36Sopenharmony_ci { ISAPNP_ANY_ID, ISAPNP_ANY_ID, ISAPNP_VENDOR('P', 'N', 'P'), 74162306a36Sopenharmony_ci ISAPNP_FUNCTION(0x0e00), (unsigned long) "Intel 82365-Compatible" }, 74262306a36Sopenharmony_ci { ISAPNP_ANY_ID, ISAPNP_ANY_ID, ISAPNP_VENDOR('P', 'N', 'P'), 74362306a36Sopenharmony_ci ISAPNP_FUNCTION(0x0e01), (unsigned long) "Cirrus Logic CL-PD6720" }, 74462306a36Sopenharmony_ci { ISAPNP_ANY_ID, ISAPNP_ANY_ID, ISAPNP_VENDOR('P', 'N', 'P'), 74562306a36Sopenharmony_ci ISAPNP_FUNCTION(0x0e02), (unsigned long) "VLSI VL82C146" }, 74662306a36Sopenharmony_ci { 0 } 74762306a36Sopenharmony_ci}; 74862306a36Sopenharmony_ciMODULE_DEVICE_TABLE(isapnp, id_table); 74962306a36Sopenharmony_ci 75062306a36Sopenharmony_cistatic struct pnp_dev *i82365_pnpdev; 75162306a36Sopenharmony_ci#endif 75262306a36Sopenharmony_ci 75362306a36Sopenharmony_cistatic void __init isa_probe(void) 75462306a36Sopenharmony_ci{ 75562306a36Sopenharmony_ci int i, j, sock, k, ns, id; 75662306a36Sopenharmony_ci unsigned int port; 75762306a36Sopenharmony_ci#ifdef CONFIG_PNP 75862306a36Sopenharmony_ci struct isapnp_device_id *devid; 75962306a36Sopenharmony_ci struct pnp_dev *dev; 76062306a36Sopenharmony_ci 76162306a36Sopenharmony_ci for (devid = id_table; devid->vendor; devid++) { 76262306a36Sopenharmony_ci if ((dev = pnp_find_dev(NULL, devid->vendor, devid->function, NULL))) { 76362306a36Sopenharmony_ci 76462306a36Sopenharmony_ci if (pnp_device_attach(dev) < 0) 76562306a36Sopenharmony_ci continue; 76662306a36Sopenharmony_ci 76762306a36Sopenharmony_ci if (pnp_activate_dev(dev) < 0) { 76862306a36Sopenharmony_ci printk("activate failed\n"); 76962306a36Sopenharmony_ci pnp_device_detach(dev); 77062306a36Sopenharmony_ci break; 77162306a36Sopenharmony_ci } 77262306a36Sopenharmony_ci 77362306a36Sopenharmony_ci if (!pnp_port_valid(dev, 0)) { 77462306a36Sopenharmony_ci printk("invalid resources ?\n"); 77562306a36Sopenharmony_ci pnp_device_detach(dev); 77662306a36Sopenharmony_ci break; 77762306a36Sopenharmony_ci } 77862306a36Sopenharmony_ci i365_base = pnp_port_start(dev, 0); 77962306a36Sopenharmony_ci i82365_pnpdev = dev; 78062306a36Sopenharmony_ci break; 78162306a36Sopenharmony_ci } 78262306a36Sopenharmony_ci } 78362306a36Sopenharmony_ci#endif 78462306a36Sopenharmony_ci 78562306a36Sopenharmony_ci if (!request_region(i365_base, 2, "i82365")) { 78662306a36Sopenharmony_ci if (sockets == 0) 78762306a36Sopenharmony_ci printk("port conflict at %#lx\n", i365_base); 78862306a36Sopenharmony_ci return; 78962306a36Sopenharmony_ci } 79062306a36Sopenharmony_ci 79162306a36Sopenharmony_ci id = identify(i365_base, 0); 79262306a36Sopenharmony_ci if ((id == IS_I82365DF) && (identify(i365_base, 1) != id)) { 79362306a36Sopenharmony_ci for (i = 0; i < 4; i++) { 79462306a36Sopenharmony_ci if (i == ignore) continue; 79562306a36Sopenharmony_ci port = i365_base + ((i & 1) << 2) + ((i & 2) << 1); 79662306a36Sopenharmony_ci sock = (i & 1) << 1; 79762306a36Sopenharmony_ci if (identify(port, sock) == IS_I82365DF) { 79862306a36Sopenharmony_ci add_socket(port, sock, IS_VLSI); 79962306a36Sopenharmony_ci add_pcic(1, IS_VLSI); 80062306a36Sopenharmony_ci } 80162306a36Sopenharmony_ci } 80262306a36Sopenharmony_ci } else { 80362306a36Sopenharmony_ci for (i = 0; i < 8; i += 2) { 80462306a36Sopenharmony_ci if (sockets && !extra_sockets && (i == 4)) 80562306a36Sopenharmony_ci break; 80662306a36Sopenharmony_ci port = i365_base + 2*(i>>2); 80762306a36Sopenharmony_ci sock = (i & 3); 80862306a36Sopenharmony_ci id = identify(port, sock); 80962306a36Sopenharmony_ci if (id < 0) continue; 81062306a36Sopenharmony_ci 81162306a36Sopenharmony_ci for (j = ns = 0; j < 2; j++) { 81262306a36Sopenharmony_ci /* Does the socket exist? */ 81362306a36Sopenharmony_ci if ((ignore == i+j) || (identify(port, sock+j) < 0)) 81462306a36Sopenharmony_ci continue; 81562306a36Sopenharmony_ci /* Check for bad socket decode */ 81662306a36Sopenharmony_ci for (k = 0; k <= sockets; k++) 81762306a36Sopenharmony_ci i365_set(k, I365_MEM(0)+I365_W_OFF, k); 81862306a36Sopenharmony_ci for (k = 0; k <= sockets; k++) 81962306a36Sopenharmony_ci if (i365_get(k, I365_MEM(0)+I365_W_OFF) != k) 82062306a36Sopenharmony_ci break; 82162306a36Sopenharmony_ci if (k <= sockets) break; 82262306a36Sopenharmony_ci add_socket(port, sock+j, id); ns++; 82362306a36Sopenharmony_ci } 82462306a36Sopenharmony_ci if (ns != 0) add_pcic(ns, id); 82562306a36Sopenharmony_ci } 82662306a36Sopenharmony_ci } 82762306a36Sopenharmony_ci} 82862306a36Sopenharmony_ci 82962306a36Sopenharmony_ci/*====================================================================*/ 83062306a36Sopenharmony_ci 83162306a36Sopenharmony_cistatic irqreturn_t pcic_interrupt(int irq, void *dev) 83262306a36Sopenharmony_ci{ 83362306a36Sopenharmony_ci int i, j, csc; 83462306a36Sopenharmony_ci u_int events, active; 83562306a36Sopenharmony_ci u_long flags = 0; 83662306a36Sopenharmony_ci int handled = 0; 83762306a36Sopenharmony_ci 83862306a36Sopenharmony_ci pr_debug("pcic_interrupt(%d)\n", irq); 83962306a36Sopenharmony_ci 84062306a36Sopenharmony_ci for (j = 0; j < 20; j++) { 84162306a36Sopenharmony_ci active = 0; 84262306a36Sopenharmony_ci for (i = 0; i < sockets; i++) { 84362306a36Sopenharmony_ci if (socket[i].cs_irq != irq) 84462306a36Sopenharmony_ci continue; 84562306a36Sopenharmony_ci handled = 1; 84662306a36Sopenharmony_ci ISA_LOCK(i, flags); 84762306a36Sopenharmony_ci csc = i365_get(i, I365_CSC); 84862306a36Sopenharmony_ci if ((csc == 0) || (i365_get(i, I365_IDENT) & 0x70)) { 84962306a36Sopenharmony_ci ISA_UNLOCK(i, flags); 85062306a36Sopenharmony_ci continue; 85162306a36Sopenharmony_ci } 85262306a36Sopenharmony_ci events = (csc & I365_CSC_DETECT) ? SS_DETECT : 0; 85362306a36Sopenharmony_ci 85462306a36Sopenharmony_ci if (i365_get(i, I365_INTCTL) & I365_PC_IOCARD) 85562306a36Sopenharmony_ci events |= (csc & I365_CSC_STSCHG) ? SS_STSCHG : 0; 85662306a36Sopenharmony_ci else { 85762306a36Sopenharmony_ci events |= (csc & I365_CSC_BVD1) ? SS_BATDEAD : 0; 85862306a36Sopenharmony_ci events |= (csc & I365_CSC_BVD2) ? SS_BATWARN : 0; 85962306a36Sopenharmony_ci events |= (csc & I365_CSC_READY) ? SS_READY : 0; 86062306a36Sopenharmony_ci } 86162306a36Sopenharmony_ci ISA_UNLOCK(i, flags); 86262306a36Sopenharmony_ci pr_debug("socket %d event 0x%02x\n", i, events); 86362306a36Sopenharmony_ci 86462306a36Sopenharmony_ci if (events) 86562306a36Sopenharmony_ci pcmcia_parse_events(&socket[i].socket, events); 86662306a36Sopenharmony_ci 86762306a36Sopenharmony_ci active |= events; 86862306a36Sopenharmony_ci } 86962306a36Sopenharmony_ci if (!active) break; 87062306a36Sopenharmony_ci } 87162306a36Sopenharmony_ci if (j == 20) 87262306a36Sopenharmony_ci printk(KERN_NOTICE "i82365: infinite loop in interrupt handler\n"); 87362306a36Sopenharmony_ci 87462306a36Sopenharmony_ci pr_debug("pcic_interrupt done\n"); 87562306a36Sopenharmony_ci return IRQ_RETVAL(handled); 87662306a36Sopenharmony_ci} /* pcic_interrupt */ 87762306a36Sopenharmony_ci 87862306a36Sopenharmony_cistatic void pcic_interrupt_wrapper(struct timer_list *unused) 87962306a36Sopenharmony_ci{ 88062306a36Sopenharmony_ci pcic_interrupt(0, NULL); 88162306a36Sopenharmony_ci poll_timer.expires = jiffies + poll_interval; 88262306a36Sopenharmony_ci add_timer(&poll_timer); 88362306a36Sopenharmony_ci} 88462306a36Sopenharmony_ci 88562306a36Sopenharmony_ci/*====================================================================*/ 88662306a36Sopenharmony_ci 88762306a36Sopenharmony_cistatic int i365_get_status(u_short sock, u_int *value) 88862306a36Sopenharmony_ci{ 88962306a36Sopenharmony_ci u_int status; 89062306a36Sopenharmony_ci 89162306a36Sopenharmony_ci status = i365_get(sock, I365_STATUS); 89262306a36Sopenharmony_ci *value = ((status & I365_CS_DETECT) == I365_CS_DETECT) 89362306a36Sopenharmony_ci ? SS_DETECT : 0; 89462306a36Sopenharmony_ci 89562306a36Sopenharmony_ci if (i365_get(sock, I365_INTCTL) & I365_PC_IOCARD) 89662306a36Sopenharmony_ci *value |= (status & I365_CS_STSCHG) ? 0 : SS_STSCHG; 89762306a36Sopenharmony_ci else { 89862306a36Sopenharmony_ci *value |= (status & I365_CS_BVD1) ? 0 : SS_BATDEAD; 89962306a36Sopenharmony_ci *value |= (status & I365_CS_BVD2) ? 0 : SS_BATWARN; 90062306a36Sopenharmony_ci } 90162306a36Sopenharmony_ci *value |= (status & I365_CS_WRPROT) ? SS_WRPROT : 0; 90262306a36Sopenharmony_ci *value |= (status & I365_CS_READY) ? SS_READY : 0; 90362306a36Sopenharmony_ci *value |= (status & I365_CS_POWERON) ? SS_POWERON : 0; 90462306a36Sopenharmony_ci 90562306a36Sopenharmony_ci if (socket[sock].type == IS_VG469) { 90662306a36Sopenharmony_ci status = i365_get(sock, VG469_VSENSE); 90762306a36Sopenharmony_ci if (socket[sock].psock & 1) { 90862306a36Sopenharmony_ci *value |= (status & VG469_VSENSE_B_VS1) ? 0 : SS_3VCARD; 90962306a36Sopenharmony_ci *value |= (status & VG469_VSENSE_B_VS2) ? 0 : SS_XVCARD; 91062306a36Sopenharmony_ci } else { 91162306a36Sopenharmony_ci *value |= (status & VG469_VSENSE_A_VS1) ? 0 : SS_3VCARD; 91262306a36Sopenharmony_ci *value |= (status & VG469_VSENSE_A_VS2) ? 0 : SS_XVCARD; 91362306a36Sopenharmony_ci } 91462306a36Sopenharmony_ci } 91562306a36Sopenharmony_ci 91662306a36Sopenharmony_ci pr_debug("GetStatus(%d) = %#4.4x\n", sock, *value); 91762306a36Sopenharmony_ci return 0; 91862306a36Sopenharmony_ci} /* i365_get_status */ 91962306a36Sopenharmony_ci 92062306a36Sopenharmony_ci/*====================================================================*/ 92162306a36Sopenharmony_ci 92262306a36Sopenharmony_cistatic int i365_set_socket(u_short sock, socket_state_t *state) 92362306a36Sopenharmony_ci{ 92462306a36Sopenharmony_ci struct i82365_socket *t = &socket[sock]; 92562306a36Sopenharmony_ci u_char reg; 92662306a36Sopenharmony_ci 92762306a36Sopenharmony_ci pr_debug("SetSocket(%d, flags %#3.3x, Vcc %d, Vpp %d, " 92862306a36Sopenharmony_ci "io_irq %d, csc_mask %#2.2x)\n", sock, state->flags, 92962306a36Sopenharmony_ci state->Vcc, state->Vpp, state->io_irq, state->csc_mask); 93062306a36Sopenharmony_ci 93162306a36Sopenharmony_ci /* First set global controller options */ 93262306a36Sopenharmony_ci set_bridge_state(sock); 93362306a36Sopenharmony_ci 93462306a36Sopenharmony_ci /* IO card, RESET flag, IO interrupt */ 93562306a36Sopenharmony_ci reg = t->intr; 93662306a36Sopenharmony_ci reg |= state->io_irq; 93762306a36Sopenharmony_ci reg |= (state->flags & SS_RESET) ? 0 : I365_PC_RESET; 93862306a36Sopenharmony_ci reg |= (state->flags & SS_IOCARD) ? I365_PC_IOCARD : 0; 93962306a36Sopenharmony_ci i365_set(sock, I365_INTCTL, reg); 94062306a36Sopenharmony_ci 94162306a36Sopenharmony_ci reg = I365_PWR_NORESET; 94262306a36Sopenharmony_ci if (state->flags & SS_PWR_AUTO) reg |= I365_PWR_AUTO; 94362306a36Sopenharmony_ci if (state->flags & SS_OUTPUT_ENA) reg |= I365_PWR_OUT; 94462306a36Sopenharmony_ci 94562306a36Sopenharmony_ci if (t->flags & IS_CIRRUS) { 94662306a36Sopenharmony_ci if (state->Vpp != 0) { 94762306a36Sopenharmony_ci if (state->Vpp == 120) 94862306a36Sopenharmony_ci reg |= I365_VPP1_12V; 94962306a36Sopenharmony_ci else if (state->Vpp == state->Vcc) 95062306a36Sopenharmony_ci reg |= I365_VPP1_5V; 95162306a36Sopenharmony_ci else return -EINVAL; 95262306a36Sopenharmony_ci } 95362306a36Sopenharmony_ci if (state->Vcc != 0) { 95462306a36Sopenharmony_ci reg |= I365_VCC_5V; 95562306a36Sopenharmony_ci if (state->Vcc == 33) 95662306a36Sopenharmony_ci i365_bset(sock, PD67_MISC_CTL_1, PD67_MC1_VCC_3V); 95762306a36Sopenharmony_ci else if (state->Vcc == 50) 95862306a36Sopenharmony_ci i365_bclr(sock, PD67_MISC_CTL_1, PD67_MC1_VCC_3V); 95962306a36Sopenharmony_ci else return -EINVAL; 96062306a36Sopenharmony_ci } 96162306a36Sopenharmony_ci } else if (t->flags & IS_VG_PWR) { 96262306a36Sopenharmony_ci if (state->Vpp != 0) { 96362306a36Sopenharmony_ci if (state->Vpp == 120) 96462306a36Sopenharmony_ci reg |= I365_VPP1_12V; 96562306a36Sopenharmony_ci else if (state->Vpp == state->Vcc) 96662306a36Sopenharmony_ci reg |= I365_VPP1_5V; 96762306a36Sopenharmony_ci else return -EINVAL; 96862306a36Sopenharmony_ci } 96962306a36Sopenharmony_ci if (state->Vcc != 0) { 97062306a36Sopenharmony_ci reg |= I365_VCC_5V; 97162306a36Sopenharmony_ci if (state->Vcc == 33) 97262306a36Sopenharmony_ci i365_bset(sock, VG469_VSELECT, VG469_VSEL_VCC); 97362306a36Sopenharmony_ci else if (state->Vcc == 50) 97462306a36Sopenharmony_ci i365_bclr(sock, VG469_VSELECT, VG469_VSEL_VCC); 97562306a36Sopenharmony_ci else return -EINVAL; 97662306a36Sopenharmony_ci } 97762306a36Sopenharmony_ci } else if (t->flags & IS_DF_PWR) { 97862306a36Sopenharmony_ci switch (state->Vcc) { 97962306a36Sopenharmony_ci case 0: break; 98062306a36Sopenharmony_ci case 33: reg |= I365_VCC_3V; break; 98162306a36Sopenharmony_ci case 50: reg |= I365_VCC_5V; break; 98262306a36Sopenharmony_ci default: return -EINVAL; 98362306a36Sopenharmony_ci } 98462306a36Sopenharmony_ci switch (state->Vpp) { 98562306a36Sopenharmony_ci case 0: break; 98662306a36Sopenharmony_ci case 50: reg |= I365_VPP1_5V; break; 98762306a36Sopenharmony_ci case 120: reg |= I365_VPP1_12V; break; 98862306a36Sopenharmony_ci default: return -EINVAL; 98962306a36Sopenharmony_ci } 99062306a36Sopenharmony_ci } else { 99162306a36Sopenharmony_ci switch (state->Vcc) { 99262306a36Sopenharmony_ci case 0: break; 99362306a36Sopenharmony_ci case 50: reg |= I365_VCC_5V; break; 99462306a36Sopenharmony_ci default: return -EINVAL; 99562306a36Sopenharmony_ci } 99662306a36Sopenharmony_ci switch (state->Vpp) { 99762306a36Sopenharmony_ci case 0: break; 99862306a36Sopenharmony_ci case 50: reg |= I365_VPP1_5V | I365_VPP2_5V; break; 99962306a36Sopenharmony_ci case 120: reg |= I365_VPP1_12V | I365_VPP2_12V; break; 100062306a36Sopenharmony_ci default: return -EINVAL; 100162306a36Sopenharmony_ci } 100262306a36Sopenharmony_ci } 100362306a36Sopenharmony_ci 100462306a36Sopenharmony_ci if (reg != i365_get(sock, I365_POWER)) 100562306a36Sopenharmony_ci i365_set(sock, I365_POWER, reg); 100662306a36Sopenharmony_ci 100762306a36Sopenharmony_ci /* Chipset-specific functions */ 100862306a36Sopenharmony_ci if (t->flags & IS_CIRRUS) { 100962306a36Sopenharmony_ci /* Speaker control */ 101062306a36Sopenharmony_ci i365_bflip(sock, PD67_MISC_CTL_1, PD67_MC1_SPKR_ENA, 101162306a36Sopenharmony_ci state->flags & SS_SPKR_ENA); 101262306a36Sopenharmony_ci } 101362306a36Sopenharmony_ci 101462306a36Sopenharmony_ci /* Card status change interrupt mask */ 101562306a36Sopenharmony_ci reg = t->cs_irq << 4; 101662306a36Sopenharmony_ci if (state->csc_mask & SS_DETECT) reg |= I365_CSC_DETECT; 101762306a36Sopenharmony_ci if (state->flags & SS_IOCARD) { 101862306a36Sopenharmony_ci if (state->csc_mask & SS_STSCHG) reg |= I365_CSC_STSCHG; 101962306a36Sopenharmony_ci } else { 102062306a36Sopenharmony_ci if (state->csc_mask & SS_BATDEAD) reg |= I365_CSC_BVD1; 102162306a36Sopenharmony_ci if (state->csc_mask & SS_BATWARN) reg |= I365_CSC_BVD2; 102262306a36Sopenharmony_ci if (state->csc_mask & SS_READY) reg |= I365_CSC_READY; 102362306a36Sopenharmony_ci } 102462306a36Sopenharmony_ci i365_set(sock, I365_CSCINT, reg); 102562306a36Sopenharmony_ci i365_get(sock, I365_CSC); 102662306a36Sopenharmony_ci 102762306a36Sopenharmony_ci return 0; 102862306a36Sopenharmony_ci} /* i365_set_socket */ 102962306a36Sopenharmony_ci 103062306a36Sopenharmony_ci/*====================================================================*/ 103162306a36Sopenharmony_ci 103262306a36Sopenharmony_cistatic int i365_set_io_map(u_short sock, struct pccard_io_map *io) 103362306a36Sopenharmony_ci{ 103462306a36Sopenharmony_ci u_char map, ioctl; 103562306a36Sopenharmony_ci 103662306a36Sopenharmony_ci pr_debug("SetIOMap(%d, %d, %#2.2x, %d ns, " 103762306a36Sopenharmony_ci "%#llx-%#llx)\n", sock, io->map, io->flags, io->speed, 103862306a36Sopenharmony_ci (unsigned long long)io->start, (unsigned long long)io->stop); 103962306a36Sopenharmony_ci map = io->map; 104062306a36Sopenharmony_ci if ((map > 1) || (io->start > 0xffff) || (io->stop > 0xffff) || 104162306a36Sopenharmony_ci (io->stop < io->start)) return -EINVAL; 104262306a36Sopenharmony_ci /* Turn off the window before changing anything */ 104362306a36Sopenharmony_ci if (i365_get(sock, I365_ADDRWIN) & I365_ENA_IO(map)) 104462306a36Sopenharmony_ci i365_bclr(sock, I365_ADDRWIN, I365_ENA_IO(map)); 104562306a36Sopenharmony_ci i365_set_pair(sock, I365_IO(map)+I365_W_START, io->start); 104662306a36Sopenharmony_ci i365_set_pair(sock, I365_IO(map)+I365_W_STOP, io->stop); 104762306a36Sopenharmony_ci ioctl = i365_get(sock, I365_IOCTL) & ~I365_IOCTL_MASK(map); 104862306a36Sopenharmony_ci if (io->speed) ioctl |= I365_IOCTL_WAIT(map); 104962306a36Sopenharmony_ci if (io->flags & MAP_0WS) ioctl |= I365_IOCTL_0WS(map); 105062306a36Sopenharmony_ci if (io->flags & MAP_16BIT) ioctl |= I365_IOCTL_16BIT(map); 105162306a36Sopenharmony_ci if (io->flags & MAP_AUTOSZ) ioctl |= I365_IOCTL_IOCS16(map); 105262306a36Sopenharmony_ci i365_set(sock, I365_IOCTL, ioctl); 105362306a36Sopenharmony_ci /* Turn on the window if necessary */ 105462306a36Sopenharmony_ci if (io->flags & MAP_ACTIVE) 105562306a36Sopenharmony_ci i365_bset(sock, I365_ADDRWIN, I365_ENA_IO(map)); 105662306a36Sopenharmony_ci return 0; 105762306a36Sopenharmony_ci} /* i365_set_io_map */ 105862306a36Sopenharmony_ci 105962306a36Sopenharmony_ci/*====================================================================*/ 106062306a36Sopenharmony_ci 106162306a36Sopenharmony_cistatic int i365_set_mem_map(u_short sock, struct pccard_mem_map *mem) 106262306a36Sopenharmony_ci{ 106362306a36Sopenharmony_ci u_short base, i; 106462306a36Sopenharmony_ci u_char map; 106562306a36Sopenharmony_ci 106662306a36Sopenharmony_ci pr_debug("SetMemMap(%d, %d, %#2.2x, %d ns, %#llx-%#llx, " 106762306a36Sopenharmony_ci "%#x)\n", sock, mem->map, mem->flags, mem->speed, 106862306a36Sopenharmony_ci (unsigned long long)mem->res->start, 106962306a36Sopenharmony_ci (unsigned long long)mem->res->end, mem->card_start); 107062306a36Sopenharmony_ci 107162306a36Sopenharmony_ci map = mem->map; 107262306a36Sopenharmony_ci if ((map > 4) || (mem->card_start > 0x3ffffff) || 107362306a36Sopenharmony_ci (mem->res->start > mem->res->end) || (mem->speed > 1000)) 107462306a36Sopenharmony_ci return -EINVAL; 107562306a36Sopenharmony_ci if ((mem->res->start > 0xffffff) || (mem->res->end > 0xffffff)) 107662306a36Sopenharmony_ci return -EINVAL; 107762306a36Sopenharmony_ci 107862306a36Sopenharmony_ci /* Turn off the window before changing anything */ 107962306a36Sopenharmony_ci if (i365_get(sock, I365_ADDRWIN) & I365_ENA_MEM(map)) 108062306a36Sopenharmony_ci i365_bclr(sock, I365_ADDRWIN, I365_ENA_MEM(map)); 108162306a36Sopenharmony_ci 108262306a36Sopenharmony_ci base = I365_MEM(map); 108362306a36Sopenharmony_ci i = (mem->res->start >> 12) & 0x0fff; 108462306a36Sopenharmony_ci if (mem->flags & MAP_16BIT) i |= I365_MEM_16BIT; 108562306a36Sopenharmony_ci if (mem->flags & MAP_0WS) i |= I365_MEM_0WS; 108662306a36Sopenharmony_ci i365_set_pair(sock, base+I365_W_START, i); 108762306a36Sopenharmony_ci 108862306a36Sopenharmony_ci i = (mem->res->end >> 12) & 0x0fff; 108962306a36Sopenharmony_ci switch (to_cycles(mem->speed)) { 109062306a36Sopenharmony_ci case 0: break; 109162306a36Sopenharmony_ci case 1: i |= I365_MEM_WS0; break; 109262306a36Sopenharmony_ci case 2: i |= I365_MEM_WS1; break; 109362306a36Sopenharmony_ci default: i |= I365_MEM_WS1 | I365_MEM_WS0; break; 109462306a36Sopenharmony_ci } 109562306a36Sopenharmony_ci i365_set_pair(sock, base+I365_W_STOP, i); 109662306a36Sopenharmony_ci 109762306a36Sopenharmony_ci i = ((mem->card_start - mem->res->start) >> 12) & 0x3fff; 109862306a36Sopenharmony_ci if (mem->flags & MAP_WRPROT) i |= I365_MEM_WRPROT; 109962306a36Sopenharmony_ci if (mem->flags & MAP_ATTRIB) i |= I365_MEM_REG; 110062306a36Sopenharmony_ci i365_set_pair(sock, base+I365_W_OFF, i); 110162306a36Sopenharmony_ci 110262306a36Sopenharmony_ci /* Turn on the window if necessary */ 110362306a36Sopenharmony_ci if (mem->flags & MAP_ACTIVE) 110462306a36Sopenharmony_ci i365_bset(sock, I365_ADDRWIN, I365_ENA_MEM(map)); 110562306a36Sopenharmony_ci return 0; 110662306a36Sopenharmony_ci} /* i365_set_mem_map */ 110762306a36Sopenharmony_ci 110862306a36Sopenharmony_ci#if 0 /* driver model ordering issue */ 110962306a36Sopenharmony_ci/*====================================================================== 111062306a36Sopenharmony_ci 111162306a36Sopenharmony_ci Routines for accessing socket information and register dumps via 111262306a36Sopenharmony_ci /sys/class/pcmcia_socket/... 111362306a36Sopenharmony_ci 111462306a36Sopenharmony_ci======================================================================*/ 111562306a36Sopenharmony_ci 111662306a36Sopenharmony_cistatic ssize_t show_info(struct class_device *class_dev, char *buf) 111762306a36Sopenharmony_ci{ 111862306a36Sopenharmony_ci struct i82365_socket *s = container_of(class_dev, struct i82365_socket, socket.dev); 111962306a36Sopenharmony_ci return sprintf(buf, "type: %s\npsock: %d\n", 112062306a36Sopenharmony_ci pcic[s->type].name, s->psock); 112162306a36Sopenharmony_ci} 112262306a36Sopenharmony_ci 112362306a36Sopenharmony_cistatic ssize_t show_exca(struct class_device *class_dev, char *buf) 112462306a36Sopenharmony_ci{ 112562306a36Sopenharmony_ci struct i82365_socket *s = container_of(class_dev, struct i82365_socket, socket.dev); 112662306a36Sopenharmony_ci unsigned short sock; 112762306a36Sopenharmony_ci int i; 112862306a36Sopenharmony_ci ssize_t ret = 0; 112962306a36Sopenharmony_ci unsigned long flags = 0; 113062306a36Sopenharmony_ci 113162306a36Sopenharmony_ci sock = s->number; 113262306a36Sopenharmony_ci 113362306a36Sopenharmony_ci ISA_LOCK(sock, flags); 113462306a36Sopenharmony_ci for (i = 0; i < 0x40; i += 4) { 113562306a36Sopenharmony_ci ret += sprintf(buf, "%02x %02x %02x %02x%s", 113662306a36Sopenharmony_ci i365_get(sock,i), i365_get(sock,i+1), 113762306a36Sopenharmony_ci i365_get(sock,i+2), i365_get(sock,i+3), 113862306a36Sopenharmony_ci ((i % 16) == 12) ? "\n" : " "); 113962306a36Sopenharmony_ci buf += ret; 114062306a36Sopenharmony_ci } 114162306a36Sopenharmony_ci ISA_UNLOCK(sock, flags); 114262306a36Sopenharmony_ci 114362306a36Sopenharmony_ci return ret; 114462306a36Sopenharmony_ci} 114562306a36Sopenharmony_ci 114662306a36Sopenharmony_cistatic CLASS_DEVICE_ATTR(exca, S_IRUGO, show_exca, NULL); 114762306a36Sopenharmony_cistatic CLASS_DEVICE_ATTR(info, S_IRUGO, show_info, NULL); 114862306a36Sopenharmony_ci#endif 114962306a36Sopenharmony_ci 115062306a36Sopenharmony_ci/*====================================================================*/ 115162306a36Sopenharmony_ci 115262306a36Sopenharmony_ci/* this is horribly ugly... proper locking needs to be done here at 115362306a36Sopenharmony_ci * some time... */ 115462306a36Sopenharmony_ci#define LOCKED(x) do { \ 115562306a36Sopenharmony_ci int retval; \ 115662306a36Sopenharmony_ci unsigned long flags; \ 115762306a36Sopenharmony_ci spin_lock_irqsave(&isa_lock, flags); \ 115862306a36Sopenharmony_ci retval = x; \ 115962306a36Sopenharmony_ci spin_unlock_irqrestore(&isa_lock, flags); \ 116062306a36Sopenharmony_ci return retval; \ 116162306a36Sopenharmony_ci} while (0) 116262306a36Sopenharmony_ci 116362306a36Sopenharmony_ci 116462306a36Sopenharmony_cistatic int pcic_get_status(struct pcmcia_socket *s, u_int *value) 116562306a36Sopenharmony_ci{ 116662306a36Sopenharmony_ci unsigned int sock = container_of(s, struct i82365_socket, socket)->number; 116762306a36Sopenharmony_ci 116862306a36Sopenharmony_ci if (socket[sock].flags & IS_ALIVE) { 116962306a36Sopenharmony_ci *value = 0; 117062306a36Sopenharmony_ci return -EINVAL; 117162306a36Sopenharmony_ci } 117262306a36Sopenharmony_ci 117362306a36Sopenharmony_ci LOCKED(i365_get_status(sock, value)); 117462306a36Sopenharmony_ci} 117562306a36Sopenharmony_ci 117662306a36Sopenharmony_cistatic int pcic_set_socket(struct pcmcia_socket *s, socket_state_t *state) 117762306a36Sopenharmony_ci{ 117862306a36Sopenharmony_ci unsigned int sock = container_of(s, struct i82365_socket, socket)->number; 117962306a36Sopenharmony_ci 118062306a36Sopenharmony_ci if (socket[sock].flags & IS_ALIVE) 118162306a36Sopenharmony_ci return -EINVAL; 118262306a36Sopenharmony_ci 118362306a36Sopenharmony_ci LOCKED(i365_set_socket(sock, state)); 118462306a36Sopenharmony_ci} 118562306a36Sopenharmony_ci 118662306a36Sopenharmony_cistatic int pcic_set_io_map(struct pcmcia_socket *s, struct pccard_io_map *io) 118762306a36Sopenharmony_ci{ 118862306a36Sopenharmony_ci unsigned int sock = container_of(s, struct i82365_socket, socket)->number; 118962306a36Sopenharmony_ci if (socket[sock].flags & IS_ALIVE) 119062306a36Sopenharmony_ci return -EINVAL; 119162306a36Sopenharmony_ci 119262306a36Sopenharmony_ci LOCKED(i365_set_io_map(sock, io)); 119362306a36Sopenharmony_ci} 119462306a36Sopenharmony_ci 119562306a36Sopenharmony_cistatic int pcic_set_mem_map(struct pcmcia_socket *s, struct pccard_mem_map *mem) 119662306a36Sopenharmony_ci{ 119762306a36Sopenharmony_ci unsigned int sock = container_of(s, struct i82365_socket, socket)->number; 119862306a36Sopenharmony_ci if (socket[sock].flags & IS_ALIVE) 119962306a36Sopenharmony_ci return -EINVAL; 120062306a36Sopenharmony_ci 120162306a36Sopenharmony_ci LOCKED(i365_set_mem_map(sock, mem)); 120262306a36Sopenharmony_ci} 120362306a36Sopenharmony_ci 120462306a36Sopenharmony_cistatic int pcic_init(struct pcmcia_socket *s) 120562306a36Sopenharmony_ci{ 120662306a36Sopenharmony_ci int i; 120762306a36Sopenharmony_ci struct resource res = { .start = 0, .end = 0x1000 }; 120862306a36Sopenharmony_ci pccard_io_map io = { 0, 0, 0, 0, 1 }; 120962306a36Sopenharmony_ci pccard_mem_map mem = { .res = &res, }; 121062306a36Sopenharmony_ci 121162306a36Sopenharmony_ci for (i = 0; i < 2; i++) { 121262306a36Sopenharmony_ci io.map = i; 121362306a36Sopenharmony_ci pcic_set_io_map(s, &io); 121462306a36Sopenharmony_ci } 121562306a36Sopenharmony_ci for (i = 0; i < 5; i++) { 121662306a36Sopenharmony_ci mem.map = i; 121762306a36Sopenharmony_ci pcic_set_mem_map(s, &mem); 121862306a36Sopenharmony_ci } 121962306a36Sopenharmony_ci return 0; 122062306a36Sopenharmony_ci} 122162306a36Sopenharmony_ci 122262306a36Sopenharmony_ci 122362306a36Sopenharmony_cistatic struct pccard_operations pcic_operations = { 122462306a36Sopenharmony_ci .init = pcic_init, 122562306a36Sopenharmony_ci .get_status = pcic_get_status, 122662306a36Sopenharmony_ci .set_socket = pcic_set_socket, 122762306a36Sopenharmony_ci .set_io_map = pcic_set_io_map, 122862306a36Sopenharmony_ci .set_mem_map = pcic_set_mem_map, 122962306a36Sopenharmony_ci}; 123062306a36Sopenharmony_ci 123162306a36Sopenharmony_ci/*====================================================================*/ 123262306a36Sopenharmony_ci 123362306a36Sopenharmony_cistatic struct platform_driver i82365_driver = { 123462306a36Sopenharmony_ci .driver = { 123562306a36Sopenharmony_ci .name = "i82365", 123662306a36Sopenharmony_ci }, 123762306a36Sopenharmony_ci}; 123862306a36Sopenharmony_ci 123962306a36Sopenharmony_cistatic struct platform_device *i82365_device; 124062306a36Sopenharmony_ci 124162306a36Sopenharmony_cistatic int __init init_i82365(void) 124262306a36Sopenharmony_ci{ 124362306a36Sopenharmony_ci int i, ret; 124462306a36Sopenharmony_ci 124562306a36Sopenharmony_ci ret = platform_driver_register(&i82365_driver); 124662306a36Sopenharmony_ci if (ret) 124762306a36Sopenharmony_ci goto err_out; 124862306a36Sopenharmony_ci 124962306a36Sopenharmony_ci i82365_device = platform_device_alloc("i82365", 0); 125062306a36Sopenharmony_ci if (i82365_device) { 125162306a36Sopenharmony_ci ret = platform_device_add(i82365_device); 125262306a36Sopenharmony_ci if (ret) 125362306a36Sopenharmony_ci platform_device_put(i82365_device); 125462306a36Sopenharmony_ci } else 125562306a36Sopenharmony_ci ret = -ENOMEM; 125662306a36Sopenharmony_ci 125762306a36Sopenharmony_ci if (ret) 125862306a36Sopenharmony_ci goto err_driver_unregister; 125962306a36Sopenharmony_ci 126062306a36Sopenharmony_ci printk(KERN_INFO "Intel ISA PCIC probe: "); 126162306a36Sopenharmony_ci sockets = 0; 126262306a36Sopenharmony_ci 126362306a36Sopenharmony_ci isa_probe(); 126462306a36Sopenharmony_ci 126562306a36Sopenharmony_ci if (sockets == 0) { 126662306a36Sopenharmony_ci printk("not found.\n"); 126762306a36Sopenharmony_ci ret = -ENODEV; 126862306a36Sopenharmony_ci goto err_dev_unregister; 126962306a36Sopenharmony_ci } 127062306a36Sopenharmony_ci 127162306a36Sopenharmony_ci /* Set up interrupt handler(s) */ 127262306a36Sopenharmony_ci if (grab_irq != 0) 127362306a36Sopenharmony_ci ret = request_irq(cs_irq, pcic_interrupt, 0, "i82365", pcic_interrupt); 127462306a36Sopenharmony_ci 127562306a36Sopenharmony_ci if (ret) 127662306a36Sopenharmony_ci goto err_socket_release; 127762306a36Sopenharmony_ci 127862306a36Sopenharmony_ci /* register sockets with the pcmcia core */ 127962306a36Sopenharmony_ci for (i = 0; i < sockets; i++) { 128062306a36Sopenharmony_ci socket[i].socket.dev.parent = &i82365_device->dev; 128162306a36Sopenharmony_ci socket[i].socket.ops = &pcic_operations; 128262306a36Sopenharmony_ci socket[i].socket.resource_ops = &pccard_nonstatic_ops; 128362306a36Sopenharmony_ci socket[i].socket.owner = THIS_MODULE; 128462306a36Sopenharmony_ci socket[i].number = i; 128562306a36Sopenharmony_ci ret = pcmcia_register_socket(&socket[i].socket); 128662306a36Sopenharmony_ci if (!ret) 128762306a36Sopenharmony_ci socket[i].flags |= IS_REGISTERED; 128862306a36Sopenharmony_ci } 128962306a36Sopenharmony_ci 129062306a36Sopenharmony_ci /* Finally, schedule a polling interrupt */ 129162306a36Sopenharmony_ci if (poll_interval != 0) { 129262306a36Sopenharmony_ci timer_setup(&poll_timer, pcic_interrupt_wrapper, 0); 129362306a36Sopenharmony_ci poll_timer.expires = jiffies + poll_interval; 129462306a36Sopenharmony_ci add_timer(&poll_timer); 129562306a36Sopenharmony_ci } 129662306a36Sopenharmony_ci 129762306a36Sopenharmony_ci return 0; 129862306a36Sopenharmony_cierr_socket_release: 129962306a36Sopenharmony_ci for (i = 0; i < sockets; i++) { 130062306a36Sopenharmony_ci /* Turn off all interrupt sources! */ 130162306a36Sopenharmony_ci i365_set(i, I365_CSCINT, 0); 130262306a36Sopenharmony_ci release_region(socket[i].ioaddr, 2); 130362306a36Sopenharmony_ci } 130462306a36Sopenharmony_cierr_dev_unregister: 130562306a36Sopenharmony_ci platform_device_unregister(i82365_device); 130662306a36Sopenharmony_ci release_region(i365_base, 2); 130762306a36Sopenharmony_ci#ifdef CONFIG_PNP 130862306a36Sopenharmony_ci if (i82365_pnpdev) 130962306a36Sopenharmony_ci pnp_disable_dev(i82365_pnpdev); 131062306a36Sopenharmony_ci#endif 131162306a36Sopenharmony_cierr_driver_unregister: 131262306a36Sopenharmony_ci platform_driver_unregister(&i82365_driver); 131362306a36Sopenharmony_cierr_out: 131462306a36Sopenharmony_ci return ret; 131562306a36Sopenharmony_ci} /* init_i82365 */ 131662306a36Sopenharmony_ci 131762306a36Sopenharmony_cistatic void __exit exit_i82365(void) 131862306a36Sopenharmony_ci{ 131962306a36Sopenharmony_ci int i; 132062306a36Sopenharmony_ci 132162306a36Sopenharmony_ci for (i = 0; i < sockets; i++) { 132262306a36Sopenharmony_ci if (socket[i].flags & IS_REGISTERED) 132362306a36Sopenharmony_ci pcmcia_unregister_socket(&socket[i].socket); 132462306a36Sopenharmony_ci } 132562306a36Sopenharmony_ci platform_device_unregister(i82365_device); 132662306a36Sopenharmony_ci if (poll_interval != 0) 132762306a36Sopenharmony_ci del_timer_sync(&poll_timer); 132862306a36Sopenharmony_ci if (grab_irq != 0) 132962306a36Sopenharmony_ci free_irq(cs_irq, pcic_interrupt); 133062306a36Sopenharmony_ci for (i = 0; i < sockets; i++) { 133162306a36Sopenharmony_ci /* Turn off all interrupt sources! */ 133262306a36Sopenharmony_ci i365_set(i, I365_CSCINT, 0); 133362306a36Sopenharmony_ci release_region(socket[i].ioaddr, 2); 133462306a36Sopenharmony_ci } 133562306a36Sopenharmony_ci release_region(i365_base, 2); 133662306a36Sopenharmony_ci#ifdef CONFIG_PNP 133762306a36Sopenharmony_ci if (i82365_pnpdev) 133862306a36Sopenharmony_ci pnp_disable_dev(i82365_pnpdev); 133962306a36Sopenharmony_ci#endif 134062306a36Sopenharmony_ci platform_driver_unregister(&i82365_driver); 134162306a36Sopenharmony_ci} /* exit_i82365 */ 134262306a36Sopenharmony_ci 134362306a36Sopenharmony_cimodule_init(init_i82365); 134462306a36Sopenharmony_cimodule_exit(exit_i82365); 134562306a36Sopenharmony_ciMODULE_LICENSE("Dual MPL/GPL"); 134662306a36Sopenharmony_ci/*====================================================================*/ 1347