18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Driver for Intel I82092AA PCI-PCMCIA bridge. 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * (C) 2001 Red Hat, Inc. 68c2ecf20Sopenharmony_ci * 78c2ecf20Sopenharmony_ci * Author: Arjan Van De Ven <arjanv@redhat.com> 88c2ecf20Sopenharmony_ci * Loosly based on i82365.c from the pcmcia-cs package 98c2ecf20Sopenharmony_ci */ 108c2ecf20Sopenharmony_ci 118c2ecf20Sopenharmony_ci#include <linux/kernel.h> 128c2ecf20Sopenharmony_ci#include <linux/module.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 198c2ecf20Sopenharmony_ci#include <pcmcia/ss.h> 208c2ecf20Sopenharmony_ci 218c2ecf20Sopenharmony_ci#include <linux/io.h> 228c2ecf20Sopenharmony_ci 238c2ecf20Sopenharmony_ci#include "i82092aa.h" 248c2ecf20Sopenharmony_ci#include "i82365.h" 258c2ecf20Sopenharmony_ci 268c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 278c2ecf20Sopenharmony_ci 288c2ecf20Sopenharmony_ci/* PCI core routines */ 298c2ecf20Sopenharmony_cistatic const struct pci_device_id i82092aa_pci_ids[] = { 308c2ecf20Sopenharmony_ci { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82092AA_0) }, 318c2ecf20Sopenharmony_ci { } 328c2ecf20Sopenharmony_ci}; 338c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(pci, i82092aa_pci_ids); 348c2ecf20Sopenharmony_ci 358c2ecf20Sopenharmony_cistatic struct pci_driver i82092aa_pci_driver = { 368c2ecf20Sopenharmony_ci .name = "i82092aa", 378c2ecf20Sopenharmony_ci .id_table = i82092aa_pci_ids, 388c2ecf20Sopenharmony_ci .probe = i82092aa_pci_probe, 398c2ecf20Sopenharmony_ci .remove = i82092aa_pci_remove, 408c2ecf20Sopenharmony_ci}; 418c2ecf20Sopenharmony_ci 428c2ecf20Sopenharmony_ci 438c2ecf20Sopenharmony_ci/* the pccard structure and its functions */ 448c2ecf20Sopenharmony_cistatic struct pccard_operations i82092aa_operations = { 458c2ecf20Sopenharmony_ci .init = i82092aa_init, 468c2ecf20Sopenharmony_ci .get_status = i82092aa_get_status, 478c2ecf20Sopenharmony_ci .set_socket = i82092aa_set_socket, 488c2ecf20Sopenharmony_ci .set_io_map = i82092aa_set_io_map, 498c2ecf20Sopenharmony_ci .set_mem_map = i82092aa_set_mem_map, 508c2ecf20Sopenharmony_ci}; 518c2ecf20Sopenharmony_ci 528c2ecf20Sopenharmony_ci/* The card can do up to 4 sockets, allocate a structure for each of them */ 538c2ecf20Sopenharmony_ci 548c2ecf20Sopenharmony_cistruct socket_info { 558c2ecf20Sopenharmony_ci int number; 568c2ecf20Sopenharmony_ci int card_state; 578c2ecf20Sopenharmony_ci /* 0 = no socket, 588c2ecf20Sopenharmony_ci * 1 = empty socket, 598c2ecf20Sopenharmony_ci * 2 = card but not initialized, 608c2ecf20Sopenharmony_ci * 3 = operational card 618c2ecf20Sopenharmony_ci */ 628c2ecf20Sopenharmony_ci unsigned int io_base; /* base io address of the socket */ 638c2ecf20Sopenharmony_ci 648c2ecf20Sopenharmony_ci struct pcmcia_socket socket; 658c2ecf20Sopenharmony_ci struct pci_dev *dev; /* The PCI device for the socket */ 668c2ecf20Sopenharmony_ci}; 678c2ecf20Sopenharmony_ci 688c2ecf20Sopenharmony_ci#define MAX_SOCKETS 4 698c2ecf20Sopenharmony_cistatic struct socket_info sockets[MAX_SOCKETS]; 708c2ecf20Sopenharmony_cistatic int socket_count; /* shortcut */ 718c2ecf20Sopenharmony_ci 728c2ecf20Sopenharmony_ci 738c2ecf20Sopenharmony_cistatic int i82092aa_pci_probe(struct pci_dev *dev, 748c2ecf20Sopenharmony_ci const struct pci_device_id *id) 758c2ecf20Sopenharmony_ci{ 768c2ecf20Sopenharmony_ci unsigned char configbyte; 778c2ecf20Sopenharmony_ci int i, ret; 788c2ecf20Sopenharmony_ci 798c2ecf20Sopenharmony_ci ret = pci_enable_device(dev); 808c2ecf20Sopenharmony_ci if (ret) 818c2ecf20Sopenharmony_ci return ret; 828c2ecf20Sopenharmony_ci 838c2ecf20Sopenharmony_ci /* PCI Configuration Control */ 848c2ecf20Sopenharmony_ci pci_read_config_byte(dev, 0x40, &configbyte); 858c2ecf20Sopenharmony_ci 868c2ecf20Sopenharmony_ci switch (configbyte&6) { 878c2ecf20Sopenharmony_ci case 0: 888c2ecf20Sopenharmony_ci socket_count = 2; 898c2ecf20Sopenharmony_ci break; 908c2ecf20Sopenharmony_ci case 2: 918c2ecf20Sopenharmony_ci socket_count = 1; 928c2ecf20Sopenharmony_ci break; 938c2ecf20Sopenharmony_ci case 4: 948c2ecf20Sopenharmony_ci case 6: 958c2ecf20Sopenharmony_ci socket_count = 4; 968c2ecf20Sopenharmony_ci break; 978c2ecf20Sopenharmony_ci 988c2ecf20Sopenharmony_ci default: 998c2ecf20Sopenharmony_ci dev_err(&dev->dev, 1008c2ecf20Sopenharmony_ci "Oops, you did something we didn't think of.\n"); 1018c2ecf20Sopenharmony_ci ret = -EIO; 1028c2ecf20Sopenharmony_ci goto err_out_disable; 1038c2ecf20Sopenharmony_ci } 1048c2ecf20Sopenharmony_ci dev_info(&dev->dev, "configured as a %d socket device.\n", 1058c2ecf20Sopenharmony_ci socket_count); 1068c2ecf20Sopenharmony_ci 1078c2ecf20Sopenharmony_ci if (!request_region(pci_resource_start(dev, 0), 2, "i82092aa")) { 1088c2ecf20Sopenharmony_ci ret = -EBUSY; 1098c2ecf20Sopenharmony_ci goto err_out_disable; 1108c2ecf20Sopenharmony_ci } 1118c2ecf20Sopenharmony_ci 1128c2ecf20Sopenharmony_ci for (i = 0; i < socket_count; i++) { 1138c2ecf20Sopenharmony_ci sockets[i].card_state = 1; /* 1 = present but empty */ 1148c2ecf20Sopenharmony_ci sockets[i].io_base = pci_resource_start(dev, 0); 1158c2ecf20Sopenharmony_ci sockets[i].dev = dev; 1168c2ecf20Sopenharmony_ci sockets[i].socket.features |= SS_CAP_PCCARD; 1178c2ecf20Sopenharmony_ci sockets[i].socket.map_size = 0x1000; 1188c2ecf20Sopenharmony_ci sockets[i].socket.irq_mask = 0; 1198c2ecf20Sopenharmony_ci sockets[i].socket.pci_irq = dev->irq; 1208c2ecf20Sopenharmony_ci sockets[i].socket.cb_dev = dev; 1218c2ecf20Sopenharmony_ci sockets[i].socket.owner = THIS_MODULE; 1228c2ecf20Sopenharmony_ci 1238c2ecf20Sopenharmony_ci sockets[i].number = i; 1248c2ecf20Sopenharmony_ci 1258c2ecf20Sopenharmony_ci if (card_present(i)) { 1268c2ecf20Sopenharmony_ci sockets[i].card_state = 3; 1278c2ecf20Sopenharmony_ci dev_dbg(&dev->dev, "slot %i is occupied\n", i); 1288c2ecf20Sopenharmony_ci } else { 1298c2ecf20Sopenharmony_ci dev_dbg(&dev->dev, "slot %i is vacant\n", i); 1308c2ecf20Sopenharmony_ci } 1318c2ecf20Sopenharmony_ci } 1328c2ecf20Sopenharmony_ci 1338c2ecf20Sopenharmony_ci /* Now, specifiy that all interrupts are to be done as PCI interrupts 1348c2ecf20Sopenharmony_ci * bitmask, one bit per event, 1 = PCI interrupt, 0 = ISA interrupt 1358c2ecf20Sopenharmony_ci */ 1368c2ecf20Sopenharmony_ci configbyte = 0xFF; 1378c2ecf20Sopenharmony_ci 1388c2ecf20Sopenharmony_ci /* PCI Interrupt Routing Register */ 1398c2ecf20Sopenharmony_ci pci_write_config_byte(dev, 0x50, configbyte); 1408c2ecf20Sopenharmony_ci 1418c2ecf20Sopenharmony_ci /* Register the interrupt handler */ 1428c2ecf20Sopenharmony_ci dev_dbg(&dev->dev, "Requesting interrupt %i\n", dev->irq); 1438c2ecf20Sopenharmony_ci ret = request_irq(dev->irq, i82092aa_interrupt, IRQF_SHARED, 1448c2ecf20Sopenharmony_ci "i82092aa", i82092aa_interrupt); 1458c2ecf20Sopenharmony_ci if (ret) { 1468c2ecf20Sopenharmony_ci dev_err(&dev->dev, "Failed to register IRQ %d, aborting\n", 1478c2ecf20Sopenharmony_ci dev->irq); 1488c2ecf20Sopenharmony_ci goto err_out_free_res; 1498c2ecf20Sopenharmony_ci } 1508c2ecf20Sopenharmony_ci 1518c2ecf20Sopenharmony_ci for (i = 0; i < socket_count; i++) { 1528c2ecf20Sopenharmony_ci sockets[i].socket.dev.parent = &dev->dev; 1538c2ecf20Sopenharmony_ci sockets[i].socket.ops = &i82092aa_operations; 1548c2ecf20Sopenharmony_ci sockets[i].socket.resource_ops = &pccard_nonstatic_ops; 1558c2ecf20Sopenharmony_ci ret = pcmcia_register_socket(&sockets[i].socket); 1568c2ecf20Sopenharmony_ci if (ret) 1578c2ecf20Sopenharmony_ci goto err_out_free_sockets; 1588c2ecf20Sopenharmony_ci } 1598c2ecf20Sopenharmony_ci 1608c2ecf20Sopenharmony_ci return 0; 1618c2ecf20Sopenharmony_ci 1628c2ecf20Sopenharmony_cierr_out_free_sockets: 1638c2ecf20Sopenharmony_ci if (i) { 1648c2ecf20Sopenharmony_ci for (i--; i >= 0; i--) 1658c2ecf20Sopenharmony_ci pcmcia_unregister_socket(&sockets[i].socket); 1668c2ecf20Sopenharmony_ci } 1678c2ecf20Sopenharmony_ci free_irq(dev->irq, i82092aa_interrupt); 1688c2ecf20Sopenharmony_cierr_out_free_res: 1698c2ecf20Sopenharmony_ci release_region(pci_resource_start(dev, 0), 2); 1708c2ecf20Sopenharmony_cierr_out_disable: 1718c2ecf20Sopenharmony_ci pci_disable_device(dev); 1728c2ecf20Sopenharmony_ci return ret; 1738c2ecf20Sopenharmony_ci} 1748c2ecf20Sopenharmony_ci 1758c2ecf20Sopenharmony_cistatic void i82092aa_pci_remove(struct pci_dev *dev) 1768c2ecf20Sopenharmony_ci{ 1778c2ecf20Sopenharmony_ci int i; 1788c2ecf20Sopenharmony_ci 1798c2ecf20Sopenharmony_ci free_irq(dev->irq, i82092aa_interrupt); 1808c2ecf20Sopenharmony_ci 1818c2ecf20Sopenharmony_ci for (i = 0; i < socket_count; i++) 1828c2ecf20Sopenharmony_ci pcmcia_unregister_socket(&sockets[i].socket); 1838c2ecf20Sopenharmony_ci} 1848c2ecf20Sopenharmony_ci 1858c2ecf20Sopenharmony_cistatic DEFINE_SPINLOCK(port_lock); 1868c2ecf20Sopenharmony_ci 1878c2ecf20Sopenharmony_ci/* basic value read/write functions */ 1888c2ecf20Sopenharmony_ci 1898c2ecf20Sopenharmony_cistatic unsigned char indirect_read(int socket, unsigned short reg) 1908c2ecf20Sopenharmony_ci{ 1918c2ecf20Sopenharmony_ci unsigned short int port; 1928c2ecf20Sopenharmony_ci unsigned char val; 1938c2ecf20Sopenharmony_ci unsigned long flags; 1948c2ecf20Sopenharmony_ci 1958c2ecf20Sopenharmony_ci spin_lock_irqsave(&port_lock, flags); 1968c2ecf20Sopenharmony_ci reg += socket * 0x40; 1978c2ecf20Sopenharmony_ci port = sockets[socket].io_base; 1988c2ecf20Sopenharmony_ci outb(reg, port); 1998c2ecf20Sopenharmony_ci val = inb(port+1); 2008c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&port_lock, flags); 2018c2ecf20Sopenharmony_ci return val; 2028c2ecf20Sopenharmony_ci} 2038c2ecf20Sopenharmony_ci 2048c2ecf20Sopenharmony_cistatic void indirect_write(int socket, unsigned short reg, unsigned char value) 2058c2ecf20Sopenharmony_ci{ 2068c2ecf20Sopenharmony_ci unsigned short int port; 2078c2ecf20Sopenharmony_ci unsigned long flags; 2088c2ecf20Sopenharmony_ci 2098c2ecf20Sopenharmony_ci spin_lock_irqsave(&port_lock, flags); 2108c2ecf20Sopenharmony_ci reg = reg + socket * 0x40; 2118c2ecf20Sopenharmony_ci port = sockets[socket].io_base; 2128c2ecf20Sopenharmony_ci outb(reg, port); 2138c2ecf20Sopenharmony_ci outb(value, port+1); 2148c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&port_lock, flags); 2158c2ecf20Sopenharmony_ci} 2168c2ecf20Sopenharmony_ci 2178c2ecf20Sopenharmony_cistatic void indirect_setbit(int socket, unsigned short reg, unsigned char mask) 2188c2ecf20Sopenharmony_ci{ 2198c2ecf20Sopenharmony_ci unsigned short int port; 2208c2ecf20Sopenharmony_ci unsigned char val; 2218c2ecf20Sopenharmony_ci unsigned long flags; 2228c2ecf20Sopenharmony_ci 2238c2ecf20Sopenharmony_ci spin_lock_irqsave(&port_lock, flags); 2248c2ecf20Sopenharmony_ci reg = reg + socket * 0x40; 2258c2ecf20Sopenharmony_ci port = sockets[socket].io_base; 2268c2ecf20Sopenharmony_ci outb(reg, port); 2278c2ecf20Sopenharmony_ci val = inb(port+1); 2288c2ecf20Sopenharmony_ci val |= mask; 2298c2ecf20Sopenharmony_ci outb(reg, port); 2308c2ecf20Sopenharmony_ci outb(val, port+1); 2318c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&port_lock, flags); 2328c2ecf20Sopenharmony_ci} 2338c2ecf20Sopenharmony_ci 2348c2ecf20Sopenharmony_ci 2358c2ecf20Sopenharmony_cistatic void indirect_resetbit(int socket, 2368c2ecf20Sopenharmony_ci unsigned short reg, unsigned char mask) 2378c2ecf20Sopenharmony_ci{ 2388c2ecf20Sopenharmony_ci unsigned short int port; 2398c2ecf20Sopenharmony_ci unsigned char val; 2408c2ecf20Sopenharmony_ci unsigned long flags; 2418c2ecf20Sopenharmony_ci 2428c2ecf20Sopenharmony_ci spin_lock_irqsave(&port_lock, flags); 2438c2ecf20Sopenharmony_ci reg = reg + socket * 0x40; 2448c2ecf20Sopenharmony_ci port = sockets[socket].io_base; 2458c2ecf20Sopenharmony_ci outb(reg, port); 2468c2ecf20Sopenharmony_ci val = inb(port+1); 2478c2ecf20Sopenharmony_ci val &= ~mask; 2488c2ecf20Sopenharmony_ci outb(reg, port); 2498c2ecf20Sopenharmony_ci outb(val, port+1); 2508c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&port_lock, flags); 2518c2ecf20Sopenharmony_ci} 2528c2ecf20Sopenharmony_ci 2538c2ecf20Sopenharmony_cistatic void indirect_write16(int socket, 2548c2ecf20Sopenharmony_ci unsigned short reg, unsigned short value) 2558c2ecf20Sopenharmony_ci{ 2568c2ecf20Sopenharmony_ci unsigned short int port; 2578c2ecf20Sopenharmony_ci unsigned char val; 2588c2ecf20Sopenharmony_ci unsigned long flags; 2598c2ecf20Sopenharmony_ci 2608c2ecf20Sopenharmony_ci spin_lock_irqsave(&port_lock, flags); 2618c2ecf20Sopenharmony_ci reg = reg + socket * 0x40; 2628c2ecf20Sopenharmony_ci port = sockets[socket].io_base; 2638c2ecf20Sopenharmony_ci 2648c2ecf20Sopenharmony_ci outb(reg, port); 2658c2ecf20Sopenharmony_ci val = value & 255; 2668c2ecf20Sopenharmony_ci outb(val, port+1); 2678c2ecf20Sopenharmony_ci 2688c2ecf20Sopenharmony_ci reg++; 2698c2ecf20Sopenharmony_ci 2708c2ecf20Sopenharmony_ci outb(reg, port); 2718c2ecf20Sopenharmony_ci val = value>>8; 2728c2ecf20Sopenharmony_ci outb(val, port+1); 2738c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&port_lock, flags); 2748c2ecf20Sopenharmony_ci} 2758c2ecf20Sopenharmony_ci 2768c2ecf20Sopenharmony_ci/* simple helper functions */ 2778c2ecf20Sopenharmony_ci/* External clock time, in nanoseconds. 120 ns = 8.33 MHz */ 2788c2ecf20Sopenharmony_cistatic int cycle_time = 120; 2798c2ecf20Sopenharmony_ci 2808c2ecf20Sopenharmony_cistatic int to_cycles(int ns) 2818c2ecf20Sopenharmony_ci{ 2828c2ecf20Sopenharmony_ci if (cycle_time != 0) 2838c2ecf20Sopenharmony_ci return ns/cycle_time; 2848c2ecf20Sopenharmony_ci else 2858c2ecf20Sopenharmony_ci return 0; 2868c2ecf20Sopenharmony_ci} 2878c2ecf20Sopenharmony_ci 2888c2ecf20Sopenharmony_ci 2898c2ecf20Sopenharmony_ci/* Interrupt handler functionality */ 2908c2ecf20Sopenharmony_ci 2918c2ecf20Sopenharmony_cistatic irqreturn_t i82092aa_interrupt(int irq, void *dev) 2928c2ecf20Sopenharmony_ci{ 2938c2ecf20Sopenharmony_ci int i; 2948c2ecf20Sopenharmony_ci int loopcount = 0; 2958c2ecf20Sopenharmony_ci int handled = 0; 2968c2ecf20Sopenharmony_ci 2978c2ecf20Sopenharmony_ci unsigned int events, active = 0; 2988c2ecf20Sopenharmony_ci 2998c2ecf20Sopenharmony_ci while (1) { 3008c2ecf20Sopenharmony_ci loopcount++; 3018c2ecf20Sopenharmony_ci if (loopcount > 20) { 3028c2ecf20Sopenharmony_ci pr_err("i82092aa: infinite eventloop in interrupt\n"); 3038c2ecf20Sopenharmony_ci break; 3048c2ecf20Sopenharmony_ci } 3058c2ecf20Sopenharmony_ci 3068c2ecf20Sopenharmony_ci active = 0; 3078c2ecf20Sopenharmony_ci 3088c2ecf20Sopenharmony_ci for (i = 0; i < socket_count; i++) { 3098c2ecf20Sopenharmony_ci int csc; 3108c2ecf20Sopenharmony_ci 3118c2ecf20Sopenharmony_ci /* Inactive socket, should not happen */ 3128c2ecf20Sopenharmony_ci if (sockets[i].card_state == 0) 3138c2ecf20Sopenharmony_ci continue; 3148c2ecf20Sopenharmony_ci 3158c2ecf20Sopenharmony_ci /* card status change register */ 3168c2ecf20Sopenharmony_ci csc = indirect_read(i, I365_CSC); 3178c2ecf20Sopenharmony_ci 3188c2ecf20Sopenharmony_ci if (csc == 0) /* no events on this socket */ 3198c2ecf20Sopenharmony_ci continue; 3208c2ecf20Sopenharmony_ci handled = 1; 3218c2ecf20Sopenharmony_ci events = 0; 3228c2ecf20Sopenharmony_ci 3238c2ecf20Sopenharmony_ci if (csc & I365_CSC_DETECT) { 3248c2ecf20Sopenharmony_ci events |= SS_DETECT; 3258c2ecf20Sopenharmony_ci dev_info(&sockets[i].dev->dev, 3268c2ecf20Sopenharmony_ci "Card detected in socket %i!\n", i); 3278c2ecf20Sopenharmony_ci } 3288c2ecf20Sopenharmony_ci 3298c2ecf20Sopenharmony_ci if (indirect_read(i, I365_INTCTL) & I365_PC_IOCARD) { 3308c2ecf20Sopenharmony_ci /* For IO/CARDS, bit 0 means "read the card" */ 3318c2ecf20Sopenharmony_ci if (csc & I365_CSC_STSCHG) 3328c2ecf20Sopenharmony_ci events |= SS_STSCHG; 3338c2ecf20Sopenharmony_ci } else { 3348c2ecf20Sopenharmony_ci /* Check for battery/ready events */ 3358c2ecf20Sopenharmony_ci if (csc & I365_CSC_BVD1) 3368c2ecf20Sopenharmony_ci events |= SS_BATDEAD; 3378c2ecf20Sopenharmony_ci if (csc & I365_CSC_BVD2) 3388c2ecf20Sopenharmony_ci events |= SS_BATWARN; 3398c2ecf20Sopenharmony_ci if (csc & I365_CSC_READY) 3408c2ecf20Sopenharmony_ci events |= SS_READY; 3418c2ecf20Sopenharmony_ci } 3428c2ecf20Sopenharmony_ci 3438c2ecf20Sopenharmony_ci if (events) 3448c2ecf20Sopenharmony_ci pcmcia_parse_events(&sockets[i].socket, events); 3458c2ecf20Sopenharmony_ci active |= events; 3468c2ecf20Sopenharmony_ci } 3478c2ecf20Sopenharmony_ci 3488c2ecf20Sopenharmony_ci if (active == 0) /* no more events to handle */ 3498c2ecf20Sopenharmony_ci break; 3508c2ecf20Sopenharmony_ci } 3518c2ecf20Sopenharmony_ci return IRQ_RETVAL(handled); 3528c2ecf20Sopenharmony_ci} 3538c2ecf20Sopenharmony_ci 3548c2ecf20Sopenharmony_ci 3558c2ecf20Sopenharmony_ci 3568c2ecf20Sopenharmony_ci/* socket functions */ 3578c2ecf20Sopenharmony_ci 3588c2ecf20Sopenharmony_cistatic int card_present(int socketno) 3598c2ecf20Sopenharmony_ci{ 3608c2ecf20Sopenharmony_ci unsigned int val; 3618c2ecf20Sopenharmony_ci 3628c2ecf20Sopenharmony_ci if ((socketno < 0) || (socketno >= MAX_SOCKETS)) 3638c2ecf20Sopenharmony_ci return 0; 3648c2ecf20Sopenharmony_ci if (sockets[socketno].io_base == 0) 3658c2ecf20Sopenharmony_ci return 0; 3668c2ecf20Sopenharmony_ci 3678c2ecf20Sopenharmony_ci 3688c2ecf20Sopenharmony_ci val = indirect_read(socketno, 1); /* Interface status register */ 3698c2ecf20Sopenharmony_ci if ((val&12) == 12) 3708c2ecf20Sopenharmony_ci return 1; 3718c2ecf20Sopenharmony_ci 3728c2ecf20Sopenharmony_ci return 0; 3738c2ecf20Sopenharmony_ci} 3748c2ecf20Sopenharmony_ci 3758c2ecf20Sopenharmony_cistatic void set_bridge_state(int sock) 3768c2ecf20Sopenharmony_ci{ 3778c2ecf20Sopenharmony_ci indirect_write(sock, I365_GBLCTL, 0x00); 3788c2ecf20Sopenharmony_ci indirect_write(sock, I365_GENCTL, 0x00); 3798c2ecf20Sopenharmony_ci 3808c2ecf20Sopenharmony_ci indirect_setbit(sock, I365_INTCTL, 0x08); 3818c2ecf20Sopenharmony_ci} 3828c2ecf20Sopenharmony_ci 3838c2ecf20Sopenharmony_ci 3848c2ecf20Sopenharmony_cistatic int i82092aa_init(struct pcmcia_socket *sock) 3858c2ecf20Sopenharmony_ci{ 3868c2ecf20Sopenharmony_ci int i; 3878c2ecf20Sopenharmony_ci struct resource res = { .start = 0, .end = 0x0fff }; 3888c2ecf20Sopenharmony_ci pccard_io_map io = { 0, 0, 0, 0, 1 }; 3898c2ecf20Sopenharmony_ci pccard_mem_map mem = { .res = &res, }; 3908c2ecf20Sopenharmony_ci 3918c2ecf20Sopenharmony_ci for (i = 0; i < 2; i++) { 3928c2ecf20Sopenharmony_ci io.map = i; 3938c2ecf20Sopenharmony_ci i82092aa_set_io_map(sock, &io); 3948c2ecf20Sopenharmony_ci } 3958c2ecf20Sopenharmony_ci for (i = 0; i < 5; i++) { 3968c2ecf20Sopenharmony_ci mem.map = i; 3978c2ecf20Sopenharmony_ci i82092aa_set_mem_map(sock, &mem); 3988c2ecf20Sopenharmony_ci } 3998c2ecf20Sopenharmony_ci 4008c2ecf20Sopenharmony_ci return 0; 4018c2ecf20Sopenharmony_ci} 4028c2ecf20Sopenharmony_ci 4038c2ecf20Sopenharmony_cistatic int i82092aa_get_status(struct pcmcia_socket *socket, u_int *value) 4048c2ecf20Sopenharmony_ci{ 4058c2ecf20Sopenharmony_ci unsigned int sock = container_of(socket, 4068c2ecf20Sopenharmony_ci struct socket_info, socket)->number; 4078c2ecf20Sopenharmony_ci unsigned int status; 4088c2ecf20Sopenharmony_ci 4098c2ecf20Sopenharmony_ci /* Interface Status Register */ 4108c2ecf20Sopenharmony_ci status = indirect_read(sock, I365_STATUS); 4118c2ecf20Sopenharmony_ci 4128c2ecf20Sopenharmony_ci *value = 0; 4138c2ecf20Sopenharmony_ci 4148c2ecf20Sopenharmony_ci if ((status & I365_CS_DETECT) == I365_CS_DETECT) 4158c2ecf20Sopenharmony_ci *value |= SS_DETECT; 4168c2ecf20Sopenharmony_ci 4178c2ecf20Sopenharmony_ci /* IO cards have a different meaning of bits 0,1 */ 4188c2ecf20Sopenharmony_ci /* Also notice the inverse-logic on the bits */ 4198c2ecf20Sopenharmony_ci if (indirect_read(sock, I365_INTCTL) & I365_PC_IOCARD) { 4208c2ecf20Sopenharmony_ci /* IO card */ 4218c2ecf20Sopenharmony_ci if (!(status & I365_CS_STSCHG)) 4228c2ecf20Sopenharmony_ci *value |= SS_STSCHG; 4238c2ecf20Sopenharmony_ci } else { /* non I/O card */ 4248c2ecf20Sopenharmony_ci if (!(status & I365_CS_BVD1)) 4258c2ecf20Sopenharmony_ci *value |= SS_BATDEAD; 4268c2ecf20Sopenharmony_ci if (!(status & I365_CS_BVD2)) 4278c2ecf20Sopenharmony_ci *value |= SS_BATWARN; 4288c2ecf20Sopenharmony_ci } 4298c2ecf20Sopenharmony_ci 4308c2ecf20Sopenharmony_ci if (status & I365_CS_WRPROT) 4318c2ecf20Sopenharmony_ci (*value) |= SS_WRPROT; /* card is write protected */ 4328c2ecf20Sopenharmony_ci 4338c2ecf20Sopenharmony_ci if (status & I365_CS_READY) 4348c2ecf20Sopenharmony_ci (*value) |= SS_READY; /* card is not busy */ 4358c2ecf20Sopenharmony_ci 4368c2ecf20Sopenharmony_ci if (status & I365_CS_POWERON) 4378c2ecf20Sopenharmony_ci (*value) |= SS_POWERON; /* power is applied to the card */ 4388c2ecf20Sopenharmony_ci 4398c2ecf20Sopenharmony_ci return 0; 4408c2ecf20Sopenharmony_ci} 4418c2ecf20Sopenharmony_ci 4428c2ecf20Sopenharmony_ci 4438c2ecf20Sopenharmony_cistatic int i82092aa_set_socket(struct pcmcia_socket *socket, 4448c2ecf20Sopenharmony_ci socket_state_t *state) 4458c2ecf20Sopenharmony_ci{ 4468c2ecf20Sopenharmony_ci struct socket_info *sock_info = container_of(socket, struct socket_info, 4478c2ecf20Sopenharmony_ci socket); 4488c2ecf20Sopenharmony_ci unsigned int sock = sock_info->number; 4498c2ecf20Sopenharmony_ci unsigned char reg; 4508c2ecf20Sopenharmony_ci 4518c2ecf20Sopenharmony_ci /* First, set the global controller options */ 4528c2ecf20Sopenharmony_ci 4538c2ecf20Sopenharmony_ci set_bridge_state(sock); 4548c2ecf20Sopenharmony_ci 4558c2ecf20Sopenharmony_ci /* Values for the IGENC register */ 4568c2ecf20Sopenharmony_ci 4578c2ecf20Sopenharmony_ci reg = 0; 4588c2ecf20Sopenharmony_ci 4598c2ecf20Sopenharmony_ci /* The reset bit has "inverse" logic */ 4608c2ecf20Sopenharmony_ci if (!(state->flags & SS_RESET)) 4618c2ecf20Sopenharmony_ci reg = reg | I365_PC_RESET; 4628c2ecf20Sopenharmony_ci if (state->flags & SS_IOCARD) 4638c2ecf20Sopenharmony_ci reg = reg | I365_PC_IOCARD; 4648c2ecf20Sopenharmony_ci 4658c2ecf20Sopenharmony_ci /* IGENC, Interrupt and General Control Register */ 4668c2ecf20Sopenharmony_ci indirect_write(sock, I365_INTCTL, reg); 4678c2ecf20Sopenharmony_ci 4688c2ecf20Sopenharmony_ci /* Power registers */ 4698c2ecf20Sopenharmony_ci 4708c2ecf20Sopenharmony_ci reg = I365_PWR_NORESET; /* default: disable resetdrv on resume */ 4718c2ecf20Sopenharmony_ci 4728c2ecf20Sopenharmony_ci if (state->flags & SS_PWR_AUTO) { 4738c2ecf20Sopenharmony_ci dev_info(&sock_info->dev->dev, "Auto power\n"); 4748c2ecf20Sopenharmony_ci reg |= I365_PWR_AUTO; /* automatic power mngmnt */ 4758c2ecf20Sopenharmony_ci } 4768c2ecf20Sopenharmony_ci if (state->flags & SS_OUTPUT_ENA) { 4778c2ecf20Sopenharmony_ci dev_info(&sock_info->dev->dev, "Power Enabled\n"); 4788c2ecf20Sopenharmony_ci reg |= I365_PWR_OUT; /* enable power */ 4798c2ecf20Sopenharmony_ci } 4808c2ecf20Sopenharmony_ci 4818c2ecf20Sopenharmony_ci switch (state->Vcc) { 4828c2ecf20Sopenharmony_ci case 0: 4838c2ecf20Sopenharmony_ci break; 4848c2ecf20Sopenharmony_ci case 50: 4858c2ecf20Sopenharmony_ci dev_info(&sock_info->dev->dev, 4868c2ecf20Sopenharmony_ci "setting voltage to Vcc to 5V on socket %i\n", 4878c2ecf20Sopenharmony_ci sock); 4888c2ecf20Sopenharmony_ci reg |= I365_VCC_5V; 4898c2ecf20Sopenharmony_ci break; 4908c2ecf20Sopenharmony_ci default: 4918c2ecf20Sopenharmony_ci dev_err(&sock_info->dev->dev, 4928c2ecf20Sopenharmony_ci "%s called with invalid VCC power value: %i", 4938c2ecf20Sopenharmony_ci __func__, state->Vcc); 4948c2ecf20Sopenharmony_ci return -EINVAL; 4958c2ecf20Sopenharmony_ci } 4968c2ecf20Sopenharmony_ci 4978c2ecf20Sopenharmony_ci switch (state->Vpp) { 4988c2ecf20Sopenharmony_ci case 0: 4998c2ecf20Sopenharmony_ci dev_info(&sock_info->dev->dev, 5008c2ecf20Sopenharmony_ci "not setting Vpp on socket %i\n", sock); 5018c2ecf20Sopenharmony_ci break; 5028c2ecf20Sopenharmony_ci case 50: 5038c2ecf20Sopenharmony_ci dev_info(&sock_info->dev->dev, 5048c2ecf20Sopenharmony_ci "setting Vpp to 5.0 for socket %i\n", sock); 5058c2ecf20Sopenharmony_ci reg |= I365_VPP1_5V | I365_VPP2_5V; 5068c2ecf20Sopenharmony_ci break; 5078c2ecf20Sopenharmony_ci case 120: 5088c2ecf20Sopenharmony_ci dev_info(&sock_info->dev->dev, "setting Vpp to 12.0\n"); 5098c2ecf20Sopenharmony_ci reg |= I365_VPP1_12V | I365_VPP2_12V; 5108c2ecf20Sopenharmony_ci break; 5118c2ecf20Sopenharmony_ci default: 5128c2ecf20Sopenharmony_ci dev_err(&sock_info->dev->dev, 5138c2ecf20Sopenharmony_ci "%s called with invalid VPP power value: %i", 5148c2ecf20Sopenharmony_ci __func__, state->Vcc); 5158c2ecf20Sopenharmony_ci return -EINVAL; 5168c2ecf20Sopenharmony_ci } 5178c2ecf20Sopenharmony_ci 5188c2ecf20Sopenharmony_ci if (reg != indirect_read(sock, I365_POWER)) /* only write if changed */ 5198c2ecf20Sopenharmony_ci indirect_write(sock, I365_POWER, reg); 5208c2ecf20Sopenharmony_ci 5218c2ecf20Sopenharmony_ci /* Enable specific interrupt events */ 5228c2ecf20Sopenharmony_ci 5238c2ecf20Sopenharmony_ci reg = 0x00; 5248c2ecf20Sopenharmony_ci if (state->csc_mask & SS_DETECT) 5258c2ecf20Sopenharmony_ci reg |= I365_CSC_DETECT; 5268c2ecf20Sopenharmony_ci if (state->flags & SS_IOCARD) { 5278c2ecf20Sopenharmony_ci if (state->csc_mask & SS_STSCHG) 5288c2ecf20Sopenharmony_ci reg |= I365_CSC_STSCHG; 5298c2ecf20Sopenharmony_ci } else { 5308c2ecf20Sopenharmony_ci if (state->csc_mask & SS_BATDEAD) 5318c2ecf20Sopenharmony_ci reg |= I365_CSC_BVD1; 5328c2ecf20Sopenharmony_ci if (state->csc_mask & SS_BATWARN) 5338c2ecf20Sopenharmony_ci reg |= I365_CSC_BVD2; 5348c2ecf20Sopenharmony_ci if (state->csc_mask & SS_READY) 5358c2ecf20Sopenharmony_ci reg |= I365_CSC_READY; 5368c2ecf20Sopenharmony_ci 5378c2ecf20Sopenharmony_ci } 5388c2ecf20Sopenharmony_ci 5398c2ecf20Sopenharmony_ci /* now write the value and clear the (probably bogus) pending stuff 5408c2ecf20Sopenharmony_ci * by doing a dummy read 5418c2ecf20Sopenharmony_ci */ 5428c2ecf20Sopenharmony_ci 5438c2ecf20Sopenharmony_ci indirect_write(sock, I365_CSCINT, reg); 5448c2ecf20Sopenharmony_ci (void)indirect_read(sock, I365_CSC); 5458c2ecf20Sopenharmony_ci 5468c2ecf20Sopenharmony_ci return 0; 5478c2ecf20Sopenharmony_ci} 5488c2ecf20Sopenharmony_ci 5498c2ecf20Sopenharmony_cistatic int i82092aa_set_io_map(struct pcmcia_socket *socket, 5508c2ecf20Sopenharmony_ci struct pccard_io_map *io) 5518c2ecf20Sopenharmony_ci{ 5528c2ecf20Sopenharmony_ci struct socket_info *sock_info = container_of(socket, struct socket_info, 5538c2ecf20Sopenharmony_ci socket); 5548c2ecf20Sopenharmony_ci unsigned int sock = sock_info->number; 5558c2ecf20Sopenharmony_ci unsigned char map, ioctl; 5568c2ecf20Sopenharmony_ci 5578c2ecf20Sopenharmony_ci map = io->map; 5588c2ecf20Sopenharmony_ci 5598c2ecf20Sopenharmony_ci /* Check error conditions */ 5608c2ecf20Sopenharmony_ci if (map > 1) 5618c2ecf20Sopenharmony_ci return -EINVAL; 5628c2ecf20Sopenharmony_ci 5638c2ecf20Sopenharmony_ci if ((io->start > 0xffff) || (io->stop > 0xffff) 5648c2ecf20Sopenharmony_ci || (io->stop < io->start)) 5658c2ecf20Sopenharmony_ci return -EINVAL; 5668c2ecf20Sopenharmony_ci 5678c2ecf20Sopenharmony_ci /* Turn off the window before changing anything */ 5688c2ecf20Sopenharmony_ci if (indirect_read(sock, I365_ADDRWIN) & I365_ENA_IO(map)) 5698c2ecf20Sopenharmony_ci indirect_resetbit(sock, I365_ADDRWIN, I365_ENA_IO(map)); 5708c2ecf20Sopenharmony_ci 5718c2ecf20Sopenharmony_ci /* write the new values */ 5728c2ecf20Sopenharmony_ci indirect_write16(sock, I365_IO(map)+I365_W_START, io->start); 5738c2ecf20Sopenharmony_ci indirect_write16(sock, I365_IO(map)+I365_W_STOP, io->stop); 5748c2ecf20Sopenharmony_ci 5758c2ecf20Sopenharmony_ci ioctl = indirect_read(sock, I365_IOCTL) & ~I365_IOCTL_MASK(map); 5768c2ecf20Sopenharmony_ci 5778c2ecf20Sopenharmony_ci if (io->flags & (MAP_16BIT|MAP_AUTOSZ)) 5788c2ecf20Sopenharmony_ci ioctl |= I365_IOCTL_16BIT(map); 5798c2ecf20Sopenharmony_ci 5808c2ecf20Sopenharmony_ci indirect_write(sock, I365_IOCTL, ioctl); 5818c2ecf20Sopenharmony_ci 5828c2ecf20Sopenharmony_ci /* Turn the window back on if needed */ 5838c2ecf20Sopenharmony_ci if (io->flags & MAP_ACTIVE) 5848c2ecf20Sopenharmony_ci indirect_setbit(sock, I365_ADDRWIN, I365_ENA_IO(map)); 5858c2ecf20Sopenharmony_ci 5868c2ecf20Sopenharmony_ci return 0; 5878c2ecf20Sopenharmony_ci} 5888c2ecf20Sopenharmony_ci 5898c2ecf20Sopenharmony_cistatic int i82092aa_set_mem_map(struct pcmcia_socket *socket, 5908c2ecf20Sopenharmony_ci struct pccard_mem_map *mem) 5918c2ecf20Sopenharmony_ci{ 5928c2ecf20Sopenharmony_ci struct socket_info *sock_info = container_of(socket, struct socket_info, 5938c2ecf20Sopenharmony_ci socket); 5948c2ecf20Sopenharmony_ci unsigned int sock = sock_info->number; 5958c2ecf20Sopenharmony_ci struct pci_bus_region region; 5968c2ecf20Sopenharmony_ci unsigned short base, i; 5978c2ecf20Sopenharmony_ci unsigned char map; 5988c2ecf20Sopenharmony_ci 5998c2ecf20Sopenharmony_ci pcibios_resource_to_bus(sock_info->dev->bus, ®ion, mem->res); 6008c2ecf20Sopenharmony_ci 6018c2ecf20Sopenharmony_ci map = mem->map; 6028c2ecf20Sopenharmony_ci if (map > 4) 6038c2ecf20Sopenharmony_ci return -EINVAL; 6048c2ecf20Sopenharmony_ci 6058c2ecf20Sopenharmony_ci if ((mem->card_start > 0x3ffffff) || (region.start > region.end) || 6068c2ecf20Sopenharmony_ci (mem->speed > 1000)) { 6078c2ecf20Sopenharmony_ci dev_err(&sock_info->dev->dev, 6088c2ecf20Sopenharmony_ci "invalid mem map for socket %i: %llx to %llx with a start of %x\n", 6098c2ecf20Sopenharmony_ci sock, 6108c2ecf20Sopenharmony_ci (unsigned long long)region.start, 6118c2ecf20Sopenharmony_ci (unsigned long long)region.end, 6128c2ecf20Sopenharmony_ci mem->card_start); 6138c2ecf20Sopenharmony_ci return -EINVAL; 6148c2ecf20Sopenharmony_ci } 6158c2ecf20Sopenharmony_ci 6168c2ecf20Sopenharmony_ci /* Turn off the window before changing anything */ 6178c2ecf20Sopenharmony_ci if (indirect_read(sock, I365_ADDRWIN) & I365_ENA_MEM(map)) 6188c2ecf20Sopenharmony_ci indirect_resetbit(sock, I365_ADDRWIN, I365_ENA_MEM(map)); 6198c2ecf20Sopenharmony_ci 6208c2ecf20Sopenharmony_ci /* write the start address */ 6218c2ecf20Sopenharmony_ci base = I365_MEM(map); 6228c2ecf20Sopenharmony_ci i = (region.start >> 12) & 0x0fff; 6238c2ecf20Sopenharmony_ci if (mem->flags & MAP_16BIT) 6248c2ecf20Sopenharmony_ci i |= I365_MEM_16BIT; 6258c2ecf20Sopenharmony_ci if (mem->flags & MAP_0WS) 6268c2ecf20Sopenharmony_ci i |= I365_MEM_0WS; 6278c2ecf20Sopenharmony_ci indirect_write16(sock, base+I365_W_START, i); 6288c2ecf20Sopenharmony_ci 6298c2ecf20Sopenharmony_ci /* write the stop address */ 6308c2ecf20Sopenharmony_ci 6318c2ecf20Sopenharmony_ci i = (region.end >> 12) & 0x0fff; 6328c2ecf20Sopenharmony_ci switch (to_cycles(mem->speed)) { 6338c2ecf20Sopenharmony_ci case 0: 6348c2ecf20Sopenharmony_ci break; 6358c2ecf20Sopenharmony_ci case 1: 6368c2ecf20Sopenharmony_ci i |= I365_MEM_WS0; 6378c2ecf20Sopenharmony_ci break; 6388c2ecf20Sopenharmony_ci case 2: 6398c2ecf20Sopenharmony_ci i |= I365_MEM_WS1; 6408c2ecf20Sopenharmony_ci break; 6418c2ecf20Sopenharmony_ci default: 6428c2ecf20Sopenharmony_ci i |= I365_MEM_WS1 | I365_MEM_WS0; 6438c2ecf20Sopenharmony_ci break; 6448c2ecf20Sopenharmony_ci } 6458c2ecf20Sopenharmony_ci 6468c2ecf20Sopenharmony_ci indirect_write16(sock, base+I365_W_STOP, i); 6478c2ecf20Sopenharmony_ci 6488c2ecf20Sopenharmony_ci /* card start */ 6498c2ecf20Sopenharmony_ci 6508c2ecf20Sopenharmony_ci i = ((mem->card_start - region.start) >> 12) & 0x3fff; 6518c2ecf20Sopenharmony_ci if (mem->flags & MAP_WRPROT) 6528c2ecf20Sopenharmony_ci i |= I365_MEM_WRPROT; 6538c2ecf20Sopenharmony_ci if (mem->flags & MAP_ATTRIB) 6548c2ecf20Sopenharmony_ci i |= I365_MEM_REG; 6558c2ecf20Sopenharmony_ci indirect_write16(sock, base+I365_W_OFF, i); 6568c2ecf20Sopenharmony_ci 6578c2ecf20Sopenharmony_ci /* Enable the window if necessary */ 6588c2ecf20Sopenharmony_ci if (mem->flags & MAP_ACTIVE) 6598c2ecf20Sopenharmony_ci indirect_setbit(sock, I365_ADDRWIN, I365_ENA_MEM(map)); 6608c2ecf20Sopenharmony_ci 6618c2ecf20Sopenharmony_ci return 0; 6628c2ecf20Sopenharmony_ci} 6638c2ecf20Sopenharmony_ci 6648c2ecf20Sopenharmony_cistatic int i82092aa_module_init(void) 6658c2ecf20Sopenharmony_ci{ 6668c2ecf20Sopenharmony_ci return pci_register_driver(&i82092aa_pci_driver); 6678c2ecf20Sopenharmony_ci} 6688c2ecf20Sopenharmony_ci 6698c2ecf20Sopenharmony_cistatic void i82092aa_module_exit(void) 6708c2ecf20Sopenharmony_ci{ 6718c2ecf20Sopenharmony_ci pci_unregister_driver(&i82092aa_pci_driver); 6728c2ecf20Sopenharmony_ci if (sockets[0].io_base > 0) 6738c2ecf20Sopenharmony_ci release_region(sockets[0].io_base, 2); 6748c2ecf20Sopenharmony_ci} 6758c2ecf20Sopenharmony_ci 6768c2ecf20Sopenharmony_cimodule_init(i82092aa_module_init); 6778c2ecf20Sopenharmony_cimodule_exit(i82092aa_module_exit); 6788c2ecf20Sopenharmony_ci 679