162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * PCMCIA socket code for the Alchemy Db1xxx/Pb1xxx boards. 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (c) 2009 Manuel Lauss <manuel.lauss@gmail.com> 662306a36Sopenharmony_ci * 762306a36Sopenharmony_ci */ 862306a36Sopenharmony_ci 962306a36Sopenharmony_ci/* This is a fairly generic PCMCIA socket driver suitable for the 1062306a36Sopenharmony_ci * following Alchemy Development boards: 1162306a36Sopenharmony_ci * Db1000, Db/Pb1500, Db/Pb1100, Db/Pb1550, Db/Pb1200, Db1300 1262306a36Sopenharmony_ci * 1362306a36Sopenharmony_ci * The Db1000 is used as a reference: Per-socket card-, carddetect- and 1462306a36Sopenharmony_ci * statuschange IRQs connected to SoC GPIOs, control and status register 1562306a36Sopenharmony_ci * bits arranged in per-socket groups in an external PLD. All boards 1662306a36Sopenharmony_ci * listed here use this layout, including bit positions and meanings. 1762306a36Sopenharmony_ci * Of course there are exceptions in later boards: 1862306a36Sopenharmony_ci * 1962306a36Sopenharmony_ci * - Pb1100/Pb1500: single socket only; voltage key bits VS are 2062306a36Sopenharmony_ci * at STATUS[5:4] (instead of STATUS[1:0]). 2162306a36Sopenharmony_ci * - Au1200-based: additional card-eject irqs, irqs not gpios! 2262306a36Sopenharmony_ci * - Db1300: Db1200-like, no pwr ctrl, single socket (#1). 2362306a36Sopenharmony_ci */ 2462306a36Sopenharmony_ci 2562306a36Sopenharmony_ci#include <linux/delay.h> 2662306a36Sopenharmony_ci#include <linux/gpio.h> 2762306a36Sopenharmony_ci#include <linux/interrupt.h> 2862306a36Sopenharmony_ci#include <linux/pm.h> 2962306a36Sopenharmony_ci#include <linux/module.h> 3062306a36Sopenharmony_ci#include <linux/platform_device.h> 3162306a36Sopenharmony_ci#include <linux/resource.h> 3262306a36Sopenharmony_ci#include <linux/slab.h> 3362306a36Sopenharmony_ci#include <linux/spinlock.h> 3462306a36Sopenharmony_ci 3562306a36Sopenharmony_ci#include <pcmcia/ss.h> 3662306a36Sopenharmony_ci 3762306a36Sopenharmony_ci#include <asm/mach-au1x00/au1000.h> 3862306a36Sopenharmony_ci#include <asm/mach-db1x00/bcsr.h> 3962306a36Sopenharmony_ci 4062306a36Sopenharmony_ci#define MEM_MAP_SIZE 0x400000 4162306a36Sopenharmony_ci#define IO_MAP_SIZE 0x1000 4262306a36Sopenharmony_ci 4362306a36Sopenharmony_cistruct db1x_pcmcia_sock { 4462306a36Sopenharmony_ci struct pcmcia_socket socket; 4562306a36Sopenharmony_ci int nr; /* socket number */ 4662306a36Sopenharmony_ci void *virt_io; 4762306a36Sopenharmony_ci 4862306a36Sopenharmony_ci phys_addr_t phys_io; 4962306a36Sopenharmony_ci phys_addr_t phys_attr; 5062306a36Sopenharmony_ci phys_addr_t phys_mem; 5162306a36Sopenharmony_ci 5262306a36Sopenharmony_ci /* previous flags for set_socket() */ 5362306a36Sopenharmony_ci unsigned int old_flags; 5462306a36Sopenharmony_ci 5562306a36Sopenharmony_ci /* interrupt sources: linux irq numbers! */ 5662306a36Sopenharmony_ci int insert_irq; /* default carddetect irq */ 5762306a36Sopenharmony_ci int stschg_irq; /* card-status-change irq */ 5862306a36Sopenharmony_ci int card_irq; /* card irq */ 5962306a36Sopenharmony_ci int eject_irq; /* db1200/pb1200 have these */ 6062306a36Sopenharmony_ci int insert_gpio; /* db1000 carddetect gpio */ 6162306a36Sopenharmony_ci 6262306a36Sopenharmony_ci#define BOARD_TYPE_DEFAULT 0 /* most boards */ 6362306a36Sopenharmony_ci#define BOARD_TYPE_DB1200 1 /* IRQs aren't gpios */ 6462306a36Sopenharmony_ci#define BOARD_TYPE_PB1100 2 /* VS bits slightly different */ 6562306a36Sopenharmony_ci#define BOARD_TYPE_DB1300 3 /* no power control */ 6662306a36Sopenharmony_ci int board_type; 6762306a36Sopenharmony_ci}; 6862306a36Sopenharmony_ci 6962306a36Sopenharmony_ci#define to_db1x_socket(x) container_of(x, struct db1x_pcmcia_sock, socket) 7062306a36Sopenharmony_ci 7162306a36Sopenharmony_cistatic int db1300_card_inserted(struct db1x_pcmcia_sock *sock) 7262306a36Sopenharmony_ci{ 7362306a36Sopenharmony_ci return bcsr_read(BCSR_SIGSTAT) & (1 << 8); 7462306a36Sopenharmony_ci} 7562306a36Sopenharmony_ci 7662306a36Sopenharmony_ci/* DB/PB1200: check CPLD SIGSTATUS register bit 10/12 */ 7762306a36Sopenharmony_cistatic int db1200_card_inserted(struct db1x_pcmcia_sock *sock) 7862306a36Sopenharmony_ci{ 7962306a36Sopenharmony_ci unsigned short sigstat; 8062306a36Sopenharmony_ci 8162306a36Sopenharmony_ci sigstat = bcsr_read(BCSR_SIGSTAT); 8262306a36Sopenharmony_ci return sigstat & 1 << (8 + 2 * sock->nr); 8362306a36Sopenharmony_ci} 8462306a36Sopenharmony_ci 8562306a36Sopenharmony_ci/* carddetect gpio: low-active */ 8662306a36Sopenharmony_cistatic int db1000_card_inserted(struct db1x_pcmcia_sock *sock) 8762306a36Sopenharmony_ci{ 8862306a36Sopenharmony_ci return !gpio_get_value(sock->insert_gpio); 8962306a36Sopenharmony_ci} 9062306a36Sopenharmony_ci 9162306a36Sopenharmony_cistatic int db1x_card_inserted(struct db1x_pcmcia_sock *sock) 9262306a36Sopenharmony_ci{ 9362306a36Sopenharmony_ci switch (sock->board_type) { 9462306a36Sopenharmony_ci case BOARD_TYPE_DB1200: 9562306a36Sopenharmony_ci return db1200_card_inserted(sock); 9662306a36Sopenharmony_ci case BOARD_TYPE_DB1300: 9762306a36Sopenharmony_ci return db1300_card_inserted(sock); 9862306a36Sopenharmony_ci default: 9962306a36Sopenharmony_ci return db1000_card_inserted(sock); 10062306a36Sopenharmony_ci } 10162306a36Sopenharmony_ci} 10262306a36Sopenharmony_ci 10362306a36Sopenharmony_ci/* STSCHG tends to bounce heavily when cards are inserted/ejected. 10462306a36Sopenharmony_ci * To avoid this, the interrupt is normally disabled and only enabled 10562306a36Sopenharmony_ci * after reset to a card has been de-asserted. 10662306a36Sopenharmony_ci */ 10762306a36Sopenharmony_cistatic inline void set_stschg(struct db1x_pcmcia_sock *sock, int en) 10862306a36Sopenharmony_ci{ 10962306a36Sopenharmony_ci if (sock->stschg_irq != -1) { 11062306a36Sopenharmony_ci if (en) 11162306a36Sopenharmony_ci enable_irq(sock->stschg_irq); 11262306a36Sopenharmony_ci else 11362306a36Sopenharmony_ci disable_irq(sock->stschg_irq); 11462306a36Sopenharmony_ci } 11562306a36Sopenharmony_ci} 11662306a36Sopenharmony_ci 11762306a36Sopenharmony_cistatic irqreturn_t db1000_pcmcia_cdirq(int irq, void *data) 11862306a36Sopenharmony_ci{ 11962306a36Sopenharmony_ci struct db1x_pcmcia_sock *sock = data; 12062306a36Sopenharmony_ci 12162306a36Sopenharmony_ci pcmcia_parse_events(&sock->socket, SS_DETECT); 12262306a36Sopenharmony_ci 12362306a36Sopenharmony_ci return IRQ_HANDLED; 12462306a36Sopenharmony_ci} 12562306a36Sopenharmony_ci 12662306a36Sopenharmony_cistatic irqreturn_t db1000_pcmcia_stschgirq(int irq, void *data) 12762306a36Sopenharmony_ci{ 12862306a36Sopenharmony_ci struct db1x_pcmcia_sock *sock = data; 12962306a36Sopenharmony_ci 13062306a36Sopenharmony_ci pcmcia_parse_events(&sock->socket, SS_STSCHG); 13162306a36Sopenharmony_ci 13262306a36Sopenharmony_ci return IRQ_HANDLED; 13362306a36Sopenharmony_ci} 13462306a36Sopenharmony_ci 13562306a36Sopenharmony_ci/* Db/Pb1200 have separate per-socket insertion and ejection 13662306a36Sopenharmony_ci * interrupts which stay asserted as long as the card is 13762306a36Sopenharmony_ci * inserted/missing. The one which caused us to be called 13862306a36Sopenharmony_ci * needs to be disabled and the other one enabled. 13962306a36Sopenharmony_ci */ 14062306a36Sopenharmony_cistatic irqreturn_t db1200_pcmcia_cdirq(int irq, void *data) 14162306a36Sopenharmony_ci{ 14262306a36Sopenharmony_ci disable_irq_nosync(irq); 14362306a36Sopenharmony_ci return IRQ_WAKE_THREAD; 14462306a36Sopenharmony_ci} 14562306a36Sopenharmony_ci 14662306a36Sopenharmony_cistatic irqreturn_t db1200_pcmcia_cdirq_fn(int irq, void *data) 14762306a36Sopenharmony_ci{ 14862306a36Sopenharmony_ci struct db1x_pcmcia_sock *sock = data; 14962306a36Sopenharmony_ci 15062306a36Sopenharmony_ci /* Wait a bit for the signals to stop bouncing. */ 15162306a36Sopenharmony_ci msleep(100); 15262306a36Sopenharmony_ci if (irq == sock->insert_irq) 15362306a36Sopenharmony_ci enable_irq(sock->eject_irq); 15462306a36Sopenharmony_ci else 15562306a36Sopenharmony_ci enable_irq(sock->insert_irq); 15662306a36Sopenharmony_ci 15762306a36Sopenharmony_ci pcmcia_parse_events(&sock->socket, SS_DETECT); 15862306a36Sopenharmony_ci 15962306a36Sopenharmony_ci return IRQ_HANDLED; 16062306a36Sopenharmony_ci} 16162306a36Sopenharmony_ci 16262306a36Sopenharmony_cistatic int db1x_pcmcia_setup_irqs(struct db1x_pcmcia_sock *sock) 16362306a36Sopenharmony_ci{ 16462306a36Sopenharmony_ci int ret; 16562306a36Sopenharmony_ci 16662306a36Sopenharmony_ci if (sock->stschg_irq != -1) { 16762306a36Sopenharmony_ci ret = request_irq(sock->stschg_irq, db1000_pcmcia_stschgirq, 16862306a36Sopenharmony_ci 0, "pcmcia_stschg", sock); 16962306a36Sopenharmony_ci if (ret) 17062306a36Sopenharmony_ci return ret; 17162306a36Sopenharmony_ci } 17262306a36Sopenharmony_ci 17362306a36Sopenharmony_ci /* Db/Pb1200 have separate per-socket insertion and ejection 17462306a36Sopenharmony_ci * interrupts, which should show edge behaviour but don't. 17562306a36Sopenharmony_ci * So interrupts are disabled until both insertion and 17662306a36Sopenharmony_ci * ejection handler have been registered and the currently 17762306a36Sopenharmony_ci * active one disabled. 17862306a36Sopenharmony_ci */ 17962306a36Sopenharmony_ci if ((sock->board_type == BOARD_TYPE_DB1200) || 18062306a36Sopenharmony_ci (sock->board_type == BOARD_TYPE_DB1300)) { 18162306a36Sopenharmony_ci ret = request_threaded_irq(sock->insert_irq, db1200_pcmcia_cdirq, 18262306a36Sopenharmony_ci db1200_pcmcia_cdirq_fn, 0, "pcmcia_insert", sock); 18362306a36Sopenharmony_ci if (ret) 18462306a36Sopenharmony_ci goto out1; 18562306a36Sopenharmony_ci 18662306a36Sopenharmony_ci ret = request_threaded_irq(sock->eject_irq, db1200_pcmcia_cdirq, 18762306a36Sopenharmony_ci db1200_pcmcia_cdirq_fn, 0, "pcmcia_eject", sock); 18862306a36Sopenharmony_ci if (ret) { 18962306a36Sopenharmony_ci free_irq(sock->insert_irq, sock); 19062306a36Sopenharmony_ci goto out1; 19162306a36Sopenharmony_ci } 19262306a36Sopenharmony_ci 19362306a36Sopenharmony_ci /* enable the currently silent one */ 19462306a36Sopenharmony_ci if (db1x_card_inserted(sock)) 19562306a36Sopenharmony_ci enable_irq(sock->eject_irq); 19662306a36Sopenharmony_ci else 19762306a36Sopenharmony_ci enable_irq(sock->insert_irq); 19862306a36Sopenharmony_ci } else { 19962306a36Sopenharmony_ci /* all other (older) Db1x00 boards use a GPIO to show 20062306a36Sopenharmony_ci * card detection status: use both-edge triggers. 20162306a36Sopenharmony_ci */ 20262306a36Sopenharmony_ci irq_set_irq_type(sock->insert_irq, IRQ_TYPE_EDGE_BOTH); 20362306a36Sopenharmony_ci ret = request_irq(sock->insert_irq, db1000_pcmcia_cdirq, 20462306a36Sopenharmony_ci 0, "pcmcia_carddetect", sock); 20562306a36Sopenharmony_ci 20662306a36Sopenharmony_ci if (ret) 20762306a36Sopenharmony_ci goto out1; 20862306a36Sopenharmony_ci } 20962306a36Sopenharmony_ci 21062306a36Sopenharmony_ci return 0; /* all done */ 21162306a36Sopenharmony_ci 21262306a36Sopenharmony_ciout1: 21362306a36Sopenharmony_ci if (sock->stschg_irq != -1) 21462306a36Sopenharmony_ci free_irq(sock->stschg_irq, sock); 21562306a36Sopenharmony_ci 21662306a36Sopenharmony_ci return ret; 21762306a36Sopenharmony_ci} 21862306a36Sopenharmony_ci 21962306a36Sopenharmony_cistatic void db1x_pcmcia_free_irqs(struct db1x_pcmcia_sock *sock) 22062306a36Sopenharmony_ci{ 22162306a36Sopenharmony_ci if (sock->stschg_irq != -1) 22262306a36Sopenharmony_ci free_irq(sock->stschg_irq, sock); 22362306a36Sopenharmony_ci 22462306a36Sopenharmony_ci free_irq(sock->insert_irq, sock); 22562306a36Sopenharmony_ci if (sock->eject_irq != -1) 22662306a36Sopenharmony_ci free_irq(sock->eject_irq, sock); 22762306a36Sopenharmony_ci} 22862306a36Sopenharmony_ci 22962306a36Sopenharmony_ci/* 23062306a36Sopenharmony_ci * configure a PCMCIA socket on the Db1x00 series of boards (and 23162306a36Sopenharmony_ci * compatibles). 23262306a36Sopenharmony_ci * 23362306a36Sopenharmony_ci * 2 external registers are involved: 23462306a36Sopenharmony_ci * pcmcia_status (offset 0x04): bits [0:1/2:3]: read card voltage id 23562306a36Sopenharmony_ci * pcmcia_control(offset 0x10): 23662306a36Sopenharmony_ci * bits[0:1] set vcc for card 23762306a36Sopenharmony_ci * bits[2:3] set vpp for card 23862306a36Sopenharmony_ci * bit 4: enable data buffers 23962306a36Sopenharmony_ci * bit 7: reset# for card 24062306a36Sopenharmony_ci * add 8 for second socket. 24162306a36Sopenharmony_ci */ 24262306a36Sopenharmony_cistatic int db1x_pcmcia_configure(struct pcmcia_socket *skt, 24362306a36Sopenharmony_ci struct socket_state_t *state) 24462306a36Sopenharmony_ci{ 24562306a36Sopenharmony_ci struct db1x_pcmcia_sock *sock = to_db1x_socket(skt); 24662306a36Sopenharmony_ci unsigned short cr_clr, cr_set; 24762306a36Sopenharmony_ci unsigned int changed; 24862306a36Sopenharmony_ci int v, p, ret; 24962306a36Sopenharmony_ci 25062306a36Sopenharmony_ci /* card voltage setup */ 25162306a36Sopenharmony_ci cr_clr = (0xf << (sock->nr * 8)); /* clear voltage settings */ 25262306a36Sopenharmony_ci cr_set = 0; 25362306a36Sopenharmony_ci v = p = ret = 0; 25462306a36Sopenharmony_ci 25562306a36Sopenharmony_ci switch (state->Vcc) { 25662306a36Sopenharmony_ci case 50: 25762306a36Sopenharmony_ci ++v; 25862306a36Sopenharmony_ci fallthrough; 25962306a36Sopenharmony_ci case 33: 26062306a36Sopenharmony_ci ++v; 26162306a36Sopenharmony_ci fallthrough; 26262306a36Sopenharmony_ci case 0: 26362306a36Sopenharmony_ci break; 26462306a36Sopenharmony_ci default: 26562306a36Sopenharmony_ci printk(KERN_INFO "pcmcia%d unsupported Vcc %d\n", 26662306a36Sopenharmony_ci sock->nr, state->Vcc); 26762306a36Sopenharmony_ci } 26862306a36Sopenharmony_ci 26962306a36Sopenharmony_ci switch (state->Vpp) { 27062306a36Sopenharmony_ci case 12: 27162306a36Sopenharmony_ci ++p; 27262306a36Sopenharmony_ci fallthrough; 27362306a36Sopenharmony_ci case 33: 27462306a36Sopenharmony_ci case 50: 27562306a36Sopenharmony_ci ++p; 27662306a36Sopenharmony_ci fallthrough; 27762306a36Sopenharmony_ci case 0: 27862306a36Sopenharmony_ci break; 27962306a36Sopenharmony_ci default: 28062306a36Sopenharmony_ci printk(KERN_INFO "pcmcia%d unsupported Vpp %d\n", 28162306a36Sopenharmony_ci sock->nr, state->Vpp); 28262306a36Sopenharmony_ci } 28362306a36Sopenharmony_ci 28462306a36Sopenharmony_ci /* sanity check: Vpp must be 0, 12, or Vcc */ 28562306a36Sopenharmony_ci if (((state->Vcc == 33) && (state->Vpp == 50)) || 28662306a36Sopenharmony_ci ((state->Vcc == 50) && (state->Vpp == 33))) { 28762306a36Sopenharmony_ci printk(KERN_INFO "pcmcia%d bad Vcc/Vpp combo (%d %d)\n", 28862306a36Sopenharmony_ci sock->nr, state->Vcc, state->Vpp); 28962306a36Sopenharmony_ci v = p = 0; 29062306a36Sopenharmony_ci ret = -EINVAL; 29162306a36Sopenharmony_ci } 29262306a36Sopenharmony_ci 29362306a36Sopenharmony_ci /* create new voltage code */ 29462306a36Sopenharmony_ci if (sock->board_type != BOARD_TYPE_DB1300) 29562306a36Sopenharmony_ci cr_set |= ((v << 2) | p) << (sock->nr * 8); 29662306a36Sopenharmony_ci 29762306a36Sopenharmony_ci changed = state->flags ^ sock->old_flags; 29862306a36Sopenharmony_ci 29962306a36Sopenharmony_ci if (changed & SS_RESET) { 30062306a36Sopenharmony_ci if (state->flags & SS_RESET) { 30162306a36Sopenharmony_ci set_stschg(sock, 0); 30262306a36Sopenharmony_ci /* assert reset, disable io buffers */ 30362306a36Sopenharmony_ci cr_clr |= (1 << (7 + (sock->nr * 8))); 30462306a36Sopenharmony_ci cr_clr |= (1 << (4 + (sock->nr * 8))); 30562306a36Sopenharmony_ci } else { 30662306a36Sopenharmony_ci /* de-assert reset, enable io buffers */ 30762306a36Sopenharmony_ci cr_set |= 1 << (7 + (sock->nr * 8)); 30862306a36Sopenharmony_ci cr_set |= 1 << (4 + (sock->nr * 8)); 30962306a36Sopenharmony_ci } 31062306a36Sopenharmony_ci } 31162306a36Sopenharmony_ci 31262306a36Sopenharmony_ci /* update PCMCIA configuration */ 31362306a36Sopenharmony_ci bcsr_mod(BCSR_PCMCIA, cr_clr, cr_set); 31462306a36Sopenharmony_ci 31562306a36Sopenharmony_ci sock->old_flags = state->flags; 31662306a36Sopenharmony_ci 31762306a36Sopenharmony_ci /* reset was taken away: give card time to initialize properly */ 31862306a36Sopenharmony_ci if ((changed & SS_RESET) && !(state->flags & SS_RESET)) { 31962306a36Sopenharmony_ci msleep(500); 32062306a36Sopenharmony_ci set_stschg(sock, 1); 32162306a36Sopenharmony_ci } 32262306a36Sopenharmony_ci 32362306a36Sopenharmony_ci return ret; 32462306a36Sopenharmony_ci} 32562306a36Sopenharmony_ci 32662306a36Sopenharmony_ci/* VCC bits at [3:2]/[11:10] */ 32762306a36Sopenharmony_ci#define GET_VCC(cr, socknr) \ 32862306a36Sopenharmony_ci ((((cr) >> 2) >> ((socknr) * 8)) & 3) 32962306a36Sopenharmony_ci 33062306a36Sopenharmony_ci/* VS bits at [0:1]/[3:2] */ 33162306a36Sopenharmony_ci#define GET_VS(sr, socknr) \ 33262306a36Sopenharmony_ci (((sr) >> (2 * (socknr))) & 3) 33362306a36Sopenharmony_ci 33462306a36Sopenharmony_ci/* reset bits at [7]/[15] */ 33562306a36Sopenharmony_ci#define GET_RESET(cr, socknr) \ 33662306a36Sopenharmony_ci ((cr) & (1 << (7 + (8 * (socknr))))) 33762306a36Sopenharmony_ci 33862306a36Sopenharmony_cistatic int db1x_pcmcia_get_status(struct pcmcia_socket *skt, 33962306a36Sopenharmony_ci unsigned int *value) 34062306a36Sopenharmony_ci{ 34162306a36Sopenharmony_ci struct db1x_pcmcia_sock *sock = to_db1x_socket(skt); 34262306a36Sopenharmony_ci unsigned short cr, sr; 34362306a36Sopenharmony_ci unsigned int status; 34462306a36Sopenharmony_ci 34562306a36Sopenharmony_ci status = db1x_card_inserted(sock) ? SS_DETECT : 0; 34662306a36Sopenharmony_ci 34762306a36Sopenharmony_ci cr = bcsr_read(BCSR_PCMCIA); 34862306a36Sopenharmony_ci sr = bcsr_read(BCSR_STATUS); 34962306a36Sopenharmony_ci 35062306a36Sopenharmony_ci /* PB1100/PB1500: voltage key bits are at [5:4] */ 35162306a36Sopenharmony_ci if (sock->board_type == BOARD_TYPE_PB1100) 35262306a36Sopenharmony_ci sr >>= 4; 35362306a36Sopenharmony_ci 35462306a36Sopenharmony_ci /* determine card type */ 35562306a36Sopenharmony_ci switch (GET_VS(sr, sock->nr)) { 35662306a36Sopenharmony_ci case 0: 35762306a36Sopenharmony_ci case 2: 35862306a36Sopenharmony_ci status |= SS_3VCARD; /* 3V card */ 35962306a36Sopenharmony_ci break; 36062306a36Sopenharmony_ci case 3: 36162306a36Sopenharmony_ci break; /* 5V card: set nothing */ 36262306a36Sopenharmony_ci default: 36362306a36Sopenharmony_ci status |= SS_XVCARD; /* treated as unsupported in core */ 36462306a36Sopenharmony_ci } 36562306a36Sopenharmony_ci 36662306a36Sopenharmony_ci /* if Vcc is not zero, we have applied power to a card */ 36762306a36Sopenharmony_ci status |= GET_VCC(cr, sock->nr) ? SS_POWERON : 0; 36862306a36Sopenharmony_ci 36962306a36Sopenharmony_ci /* DB1300: power always on, but don't tell when no card present */ 37062306a36Sopenharmony_ci if ((sock->board_type == BOARD_TYPE_DB1300) && (status & SS_DETECT)) 37162306a36Sopenharmony_ci status = SS_POWERON | SS_3VCARD | SS_DETECT; 37262306a36Sopenharmony_ci 37362306a36Sopenharmony_ci /* reset de-asserted? then we're ready */ 37462306a36Sopenharmony_ci status |= (GET_RESET(cr, sock->nr)) ? SS_READY : SS_RESET; 37562306a36Sopenharmony_ci 37662306a36Sopenharmony_ci *value = status; 37762306a36Sopenharmony_ci 37862306a36Sopenharmony_ci return 0; 37962306a36Sopenharmony_ci} 38062306a36Sopenharmony_ci 38162306a36Sopenharmony_cistatic int db1x_pcmcia_sock_init(struct pcmcia_socket *skt) 38262306a36Sopenharmony_ci{ 38362306a36Sopenharmony_ci return 0; 38462306a36Sopenharmony_ci} 38562306a36Sopenharmony_ci 38662306a36Sopenharmony_cistatic int db1x_pcmcia_sock_suspend(struct pcmcia_socket *skt) 38762306a36Sopenharmony_ci{ 38862306a36Sopenharmony_ci return 0; 38962306a36Sopenharmony_ci} 39062306a36Sopenharmony_ci 39162306a36Sopenharmony_cistatic int au1x00_pcmcia_set_io_map(struct pcmcia_socket *skt, 39262306a36Sopenharmony_ci struct pccard_io_map *map) 39362306a36Sopenharmony_ci{ 39462306a36Sopenharmony_ci struct db1x_pcmcia_sock *sock = to_db1x_socket(skt); 39562306a36Sopenharmony_ci 39662306a36Sopenharmony_ci map->start = (u32)sock->virt_io; 39762306a36Sopenharmony_ci map->stop = map->start + IO_MAP_SIZE; 39862306a36Sopenharmony_ci 39962306a36Sopenharmony_ci return 0; 40062306a36Sopenharmony_ci} 40162306a36Sopenharmony_ci 40262306a36Sopenharmony_cistatic int au1x00_pcmcia_set_mem_map(struct pcmcia_socket *skt, 40362306a36Sopenharmony_ci struct pccard_mem_map *map) 40462306a36Sopenharmony_ci{ 40562306a36Sopenharmony_ci struct db1x_pcmcia_sock *sock = to_db1x_socket(skt); 40662306a36Sopenharmony_ci 40762306a36Sopenharmony_ci if (map->flags & MAP_ATTRIB) 40862306a36Sopenharmony_ci map->static_start = sock->phys_attr + map->card_start; 40962306a36Sopenharmony_ci else 41062306a36Sopenharmony_ci map->static_start = sock->phys_mem + map->card_start; 41162306a36Sopenharmony_ci 41262306a36Sopenharmony_ci return 0; 41362306a36Sopenharmony_ci} 41462306a36Sopenharmony_ci 41562306a36Sopenharmony_cistatic struct pccard_operations db1x_pcmcia_operations = { 41662306a36Sopenharmony_ci .init = db1x_pcmcia_sock_init, 41762306a36Sopenharmony_ci .suspend = db1x_pcmcia_sock_suspend, 41862306a36Sopenharmony_ci .get_status = db1x_pcmcia_get_status, 41962306a36Sopenharmony_ci .set_socket = db1x_pcmcia_configure, 42062306a36Sopenharmony_ci .set_io_map = au1x00_pcmcia_set_io_map, 42162306a36Sopenharmony_ci .set_mem_map = au1x00_pcmcia_set_mem_map, 42262306a36Sopenharmony_ci}; 42362306a36Sopenharmony_ci 42462306a36Sopenharmony_cistatic int db1x_pcmcia_socket_probe(struct platform_device *pdev) 42562306a36Sopenharmony_ci{ 42662306a36Sopenharmony_ci struct db1x_pcmcia_sock *sock; 42762306a36Sopenharmony_ci struct resource *r; 42862306a36Sopenharmony_ci int ret, bid; 42962306a36Sopenharmony_ci 43062306a36Sopenharmony_ci sock = kzalloc(sizeof(struct db1x_pcmcia_sock), GFP_KERNEL); 43162306a36Sopenharmony_ci if (!sock) 43262306a36Sopenharmony_ci return -ENOMEM; 43362306a36Sopenharmony_ci 43462306a36Sopenharmony_ci sock->nr = pdev->id; 43562306a36Sopenharmony_ci 43662306a36Sopenharmony_ci bid = BCSR_WHOAMI_BOARD(bcsr_read(BCSR_WHOAMI)); 43762306a36Sopenharmony_ci switch (bid) { 43862306a36Sopenharmony_ci case BCSR_WHOAMI_PB1500: 43962306a36Sopenharmony_ci case BCSR_WHOAMI_PB1500R2: 44062306a36Sopenharmony_ci case BCSR_WHOAMI_PB1100: 44162306a36Sopenharmony_ci sock->board_type = BOARD_TYPE_PB1100; 44262306a36Sopenharmony_ci break; 44362306a36Sopenharmony_ci case BCSR_WHOAMI_DB1000 ... BCSR_WHOAMI_PB1550_SDR: 44462306a36Sopenharmony_ci sock->board_type = BOARD_TYPE_DEFAULT; 44562306a36Sopenharmony_ci break; 44662306a36Sopenharmony_ci case BCSR_WHOAMI_PB1200 ... BCSR_WHOAMI_DB1200: 44762306a36Sopenharmony_ci sock->board_type = BOARD_TYPE_DB1200; 44862306a36Sopenharmony_ci break; 44962306a36Sopenharmony_ci case BCSR_WHOAMI_DB1300: 45062306a36Sopenharmony_ci sock->board_type = BOARD_TYPE_DB1300; 45162306a36Sopenharmony_ci break; 45262306a36Sopenharmony_ci default: 45362306a36Sopenharmony_ci printk(KERN_INFO "db1xxx-ss: unknown board %d!\n", bid); 45462306a36Sopenharmony_ci ret = -ENODEV; 45562306a36Sopenharmony_ci goto out0; 45662306a36Sopenharmony_ci } 45762306a36Sopenharmony_ci 45862306a36Sopenharmony_ci /* 45962306a36Sopenharmony_ci * gather resources necessary and optional nice-to-haves to 46062306a36Sopenharmony_ci * operate a socket: 46162306a36Sopenharmony_ci * This includes IRQs for Carddetection/ejection, the card 46262306a36Sopenharmony_ci * itself and optional status change detection. 46362306a36Sopenharmony_ci * Also, the memory areas covered by a socket. For these 46462306a36Sopenharmony_ci * we require the real 36bit addresses (see the au1000.h 46562306a36Sopenharmony_ci * header for more information). 46662306a36Sopenharmony_ci */ 46762306a36Sopenharmony_ci 46862306a36Sopenharmony_ci /* card: irq assigned to the card itself. */ 46962306a36Sopenharmony_ci r = platform_get_resource_byname(pdev, IORESOURCE_IRQ, "card"); 47062306a36Sopenharmony_ci sock->card_irq = r ? r->start : 0; 47162306a36Sopenharmony_ci 47262306a36Sopenharmony_ci /* insert: irq which triggers on card insertion/ejection 47362306a36Sopenharmony_ci * BIG FAT NOTE: on DB1000/1100/1500/1550 we pass a GPIO here! 47462306a36Sopenharmony_ci */ 47562306a36Sopenharmony_ci r = platform_get_resource_byname(pdev, IORESOURCE_IRQ, "insert"); 47662306a36Sopenharmony_ci sock->insert_irq = r ? r->start : -1; 47762306a36Sopenharmony_ci if (sock->board_type == BOARD_TYPE_DEFAULT) { 47862306a36Sopenharmony_ci sock->insert_gpio = r ? r->start : -1; 47962306a36Sopenharmony_ci sock->insert_irq = r ? gpio_to_irq(r->start) : -1; 48062306a36Sopenharmony_ci } 48162306a36Sopenharmony_ci 48262306a36Sopenharmony_ci /* stschg: irq which trigger on card status change (optional) */ 48362306a36Sopenharmony_ci r = platform_get_resource_byname(pdev, IORESOURCE_IRQ, "stschg"); 48462306a36Sopenharmony_ci sock->stschg_irq = r ? r->start : -1; 48562306a36Sopenharmony_ci 48662306a36Sopenharmony_ci /* eject: irq which triggers on ejection (DB1200/PB1200 only) */ 48762306a36Sopenharmony_ci r = platform_get_resource_byname(pdev, IORESOURCE_IRQ, "eject"); 48862306a36Sopenharmony_ci sock->eject_irq = r ? r->start : -1; 48962306a36Sopenharmony_ci 49062306a36Sopenharmony_ci ret = -ENODEV; 49162306a36Sopenharmony_ci 49262306a36Sopenharmony_ci /* 36bit PCMCIA Attribute area address */ 49362306a36Sopenharmony_ci r = platform_get_resource_byname(pdev, IORESOURCE_MEM, "pcmcia-attr"); 49462306a36Sopenharmony_ci if (!r) { 49562306a36Sopenharmony_ci printk(KERN_ERR "pcmcia%d has no 'pseudo-attr' resource!\n", 49662306a36Sopenharmony_ci sock->nr); 49762306a36Sopenharmony_ci goto out0; 49862306a36Sopenharmony_ci } 49962306a36Sopenharmony_ci sock->phys_attr = r->start; 50062306a36Sopenharmony_ci 50162306a36Sopenharmony_ci /* 36bit PCMCIA Memory area address */ 50262306a36Sopenharmony_ci r = platform_get_resource_byname(pdev, IORESOURCE_MEM, "pcmcia-mem"); 50362306a36Sopenharmony_ci if (!r) { 50462306a36Sopenharmony_ci printk(KERN_ERR "pcmcia%d has no 'pseudo-mem' resource!\n", 50562306a36Sopenharmony_ci sock->nr); 50662306a36Sopenharmony_ci goto out0; 50762306a36Sopenharmony_ci } 50862306a36Sopenharmony_ci sock->phys_mem = r->start; 50962306a36Sopenharmony_ci 51062306a36Sopenharmony_ci /* 36bit PCMCIA IO area address */ 51162306a36Sopenharmony_ci r = platform_get_resource_byname(pdev, IORESOURCE_MEM, "pcmcia-io"); 51262306a36Sopenharmony_ci if (!r) { 51362306a36Sopenharmony_ci printk(KERN_ERR "pcmcia%d has no 'pseudo-io' resource!\n", 51462306a36Sopenharmony_ci sock->nr); 51562306a36Sopenharmony_ci goto out0; 51662306a36Sopenharmony_ci } 51762306a36Sopenharmony_ci sock->phys_io = r->start; 51862306a36Sopenharmony_ci 51962306a36Sopenharmony_ci /* 52062306a36Sopenharmony_ci * PCMCIA client drivers use the inb/outb macros to access 52162306a36Sopenharmony_ci * the IO registers. Since mips_io_port_base is added 52262306a36Sopenharmony_ci * to the access address of the mips implementation of 52362306a36Sopenharmony_ci * inb/outb, we need to subtract it here because we want 52462306a36Sopenharmony_ci * to access the I/O or MEM address directly, without 52562306a36Sopenharmony_ci * going through this "mips_io_port_base" mechanism. 52662306a36Sopenharmony_ci */ 52762306a36Sopenharmony_ci sock->virt_io = (void *)(ioremap(sock->phys_io, IO_MAP_SIZE) - 52862306a36Sopenharmony_ci mips_io_port_base); 52962306a36Sopenharmony_ci 53062306a36Sopenharmony_ci if (!sock->virt_io) { 53162306a36Sopenharmony_ci printk(KERN_ERR "pcmcia%d: cannot remap IO area\n", 53262306a36Sopenharmony_ci sock->nr); 53362306a36Sopenharmony_ci ret = -ENOMEM; 53462306a36Sopenharmony_ci goto out0; 53562306a36Sopenharmony_ci } 53662306a36Sopenharmony_ci 53762306a36Sopenharmony_ci sock->socket.ops = &db1x_pcmcia_operations; 53862306a36Sopenharmony_ci sock->socket.owner = THIS_MODULE; 53962306a36Sopenharmony_ci sock->socket.pci_irq = sock->card_irq; 54062306a36Sopenharmony_ci sock->socket.features = SS_CAP_STATIC_MAP | SS_CAP_PCCARD; 54162306a36Sopenharmony_ci sock->socket.map_size = MEM_MAP_SIZE; 54262306a36Sopenharmony_ci sock->socket.io_offset = (unsigned long)sock->virt_io; 54362306a36Sopenharmony_ci sock->socket.dev.parent = &pdev->dev; 54462306a36Sopenharmony_ci sock->socket.resource_ops = &pccard_static_ops; 54562306a36Sopenharmony_ci 54662306a36Sopenharmony_ci platform_set_drvdata(pdev, sock); 54762306a36Sopenharmony_ci 54862306a36Sopenharmony_ci ret = db1x_pcmcia_setup_irqs(sock); 54962306a36Sopenharmony_ci if (ret) { 55062306a36Sopenharmony_ci printk(KERN_ERR "pcmcia%d cannot setup interrupts\n", 55162306a36Sopenharmony_ci sock->nr); 55262306a36Sopenharmony_ci goto out1; 55362306a36Sopenharmony_ci } 55462306a36Sopenharmony_ci 55562306a36Sopenharmony_ci set_stschg(sock, 0); 55662306a36Sopenharmony_ci 55762306a36Sopenharmony_ci ret = pcmcia_register_socket(&sock->socket); 55862306a36Sopenharmony_ci if (ret) { 55962306a36Sopenharmony_ci printk(KERN_ERR "pcmcia%d failed to register\n", sock->nr); 56062306a36Sopenharmony_ci goto out2; 56162306a36Sopenharmony_ci } 56262306a36Sopenharmony_ci 56362306a36Sopenharmony_ci printk(KERN_INFO "Alchemy Db/Pb1xxx pcmcia%d @ io/attr/mem %09llx" 56462306a36Sopenharmony_ci "(%p) %09llx %09llx card/insert/stschg/eject irqs @ %d " 56562306a36Sopenharmony_ci "%d %d %d\n", sock->nr, sock->phys_io, sock->virt_io, 56662306a36Sopenharmony_ci sock->phys_attr, sock->phys_mem, sock->card_irq, 56762306a36Sopenharmony_ci sock->insert_irq, sock->stschg_irq, sock->eject_irq); 56862306a36Sopenharmony_ci 56962306a36Sopenharmony_ci return 0; 57062306a36Sopenharmony_ci 57162306a36Sopenharmony_ciout2: 57262306a36Sopenharmony_ci db1x_pcmcia_free_irqs(sock); 57362306a36Sopenharmony_ciout1: 57462306a36Sopenharmony_ci iounmap((void *)(sock->virt_io + (u32)mips_io_port_base)); 57562306a36Sopenharmony_ciout0: 57662306a36Sopenharmony_ci kfree(sock); 57762306a36Sopenharmony_ci return ret; 57862306a36Sopenharmony_ci} 57962306a36Sopenharmony_ci 58062306a36Sopenharmony_cistatic int db1x_pcmcia_socket_remove(struct platform_device *pdev) 58162306a36Sopenharmony_ci{ 58262306a36Sopenharmony_ci struct db1x_pcmcia_sock *sock = platform_get_drvdata(pdev); 58362306a36Sopenharmony_ci 58462306a36Sopenharmony_ci db1x_pcmcia_free_irqs(sock); 58562306a36Sopenharmony_ci pcmcia_unregister_socket(&sock->socket); 58662306a36Sopenharmony_ci iounmap((void *)(sock->virt_io + (u32)mips_io_port_base)); 58762306a36Sopenharmony_ci kfree(sock); 58862306a36Sopenharmony_ci 58962306a36Sopenharmony_ci return 0; 59062306a36Sopenharmony_ci} 59162306a36Sopenharmony_ci 59262306a36Sopenharmony_cistatic struct platform_driver db1x_pcmcia_socket_driver = { 59362306a36Sopenharmony_ci .driver = { 59462306a36Sopenharmony_ci .name = "db1xxx_pcmcia", 59562306a36Sopenharmony_ci }, 59662306a36Sopenharmony_ci .probe = db1x_pcmcia_socket_probe, 59762306a36Sopenharmony_ci .remove = db1x_pcmcia_socket_remove, 59862306a36Sopenharmony_ci}; 59962306a36Sopenharmony_ci 60062306a36Sopenharmony_cimodule_platform_driver(db1x_pcmcia_socket_driver); 60162306a36Sopenharmony_ci 60262306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 60362306a36Sopenharmony_ciMODULE_DESCRIPTION("PCMCIA Socket Services for Alchemy Db/Pb1x00 boards"); 60462306a36Sopenharmony_ciMODULE_AUTHOR("Manuel Lauss"); 605