162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Driver for Intel I82092AA PCI-PCMCIA bridge. 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * (C) 2001 Red Hat, Inc. 662306a36Sopenharmony_ci * 762306a36Sopenharmony_ci * Author: Arjan Van De Ven <arjanv@redhat.com> 862306a36Sopenharmony_ci * Loosly based on i82365.c from the pcmcia-cs package 962306a36Sopenharmony_ci */ 1062306a36Sopenharmony_ci 1162306a36Sopenharmony_ci#include <linux/kernel.h> 1262306a36Sopenharmony_ci#include <linux/module.h> 1362306a36Sopenharmony_ci#include <linux/pci.h> 1462306a36Sopenharmony_ci#include <linux/init.h> 1562306a36Sopenharmony_ci#include <linux/workqueue.h> 1662306a36Sopenharmony_ci#include <linux/interrupt.h> 1762306a36Sopenharmony_ci#include <linux/device.h> 1862306a36Sopenharmony_ci 1962306a36Sopenharmony_ci#include <pcmcia/ss.h> 2062306a36Sopenharmony_ci 2162306a36Sopenharmony_ci#include <linux/io.h> 2262306a36Sopenharmony_ci 2362306a36Sopenharmony_ci#include "i82092aa.h" 2462306a36Sopenharmony_ci#include "i82365.h" 2562306a36Sopenharmony_ci 2662306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 2762306a36Sopenharmony_ci 2862306a36Sopenharmony_ci/* PCI core routines */ 2962306a36Sopenharmony_cistatic const struct pci_device_id i82092aa_pci_ids[] = { 3062306a36Sopenharmony_ci { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82092AA_0) }, 3162306a36Sopenharmony_ci { } 3262306a36Sopenharmony_ci}; 3362306a36Sopenharmony_ciMODULE_DEVICE_TABLE(pci, i82092aa_pci_ids); 3462306a36Sopenharmony_ci 3562306a36Sopenharmony_cistatic struct pci_driver i82092aa_pci_driver = { 3662306a36Sopenharmony_ci .name = "i82092aa", 3762306a36Sopenharmony_ci .id_table = i82092aa_pci_ids, 3862306a36Sopenharmony_ci .probe = i82092aa_pci_probe, 3962306a36Sopenharmony_ci .remove = i82092aa_pci_remove, 4062306a36Sopenharmony_ci}; 4162306a36Sopenharmony_ci 4262306a36Sopenharmony_ci 4362306a36Sopenharmony_ci/* the pccard structure and its functions */ 4462306a36Sopenharmony_cistatic struct pccard_operations i82092aa_operations = { 4562306a36Sopenharmony_ci .init = i82092aa_init, 4662306a36Sopenharmony_ci .get_status = i82092aa_get_status, 4762306a36Sopenharmony_ci .set_socket = i82092aa_set_socket, 4862306a36Sopenharmony_ci .set_io_map = i82092aa_set_io_map, 4962306a36Sopenharmony_ci .set_mem_map = i82092aa_set_mem_map, 5062306a36Sopenharmony_ci}; 5162306a36Sopenharmony_ci 5262306a36Sopenharmony_ci/* The card can do up to 4 sockets, allocate a structure for each of them */ 5362306a36Sopenharmony_ci 5462306a36Sopenharmony_cistruct socket_info { 5562306a36Sopenharmony_ci int number; 5662306a36Sopenharmony_ci int card_state; 5762306a36Sopenharmony_ci /* 0 = no socket, 5862306a36Sopenharmony_ci * 1 = empty socket, 5962306a36Sopenharmony_ci * 2 = card but not initialized, 6062306a36Sopenharmony_ci * 3 = operational card 6162306a36Sopenharmony_ci */ 6262306a36Sopenharmony_ci unsigned int io_base; /* base io address of the socket */ 6362306a36Sopenharmony_ci 6462306a36Sopenharmony_ci struct pcmcia_socket socket; 6562306a36Sopenharmony_ci struct pci_dev *dev; /* The PCI device for the socket */ 6662306a36Sopenharmony_ci}; 6762306a36Sopenharmony_ci 6862306a36Sopenharmony_ci#define MAX_SOCKETS 4 6962306a36Sopenharmony_cistatic struct socket_info sockets[MAX_SOCKETS]; 7062306a36Sopenharmony_cistatic int socket_count; /* shortcut */ 7162306a36Sopenharmony_ci 7262306a36Sopenharmony_ci 7362306a36Sopenharmony_cistatic int i82092aa_pci_probe(struct pci_dev *dev, 7462306a36Sopenharmony_ci const struct pci_device_id *id) 7562306a36Sopenharmony_ci{ 7662306a36Sopenharmony_ci unsigned char configbyte; 7762306a36Sopenharmony_ci int i, ret; 7862306a36Sopenharmony_ci 7962306a36Sopenharmony_ci ret = pci_enable_device(dev); 8062306a36Sopenharmony_ci if (ret) 8162306a36Sopenharmony_ci return ret; 8262306a36Sopenharmony_ci 8362306a36Sopenharmony_ci /* PCI Configuration Control */ 8462306a36Sopenharmony_ci pci_read_config_byte(dev, 0x40, &configbyte); 8562306a36Sopenharmony_ci 8662306a36Sopenharmony_ci switch (configbyte&6) { 8762306a36Sopenharmony_ci case 0: 8862306a36Sopenharmony_ci socket_count = 2; 8962306a36Sopenharmony_ci break; 9062306a36Sopenharmony_ci case 2: 9162306a36Sopenharmony_ci socket_count = 1; 9262306a36Sopenharmony_ci break; 9362306a36Sopenharmony_ci case 4: 9462306a36Sopenharmony_ci case 6: 9562306a36Sopenharmony_ci socket_count = 4; 9662306a36Sopenharmony_ci break; 9762306a36Sopenharmony_ci 9862306a36Sopenharmony_ci default: 9962306a36Sopenharmony_ci dev_err(&dev->dev, 10062306a36Sopenharmony_ci "Oops, you did something we didn't think of.\n"); 10162306a36Sopenharmony_ci ret = -EIO; 10262306a36Sopenharmony_ci goto err_out_disable; 10362306a36Sopenharmony_ci } 10462306a36Sopenharmony_ci dev_info(&dev->dev, "configured as a %d socket device.\n", 10562306a36Sopenharmony_ci socket_count); 10662306a36Sopenharmony_ci 10762306a36Sopenharmony_ci if (!request_region(pci_resource_start(dev, 0), 2, "i82092aa")) { 10862306a36Sopenharmony_ci ret = -EBUSY; 10962306a36Sopenharmony_ci goto err_out_disable; 11062306a36Sopenharmony_ci } 11162306a36Sopenharmony_ci 11262306a36Sopenharmony_ci for (i = 0; i < socket_count; i++) { 11362306a36Sopenharmony_ci sockets[i].card_state = 1; /* 1 = present but empty */ 11462306a36Sopenharmony_ci sockets[i].io_base = pci_resource_start(dev, 0); 11562306a36Sopenharmony_ci sockets[i].dev = dev; 11662306a36Sopenharmony_ci sockets[i].socket.features |= SS_CAP_PCCARD; 11762306a36Sopenharmony_ci sockets[i].socket.map_size = 0x1000; 11862306a36Sopenharmony_ci sockets[i].socket.irq_mask = 0; 11962306a36Sopenharmony_ci sockets[i].socket.pci_irq = dev->irq; 12062306a36Sopenharmony_ci sockets[i].socket.cb_dev = dev; 12162306a36Sopenharmony_ci sockets[i].socket.owner = THIS_MODULE; 12262306a36Sopenharmony_ci 12362306a36Sopenharmony_ci sockets[i].number = i; 12462306a36Sopenharmony_ci 12562306a36Sopenharmony_ci if (card_present(i)) { 12662306a36Sopenharmony_ci sockets[i].card_state = 3; 12762306a36Sopenharmony_ci dev_dbg(&dev->dev, "slot %i is occupied\n", i); 12862306a36Sopenharmony_ci } else { 12962306a36Sopenharmony_ci dev_dbg(&dev->dev, "slot %i is vacant\n", i); 13062306a36Sopenharmony_ci } 13162306a36Sopenharmony_ci } 13262306a36Sopenharmony_ci 13362306a36Sopenharmony_ci /* Now, specifiy that all interrupts are to be done as PCI interrupts 13462306a36Sopenharmony_ci * bitmask, one bit per event, 1 = PCI interrupt, 0 = ISA interrupt 13562306a36Sopenharmony_ci */ 13662306a36Sopenharmony_ci configbyte = 0xFF; 13762306a36Sopenharmony_ci 13862306a36Sopenharmony_ci /* PCI Interrupt Routing Register */ 13962306a36Sopenharmony_ci pci_write_config_byte(dev, 0x50, configbyte); 14062306a36Sopenharmony_ci 14162306a36Sopenharmony_ci /* Register the interrupt handler */ 14262306a36Sopenharmony_ci dev_dbg(&dev->dev, "Requesting interrupt %i\n", dev->irq); 14362306a36Sopenharmony_ci ret = request_irq(dev->irq, i82092aa_interrupt, IRQF_SHARED, 14462306a36Sopenharmony_ci "i82092aa", i82092aa_interrupt); 14562306a36Sopenharmony_ci if (ret) { 14662306a36Sopenharmony_ci dev_err(&dev->dev, "Failed to register IRQ %d, aborting\n", 14762306a36Sopenharmony_ci dev->irq); 14862306a36Sopenharmony_ci goto err_out_free_res; 14962306a36Sopenharmony_ci } 15062306a36Sopenharmony_ci 15162306a36Sopenharmony_ci for (i = 0; i < socket_count; i++) { 15262306a36Sopenharmony_ci sockets[i].socket.dev.parent = &dev->dev; 15362306a36Sopenharmony_ci sockets[i].socket.ops = &i82092aa_operations; 15462306a36Sopenharmony_ci sockets[i].socket.resource_ops = &pccard_nonstatic_ops; 15562306a36Sopenharmony_ci ret = pcmcia_register_socket(&sockets[i].socket); 15662306a36Sopenharmony_ci if (ret) 15762306a36Sopenharmony_ci goto err_out_free_sockets; 15862306a36Sopenharmony_ci } 15962306a36Sopenharmony_ci 16062306a36Sopenharmony_ci return 0; 16162306a36Sopenharmony_ci 16262306a36Sopenharmony_cierr_out_free_sockets: 16362306a36Sopenharmony_ci if (i) { 16462306a36Sopenharmony_ci for (i--; i >= 0; i--) 16562306a36Sopenharmony_ci pcmcia_unregister_socket(&sockets[i].socket); 16662306a36Sopenharmony_ci } 16762306a36Sopenharmony_ci free_irq(dev->irq, i82092aa_interrupt); 16862306a36Sopenharmony_cierr_out_free_res: 16962306a36Sopenharmony_ci release_region(pci_resource_start(dev, 0), 2); 17062306a36Sopenharmony_cierr_out_disable: 17162306a36Sopenharmony_ci pci_disable_device(dev); 17262306a36Sopenharmony_ci return ret; 17362306a36Sopenharmony_ci} 17462306a36Sopenharmony_ci 17562306a36Sopenharmony_cistatic void i82092aa_pci_remove(struct pci_dev *dev) 17662306a36Sopenharmony_ci{ 17762306a36Sopenharmony_ci int i; 17862306a36Sopenharmony_ci 17962306a36Sopenharmony_ci free_irq(dev->irq, i82092aa_interrupt); 18062306a36Sopenharmony_ci 18162306a36Sopenharmony_ci for (i = 0; i < socket_count; i++) 18262306a36Sopenharmony_ci pcmcia_unregister_socket(&sockets[i].socket); 18362306a36Sopenharmony_ci} 18462306a36Sopenharmony_ci 18562306a36Sopenharmony_cistatic DEFINE_SPINLOCK(port_lock); 18662306a36Sopenharmony_ci 18762306a36Sopenharmony_ci/* basic value read/write functions */ 18862306a36Sopenharmony_ci 18962306a36Sopenharmony_cistatic unsigned char indirect_read(int socket, unsigned short reg) 19062306a36Sopenharmony_ci{ 19162306a36Sopenharmony_ci unsigned short int port; 19262306a36Sopenharmony_ci unsigned char val; 19362306a36Sopenharmony_ci unsigned long flags; 19462306a36Sopenharmony_ci 19562306a36Sopenharmony_ci spin_lock_irqsave(&port_lock, flags); 19662306a36Sopenharmony_ci reg += socket * 0x40; 19762306a36Sopenharmony_ci port = sockets[socket].io_base; 19862306a36Sopenharmony_ci outb(reg, port); 19962306a36Sopenharmony_ci val = inb(port+1); 20062306a36Sopenharmony_ci spin_unlock_irqrestore(&port_lock, flags); 20162306a36Sopenharmony_ci return val; 20262306a36Sopenharmony_ci} 20362306a36Sopenharmony_ci 20462306a36Sopenharmony_cistatic void indirect_write(int socket, unsigned short reg, unsigned char value) 20562306a36Sopenharmony_ci{ 20662306a36Sopenharmony_ci unsigned short int port; 20762306a36Sopenharmony_ci unsigned long flags; 20862306a36Sopenharmony_ci 20962306a36Sopenharmony_ci spin_lock_irqsave(&port_lock, flags); 21062306a36Sopenharmony_ci reg = reg + socket * 0x40; 21162306a36Sopenharmony_ci port = sockets[socket].io_base; 21262306a36Sopenharmony_ci outb(reg, port); 21362306a36Sopenharmony_ci outb(value, port+1); 21462306a36Sopenharmony_ci spin_unlock_irqrestore(&port_lock, flags); 21562306a36Sopenharmony_ci} 21662306a36Sopenharmony_ci 21762306a36Sopenharmony_cistatic void indirect_setbit(int socket, unsigned short reg, unsigned char mask) 21862306a36Sopenharmony_ci{ 21962306a36Sopenharmony_ci unsigned short int port; 22062306a36Sopenharmony_ci unsigned char val; 22162306a36Sopenharmony_ci unsigned long flags; 22262306a36Sopenharmony_ci 22362306a36Sopenharmony_ci spin_lock_irqsave(&port_lock, flags); 22462306a36Sopenharmony_ci reg = reg + socket * 0x40; 22562306a36Sopenharmony_ci port = sockets[socket].io_base; 22662306a36Sopenharmony_ci outb(reg, port); 22762306a36Sopenharmony_ci val = inb(port+1); 22862306a36Sopenharmony_ci val |= mask; 22962306a36Sopenharmony_ci outb(reg, port); 23062306a36Sopenharmony_ci outb(val, port+1); 23162306a36Sopenharmony_ci spin_unlock_irqrestore(&port_lock, flags); 23262306a36Sopenharmony_ci} 23362306a36Sopenharmony_ci 23462306a36Sopenharmony_ci 23562306a36Sopenharmony_cistatic void indirect_resetbit(int socket, 23662306a36Sopenharmony_ci unsigned short reg, unsigned char mask) 23762306a36Sopenharmony_ci{ 23862306a36Sopenharmony_ci unsigned short int port; 23962306a36Sopenharmony_ci unsigned char val; 24062306a36Sopenharmony_ci unsigned long flags; 24162306a36Sopenharmony_ci 24262306a36Sopenharmony_ci spin_lock_irqsave(&port_lock, flags); 24362306a36Sopenharmony_ci reg = reg + socket * 0x40; 24462306a36Sopenharmony_ci port = sockets[socket].io_base; 24562306a36Sopenharmony_ci outb(reg, port); 24662306a36Sopenharmony_ci val = inb(port+1); 24762306a36Sopenharmony_ci val &= ~mask; 24862306a36Sopenharmony_ci outb(reg, port); 24962306a36Sopenharmony_ci outb(val, port+1); 25062306a36Sopenharmony_ci spin_unlock_irqrestore(&port_lock, flags); 25162306a36Sopenharmony_ci} 25262306a36Sopenharmony_ci 25362306a36Sopenharmony_cistatic void indirect_write16(int socket, 25462306a36Sopenharmony_ci unsigned short reg, unsigned short value) 25562306a36Sopenharmony_ci{ 25662306a36Sopenharmony_ci unsigned short int port; 25762306a36Sopenharmony_ci unsigned char val; 25862306a36Sopenharmony_ci unsigned long flags; 25962306a36Sopenharmony_ci 26062306a36Sopenharmony_ci spin_lock_irqsave(&port_lock, flags); 26162306a36Sopenharmony_ci reg = reg + socket * 0x40; 26262306a36Sopenharmony_ci port = sockets[socket].io_base; 26362306a36Sopenharmony_ci 26462306a36Sopenharmony_ci outb(reg, port); 26562306a36Sopenharmony_ci val = value & 255; 26662306a36Sopenharmony_ci outb(val, port+1); 26762306a36Sopenharmony_ci 26862306a36Sopenharmony_ci reg++; 26962306a36Sopenharmony_ci 27062306a36Sopenharmony_ci outb(reg, port); 27162306a36Sopenharmony_ci val = value>>8; 27262306a36Sopenharmony_ci outb(val, port+1); 27362306a36Sopenharmony_ci spin_unlock_irqrestore(&port_lock, flags); 27462306a36Sopenharmony_ci} 27562306a36Sopenharmony_ci 27662306a36Sopenharmony_ci/* simple helper functions */ 27762306a36Sopenharmony_ci/* External clock time, in nanoseconds. 120 ns = 8.33 MHz */ 27862306a36Sopenharmony_cistatic int cycle_time = 120; 27962306a36Sopenharmony_ci 28062306a36Sopenharmony_cistatic int to_cycles(int ns) 28162306a36Sopenharmony_ci{ 28262306a36Sopenharmony_ci if (cycle_time != 0) 28362306a36Sopenharmony_ci return ns/cycle_time; 28462306a36Sopenharmony_ci else 28562306a36Sopenharmony_ci return 0; 28662306a36Sopenharmony_ci} 28762306a36Sopenharmony_ci 28862306a36Sopenharmony_ci 28962306a36Sopenharmony_ci/* Interrupt handler functionality */ 29062306a36Sopenharmony_ci 29162306a36Sopenharmony_cistatic irqreturn_t i82092aa_interrupt(int irq, void *dev) 29262306a36Sopenharmony_ci{ 29362306a36Sopenharmony_ci int i; 29462306a36Sopenharmony_ci int loopcount = 0; 29562306a36Sopenharmony_ci int handled = 0; 29662306a36Sopenharmony_ci 29762306a36Sopenharmony_ci unsigned int events, active = 0; 29862306a36Sopenharmony_ci 29962306a36Sopenharmony_ci while (1) { 30062306a36Sopenharmony_ci loopcount++; 30162306a36Sopenharmony_ci if (loopcount > 20) { 30262306a36Sopenharmony_ci pr_err("i82092aa: infinite eventloop in interrupt\n"); 30362306a36Sopenharmony_ci break; 30462306a36Sopenharmony_ci } 30562306a36Sopenharmony_ci 30662306a36Sopenharmony_ci active = 0; 30762306a36Sopenharmony_ci 30862306a36Sopenharmony_ci for (i = 0; i < socket_count; i++) { 30962306a36Sopenharmony_ci int csc; 31062306a36Sopenharmony_ci 31162306a36Sopenharmony_ci /* Inactive socket, should not happen */ 31262306a36Sopenharmony_ci if (sockets[i].card_state == 0) 31362306a36Sopenharmony_ci continue; 31462306a36Sopenharmony_ci 31562306a36Sopenharmony_ci /* card status change register */ 31662306a36Sopenharmony_ci csc = indirect_read(i, I365_CSC); 31762306a36Sopenharmony_ci 31862306a36Sopenharmony_ci if (csc == 0) /* no events on this socket */ 31962306a36Sopenharmony_ci continue; 32062306a36Sopenharmony_ci handled = 1; 32162306a36Sopenharmony_ci events = 0; 32262306a36Sopenharmony_ci 32362306a36Sopenharmony_ci if (csc & I365_CSC_DETECT) { 32462306a36Sopenharmony_ci events |= SS_DETECT; 32562306a36Sopenharmony_ci dev_info(&sockets[i].dev->dev, 32662306a36Sopenharmony_ci "Card detected in socket %i!\n", i); 32762306a36Sopenharmony_ci } 32862306a36Sopenharmony_ci 32962306a36Sopenharmony_ci if (indirect_read(i, I365_INTCTL) & I365_PC_IOCARD) { 33062306a36Sopenharmony_ci /* For IO/CARDS, bit 0 means "read the card" */ 33162306a36Sopenharmony_ci if (csc & I365_CSC_STSCHG) 33262306a36Sopenharmony_ci events |= SS_STSCHG; 33362306a36Sopenharmony_ci } else { 33462306a36Sopenharmony_ci /* Check for battery/ready events */ 33562306a36Sopenharmony_ci if (csc & I365_CSC_BVD1) 33662306a36Sopenharmony_ci events |= SS_BATDEAD; 33762306a36Sopenharmony_ci if (csc & I365_CSC_BVD2) 33862306a36Sopenharmony_ci events |= SS_BATWARN; 33962306a36Sopenharmony_ci if (csc & I365_CSC_READY) 34062306a36Sopenharmony_ci events |= SS_READY; 34162306a36Sopenharmony_ci } 34262306a36Sopenharmony_ci 34362306a36Sopenharmony_ci if (events) 34462306a36Sopenharmony_ci pcmcia_parse_events(&sockets[i].socket, events); 34562306a36Sopenharmony_ci active |= events; 34662306a36Sopenharmony_ci } 34762306a36Sopenharmony_ci 34862306a36Sopenharmony_ci if (active == 0) /* no more events to handle */ 34962306a36Sopenharmony_ci break; 35062306a36Sopenharmony_ci } 35162306a36Sopenharmony_ci return IRQ_RETVAL(handled); 35262306a36Sopenharmony_ci} 35362306a36Sopenharmony_ci 35462306a36Sopenharmony_ci 35562306a36Sopenharmony_ci 35662306a36Sopenharmony_ci/* socket functions */ 35762306a36Sopenharmony_ci 35862306a36Sopenharmony_cistatic int card_present(int socketno) 35962306a36Sopenharmony_ci{ 36062306a36Sopenharmony_ci unsigned int val; 36162306a36Sopenharmony_ci 36262306a36Sopenharmony_ci if ((socketno < 0) || (socketno >= MAX_SOCKETS)) 36362306a36Sopenharmony_ci return 0; 36462306a36Sopenharmony_ci if (sockets[socketno].io_base == 0) 36562306a36Sopenharmony_ci return 0; 36662306a36Sopenharmony_ci 36762306a36Sopenharmony_ci 36862306a36Sopenharmony_ci val = indirect_read(socketno, 1); /* Interface status register */ 36962306a36Sopenharmony_ci if ((val&12) == 12) 37062306a36Sopenharmony_ci return 1; 37162306a36Sopenharmony_ci 37262306a36Sopenharmony_ci return 0; 37362306a36Sopenharmony_ci} 37462306a36Sopenharmony_ci 37562306a36Sopenharmony_cistatic void set_bridge_state(int sock) 37662306a36Sopenharmony_ci{ 37762306a36Sopenharmony_ci indirect_write(sock, I365_GBLCTL, 0x00); 37862306a36Sopenharmony_ci indirect_write(sock, I365_GENCTL, 0x00); 37962306a36Sopenharmony_ci 38062306a36Sopenharmony_ci indirect_setbit(sock, I365_INTCTL, 0x08); 38162306a36Sopenharmony_ci} 38262306a36Sopenharmony_ci 38362306a36Sopenharmony_ci 38462306a36Sopenharmony_cistatic int i82092aa_init(struct pcmcia_socket *sock) 38562306a36Sopenharmony_ci{ 38662306a36Sopenharmony_ci int i; 38762306a36Sopenharmony_ci struct resource res = { .start = 0, .end = 0x0fff }; 38862306a36Sopenharmony_ci pccard_io_map io = { 0, 0, 0, 0, 1 }; 38962306a36Sopenharmony_ci pccard_mem_map mem = { .res = &res, }; 39062306a36Sopenharmony_ci 39162306a36Sopenharmony_ci for (i = 0; i < 2; i++) { 39262306a36Sopenharmony_ci io.map = i; 39362306a36Sopenharmony_ci i82092aa_set_io_map(sock, &io); 39462306a36Sopenharmony_ci } 39562306a36Sopenharmony_ci for (i = 0; i < 5; i++) { 39662306a36Sopenharmony_ci mem.map = i; 39762306a36Sopenharmony_ci i82092aa_set_mem_map(sock, &mem); 39862306a36Sopenharmony_ci } 39962306a36Sopenharmony_ci 40062306a36Sopenharmony_ci return 0; 40162306a36Sopenharmony_ci} 40262306a36Sopenharmony_ci 40362306a36Sopenharmony_cistatic int i82092aa_get_status(struct pcmcia_socket *socket, u_int *value) 40462306a36Sopenharmony_ci{ 40562306a36Sopenharmony_ci unsigned int sock = container_of(socket, 40662306a36Sopenharmony_ci struct socket_info, socket)->number; 40762306a36Sopenharmony_ci unsigned int status; 40862306a36Sopenharmony_ci 40962306a36Sopenharmony_ci /* Interface Status Register */ 41062306a36Sopenharmony_ci status = indirect_read(sock, I365_STATUS); 41162306a36Sopenharmony_ci 41262306a36Sopenharmony_ci *value = 0; 41362306a36Sopenharmony_ci 41462306a36Sopenharmony_ci if ((status & I365_CS_DETECT) == I365_CS_DETECT) 41562306a36Sopenharmony_ci *value |= SS_DETECT; 41662306a36Sopenharmony_ci 41762306a36Sopenharmony_ci /* IO cards have a different meaning of bits 0,1 */ 41862306a36Sopenharmony_ci /* Also notice the inverse-logic on the bits */ 41962306a36Sopenharmony_ci if (indirect_read(sock, I365_INTCTL) & I365_PC_IOCARD) { 42062306a36Sopenharmony_ci /* IO card */ 42162306a36Sopenharmony_ci if (!(status & I365_CS_STSCHG)) 42262306a36Sopenharmony_ci *value |= SS_STSCHG; 42362306a36Sopenharmony_ci } else { /* non I/O card */ 42462306a36Sopenharmony_ci if (!(status & I365_CS_BVD1)) 42562306a36Sopenharmony_ci *value |= SS_BATDEAD; 42662306a36Sopenharmony_ci if (!(status & I365_CS_BVD2)) 42762306a36Sopenharmony_ci *value |= SS_BATWARN; 42862306a36Sopenharmony_ci } 42962306a36Sopenharmony_ci 43062306a36Sopenharmony_ci if (status & I365_CS_WRPROT) 43162306a36Sopenharmony_ci (*value) |= SS_WRPROT; /* card is write protected */ 43262306a36Sopenharmony_ci 43362306a36Sopenharmony_ci if (status & I365_CS_READY) 43462306a36Sopenharmony_ci (*value) |= SS_READY; /* card is not busy */ 43562306a36Sopenharmony_ci 43662306a36Sopenharmony_ci if (status & I365_CS_POWERON) 43762306a36Sopenharmony_ci (*value) |= SS_POWERON; /* power is applied to the card */ 43862306a36Sopenharmony_ci 43962306a36Sopenharmony_ci return 0; 44062306a36Sopenharmony_ci} 44162306a36Sopenharmony_ci 44262306a36Sopenharmony_ci 44362306a36Sopenharmony_cistatic int i82092aa_set_socket(struct pcmcia_socket *socket, 44462306a36Sopenharmony_ci socket_state_t *state) 44562306a36Sopenharmony_ci{ 44662306a36Sopenharmony_ci struct socket_info *sock_info = container_of(socket, struct socket_info, 44762306a36Sopenharmony_ci socket); 44862306a36Sopenharmony_ci unsigned int sock = sock_info->number; 44962306a36Sopenharmony_ci unsigned char reg; 45062306a36Sopenharmony_ci 45162306a36Sopenharmony_ci /* First, set the global controller options */ 45262306a36Sopenharmony_ci 45362306a36Sopenharmony_ci set_bridge_state(sock); 45462306a36Sopenharmony_ci 45562306a36Sopenharmony_ci /* Values for the IGENC register */ 45662306a36Sopenharmony_ci 45762306a36Sopenharmony_ci reg = 0; 45862306a36Sopenharmony_ci 45962306a36Sopenharmony_ci /* The reset bit has "inverse" logic */ 46062306a36Sopenharmony_ci if (!(state->flags & SS_RESET)) 46162306a36Sopenharmony_ci reg = reg | I365_PC_RESET; 46262306a36Sopenharmony_ci if (state->flags & SS_IOCARD) 46362306a36Sopenharmony_ci reg = reg | I365_PC_IOCARD; 46462306a36Sopenharmony_ci 46562306a36Sopenharmony_ci /* IGENC, Interrupt and General Control Register */ 46662306a36Sopenharmony_ci indirect_write(sock, I365_INTCTL, reg); 46762306a36Sopenharmony_ci 46862306a36Sopenharmony_ci /* Power registers */ 46962306a36Sopenharmony_ci 47062306a36Sopenharmony_ci reg = I365_PWR_NORESET; /* default: disable resetdrv on resume */ 47162306a36Sopenharmony_ci 47262306a36Sopenharmony_ci if (state->flags & SS_PWR_AUTO) { 47362306a36Sopenharmony_ci dev_info(&sock_info->dev->dev, "Auto power\n"); 47462306a36Sopenharmony_ci reg |= I365_PWR_AUTO; /* automatic power mngmnt */ 47562306a36Sopenharmony_ci } 47662306a36Sopenharmony_ci if (state->flags & SS_OUTPUT_ENA) { 47762306a36Sopenharmony_ci dev_info(&sock_info->dev->dev, "Power Enabled\n"); 47862306a36Sopenharmony_ci reg |= I365_PWR_OUT; /* enable power */ 47962306a36Sopenharmony_ci } 48062306a36Sopenharmony_ci 48162306a36Sopenharmony_ci switch (state->Vcc) { 48262306a36Sopenharmony_ci case 0: 48362306a36Sopenharmony_ci break; 48462306a36Sopenharmony_ci case 50: 48562306a36Sopenharmony_ci dev_info(&sock_info->dev->dev, 48662306a36Sopenharmony_ci "setting voltage to Vcc to 5V on socket %i\n", 48762306a36Sopenharmony_ci sock); 48862306a36Sopenharmony_ci reg |= I365_VCC_5V; 48962306a36Sopenharmony_ci break; 49062306a36Sopenharmony_ci default: 49162306a36Sopenharmony_ci dev_err(&sock_info->dev->dev, 49262306a36Sopenharmony_ci "%s called with invalid VCC power value: %i", 49362306a36Sopenharmony_ci __func__, state->Vcc); 49462306a36Sopenharmony_ci return -EINVAL; 49562306a36Sopenharmony_ci } 49662306a36Sopenharmony_ci 49762306a36Sopenharmony_ci switch (state->Vpp) { 49862306a36Sopenharmony_ci case 0: 49962306a36Sopenharmony_ci dev_info(&sock_info->dev->dev, 50062306a36Sopenharmony_ci "not setting Vpp on socket %i\n", sock); 50162306a36Sopenharmony_ci break; 50262306a36Sopenharmony_ci case 50: 50362306a36Sopenharmony_ci dev_info(&sock_info->dev->dev, 50462306a36Sopenharmony_ci "setting Vpp to 5.0 for socket %i\n", sock); 50562306a36Sopenharmony_ci reg |= I365_VPP1_5V | I365_VPP2_5V; 50662306a36Sopenharmony_ci break; 50762306a36Sopenharmony_ci case 120: 50862306a36Sopenharmony_ci dev_info(&sock_info->dev->dev, "setting Vpp to 12.0\n"); 50962306a36Sopenharmony_ci reg |= I365_VPP1_12V | I365_VPP2_12V; 51062306a36Sopenharmony_ci break; 51162306a36Sopenharmony_ci default: 51262306a36Sopenharmony_ci dev_err(&sock_info->dev->dev, 51362306a36Sopenharmony_ci "%s called with invalid VPP power value: %i", 51462306a36Sopenharmony_ci __func__, state->Vcc); 51562306a36Sopenharmony_ci return -EINVAL; 51662306a36Sopenharmony_ci } 51762306a36Sopenharmony_ci 51862306a36Sopenharmony_ci if (reg != indirect_read(sock, I365_POWER)) /* only write if changed */ 51962306a36Sopenharmony_ci indirect_write(sock, I365_POWER, reg); 52062306a36Sopenharmony_ci 52162306a36Sopenharmony_ci /* Enable specific interrupt events */ 52262306a36Sopenharmony_ci 52362306a36Sopenharmony_ci reg = 0x00; 52462306a36Sopenharmony_ci if (state->csc_mask & SS_DETECT) 52562306a36Sopenharmony_ci reg |= I365_CSC_DETECT; 52662306a36Sopenharmony_ci if (state->flags & SS_IOCARD) { 52762306a36Sopenharmony_ci if (state->csc_mask & SS_STSCHG) 52862306a36Sopenharmony_ci reg |= I365_CSC_STSCHG; 52962306a36Sopenharmony_ci } else { 53062306a36Sopenharmony_ci if (state->csc_mask & SS_BATDEAD) 53162306a36Sopenharmony_ci reg |= I365_CSC_BVD1; 53262306a36Sopenharmony_ci if (state->csc_mask & SS_BATWARN) 53362306a36Sopenharmony_ci reg |= I365_CSC_BVD2; 53462306a36Sopenharmony_ci if (state->csc_mask & SS_READY) 53562306a36Sopenharmony_ci reg |= I365_CSC_READY; 53662306a36Sopenharmony_ci 53762306a36Sopenharmony_ci } 53862306a36Sopenharmony_ci 53962306a36Sopenharmony_ci /* now write the value and clear the (probably bogus) pending stuff 54062306a36Sopenharmony_ci * by doing a dummy read 54162306a36Sopenharmony_ci */ 54262306a36Sopenharmony_ci 54362306a36Sopenharmony_ci indirect_write(sock, I365_CSCINT, reg); 54462306a36Sopenharmony_ci (void)indirect_read(sock, I365_CSC); 54562306a36Sopenharmony_ci 54662306a36Sopenharmony_ci return 0; 54762306a36Sopenharmony_ci} 54862306a36Sopenharmony_ci 54962306a36Sopenharmony_cistatic int i82092aa_set_io_map(struct pcmcia_socket *socket, 55062306a36Sopenharmony_ci struct pccard_io_map *io) 55162306a36Sopenharmony_ci{ 55262306a36Sopenharmony_ci struct socket_info *sock_info = container_of(socket, struct socket_info, 55362306a36Sopenharmony_ci socket); 55462306a36Sopenharmony_ci unsigned int sock = sock_info->number; 55562306a36Sopenharmony_ci unsigned char map, ioctl; 55662306a36Sopenharmony_ci 55762306a36Sopenharmony_ci map = io->map; 55862306a36Sopenharmony_ci 55962306a36Sopenharmony_ci /* Check error conditions */ 56062306a36Sopenharmony_ci if (map > 1) 56162306a36Sopenharmony_ci return -EINVAL; 56262306a36Sopenharmony_ci 56362306a36Sopenharmony_ci if ((io->start > 0xffff) || (io->stop > 0xffff) 56462306a36Sopenharmony_ci || (io->stop < io->start)) 56562306a36Sopenharmony_ci return -EINVAL; 56662306a36Sopenharmony_ci 56762306a36Sopenharmony_ci /* Turn off the window before changing anything */ 56862306a36Sopenharmony_ci if (indirect_read(sock, I365_ADDRWIN) & I365_ENA_IO(map)) 56962306a36Sopenharmony_ci indirect_resetbit(sock, I365_ADDRWIN, I365_ENA_IO(map)); 57062306a36Sopenharmony_ci 57162306a36Sopenharmony_ci /* write the new values */ 57262306a36Sopenharmony_ci indirect_write16(sock, I365_IO(map)+I365_W_START, io->start); 57362306a36Sopenharmony_ci indirect_write16(sock, I365_IO(map)+I365_W_STOP, io->stop); 57462306a36Sopenharmony_ci 57562306a36Sopenharmony_ci ioctl = indirect_read(sock, I365_IOCTL) & ~I365_IOCTL_MASK(map); 57662306a36Sopenharmony_ci 57762306a36Sopenharmony_ci if (io->flags & (MAP_16BIT|MAP_AUTOSZ)) 57862306a36Sopenharmony_ci ioctl |= I365_IOCTL_16BIT(map); 57962306a36Sopenharmony_ci 58062306a36Sopenharmony_ci indirect_write(sock, I365_IOCTL, ioctl); 58162306a36Sopenharmony_ci 58262306a36Sopenharmony_ci /* Turn the window back on if needed */ 58362306a36Sopenharmony_ci if (io->flags & MAP_ACTIVE) 58462306a36Sopenharmony_ci indirect_setbit(sock, I365_ADDRWIN, I365_ENA_IO(map)); 58562306a36Sopenharmony_ci 58662306a36Sopenharmony_ci return 0; 58762306a36Sopenharmony_ci} 58862306a36Sopenharmony_ci 58962306a36Sopenharmony_cistatic int i82092aa_set_mem_map(struct pcmcia_socket *socket, 59062306a36Sopenharmony_ci struct pccard_mem_map *mem) 59162306a36Sopenharmony_ci{ 59262306a36Sopenharmony_ci struct socket_info *sock_info = container_of(socket, struct socket_info, 59362306a36Sopenharmony_ci socket); 59462306a36Sopenharmony_ci unsigned int sock = sock_info->number; 59562306a36Sopenharmony_ci struct pci_bus_region region; 59662306a36Sopenharmony_ci unsigned short base, i; 59762306a36Sopenharmony_ci unsigned char map; 59862306a36Sopenharmony_ci 59962306a36Sopenharmony_ci pcibios_resource_to_bus(sock_info->dev->bus, ®ion, mem->res); 60062306a36Sopenharmony_ci 60162306a36Sopenharmony_ci map = mem->map; 60262306a36Sopenharmony_ci if (map > 4) 60362306a36Sopenharmony_ci return -EINVAL; 60462306a36Sopenharmony_ci 60562306a36Sopenharmony_ci if ((mem->card_start > 0x3ffffff) || (region.start > region.end) || 60662306a36Sopenharmony_ci (mem->speed > 1000)) { 60762306a36Sopenharmony_ci dev_err(&sock_info->dev->dev, 60862306a36Sopenharmony_ci "invalid mem map for socket %i: %llx to %llx with a start of %x\n", 60962306a36Sopenharmony_ci sock, 61062306a36Sopenharmony_ci (unsigned long long)region.start, 61162306a36Sopenharmony_ci (unsigned long long)region.end, 61262306a36Sopenharmony_ci mem->card_start); 61362306a36Sopenharmony_ci return -EINVAL; 61462306a36Sopenharmony_ci } 61562306a36Sopenharmony_ci 61662306a36Sopenharmony_ci /* Turn off the window before changing anything */ 61762306a36Sopenharmony_ci if (indirect_read(sock, I365_ADDRWIN) & I365_ENA_MEM(map)) 61862306a36Sopenharmony_ci indirect_resetbit(sock, I365_ADDRWIN, I365_ENA_MEM(map)); 61962306a36Sopenharmony_ci 62062306a36Sopenharmony_ci /* write the start address */ 62162306a36Sopenharmony_ci base = I365_MEM(map); 62262306a36Sopenharmony_ci i = (region.start >> 12) & 0x0fff; 62362306a36Sopenharmony_ci if (mem->flags & MAP_16BIT) 62462306a36Sopenharmony_ci i |= I365_MEM_16BIT; 62562306a36Sopenharmony_ci if (mem->flags & MAP_0WS) 62662306a36Sopenharmony_ci i |= I365_MEM_0WS; 62762306a36Sopenharmony_ci indirect_write16(sock, base+I365_W_START, i); 62862306a36Sopenharmony_ci 62962306a36Sopenharmony_ci /* write the stop address */ 63062306a36Sopenharmony_ci 63162306a36Sopenharmony_ci i = (region.end >> 12) & 0x0fff; 63262306a36Sopenharmony_ci switch (to_cycles(mem->speed)) { 63362306a36Sopenharmony_ci case 0: 63462306a36Sopenharmony_ci break; 63562306a36Sopenharmony_ci case 1: 63662306a36Sopenharmony_ci i |= I365_MEM_WS0; 63762306a36Sopenharmony_ci break; 63862306a36Sopenharmony_ci case 2: 63962306a36Sopenharmony_ci i |= I365_MEM_WS1; 64062306a36Sopenharmony_ci break; 64162306a36Sopenharmony_ci default: 64262306a36Sopenharmony_ci i |= I365_MEM_WS1 | I365_MEM_WS0; 64362306a36Sopenharmony_ci break; 64462306a36Sopenharmony_ci } 64562306a36Sopenharmony_ci 64662306a36Sopenharmony_ci indirect_write16(sock, base+I365_W_STOP, i); 64762306a36Sopenharmony_ci 64862306a36Sopenharmony_ci /* card start */ 64962306a36Sopenharmony_ci 65062306a36Sopenharmony_ci i = ((mem->card_start - region.start) >> 12) & 0x3fff; 65162306a36Sopenharmony_ci if (mem->flags & MAP_WRPROT) 65262306a36Sopenharmony_ci i |= I365_MEM_WRPROT; 65362306a36Sopenharmony_ci if (mem->flags & MAP_ATTRIB) 65462306a36Sopenharmony_ci i |= I365_MEM_REG; 65562306a36Sopenharmony_ci indirect_write16(sock, base+I365_W_OFF, i); 65662306a36Sopenharmony_ci 65762306a36Sopenharmony_ci /* Enable the window if necessary */ 65862306a36Sopenharmony_ci if (mem->flags & MAP_ACTIVE) 65962306a36Sopenharmony_ci indirect_setbit(sock, I365_ADDRWIN, I365_ENA_MEM(map)); 66062306a36Sopenharmony_ci 66162306a36Sopenharmony_ci return 0; 66262306a36Sopenharmony_ci} 66362306a36Sopenharmony_ci 66462306a36Sopenharmony_cistatic int __init i82092aa_module_init(void) 66562306a36Sopenharmony_ci{ 66662306a36Sopenharmony_ci return pci_register_driver(&i82092aa_pci_driver); 66762306a36Sopenharmony_ci} 66862306a36Sopenharmony_ci 66962306a36Sopenharmony_cistatic void __exit i82092aa_module_exit(void) 67062306a36Sopenharmony_ci{ 67162306a36Sopenharmony_ci pci_unregister_driver(&i82092aa_pci_driver); 67262306a36Sopenharmony_ci if (sockets[0].io_base > 0) 67362306a36Sopenharmony_ci release_region(sockets[0].io_base, 2); 67462306a36Sopenharmony_ci} 67562306a36Sopenharmony_ci 67662306a36Sopenharmony_cimodule_init(i82092aa_module_init); 67762306a36Sopenharmony_cimodule_exit(i82092aa_module_exit); 67862306a36Sopenharmony_ci 679