18c2ecf20Sopenharmony_ci/* 28c2ecf20Sopenharmony_ci * Driver for the Cirrus PD6729 PCI-PCMCIA bridge. 38c2ecf20Sopenharmony_ci * 48c2ecf20Sopenharmony_ci * Based on the i82092.c driver. 58c2ecf20Sopenharmony_ci * 68c2ecf20Sopenharmony_ci * This software may be used and distributed according to the terms of 78c2ecf20Sopenharmony_ci * the GNU General Public License, incorporated herein by reference. 88c2ecf20Sopenharmony_ci */ 98c2ecf20Sopenharmony_ci 108c2ecf20Sopenharmony_ci#include <linux/kernel.h> 118c2ecf20Sopenharmony_ci#include <linux/module.h> 128c2ecf20Sopenharmony_ci#include <linux/slab.h> 138c2ecf20Sopenharmony_ci#include <linux/pci.h> 148c2ecf20Sopenharmony_ci#include <linux/init.h> 158c2ecf20Sopenharmony_ci#include <linux/workqueue.h> 168c2ecf20Sopenharmony_ci#include <linux/interrupt.h> 178c2ecf20Sopenharmony_ci#include <linux/device.h> 188c2ecf20Sopenharmony_ci#include <linux/io.h> 198c2ecf20Sopenharmony_ci 208c2ecf20Sopenharmony_ci#include <pcmcia/ss.h> 218c2ecf20Sopenharmony_ci 228c2ecf20Sopenharmony_ci 238c2ecf20Sopenharmony_ci#include "pd6729.h" 248c2ecf20Sopenharmony_ci#include "i82365.h" 258c2ecf20Sopenharmony_ci#include "cirrus.h" 268c2ecf20Sopenharmony_ci 278c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 288c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Driver for the Cirrus PD6729 PCI-PCMCIA bridge"); 298c2ecf20Sopenharmony_ciMODULE_AUTHOR("Jun Komuro <komurojun-mbn@nifty.com>"); 308c2ecf20Sopenharmony_ci 318c2ecf20Sopenharmony_ci#define MAX_SOCKETS 2 328c2ecf20Sopenharmony_ci 338c2ecf20Sopenharmony_ci/* 348c2ecf20Sopenharmony_ci * simple helper functions 358c2ecf20Sopenharmony_ci * External clock time, in nanoseconds. 120 ns = 8.33 MHz 368c2ecf20Sopenharmony_ci */ 378c2ecf20Sopenharmony_ci#define to_cycles(ns) ((ns)/120) 388c2ecf20Sopenharmony_ci 398c2ecf20Sopenharmony_ci#ifndef NO_IRQ 408c2ecf20Sopenharmony_ci#define NO_IRQ ((unsigned int)(0)) 418c2ecf20Sopenharmony_ci#endif 428c2ecf20Sopenharmony_ci 438c2ecf20Sopenharmony_ci/* 448c2ecf20Sopenharmony_ci * PARAMETERS 458c2ecf20Sopenharmony_ci * irq_mode=n 468c2ecf20Sopenharmony_ci * Specifies the interrupt delivery mode. The default (1) is to use PCI 478c2ecf20Sopenharmony_ci * interrupts; a value of 0 selects ISA interrupts. This must be set for 488c2ecf20Sopenharmony_ci * correct operation of PCI card readers. 498c2ecf20Sopenharmony_ci */ 508c2ecf20Sopenharmony_ci 518c2ecf20Sopenharmony_cistatic int irq_mode = 1; /* 0 = ISA interrupt, 1 = PCI interrupt */ 528c2ecf20Sopenharmony_ci 538c2ecf20Sopenharmony_cimodule_param(irq_mode, int, 0444); 548c2ecf20Sopenharmony_ciMODULE_PARM_DESC(irq_mode, 558c2ecf20Sopenharmony_ci "interrupt delivery mode. 0 = ISA, 1 = PCI. default is 1"); 568c2ecf20Sopenharmony_ci 578c2ecf20Sopenharmony_cistatic DEFINE_SPINLOCK(port_lock); 588c2ecf20Sopenharmony_ci 598c2ecf20Sopenharmony_ci/* basic value read/write functions */ 608c2ecf20Sopenharmony_ci 618c2ecf20Sopenharmony_cistatic unsigned char indirect_read(struct pd6729_socket *socket, 628c2ecf20Sopenharmony_ci unsigned short reg) 638c2ecf20Sopenharmony_ci{ 648c2ecf20Sopenharmony_ci unsigned long port; 658c2ecf20Sopenharmony_ci unsigned char val; 668c2ecf20Sopenharmony_ci unsigned long flags; 678c2ecf20Sopenharmony_ci 688c2ecf20Sopenharmony_ci spin_lock_irqsave(&port_lock, flags); 698c2ecf20Sopenharmony_ci reg += socket->number * 0x40; 708c2ecf20Sopenharmony_ci port = socket->io_base; 718c2ecf20Sopenharmony_ci outb(reg, port); 728c2ecf20Sopenharmony_ci val = inb(port + 1); 738c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&port_lock, flags); 748c2ecf20Sopenharmony_ci 758c2ecf20Sopenharmony_ci return val; 768c2ecf20Sopenharmony_ci} 778c2ecf20Sopenharmony_ci 788c2ecf20Sopenharmony_cistatic unsigned short indirect_read16(struct pd6729_socket *socket, 798c2ecf20Sopenharmony_ci unsigned short reg) 808c2ecf20Sopenharmony_ci{ 818c2ecf20Sopenharmony_ci unsigned long port; 828c2ecf20Sopenharmony_ci unsigned short tmp; 838c2ecf20Sopenharmony_ci unsigned long flags; 848c2ecf20Sopenharmony_ci 858c2ecf20Sopenharmony_ci spin_lock_irqsave(&port_lock, flags); 868c2ecf20Sopenharmony_ci reg = reg + socket->number * 0x40; 878c2ecf20Sopenharmony_ci port = socket->io_base; 888c2ecf20Sopenharmony_ci outb(reg, port); 898c2ecf20Sopenharmony_ci tmp = inb(port + 1); 908c2ecf20Sopenharmony_ci reg++; 918c2ecf20Sopenharmony_ci outb(reg, port); 928c2ecf20Sopenharmony_ci tmp = tmp | (inb(port + 1) << 8); 938c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&port_lock, flags); 948c2ecf20Sopenharmony_ci 958c2ecf20Sopenharmony_ci return tmp; 968c2ecf20Sopenharmony_ci} 978c2ecf20Sopenharmony_ci 988c2ecf20Sopenharmony_cistatic void indirect_write(struct pd6729_socket *socket, unsigned short reg, 998c2ecf20Sopenharmony_ci unsigned char value) 1008c2ecf20Sopenharmony_ci{ 1018c2ecf20Sopenharmony_ci unsigned long port; 1028c2ecf20Sopenharmony_ci unsigned long flags; 1038c2ecf20Sopenharmony_ci 1048c2ecf20Sopenharmony_ci spin_lock_irqsave(&port_lock, flags); 1058c2ecf20Sopenharmony_ci reg = reg + socket->number * 0x40; 1068c2ecf20Sopenharmony_ci port = socket->io_base; 1078c2ecf20Sopenharmony_ci outb(reg, port); 1088c2ecf20Sopenharmony_ci outb(value, port + 1); 1098c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&port_lock, flags); 1108c2ecf20Sopenharmony_ci} 1118c2ecf20Sopenharmony_ci 1128c2ecf20Sopenharmony_cistatic void indirect_setbit(struct pd6729_socket *socket, unsigned short reg, 1138c2ecf20Sopenharmony_ci unsigned char mask) 1148c2ecf20Sopenharmony_ci{ 1158c2ecf20Sopenharmony_ci unsigned long port; 1168c2ecf20Sopenharmony_ci unsigned char val; 1178c2ecf20Sopenharmony_ci unsigned long flags; 1188c2ecf20Sopenharmony_ci 1198c2ecf20Sopenharmony_ci spin_lock_irqsave(&port_lock, flags); 1208c2ecf20Sopenharmony_ci reg = reg + socket->number * 0x40; 1218c2ecf20Sopenharmony_ci port = socket->io_base; 1228c2ecf20Sopenharmony_ci outb(reg, port); 1238c2ecf20Sopenharmony_ci val = inb(port + 1); 1248c2ecf20Sopenharmony_ci val |= mask; 1258c2ecf20Sopenharmony_ci outb(reg, port); 1268c2ecf20Sopenharmony_ci outb(val, port + 1); 1278c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&port_lock, flags); 1288c2ecf20Sopenharmony_ci} 1298c2ecf20Sopenharmony_ci 1308c2ecf20Sopenharmony_cistatic void indirect_resetbit(struct pd6729_socket *socket, unsigned short reg, 1318c2ecf20Sopenharmony_ci unsigned char mask) 1328c2ecf20Sopenharmony_ci{ 1338c2ecf20Sopenharmony_ci unsigned long port; 1348c2ecf20Sopenharmony_ci unsigned char val; 1358c2ecf20Sopenharmony_ci unsigned long flags; 1368c2ecf20Sopenharmony_ci 1378c2ecf20Sopenharmony_ci spin_lock_irqsave(&port_lock, flags); 1388c2ecf20Sopenharmony_ci reg = reg + socket->number * 0x40; 1398c2ecf20Sopenharmony_ci port = socket->io_base; 1408c2ecf20Sopenharmony_ci outb(reg, port); 1418c2ecf20Sopenharmony_ci val = inb(port + 1); 1428c2ecf20Sopenharmony_ci val &= ~mask; 1438c2ecf20Sopenharmony_ci outb(reg, port); 1448c2ecf20Sopenharmony_ci outb(val, port + 1); 1458c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&port_lock, flags); 1468c2ecf20Sopenharmony_ci} 1478c2ecf20Sopenharmony_ci 1488c2ecf20Sopenharmony_cistatic void indirect_write16(struct pd6729_socket *socket, unsigned short reg, 1498c2ecf20Sopenharmony_ci unsigned short value) 1508c2ecf20Sopenharmony_ci{ 1518c2ecf20Sopenharmony_ci unsigned long port; 1528c2ecf20Sopenharmony_ci unsigned char val; 1538c2ecf20Sopenharmony_ci unsigned long flags; 1548c2ecf20Sopenharmony_ci 1558c2ecf20Sopenharmony_ci spin_lock_irqsave(&port_lock, flags); 1568c2ecf20Sopenharmony_ci reg = reg + socket->number * 0x40; 1578c2ecf20Sopenharmony_ci port = socket->io_base; 1588c2ecf20Sopenharmony_ci 1598c2ecf20Sopenharmony_ci outb(reg, port); 1608c2ecf20Sopenharmony_ci val = value & 255; 1618c2ecf20Sopenharmony_ci outb(val, port + 1); 1628c2ecf20Sopenharmony_ci 1638c2ecf20Sopenharmony_ci reg++; 1648c2ecf20Sopenharmony_ci 1658c2ecf20Sopenharmony_ci outb(reg, port); 1668c2ecf20Sopenharmony_ci val = value >> 8; 1678c2ecf20Sopenharmony_ci outb(val, port + 1); 1688c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&port_lock, flags); 1698c2ecf20Sopenharmony_ci} 1708c2ecf20Sopenharmony_ci 1718c2ecf20Sopenharmony_ci/* Interrupt handler functionality */ 1728c2ecf20Sopenharmony_ci 1738c2ecf20Sopenharmony_cistatic irqreturn_t pd6729_interrupt(int irq, void *dev) 1748c2ecf20Sopenharmony_ci{ 1758c2ecf20Sopenharmony_ci struct pd6729_socket *socket = (struct pd6729_socket *)dev; 1768c2ecf20Sopenharmony_ci int i; 1778c2ecf20Sopenharmony_ci int loopcount = 0; 1788c2ecf20Sopenharmony_ci int handled = 0; 1798c2ecf20Sopenharmony_ci unsigned int events, active = 0; 1808c2ecf20Sopenharmony_ci 1818c2ecf20Sopenharmony_ci while (1) { 1828c2ecf20Sopenharmony_ci loopcount++; 1838c2ecf20Sopenharmony_ci if (loopcount > 20) { 1848c2ecf20Sopenharmony_ci printk(KERN_ERR "pd6729: infinite eventloop " 1858c2ecf20Sopenharmony_ci "in interrupt\n"); 1868c2ecf20Sopenharmony_ci break; 1878c2ecf20Sopenharmony_ci } 1888c2ecf20Sopenharmony_ci 1898c2ecf20Sopenharmony_ci active = 0; 1908c2ecf20Sopenharmony_ci 1918c2ecf20Sopenharmony_ci for (i = 0; i < MAX_SOCKETS; i++) { 1928c2ecf20Sopenharmony_ci unsigned int csc; 1938c2ecf20Sopenharmony_ci 1948c2ecf20Sopenharmony_ci /* card status change register */ 1958c2ecf20Sopenharmony_ci csc = indirect_read(&socket[i], I365_CSC); 1968c2ecf20Sopenharmony_ci if (csc == 0) /* no events on this socket */ 1978c2ecf20Sopenharmony_ci continue; 1988c2ecf20Sopenharmony_ci 1998c2ecf20Sopenharmony_ci handled = 1; 2008c2ecf20Sopenharmony_ci events = 0; 2018c2ecf20Sopenharmony_ci 2028c2ecf20Sopenharmony_ci if (csc & I365_CSC_DETECT) { 2038c2ecf20Sopenharmony_ci events |= SS_DETECT; 2048c2ecf20Sopenharmony_ci dev_vdbg(&socket[i].socket.dev, 2058c2ecf20Sopenharmony_ci "Card detected in socket %i!\n", i); 2068c2ecf20Sopenharmony_ci } 2078c2ecf20Sopenharmony_ci 2088c2ecf20Sopenharmony_ci if (indirect_read(&socket[i], I365_INTCTL) 2098c2ecf20Sopenharmony_ci & I365_PC_IOCARD) { 2108c2ecf20Sopenharmony_ci /* For IO/CARDS, bit 0 means "read the card" */ 2118c2ecf20Sopenharmony_ci events |= (csc & I365_CSC_STSCHG) 2128c2ecf20Sopenharmony_ci ? SS_STSCHG : 0; 2138c2ecf20Sopenharmony_ci } else { 2148c2ecf20Sopenharmony_ci /* Check for battery/ready events */ 2158c2ecf20Sopenharmony_ci events |= (csc & I365_CSC_BVD1) 2168c2ecf20Sopenharmony_ci ? SS_BATDEAD : 0; 2178c2ecf20Sopenharmony_ci events |= (csc & I365_CSC_BVD2) 2188c2ecf20Sopenharmony_ci ? SS_BATWARN : 0; 2198c2ecf20Sopenharmony_ci events |= (csc & I365_CSC_READY) 2208c2ecf20Sopenharmony_ci ? SS_READY : 0; 2218c2ecf20Sopenharmony_ci } 2228c2ecf20Sopenharmony_ci 2238c2ecf20Sopenharmony_ci if (events) 2248c2ecf20Sopenharmony_ci pcmcia_parse_events(&socket[i].socket, events); 2258c2ecf20Sopenharmony_ci 2268c2ecf20Sopenharmony_ci active |= events; 2278c2ecf20Sopenharmony_ci } 2288c2ecf20Sopenharmony_ci 2298c2ecf20Sopenharmony_ci if (active == 0) /* no more events to handle */ 2308c2ecf20Sopenharmony_ci break; 2318c2ecf20Sopenharmony_ci } 2328c2ecf20Sopenharmony_ci return IRQ_RETVAL(handled); 2338c2ecf20Sopenharmony_ci} 2348c2ecf20Sopenharmony_ci 2358c2ecf20Sopenharmony_ci/* socket functions */ 2368c2ecf20Sopenharmony_ci 2378c2ecf20Sopenharmony_cistatic void pd6729_interrupt_wrapper(struct timer_list *t) 2388c2ecf20Sopenharmony_ci{ 2398c2ecf20Sopenharmony_ci struct pd6729_socket *socket = from_timer(socket, t, poll_timer); 2408c2ecf20Sopenharmony_ci 2418c2ecf20Sopenharmony_ci pd6729_interrupt(0, (void *)socket); 2428c2ecf20Sopenharmony_ci mod_timer(&socket->poll_timer, jiffies + HZ); 2438c2ecf20Sopenharmony_ci} 2448c2ecf20Sopenharmony_ci 2458c2ecf20Sopenharmony_cistatic int pd6729_get_status(struct pcmcia_socket *sock, u_int *value) 2468c2ecf20Sopenharmony_ci{ 2478c2ecf20Sopenharmony_ci struct pd6729_socket *socket 2488c2ecf20Sopenharmony_ci = container_of(sock, struct pd6729_socket, socket); 2498c2ecf20Sopenharmony_ci unsigned int status; 2508c2ecf20Sopenharmony_ci unsigned int data; 2518c2ecf20Sopenharmony_ci struct pd6729_socket *t; 2528c2ecf20Sopenharmony_ci 2538c2ecf20Sopenharmony_ci /* Interface Status Register */ 2548c2ecf20Sopenharmony_ci status = indirect_read(socket, I365_STATUS); 2558c2ecf20Sopenharmony_ci *value = 0; 2568c2ecf20Sopenharmony_ci 2578c2ecf20Sopenharmony_ci if ((status & I365_CS_DETECT) == I365_CS_DETECT) 2588c2ecf20Sopenharmony_ci *value |= SS_DETECT; 2598c2ecf20Sopenharmony_ci 2608c2ecf20Sopenharmony_ci /* 2618c2ecf20Sopenharmony_ci * IO cards have a different meaning of bits 0,1 2628c2ecf20Sopenharmony_ci * Also notice the inverse-logic on the bits 2638c2ecf20Sopenharmony_ci */ 2648c2ecf20Sopenharmony_ci if (indirect_read(socket, I365_INTCTL) & I365_PC_IOCARD) { 2658c2ecf20Sopenharmony_ci /* IO card */ 2668c2ecf20Sopenharmony_ci if (!(status & I365_CS_STSCHG)) 2678c2ecf20Sopenharmony_ci *value |= SS_STSCHG; 2688c2ecf20Sopenharmony_ci } else { 2698c2ecf20Sopenharmony_ci /* non I/O card */ 2708c2ecf20Sopenharmony_ci if (!(status & I365_CS_BVD1)) 2718c2ecf20Sopenharmony_ci *value |= SS_BATDEAD; 2728c2ecf20Sopenharmony_ci if (!(status & I365_CS_BVD2)) 2738c2ecf20Sopenharmony_ci *value |= SS_BATWARN; 2748c2ecf20Sopenharmony_ci } 2758c2ecf20Sopenharmony_ci 2768c2ecf20Sopenharmony_ci if (status & I365_CS_WRPROT) 2778c2ecf20Sopenharmony_ci *value |= SS_WRPROT; /* card is write protected */ 2788c2ecf20Sopenharmony_ci 2798c2ecf20Sopenharmony_ci if (status & I365_CS_READY) 2808c2ecf20Sopenharmony_ci *value |= SS_READY; /* card is not busy */ 2818c2ecf20Sopenharmony_ci 2828c2ecf20Sopenharmony_ci if (status & I365_CS_POWERON) 2838c2ecf20Sopenharmony_ci *value |= SS_POWERON; /* power is applied to the card */ 2848c2ecf20Sopenharmony_ci 2858c2ecf20Sopenharmony_ci t = (socket->number) ? socket : socket + 1; 2868c2ecf20Sopenharmony_ci indirect_write(t, PD67_EXT_INDEX, PD67_EXTERN_DATA); 2878c2ecf20Sopenharmony_ci data = indirect_read16(t, PD67_EXT_DATA); 2888c2ecf20Sopenharmony_ci *value |= (data & PD67_EXD_VS1(socket->number)) ? 0 : SS_3VCARD; 2898c2ecf20Sopenharmony_ci 2908c2ecf20Sopenharmony_ci return 0; 2918c2ecf20Sopenharmony_ci} 2928c2ecf20Sopenharmony_ci 2938c2ecf20Sopenharmony_ci 2948c2ecf20Sopenharmony_cistatic int pd6729_set_socket(struct pcmcia_socket *sock, socket_state_t *state) 2958c2ecf20Sopenharmony_ci{ 2968c2ecf20Sopenharmony_ci struct pd6729_socket *socket 2978c2ecf20Sopenharmony_ci = container_of(sock, struct pd6729_socket, socket); 2988c2ecf20Sopenharmony_ci unsigned char reg, data; 2998c2ecf20Sopenharmony_ci 3008c2ecf20Sopenharmony_ci /* First, set the global controller options */ 3018c2ecf20Sopenharmony_ci indirect_write(socket, I365_GBLCTL, 0x00); 3028c2ecf20Sopenharmony_ci indirect_write(socket, I365_GENCTL, 0x00); 3038c2ecf20Sopenharmony_ci 3048c2ecf20Sopenharmony_ci /* Values for the IGENC register */ 3058c2ecf20Sopenharmony_ci socket->card_irq = state->io_irq; 3068c2ecf20Sopenharmony_ci 3078c2ecf20Sopenharmony_ci reg = 0; 3088c2ecf20Sopenharmony_ci /* The reset bit has "inverse" logic */ 3098c2ecf20Sopenharmony_ci if (!(state->flags & SS_RESET)) 3108c2ecf20Sopenharmony_ci reg |= I365_PC_RESET; 3118c2ecf20Sopenharmony_ci if (state->flags & SS_IOCARD) 3128c2ecf20Sopenharmony_ci reg |= I365_PC_IOCARD; 3138c2ecf20Sopenharmony_ci 3148c2ecf20Sopenharmony_ci /* IGENC, Interrupt and General Control Register */ 3158c2ecf20Sopenharmony_ci indirect_write(socket, I365_INTCTL, reg); 3168c2ecf20Sopenharmony_ci 3178c2ecf20Sopenharmony_ci /* Power registers */ 3188c2ecf20Sopenharmony_ci 3198c2ecf20Sopenharmony_ci reg = I365_PWR_NORESET; /* default: disable resetdrv on resume */ 3208c2ecf20Sopenharmony_ci 3218c2ecf20Sopenharmony_ci if (state->flags & SS_PWR_AUTO) { 3228c2ecf20Sopenharmony_ci dev_dbg(&sock->dev, "Auto power\n"); 3238c2ecf20Sopenharmony_ci reg |= I365_PWR_AUTO; /* automatic power mngmnt */ 3248c2ecf20Sopenharmony_ci } 3258c2ecf20Sopenharmony_ci if (state->flags & SS_OUTPUT_ENA) { 3268c2ecf20Sopenharmony_ci dev_dbg(&sock->dev, "Power Enabled\n"); 3278c2ecf20Sopenharmony_ci reg |= I365_PWR_OUT; /* enable power */ 3288c2ecf20Sopenharmony_ci } 3298c2ecf20Sopenharmony_ci 3308c2ecf20Sopenharmony_ci switch (state->Vcc) { 3318c2ecf20Sopenharmony_ci case 0: 3328c2ecf20Sopenharmony_ci break; 3338c2ecf20Sopenharmony_ci case 33: 3348c2ecf20Sopenharmony_ci dev_dbg(&sock->dev, 3358c2ecf20Sopenharmony_ci "setting voltage to Vcc to 3.3V on socket %i\n", 3368c2ecf20Sopenharmony_ci socket->number); 3378c2ecf20Sopenharmony_ci reg |= I365_VCC_5V; 3388c2ecf20Sopenharmony_ci indirect_setbit(socket, PD67_MISC_CTL_1, PD67_MC1_VCC_3V); 3398c2ecf20Sopenharmony_ci break; 3408c2ecf20Sopenharmony_ci case 50: 3418c2ecf20Sopenharmony_ci dev_dbg(&sock->dev, 3428c2ecf20Sopenharmony_ci "setting voltage to Vcc to 5V on socket %i\n", 3438c2ecf20Sopenharmony_ci socket->number); 3448c2ecf20Sopenharmony_ci reg |= I365_VCC_5V; 3458c2ecf20Sopenharmony_ci indirect_resetbit(socket, PD67_MISC_CTL_1, PD67_MC1_VCC_3V); 3468c2ecf20Sopenharmony_ci break; 3478c2ecf20Sopenharmony_ci default: 3488c2ecf20Sopenharmony_ci dev_dbg(&sock->dev, 3498c2ecf20Sopenharmony_ci "pd6729_set_socket called with invalid VCC power " 3508c2ecf20Sopenharmony_ci "value: %i\n", state->Vcc); 3518c2ecf20Sopenharmony_ci return -EINVAL; 3528c2ecf20Sopenharmony_ci } 3538c2ecf20Sopenharmony_ci 3548c2ecf20Sopenharmony_ci switch (state->Vpp) { 3558c2ecf20Sopenharmony_ci case 0: 3568c2ecf20Sopenharmony_ci dev_dbg(&sock->dev, "not setting Vpp on socket %i\n", 3578c2ecf20Sopenharmony_ci socket->number); 3588c2ecf20Sopenharmony_ci break; 3598c2ecf20Sopenharmony_ci case 33: 3608c2ecf20Sopenharmony_ci case 50: 3618c2ecf20Sopenharmony_ci dev_dbg(&sock->dev, "setting Vpp to Vcc for socket %i\n", 3628c2ecf20Sopenharmony_ci socket->number); 3638c2ecf20Sopenharmony_ci reg |= I365_VPP1_5V; 3648c2ecf20Sopenharmony_ci break; 3658c2ecf20Sopenharmony_ci case 120: 3668c2ecf20Sopenharmony_ci dev_dbg(&sock->dev, "setting Vpp to 12.0\n"); 3678c2ecf20Sopenharmony_ci reg |= I365_VPP1_12V; 3688c2ecf20Sopenharmony_ci break; 3698c2ecf20Sopenharmony_ci default: 3708c2ecf20Sopenharmony_ci dev_dbg(&sock->dev, "pd6729: pd6729_set_socket called with " 3718c2ecf20Sopenharmony_ci "invalid VPP power value: %i\n", state->Vpp); 3728c2ecf20Sopenharmony_ci return -EINVAL; 3738c2ecf20Sopenharmony_ci } 3748c2ecf20Sopenharmony_ci 3758c2ecf20Sopenharmony_ci /* only write if changed */ 3768c2ecf20Sopenharmony_ci if (reg != indirect_read(socket, I365_POWER)) 3778c2ecf20Sopenharmony_ci indirect_write(socket, I365_POWER, reg); 3788c2ecf20Sopenharmony_ci 3798c2ecf20Sopenharmony_ci if (irq_mode == 1) { 3808c2ecf20Sopenharmony_ci /* all interrupts are to be done as PCI interrupts */ 3818c2ecf20Sopenharmony_ci data = PD67_EC1_INV_MGMT_IRQ | PD67_EC1_INV_CARD_IRQ; 3828c2ecf20Sopenharmony_ci } else 3838c2ecf20Sopenharmony_ci data = 0; 3848c2ecf20Sopenharmony_ci 3858c2ecf20Sopenharmony_ci indirect_write(socket, PD67_EXT_INDEX, PD67_EXT_CTL_1); 3868c2ecf20Sopenharmony_ci indirect_write(socket, PD67_EXT_DATA, data); 3878c2ecf20Sopenharmony_ci 3888c2ecf20Sopenharmony_ci /* Enable specific interrupt events */ 3898c2ecf20Sopenharmony_ci 3908c2ecf20Sopenharmony_ci reg = 0x00; 3918c2ecf20Sopenharmony_ci if (state->csc_mask & SS_DETECT) 3928c2ecf20Sopenharmony_ci reg |= I365_CSC_DETECT; 3938c2ecf20Sopenharmony_ci 3948c2ecf20Sopenharmony_ci if (state->flags & SS_IOCARD) { 3958c2ecf20Sopenharmony_ci if (state->csc_mask & SS_STSCHG) 3968c2ecf20Sopenharmony_ci reg |= I365_CSC_STSCHG; 3978c2ecf20Sopenharmony_ci } else { 3988c2ecf20Sopenharmony_ci if (state->csc_mask & SS_BATDEAD) 3998c2ecf20Sopenharmony_ci reg |= I365_CSC_BVD1; 4008c2ecf20Sopenharmony_ci if (state->csc_mask & SS_BATWARN) 4018c2ecf20Sopenharmony_ci reg |= I365_CSC_BVD2; 4028c2ecf20Sopenharmony_ci if (state->csc_mask & SS_READY) 4038c2ecf20Sopenharmony_ci reg |= I365_CSC_READY; 4048c2ecf20Sopenharmony_ci } 4058c2ecf20Sopenharmony_ci if (irq_mode == 1) 4068c2ecf20Sopenharmony_ci reg |= 0x30; /* management IRQ: PCI INTA# = "irq 3" */ 4078c2ecf20Sopenharmony_ci indirect_write(socket, I365_CSCINT, reg); 4088c2ecf20Sopenharmony_ci 4098c2ecf20Sopenharmony_ci reg = indirect_read(socket, I365_INTCTL); 4108c2ecf20Sopenharmony_ci if (irq_mode == 1) 4118c2ecf20Sopenharmony_ci reg |= 0x03; /* card IRQ: PCI INTA# = "irq 3" */ 4128c2ecf20Sopenharmony_ci else 4138c2ecf20Sopenharmony_ci reg |= socket->card_irq; 4148c2ecf20Sopenharmony_ci indirect_write(socket, I365_INTCTL, reg); 4158c2ecf20Sopenharmony_ci 4168c2ecf20Sopenharmony_ci /* now clear the (probably bogus) pending stuff by doing a dummy read */ 4178c2ecf20Sopenharmony_ci (void)indirect_read(socket, I365_CSC); 4188c2ecf20Sopenharmony_ci 4198c2ecf20Sopenharmony_ci return 0; 4208c2ecf20Sopenharmony_ci} 4218c2ecf20Sopenharmony_ci 4228c2ecf20Sopenharmony_cistatic int pd6729_set_io_map(struct pcmcia_socket *sock, 4238c2ecf20Sopenharmony_ci struct pccard_io_map *io) 4248c2ecf20Sopenharmony_ci{ 4258c2ecf20Sopenharmony_ci struct pd6729_socket *socket 4268c2ecf20Sopenharmony_ci = container_of(sock, struct pd6729_socket, socket); 4278c2ecf20Sopenharmony_ci unsigned char map, ioctl; 4288c2ecf20Sopenharmony_ci 4298c2ecf20Sopenharmony_ci map = io->map; 4308c2ecf20Sopenharmony_ci 4318c2ecf20Sopenharmony_ci /* Check error conditions */ 4328c2ecf20Sopenharmony_ci if (map > 1) { 4338c2ecf20Sopenharmony_ci dev_dbg(&sock->dev, "pd6729_set_io_map with invalid map\n"); 4348c2ecf20Sopenharmony_ci return -EINVAL; 4358c2ecf20Sopenharmony_ci } 4368c2ecf20Sopenharmony_ci 4378c2ecf20Sopenharmony_ci /* Turn off the window before changing anything */ 4388c2ecf20Sopenharmony_ci if (indirect_read(socket, I365_ADDRWIN) & I365_ENA_IO(map)) 4398c2ecf20Sopenharmony_ci indirect_resetbit(socket, I365_ADDRWIN, I365_ENA_IO(map)); 4408c2ecf20Sopenharmony_ci 4418c2ecf20Sopenharmony_ci /* dev_dbg(&sock->dev, "set_io_map: Setting range to %x - %x\n", 4428c2ecf20Sopenharmony_ci io->start, io->stop);*/ 4438c2ecf20Sopenharmony_ci 4448c2ecf20Sopenharmony_ci /* write the new values */ 4458c2ecf20Sopenharmony_ci indirect_write16(socket, I365_IO(map)+I365_W_START, io->start); 4468c2ecf20Sopenharmony_ci indirect_write16(socket, I365_IO(map)+I365_W_STOP, io->stop); 4478c2ecf20Sopenharmony_ci 4488c2ecf20Sopenharmony_ci ioctl = indirect_read(socket, I365_IOCTL) & ~I365_IOCTL_MASK(map); 4498c2ecf20Sopenharmony_ci 4508c2ecf20Sopenharmony_ci if (io->flags & MAP_0WS) 4518c2ecf20Sopenharmony_ci ioctl |= I365_IOCTL_0WS(map); 4528c2ecf20Sopenharmony_ci if (io->flags & MAP_16BIT) 4538c2ecf20Sopenharmony_ci ioctl |= I365_IOCTL_16BIT(map); 4548c2ecf20Sopenharmony_ci if (io->flags & MAP_AUTOSZ) 4558c2ecf20Sopenharmony_ci ioctl |= I365_IOCTL_IOCS16(map); 4568c2ecf20Sopenharmony_ci 4578c2ecf20Sopenharmony_ci indirect_write(socket, I365_IOCTL, ioctl); 4588c2ecf20Sopenharmony_ci 4598c2ecf20Sopenharmony_ci /* Turn the window back on if needed */ 4608c2ecf20Sopenharmony_ci if (io->flags & MAP_ACTIVE) 4618c2ecf20Sopenharmony_ci indirect_setbit(socket, I365_ADDRWIN, I365_ENA_IO(map)); 4628c2ecf20Sopenharmony_ci 4638c2ecf20Sopenharmony_ci return 0; 4648c2ecf20Sopenharmony_ci} 4658c2ecf20Sopenharmony_ci 4668c2ecf20Sopenharmony_cistatic int pd6729_set_mem_map(struct pcmcia_socket *sock, 4678c2ecf20Sopenharmony_ci struct pccard_mem_map *mem) 4688c2ecf20Sopenharmony_ci{ 4698c2ecf20Sopenharmony_ci struct pd6729_socket *socket 4708c2ecf20Sopenharmony_ci = container_of(sock, struct pd6729_socket, socket); 4718c2ecf20Sopenharmony_ci unsigned short base, i; 4728c2ecf20Sopenharmony_ci unsigned char map; 4738c2ecf20Sopenharmony_ci 4748c2ecf20Sopenharmony_ci map = mem->map; 4758c2ecf20Sopenharmony_ci if (map > 4) { 4768c2ecf20Sopenharmony_ci dev_warn(&sock->dev, "invalid map requested\n"); 4778c2ecf20Sopenharmony_ci return -EINVAL; 4788c2ecf20Sopenharmony_ci } 4798c2ecf20Sopenharmony_ci 4808c2ecf20Sopenharmony_ci if ((mem->res->start > mem->res->end) || (mem->speed > 1000)) { 4818c2ecf20Sopenharmony_ci dev_warn(&sock->dev, "invalid invalid address / speed\n"); 4828c2ecf20Sopenharmony_ci return -EINVAL; 4838c2ecf20Sopenharmony_ci } 4848c2ecf20Sopenharmony_ci 4858c2ecf20Sopenharmony_ci /* Turn off the window before changing anything */ 4868c2ecf20Sopenharmony_ci if (indirect_read(socket, I365_ADDRWIN) & I365_ENA_MEM(map)) 4878c2ecf20Sopenharmony_ci indirect_resetbit(socket, I365_ADDRWIN, I365_ENA_MEM(map)); 4888c2ecf20Sopenharmony_ci 4898c2ecf20Sopenharmony_ci /* write the start address */ 4908c2ecf20Sopenharmony_ci base = I365_MEM(map); 4918c2ecf20Sopenharmony_ci i = (mem->res->start >> 12) & 0x0fff; 4928c2ecf20Sopenharmony_ci if (mem->flags & MAP_16BIT) 4938c2ecf20Sopenharmony_ci i |= I365_MEM_16BIT; 4948c2ecf20Sopenharmony_ci if (mem->flags & MAP_0WS) 4958c2ecf20Sopenharmony_ci i |= I365_MEM_0WS; 4968c2ecf20Sopenharmony_ci indirect_write16(socket, base + I365_W_START, i); 4978c2ecf20Sopenharmony_ci 4988c2ecf20Sopenharmony_ci /* write the stop address */ 4998c2ecf20Sopenharmony_ci 5008c2ecf20Sopenharmony_ci i = (mem->res->end >> 12) & 0x0fff; 5018c2ecf20Sopenharmony_ci switch (to_cycles(mem->speed)) { 5028c2ecf20Sopenharmony_ci case 0: 5038c2ecf20Sopenharmony_ci break; 5048c2ecf20Sopenharmony_ci case 1: 5058c2ecf20Sopenharmony_ci i |= I365_MEM_WS0; 5068c2ecf20Sopenharmony_ci break; 5078c2ecf20Sopenharmony_ci case 2: 5088c2ecf20Sopenharmony_ci i |= I365_MEM_WS1; 5098c2ecf20Sopenharmony_ci break; 5108c2ecf20Sopenharmony_ci default: 5118c2ecf20Sopenharmony_ci i |= I365_MEM_WS1 | I365_MEM_WS0; 5128c2ecf20Sopenharmony_ci break; 5138c2ecf20Sopenharmony_ci } 5148c2ecf20Sopenharmony_ci 5158c2ecf20Sopenharmony_ci indirect_write16(socket, base + I365_W_STOP, i); 5168c2ecf20Sopenharmony_ci 5178c2ecf20Sopenharmony_ci /* Take care of high byte */ 5188c2ecf20Sopenharmony_ci indirect_write(socket, PD67_EXT_INDEX, PD67_MEM_PAGE(map)); 5198c2ecf20Sopenharmony_ci indirect_write(socket, PD67_EXT_DATA, mem->res->start >> 24); 5208c2ecf20Sopenharmony_ci 5218c2ecf20Sopenharmony_ci /* card start */ 5228c2ecf20Sopenharmony_ci 5238c2ecf20Sopenharmony_ci i = ((mem->card_start - mem->res->start) >> 12) & 0x3fff; 5248c2ecf20Sopenharmony_ci if (mem->flags & MAP_WRPROT) 5258c2ecf20Sopenharmony_ci i |= I365_MEM_WRPROT; 5268c2ecf20Sopenharmony_ci if (mem->flags & MAP_ATTRIB) { 5278c2ecf20Sopenharmony_ci /* dev_dbg(&sock->dev, "requesting attribute memory for " 5288c2ecf20Sopenharmony_ci "socket %i\n", socket->number);*/ 5298c2ecf20Sopenharmony_ci i |= I365_MEM_REG; 5308c2ecf20Sopenharmony_ci } else { 5318c2ecf20Sopenharmony_ci /* dev_dbg(&sock->dev, "requesting normal memory for " 5328c2ecf20Sopenharmony_ci "socket %i\n", socket->number);*/ 5338c2ecf20Sopenharmony_ci } 5348c2ecf20Sopenharmony_ci indirect_write16(socket, base + I365_W_OFF, i); 5358c2ecf20Sopenharmony_ci 5368c2ecf20Sopenharmony_ci /* Enable the window if necessary */ 5378c2ecf20Sopenharmony_ci if (mem->flags & MAP_ACTIVE) 5388c2ecf20Sopenharmony_ci indirect_setbit(socket, I365_ADDRWIN, I365_ENA_MEM(map)); 5398c2ecf20Sopenharmony_ci 5408c2ecf20Sopenharmony_ci return 0; 5418c2ecf20Sopenharmony_ci} 5428c2ecf20Sopenharmony_ci 5438c2ecf20Sopenharmony_cistatic int pd6729_init(struct pcmcia_socket *sock) 5448c2ecf20Sopenharmony_ci{ 5458c2ecf20Sopenharmony_ci int i; 5468c2ecf20Sopenharmony_ci struct resource res = { .end = 0x0fff }; 5478c2ecf20Sopenharmony_ci pccard_io_map io = { 0, 0, 0, 0, 1 }; 5488c2ecf20Sopenharmony_ci pccard_mem_map mem = { .res = &res, }; 5498c2ecf20Sopenharmony_ci 5508c2ecf20Sopenharmony_ci pd6729_set_socket(sock, &dead_socket); 5518c2ecf20Sopenharmony_ci for (i = 0; i < 2; i++) { 5528c2ecf20Sopenharmony_ci io.map = i; 5538c2ecf20Sopenharmony_ci pd6729_set_io_map(sock, &io); 5548c2ecf20Sopenharmony_ci } 5558c2ecf20Sopenharmony_ci for (i = 0; i < 5; i++) { 5568c2ecf20Sopenharmony_ci mem.map = i; 5578c2ecf20Sopenharmony_ci pd6729_set_mem_map(sock, &mem); 5588c2ecf20Sopenharmony_ci } 5598c2ecf20Sopenharmony_ci 5608c2ecf20Sopenharmony_ci return 0; 5618c2ecf20Sopenharmony_ci} 5628c2ecf20Sopenharmony_ci 5638c2ecf20Sopenharmony_ci 5648c2ecf20Sopenharmony_ci/* the pccard structure and its functions */ 5658c2ecf20Sopenharmony_cistatic struct pccard_operations pd6729_operations = { 5668c2ecf20Sopenharmony_ci .init = pd6729_init, 5678c2ecf20Sopenharmony_ci .get_status = pd6729_get_status, 5688c2ecf20Sopenharmony_ci .set_socket = pd6729_set_socket, 5698c2ecf20Sopenharmony_ci .set_io_map = pd6729_set_io_map, 5708c2ecf20Sopenharmony_ci .set_mem_map = pd6729_set_mem_map, 5718c2ecf20Sopenharmony_ci}; 5728c2ecf20Sopenharmony_ci 5738c2ecf20Sopenharmony_cistatic irqreturn_t pd6729_test(int irq, void *dev) 5748c2ecf20Sopenharmony_ci{ 5758c2ecf20Sopenharmony_ci pr_devel("-> hit on irq %d\n", irq); 5768c2ecf20Sopenharmony_ci return IRQ_HANDLED; 5778c2ecf20Sopenharmony_ci} 5788c2ecf20Sopenharmony_ci 5798c2ecf20Sopenharmony_cistatic int pd6729_check_irq(int irq) 5808c2ecf20Sopenharmony_ci{ 5818c2ecf20Sopenharmony_ci int ret; 5828c2ecf20Sopenharmony_ci 5838c2ecf20Sopenharmony_ci ret = request_irq(irq, pd6729_test, IRQF_PROBE_SHARED, "x", 5848c2ecf20Sopenharmony_ci pd6729_test); 5858c2ecf20Sopenharmony_ci if (ret) 5868c2ecf20Sopenharmony_ci return -1; 5878c2ecf20Sopenharmony_ci 5888c2ecf20Sopenharmony_ci free_irq(irq, pd6729_test); 5898c2ecf20Sopenharmony_ci return 0; 5908c2ecf20Sopenharmony_ci} 5918c2ecf20Sopenharmony_ci 5928c2ecf20Sopenharmony_cistatic u_int pd6729_isa_scan(void) 5938c2ecf20Sopenharmony_ci{ 5948c2ecf20Sopenharmony_ci u_int mask0, mask = 0; 5958c2ecf20Sopenharmony_ci int i; 5968c2ecf20Sopenharmony_ci 5978c2ecf20Sopenharmony_ci if (irq_mode == 1) { 5988c2ecf20Sopenharmony_ci printk(KERN_INFO "pd6729: PCI card interrupts, " 5998c2ecf20Sopenharmony_ci "PCI status changes\n"); 6008c2ecf20Sopenharmony_ci return 0; 6018c2ecf20Sopenharmony_ci } 6028c2ecf20Sopenharmony_ci 6038c2ecf20Sopenharmony_ci mask0 = PD67_MASK; 6048c2ecf20Sopenharmony_ci 6058c2ecf20Sopenharmony_ci /* just find interrupts that aren't in use */ 6068c2ecf20Sopenharmony_ci for (i = 0; i < 16; i++) 6078c2ecf20Sopenharmony_ci if ((mask0 & (1 << i)) && (pd6729_check_irq(i) == 0)) 6088c2ecf20Sopenharmony_ci mask |= (1 << i); 6098c2ecf20Sopenharmony_ci 6108c2ecf20Sopenharmony_ci printk(KERN_INFO "pd6729: ISA irqs = "); 6118c2ecf20Sopenharmony_ci for (i = 0; i < 16; i++) 6128c2ecf20Sopenharmony_ci if (mask & (1<<i)) 6138c2ecf20Sopenharmony_ci printk("%s%d", ((mask & ((1<<i)-1)) ? "," : ""), i); 6148c2ecf20Sopenharmony_ci 6158c2ecf20Sopenharmony_ci if (mask == 0) 6168c2ecf20Sopenharmony_ci printk("none!"); 6178c2ecf20Sopenharmony_ci else 6188c2ecf20Sopenharmony_ci printk(" polling status changes.\n"); 6198c2ecf20Sopenharmony_ci 6208c2ecf20Sopenharmony_ci return mask; 6218c2ecf20Sopenharmony_ci} 6228c2ecf20Sopenharmony_ci 6238c2ecf20Sopenharmony_cistatic int pd6729_pci_probe(struct pci_dev *dev, 6248c2ecf20Sopenharmony_ci const struct pci_device_id *id) 6258c2ecf20Sopenharmony_ci{ 6268c2ecf20Sopenharmony_ci int i, j, ret; 6278c2ecf20Sopenharmony_ci u_int mask; 6288c2ecf20Sopenharmony_ci char configbyte; 6298c2ecf20Sopenharmony_ci struct pd6729_socket *socket; 6308c2ecf20Sopenharmony_ci 6318c2ecf20Sopenharmony_ci socket = kcalloc(MAX_SOCKETS, sizeof(struct pd6729_socket), 6328c2ecf20Sopenharmony_ci GFP_KERNEL); 6338c2ecf20Sopenharmony_ci if (!socket) { 6348c2ecf20Sopenharmony_ci dev_warn(&dev->dev, "failed to kzalloc socket.\n"); 6358c2ecf20Sopenharmony_ci return -ENOMEM; 6368c2ecf20Sopenharmony_ci } 6378c2ecf20Sopenharmony_ci 6388c2ecf20Sopenharmony_ci ret = pci_enable_device(dev); 6398c2ecf20Sopenharmony_ci if (ret) { 6408c2ecf20Sopenharmony_ci dev_warn(&dev->dev, "failed to enable pci_device.\n"); 6418c2ecf20Sopenharmony_ci goto err_out_free_mem; 6428c2ecf20Sopenharmony_ci } 6438c2ecf20Sopenharmony_ci 6448c2ecf20Sopenharmony_ci if (!pci_resource_start(dev, 0)) { 6458c2ecf20Sopenharmony_ci dev_warn(&dev->dev, "refusing to load the driver as the " 6468c2ecf20Sopenharmony_ci "io_base is NULL.\n"); 6478c2ecf20Sopenharmony_ci ret = -ENOMEM; 6488c2ecf20Sopenharmony_ci goto err_out_disable; 6498c2ecf20Sopenharmony_ci } 6508c2ecf20Sopenharmony_ci 6518c2ecf20Sopenharmony_ci dev_info(&dev->dev, "Cirrus PD6729 PCI to PCMCIA Bridge at 0x%llx " 6528c2ecf20Sopenharmony_ci "on irq %d\n", 6538c2ecf20Sopenharmony_ci (unsigned long long)pci_resource_start(dev, 0), dev->irq); 6548c2ecf20Sopenharmony_ci /* 6558c2ecf20Sopenharmony_ci * Since we have no memory BARs some firmware may not 6568c2ecf20Sopenharmony_ci * have had PCI_COMMAND_MEMORY enabled, yet the device needs it. 6578c2ecf20Sopenharmony_ci */ 6588c2ecf20Sopenharmony_ci pci_read_config_byte(dev, PCI_COMMAND, &configbyte); 6598c2ecf20Sopenharmony_ci if (!(configbyte & PCI_COMMAND_MEMORY)) { 6608c2ecf20Sopenharmony_ci dev_dbg(&dev->dev, "pd6729: Enabling PCI_COMMAND_MEMORY.\n"); 6618c2ecf20Sopenharmony_ci configbyte |= PCI_COMMAND_MEMORY; 6628c2ecf20Sopenharmony_ci pci_write_config_byte(dev, PCI_COMMAND, configbyte); 6638c2ecf20Sopenharmony_ci } 6648c2ecf20Sopenharmony_ci 6658c2ecf20Sopenharmony_ci ret = pci_request_regions(dev, "pd6729"); 6668c2ecf20Sopenharmony_ci if (ret) { 6678c2ecf20Sopenharmony_ci dev_warn(&dev->dev, "pci request region failed.\n"); 6688c2ecf20Sopenharmony_ci goto err_out_disable; 6698c2ecf20Sopenharmony_ci } 6708c2ecf20Sopenharmony_ci 6718c2ecf20Sopenharmony_ci if (dev->irq == NO_IRQ) 6728c2ecf20Sopenharmony_ci irq_mode = 0; /* fall back to ISA interrupt mode */ 6738c2ecf20Sopenharmony_ci 6748c2ecf20Sopenharmony_ci mask = pd6729_isa_scan(); 6758c2ecf20Sopenharmony_ci if (irq_mode == 0 && mask == 0) { 6768c2ecf20Sopenharmony_ci dev_warn(&dev->dev, "no ISA interrupt is available.\n"); 6778c2ecf20Sopenharmony_ci ret = -ENODEV; 6788c2ecf20Sopenharmony_ci goto err_out_free_res; 6798c2ecf20Sopenharmony_ci } 6808c2ecf20Sopenharmony_ci 6818c2ecf20Sopenharmony_ci for (i = 0; i < MAX_SOCKETS; i++) { 6828c2ecf20Sopenharmony_ci socket[i].io_base = pci_resource_start(dev, 0); 6838c2ecf20Sopenharmony_ci socket[i].socket.features |= SS_CAP_PAGE_REGS | SS_CAP_PCCARD; 6848c2ecf20Sopenharmony_ci socket[i].socket.map_size = 0x1000; 6858c2ecf20Sopenharmony_ci socket[i].socket.irq_mask = mask; 6868c2ecf20Sopenharmony_ci socket[i].socket.pci_irq = dev->irq; 6878c2ecf20Sopenharmony_ci socket[i].socket.cb_dev = dev; 6888c2ecf20Sopenharmony_ci socket[i].socket.owner = THIS_MODULE; 6898c2ecf20Sopenharmony_ci 6908c2ecf20Sopenharmony_ci socket[i].number = i; 6918c2ecf20Sopenharmony_ci 6928c2ecf20Sopenharmony_ci socket[i].socket.ops = &pd6729_operations; 6938c2ecf20Sopenharmony_ci socket[i].socket.resource_ops = &pccard_nonstatic_ops; 6948c2ecf20Sopenharmony_ci socket[i].socket.dev.parent = &dev->dev; 6958c2ecf20Sopenharmony_ci socket[i].socket.driver_data = &socket[i]; 6968c2ecf20Sopenharmony_ci } 6978c2ecf20Sopenharmony_ci 6988c2ecf20Sopenharmony_ci pci_set_drvdata(dev, socket); 6998c2ecf20Sopenharmony_ci if (irq_mode == 1) { 7008c2ecf20Sopenharmony_ci /* Register the interrupt handler */ 7018c2ecf20Sopenharmony_ci ret = request_irq(dev->irq, pd6729_interrupt, IRQF_SHARED, 7028c2ecf20Sopenharmony_ci "pd6729", socket); 7038c2ecf20Sopenharmony_ci if (ret) { 7048c2ecf20Sopenharmony_ci dev_err(&dev->dev, "Failed to register irq %d\n", 7058c2ecf20Sopenharmony_ci dev->irq); 7068c2ecf20Sopenharmony_ci goto err_out_free_res; 7078c2ecf20Sopenharmony_ci } 7088c2ecf20Sopenharmony_ci } else { 7098c2ecf20Sopenharmony_ci /* poll Card status change */ 7108c2ecf20Sopenharmony_ci timer_setup(&socket->poll_timer, pd6729_interrupt_wrapper, 0); 7118c2ecf20Sopenharmony_ci mod_timer(&socket->poll_timer, jiffies + HZ); 7128c2ecf20Sopenharmony_ci } 7138c2ecf20Sopenharmony_ci 7148c2ecf20Sopenharmony_ci for (i = 0; i < MAX_SOCKETS; i++) { 7158c2ecf20Sopenharmony_ci ret = pcmcia_register_socket(&socket[i].socket); 7168c2ecf20Sopenharmony_ci if (ret) { 7178c2ecf20Sopenharmony_ci dev_warn(&dev->dev, "pcmcia_register_socket failed.\n"); 7188c2ecf20Sopenharmony_ci for (j = 0; j < i ; j++) 7198c2ecf20Sopenharmony_ci pcmcia_unregister_socket(&socket[j].socket); 7208c2ecf20Sopenharmony_ci goto err_out_free_res2; 7218c2ecf20Sopenharmony_ci } 7228c2ecf20Sopenharmony_ci } 7238c2ecf20Sopenharmony_ci 7248c2ecf20Sopenharmony_ci return 0; 7258c2ecf20Sopenharmony_ci 7268c2ecf20Sopenharmony_cierr_out_free_res2: 7278c2ecf20Sopenharmony_ci if (irq_mode == 1) 7288c2ecf20Sopenharmony_ci free_irq(dev->irq, socket); 7298c2ecf20Sopenharmony_ci else 7308c2ecf20Sopenharmony_ci del_timer_sync(&socket->poll_timer); 7318c2ecf20Sopenharmony_cierr_out_free_res: 7328c2ecf20Sopenharmony_ci pci_release_regions(dev); 7338c2ecf20Sopenharmony_cierr_out_disable: 7348c2ecf20Sopenharmony_ci pci_disable_device(dev); 7358c2ecf20Sopenharmony_ci 7368c2ecf20Sopenharmony_cierr_out_free_mem: 7378c2ecf20Sopenharmony_ci kfree(socket); 7388c2ecf20Sopenharmony_ci return ret; 7398c2ecf20Sopenharmony_ci} 7408c2ecf20Sopenharmony_ci 7418c2ecf20Sopenharmony_cistatic void pd6729_pci_remove(struct pci_dev *dev) 7428c2ecf20Sopenharmony_ci{ 7438c2ecf20Sopenharmony_ci int i; 7448c2ecf20Sopenharmony_ci struct pd6729_socket *socket = pci_get_drvdata(dev); 7458c2ecf20Sopenharmony_ci 7468c2ecf20Sopenharmony_ci for (i = 0; i < MAX_SOCKETS; i++) { 7478c2ecf20Sopenharmony_ci /* Turn off all interrupt sources */ 7488c2ecf20Sopenharmony_ci indirect_write(&socket[i], I365_CSCINT, 0); 7498c2ecf20Sopenharmony_ci indirect_write(&socket[i], I365_INTCTL, 0); 7508c2ecf20Sopenharmony_ci 7518c2ecf20Sopenharmony_ci pcmcia_unregister_socket(&socket[i].socket); 7528c2ecf20Sopenharmony_ci } 7538c2ecf20Sopenharmony_ci 7548c2ecf20Sopenharmony_ci if (irq_mode == 1) 7558c2ecf20Sopenharmony_ci free_irq(dev->irq, socket); 7568c2ecf20Sopenharmony_ci else 7578c2ecf20Sopenharmony_ci del_timer_sync(&socket->poll_timer); 7588c2ecf20Sopenharmony_ci pci_release_regions(dev); 7598c2ecf20Sopenharmony_ci pci_disable_device(dev); 7608c2ecf20Sopenharmony_ci 7618c2ecf20Sopenharmony_ci kfree(socket); 7628c2ecf20Sopenharmony_ci} 7638c2ecf20Sopenharmony_ci 7648c2ecf20Sopenharmony_cistatic const struct pci_device_id pd6729_pci_ids[] = { 7658c2ecf20Sopenharmony_ci { PCI_DEVICE(PCI_VENDOR_ID_CIRRUS, PCI_DEVICE_ID_CIRRUS_6729) }, 7668c2ecf20Sopenharmony_ci { } 7678c2ecf20Sopenharmony_ci}; 7688c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(pci, pd6729_pci_ids); 7698c2ecf20Sopenharmony_ci 7708c2ecf20Sopenharmony_cistatic struct pci_driver pd6729_pci_driver = { 7718c2ecf20Sopenharmony_ci .name = "pd6729", 7728c2ecf20Sopenharmony_ci .id_table = pd6729_pci_ids, 7738c2ecf20Sopenharmony_ci .probe = pd6729_pci_probe, 7748c2ecf20Sopenharmony_ci .remove = pd6729_pci_remove, 7758c2ecf20Sopenharmony_ci}; 7768c2ecf20Sopenharmony_ci 7778c2ecf20Sopenharmony_cimodule_pci_driver(pd6729_pci_driver); 778