18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * PCMCIA socket code for the Alchemy Db1xxx/Pb1xxx boards. 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (c) 2009 Manuel Lauss <manuel.lauss@gmail.com> 68c2ecf20Sopenharmony_ci * 78c2ecf20Sopenharmony_ci */ 88c2ecf20Sopenharmony_ci 98c2ecf20Sopenharmony_ci/* This is a fairly generic PCMCIA socket driver suitable for the 108c2ecf20Sopenharmony_ci * following Alchemy Development boards: 118c2ecf20Sopenharmony_ci * Db1000, Db/Pb1500, Db/Pb1100, Db/Pb1550, Db/Pb1200, Db1300 128c2ecf20Sopenharmony_ci * 138c2ecf20Sopenharmony_ci * The Db1000 is used as a reference: Per-socket card-, carddetect- and 148c2ecf20Sopenharmony_ci * statuschange IRQs connected to SoC GPIOs, control and status register 158c2ecf20Sopenharmony_ci * bits arranged in per-socket groups in an external PLD. All boards 168c2ecf20Sopenharmony_ci * listed here use this layout, including bit positions and meanings. 178c2ecf20Sopenharmony_ci * Of course there are exceptions in later boards: 188c2ecf20Sopenharmony_ci * 198c2ecf20Sopenharmony_ci * - Pb1100/Pb1500: single socket only; voltage key bits VS are 208c2ecf20Sopenharmony_ci * at STATUS[5:4] (instead of STATUS[1:0]). 218c2ecf20Sopenharmony_ci * - Au1200-based: additional card-eject irqs, irqs not gpios! 228c2ecf20Sopenharmony_ci * - Db1300: Db1200-like, no pwr ctrl, single socket (#1). 238c2ecf20Sopenharmony_ci */ 248c2ecf20Sopenharmony_ci 258c2ecf20Sopenharmony_ci#include <linux/delay.h> 268c2ecf20Sopenharmony_ci#include <linux/gpio.h> 278c2ecf20Sopenharmony_ci#include <linux/interrupt.h> 288c2ecf20Sopenharmony_ci#include <linux/pm.h> 298c2ecf20Sopenharmony_ci#include <linux/module.h> 308c2ecf20Sopenharmony_ci#include <linux/platform_device.h> 318c2ecf20Sopenharmony_ci#include <linux/resource.h> 328c2ecf20Sopenharmony_ci#include <linux/slab.h> 338c2ecf20Sopenharmony_ci#include <linux/spinlock.h> 348c2ecf20Sopenharmony_ci 358c2ecf20Sopenharmony_ci#include <pcmcia/ss.h> 368c2ecf20Sopenharmony_ci 378c2ecf20Sopenharmony_ci#include <asm/mach-au1x00/au1000.h> 388c2ecf20Sopenharmony_ci#include <asm/mach-db1x00/bcsr.h> 398c2ecf20Sopenharmony_ci 408c2ecf20Sopenharmony_ci#define MEM_MAP_SIZE 0x400000 418c2ecf20Sopenharmony_ci#define IO_MAP_SIZE 0x1000 428c2ecf20Sopenharmony_ci 438c2ecf20Sopenharmony_cistruct db1x_pcmcia_sock { 448c2ecf20Sopenharmony_ci struct pcmcia_socket socket; 458c2ecf20Sopenharmony_ci int nr; /* socket number */ 468c2ecf20Sopenharmony_ci void *virt_io; 478c2ecf20Sopenharmony_ci 488c2ecf20Sopenharmony_ci phys_addr_t phys_io; 498c2ecf20Sopenharmony_ci phys_addr_t phys_attr; 508c2ecf20Sopenharmony_ci phys_addr_t phys_mem; 518c2ecf20Sopenharmony_ci 528c2ecf20Sopenharmony_ci /* previous flags for set_socket() */ 538c2ecf20Sopenharmony_ci unsigned int old_flags; 548c2ecf20Sopenharmony_ci 558c2ecf20Sopenharmony_ci /* interrupt sources: linux irq numbers! */ 568c2ecf20Sopenharmony_ci int insert_irq; /* default carddetect irq */ 578c2ecf20Sopenharmony_ci int stschg_irq; /* card-status-change irq */ 588c2ecf20Sopenharmony_ci int card_irq; /* card irq */ 598c2ecf20Sopenharmony_ci int eject_irq; /* db1200/pb1200 have these */ 608c2ecf20Sopenharmony_ci int insert_gpio; /* db1000 carddetect gpio */ 618c2ecf20Sopenharmony_ci 628c2ecf20Sopenharmony_ci#define BOARD_TYPE_DEFAULT 0 /* most boards */ 638c2ecf20Sopenharmony_ci#define BOARD_TYPE_DB1200 1 /* IRQs aren't gpios */ 648c2ecf20Sopenharmony_ci#define BOARD_TYPE_PB1100 2 /* VS bits slightly different */ 658c2ecf20Sopenharmony_ci#define BOARD_TYPE_DB1300 3 /* no power control */ 668c2ecf20Sopenharmony_ci int board_type; 678c2ecf20Sopenharmony_ci}; 688c2ecf20Sopenharmony_ci 698c2ecf20Sopenharmony_ci#define to_db1x_socket(x) container_of(x, struct db1x_pcmcia_sock, socket) 708c2ecf20Sopenharmony_ci 718c2ecf20Sopenharmony_cistatic int db1300_card_inserted(struct db1x_pcmcia_sock *sock) 728c2ecf20Sopenharmony_ci{ 738c2ecf20Sopenharmony_ci return bcsr_read(BCSR_SIGSTAT) & (1 << 8); 748c2ecf20Sopenharmony_ci} 758c2ecf20Sopenharmony_ci 768c2ecf20Sopenharmony_ci/* DB/PB1200: check CPLD SIGSTATUS register bit 10/12 */ 778c2ecf20Sopenharmony_cistatic int db1200_card_inserted(struct db1x_pcmcia_sock *sock) 788c2ecf20Sopenharmony_ci{ 798c2ecf20Sopenharmony_ci unsigned short sigstat; 808c2ecf20Sopenharmony_ci 818c2ecf20Sopenharmony_ci sigstat = bcsr_read(BCSR_SIGSTAT); 828c2ecf20Sopenharmony_ci return sigstat & 1 << (8 + 2 * sock->nr); 838c2ecf20Sopenharmony_ci} 848c2ecf20Sopenharmony_ci 858c2ecf20Sopenharmony_ci/* carddetect gpio: low-active */ 868c2ecf20Sopenharmony_cistatic int db1000_card_inserted(struct db1x_pcmcia_sock *sock) 878c2ecf20Sopenharmony_ci{ 888c2ecf20Sopenharmony_ci return !gpio_get_value(sock->insert_gpio); 898c2ecf20Sopenharmony_ci} 908c2ecf20Sopenharmony_ci 918c2ecf20Sopenharmony_cistatic int db1x_card_inserted(struct db1x_pcmcia_sock *sock) 928c2ecf20Sopenharmony_ci{ 938c2ecf20Sopenharmony_ci switch (sock->board_type) { 948c2ecf20Sopenharmony_ci case BOARD_TYPE_DB1200: 958c2ecf20Sopenharmony_ci return db1200_card_inserted(sock); 968c2ecf20Sopenharmony_ci case BOARD_TYPE_DB1300: 978c2ecf20Sopenharmony_ci return db1300_card_inserted(sock); 988c2ecf20Sopenharmony_ci default: 998c2ecf20Sopenharmony_ci return db1000_card_inserted(sock); 1008c2ecf20Sopenharmony_ci } 1018c2ecf20Sopenharmony_ci} 1028c2ecf20Sopenharmony_ci 1038c2ecf20Sopenharmony_ci/* STSCHG tends to bounce heavily when cards are inserted/ejected. 1048c2ecf20Sopenharmony_ci * To avoid this, the interrupt is normally disabled and only enabled 1058c2ecf20Sopenharmony_ci * after reset to a card has been de-asserted. 1068c2ecf20Sopenharmony_ci */ 1078c2ecf20Sopenharmony_cistatic inline void set_stschg(struct db1x_pcmcia_sock *sock, int en) 1088c2ecf20Sopenharmony_ci{ 1098c2ecf20Sopenharmony_ci if (sock->stschg_irq != -1) { 1108c2ecf20Sopenharmony_ci if (en) 1118c2ecf20Sopenharmony_ci enable_irq(sock->stschg_irq); 1128c2ecf20Sopenharmony_ci else 1138c2ecf20Sopenharmony_ci disable_irq(sock->stschg_irq); 1148c2ecf20Sopenharmony_ci } 1158c2ecf20Sopenharmony_ci} 1168c2ecf20Sopenharmony_ci 1178c2ecf20Sopenharmony_cistatic irqreturn_t db1000_pcmcia_cdirq(int irq, void *data) 1188c2ecf20Sopenharmony_ci{ 1198c2ecf20Sopenharmony_ci struct db1x_pcmcia_sock *sock = data; 1208c2ecf20Sopenharmony_ci 1218c2ecf20Sopenharmony_ci pcmcia_parse_events(&sock->socket, SS_DETECT); 1228c2ecf20Sopenharmony_ci 1238c2ecf20Sopenharmony_ci return IRQ_HANDLED; 1248c2ecf20Sopenharmony_ci} 1258c2ecf20Sopenharmony_ci 1268c2ecf20Sopenharmony_cistatic irqreturn_t db1000_pcmcia_stschgirq(int irq, void *data) 1278c2ecf20Sopenharmony_ci{ 1288c2ecf20Sopenharmony_ci struct db1x_pcmcia_sock *sock = data; 1298c2ecf20Sopenharmony_ci 1308c2ecf20Sopenharmony_ci pcmcia_parse_events(&sock->socket, SS_STSCHG); 1318c2ecf20Sopenharmony_ci 1328c2ecf20Sopenharmony_ci return IRQ_HANDLED; 1338c2ecf20Sopenharmony_ci} 1348c2ecf20Sopenharmony_ci 1358c2ecf20Sopenharmony_ci/* Db/Pb1200 have separate per-socket insertion and ejection 1368c2ecf20Sopenharmony_ci * interrupts which stay asserted as long as the card is 1378c2ecf20Sopenharmony_ci * inserted/missing. The one which caused us to be called 1388c2ecf20Sopenharmony_ci * needs to be disabled and the other one enabled. 1398c2ecf20Sopenharmony_ci */ 1408c2ecf20Sopenharmony_cistatic irqreturn_t db1200_pcmcia_cdirq(int irq, void *data) 1418c2ecf20Sopenharmony_ci{ 1428c2ecf20Sopenharmony_ci disable_irq_nosync(irq); 1438c2ecf20Sopenharmony_ci return IRQ_WAKE_THREAD; 1448c2ecf20Sopenharmony_ci} 1458c2ecf20Sopenharmony_ci 1468c2ecf20Sopenharmony_cistatic irqreturn_t db1200_pcmcia_cdirq_fn(int irq, void *data) 1478c2ecf20Sopenharmony_ci{ 1488c2ecf20Sopenharmony_ci struct db1x_pcmcia_sock *sock = data; 1498c2ecf20Sopenharmony_ci 1508c2ecf20Sopenharmony_ci /* Wait a bit for the signals to stop bouncing. */ 1518c2ecf20Sopenharmony_ci msleep(100); 1528c2ecf20Sopenharmony_ci if (irq == sock->insert_irq) 1538c2ecf20Sopenharmony_ci enable_irq(sock->eject_irq); 1548c2ecf20Sopenharmony_ci else 1558c2ecf20Sopenharmony_ci enable_irq(sock->insert_irq); 1568c2ecf20Sopenharmony_ci 1578c2ecf20Sopenharmony_ci pcmcia_parse_events(&sock->socket, SS_DETECT); 1588c2ecf20Sopenharmony_ci 1598c2ecf20Sopenharmony_ci return IRQ_HANDLED; 1608c2ecf20Sopenharmony_ci} 1618c2ecf20Sopenharmony_ci 1628c2ecf20Sopenharmony_cistatic int db1x_pcmcia_setup_irqs(struct db1x_pcmcia_sock *sock) 1638c2ecf20Sopenharmony_ci{ 1648c2ecf20Sopenharmony_ci int ret; 1658c2ecf20Sopenharmony_ci 1668c2ecf20Sopenharmony_ci if (sock->stschg_irq != -1) { 1678c2ecf20Sopenharmony_ci ret = request_irq(sock->stschg_irq, db1000_pcmcia_stschgirq, 1688c2ecf20Sopenharmony_ci 0, "pcmcia_stschg", sock); 1698c2ecf20Sopenharmony_ci if (ret) 1708c2ecf20Sopenharmony_ci return ret; 1718c2ecf20Sopenharmony_ci } 1728c2ecf20Sopenharmony_ci 1738c2ecf20Sopenharmony_ci /* Db/Pb1200 have separate per-socket insertion and ejection 1748c2ecf20Sopenharmony_ci * interrupts, which should show edge behaviour but don't. 1758c2ecf20Sopenharmony_ci * So interrupts are disabled until both insertion and 1768c2ecf20Sopenharmony_ci * ejection handler have been registered and the currently 1778c2ecf20Sopenharmony_ci * active one disabled. 1788c2ecf20Sopenharmony_ci */ 1798c2ecf20Sopenharmony_ci if ((sock->board_type == BOARD_TYPE_DB1200) || 1808c2ecf20Sopenharmony_ci (sock->board_type == BOARD_TYPE_DB1300)) { 1818c2ecf20Sopenharmony_ci ret = request_threaded_irq(sock->insert_irq, db1200_pcmcia_cdirq, 1828c2ecf20Sopenharmony_ci db1200_pcmcia_cdirq_fn, 0, "pcmcia_insert", sock); 1838c2ecf20Sopenharmony_ci if (ret) 1848c2ecf20Sopenharmony_ci goto out1; 1858c2ecf20Sopenharmony_ci 1868c2ecf20Sopenharmony_ci ret = request_threaded_irq(sock->eject_irq, db1200_pcmcia_cdirq, 1878c2ecf20Sopenharmony_ci db1200_pcmcia_cdirq_fn, 0, "pcmcia_eject", sock); 1888c2ecf20Sopenharmony_ci if (ret) { 1898c2ecf20Sopenharmony_ci free_irq(sock->insert_irq, sock); 1908c2ecf20Sopenharmony_ci goto out1; 1918c2ecf20Sopenharmony_ci } 1928c2ecf20Sopenharmony_ci 1938c2ecf20Sopenharmony_ci /* enable the currently silent one */ 1948c2ecf20Sopenharmony_ci if (db1x_card_inserted(sock)) 1958c2ecf20Sopenharmony_ci enable_irq(sock->eject_irq); 1968c2ecf20Sopenharmony_ci else 1978c2ecf20Sopenharmony_ci enable_irq(sock->insert_irq); 1988c2ecf20Sopenharmony_ci } else { 1998c2ecf20Sopenharmony_ci /* all other (older) Db1x00 boards use a GPIO to show 2008c2ecf20Sopenharmony_ci * card detection status: use both-edge triggers. 2018c2ecf20Sopenharmony_ci */ 2028c2ecf20Sopenharmony_ci irq_set_irq_type(sock->insert_irq, IRQ_TYPE_EDGE_BOTH); 2038c2ecf20Sopenharmony_ci ret = request_irq(sock->insert_irq, db1000_pcmcia_cdirq, 2048c2ecf20Sopenharmony_ci 0, "pcmcia_carddetect", sock); 2058c2ecf20Sopenharmony_ci 2068c2ecf20Sopenharmony_ci if (ret) 2078c2ecf20Sopenharmony_ci goto out1; 2088c2ecf20Sopenharmony_ci } 2098c2ecf20Sopenharmony_ci 2108c2ecf20Sopenharmony_ci return 0; /* all done */ 2118c2ecf20Sopenharmony_ci 2128c2ecf20Sopenharmony_ciout1: 2138c2ecf20Sopenharmony_ci if (sock->stschg_irq != -1) 2148c2ecf20Sopenharmony_ci free_irq(sock->stschg_irq, sock); 2158c2ecf20Sopenharmony_ci 2168c2ecf20Sopenharmony_ci return ret; 2178c2ecf20Sopenharmony_ci} 2188c2ecf20Sopenharmony_ci 2198c2ecf20Sopenharmony_cistatic void db1x_pcmcia_free_irqs(struct db1x_pcmcia_sock *sock) 2208c2ecf20Sopenharmony_ci{ 2218c2ecf20Sopenharmony_ci if (sock->stschg_irq != -1) 2228c2ecf20Sopenharmony_ci free_irq(sock->stschg_irq, sock); 2238c2ecf20Sopenharmony_ci 2248c2ecf20Sopenharmony_ci free_irq(sock->insert_irq, sock); 2258c2ecf20Sopenharmony_ci if (sock->eject_irq != -1) 2268c2ecf20Sopenharmony_ci free_irq(sock->eject_irq, sock); 2278c2ecf20Sopenharmony_ci} 2288c2ecf20Sopenharmony_ci 2298c2ecf20Sopenharmony_ci/* 2308c2ecf20Sopenharmony_ci * configure a PCMCIA socket on the Db1x00 series of boards (and 2318c2ecf20Sopenharmony_ci * compatibles). 2328c2ecf20Sopenharmony_ci * 2338c2ecf20Sopenharmony_ci * 2 external registers are involved: 2348c2ecf20Sopenharmony_ci * pcmcia_status (offset 0x04): bits [0:1/2:3]: read card voltage id 2358c2ecf20Sopenharmony_ci * pcmcia_control(offset 0x10): 2368c2ecf20Sopenharmony_ci * bits[0:1] set vcc for card 2378c2ecf20Sopenharmony_ci * bits[2:3] set vpp for card 2388c2ecf20Sopenharmony_ci * bit 4: enable data buffers 2398c2ecf20Sopenharmony_ci * bit 7: reset# for card 2408c2ecf20Sopenharmony_ci * add 8 for second socket. 2418c2ecf20Sopenharmony_ci */ 2428c2ecf20Sopenharmony_cistatic int db1x_pcmcia_configure(struct pcmcia_socket *skt, 2438c2ecf20Sopenharmony_ci struct socket_state_t *state) 2448c2ecf20Sopenharmony_ci{ 2458c2ecf20Sopenharmony_ci struct db1x_pcmcia_sock *sock = to_db1x_socket(skt); 2468c2ecf20Sopenharmony_ci unsigned short cr_clr, cr_set; 2478c2ecf20Sopenharmony_ci unsigned int changed; 2488c2ecf20Sopenharmony_ci int v, p, ret; 2498c2ecf20Sopenharmony_ci 2508c2ecf20Sopenharmony_ci /* card voltage setup */ 2518c2ecf20Sopenharmony_ci cr_clr = (0xf << (sock->nr * 8)); /* clear voltage settings */ 2528c2ecf20Sopenharmony_ci cr_set = 0; 2538c2ecf20Sopenharmony_ci v = p = ret = 0; 2548c2ecf20Sopenharmony_ci 2558c2ecf20Sopenharmony_ci switch (state->Vcc) { 2568c2ecf20Sopenharmony_ci case 50: 2578c2ecf20Sopenharmony_ci ++v; 2588c2ecf20Sopenharmony_ci fallthrough; 2598c2ecf20Sopenharmony_ci case 33: 2608c2ecf20Sopenharmony_ci ++v; 2618c2ecf20Sopenharmony_ci fallthrough; 2628c2ecf20Sopenharmony_ci case 0: 2638c2ecf20Sopenharmony_ci break; 2648c2ecf20Sopenharmony_ci default: 2658c2ecf20Sopenharmony_ci printk(KERN_INFO "pcmcia%d unsupported Vcc %d\n", 2668c2ecf20Sopenharmony_ci sock->nr, state->Vcc); 2678c2ecf20Sopenharmony_ci } 2688c2ecf20Sopenharmony_ci 2698c2ecf20Sopenharmony_ci switch (state->Vpp) { 2708c2ecf20Sopenharmony_ci case 12: 2718c2ecf20Sopenharmony_ci ++p; 2728c2ecf20Sopenharmony_ci fallthrough; 2738c2ecf20Sopenharmony_ci case 33: 2748c2ecf20Sopenharmony_ci case 50: 2758c2ecf20Sopenharmony_ci ++p; 2768c2ecf20Sopenharmony_ci fallthrough; 2778c2ecf20Sopenharmony_ci case 0: 2788c2ecf20Sopenharmony_ci break; 2798c2ecf20Sopenharmony_ci default: 2808c2ecf20Sopenharmony_ci printk(KERN_INFO "pcmcia%d unsupported Vpp %d\n", 2818c2ecf20Sopenharmony_ci sock->nr, state->Vpp); 2828c2ecf20Sopenharmony_ci } 2838c2ecf20Sopenharmony_ci 2848c2ecf20Sopenharmony_ci /* sanity check: Vpp must be 0, 12, or Vcc */ 2858c2ecf20Sopenharmony_ci if (((state->Vcc == 33) && (state->Vpp == 50)) || 2868c2ecf20Sopenharmony_ci ((state->Vcc == 50) && (state->Vpp == 33))) { 2878c2ecf20Sopenharmony_ci printk(KERN_INFO "pcmcia%d bad Vcc/Vpp combo (%d %d)\n", 2888c2ecf20Sopenharmony_ci sock->nr, state->Vcc, state->Vpp); 2898c2ecf20Sopenharmony_ci v = p = 0; 2908c2ecf20Sopenharmony_ci ret = -EINVAL; 2918c2ecf20Sopenharmony_ci } 2928c2ecf20Sopenharmony_ci 2938c2ecf20Sopenharmony_ci /* create new voltage code */ 2948c2ecf20Sopenharmony_ci if (sock->board_type != BOARD_TYPE_DB1300) 2958c2ecf20Sopenharmony_ci cr_set |= ((v << 2) | p) << (sock->nr * 8); 2968c2ecf20Sopenharmony_ci 2978c2ecf20Sopenharmony_ci changed = state->flags ^ sock->old_flags; 2988c2ecf20Sopenharmony_ci 2998c2ecf20Sopenharmony_ci if (changed & SS_RESET) { 3008c2ecf20Sopenharmony_ci if (state->flags & SS_RESET) { 3018c2ecf20Sopenharmony_ci set_stschg(sock, 0); 3028c2ecf20Sopenharmony_ci /* assert reset, disable io buffers */ 3038c2ecf20Sopenharmony_ci cr_clr |= (1 << (7 + (sock->nr * 8))); 3048c2ecf20Sopenharmony_ci cr_clr |= (1 << (4 + (sock->nr * 8))); 3058c2ecf20Sopenharmony_ci } else { 3068c2ecf20Sopenharmony_ci /* de-assert reset, enable io buffers */ 3078c2ecf20Sopenharmony_ci cr_set |= 1 << (7 + (sock->nr * 8)); 3088c2ecf20Sopenharmony_ci cr_set |= 1 << (4 + (sock->nr * 8)); 3098c2ecf20Sopenharmony_ci } 3108c2ecf20Sopenharmony_ci } 3118c2ecf20Sopenharmony_ci 3128c2ecf20Sopenharmony_ci /* update PCMCIA configuration */ 3138c2ecf20Sopenharmony_ci bcsr_mod(BCSR_PCMCIA, cr_clr, cr_set); 3148c2ecf20Sopenharmony_ci 3158c2ecf20Sopenharmony_ci sock->old_flags = state->flags; 3168c2ecf20Sopenharmony_ci 3178c2ecf20Sopenharmony_ci /* reset was taken away: give card time to initialize properly */ 3188c2ecf20Sopenharmony_ci if ((changed & SS_RESET) && !(state->flags & SS_RESET)) { 3198c2ecf20Sopenharmony_ci msleep(500); 3208c2ecf20Sopenharmony_ci set_stschg(sock, 1); 3218c2ecf20Sopenharmony_ci } 3228c2ecf20Sopenharmony_ci 3238c2ecf20Sopenharmony_ci return ret; 3248c2ecf20Sopenharmony_ci} 3258c2ecf20Sopenharmony_ci 3268c2ecf20Sopenharmony_ci/* VCC bits at [3:2]/[11:10] */ 3278c2ecf20Sopenharmony_ci#define GET_VCC(cr, socknr) \ 3288c2ecf20Sopenharmony_ci ((((cr) >> 2) >> ((socknr) * 8)) & 3) 3298c2ecf20Sopenharmony_ci 3308c2ecf20Sopenharmony_ci/* VS bits at [0:1]/[3:2] */ 3318c2ecf20Sopenharmony_ci#define GET_VS(sr, socknr) \ 3328c2ecf20Sopenharmony_ci (((sr) >> (2 * (socknr))) & 3) 3338c2ecf20Sopenharmony_ci 3348c2ecf20Sopenharmony_ci/* reset bits at [7]/[15] */ 3358c2ecf20Sopenharmony_ci#define GET_RESET(cr, socknr) \ 3368c2ecf20Sopenharmony_ci ((cr) & (1 << (7 + (8 * (socknr))))) 3378c2ecf20Sopenharmony_ci 3388c2ecf20Sopenharmony_cistatic int db1x_pcmcia_get_status(struct pcmcia_socket *skt, 3398c2ecf20Sopenharmony_ci unsigned int *value) 3408c2ecf20Sopenharmony_ci{ 3418c2ecf20Sopenharmony_ci struct db1x_pcmcia_sock *sock = to_db1x_socket(skt); 3428c2ecf20Sopenharmony_ci unsigned short cr, sr; 3438c2ecf20Sopenharmony_ci unsigned int status; 3448c2ecf20Sopenharmony_ci 3458c2ecf20Sopenharmony_ci status = db1x_card_inserted(sock) ? SS_DETECT : 0; 3468c2ecf20Sopenharmony_ci 3478c2ecf20Sopenharmony_ci cr = bcsr_read(BCSR_PCMCIA); 3488c2ecf20Sopenharmony_ci sr = bcsr_read(BCSR_STATUS); 3498c2ecf20Sopenharmony_ci 3508c2ecf20Sopenharmony_ci /* PB1100/PB1500: voltage key bits are at [5:4] */ 3518c2ecf20Sopenharmony_ci if (sock->board_type == BOARD_TYPE_PB1100) 3528c2ecf20Sopenharmony_ci sr >>= 4; 3538c2ecf20Sopenharmony_ci 3548c2ecf20Sopenharmony_ci /* determine card type */ 3558c2ecf20Sopenharmony_ci switch (GET_VS(sr, sock->nr)) { 3568c2ecf20Sopenharmony_ci case 0: 3578c2ecf20Sopenharmony_ci case 2: 3588c2ecf20Sopenharmony_ci status |= SS_3VCARD; /* 3V card */ 3598c2ecf20Sopenharmony_ci case 3: 3608c2ecf20Sopenharmony_ci break; /* 5V card: set nothing */ 3618c2ecf20Sopenharmony_ci default: 3628c2ecf20Sopenharmony_ci status |= SS_XVCARD; /* treated as unsupported in core */ 3638c2ecf20Sopenharmony_ci } 3648c2ecf20Sopenharmony_ci 3658c2ecf20Sopenharmony_ci /* if Vcc is not zero, we have applied power to a card */ 3668c2ecf20Sopenharmony_ci status |= GET_VCC(cr, sock->nr) ? SS_POWERON : 0; 3678c2ecf20Sopenharmony_ci 3688c2ecf20Sopenharmony_ci /* DB1300: power always on, but don't tell when no card present */ 3698c2ecf20Sopenharmony_ci if ((sock->board_type == BOARD_TYPE_DB1300) && (status & SS_DETECT)) 3708c2ecf20Sopenharmony_ci status = SS_POWERON | SS_3VCARD | SS_DETECT; 3718c2ecf20Sopenharmony_ci 3728c2ecf20Sopenharmony_ci /* reset de-asserted? then we're ready */ 3738c2ecf20Sopenharmony_ci status |= (GET_RESET(cr, sock->nr)) ? SS_READY : SS_RESET; 3748c2ecf20Sopenharmony_ci 3758c2ecf20Sopenharmony_ci *value = status; 3768c2ecf20Sopenharmony_ci 3778c2ecf20Sopenharmony_ci return 0; 3788c2ecf20Sopenharmony_ci} 3798c2ecf20Sopenharmony_ci 3808c2ecf20Sopenharmony_cistatic int db1x_pcmcia_sock_init(struct pcmcia_socket *skt) 3818c2ecf20Sopenharmony_ci{ 3828c2ecf20Sopenharmony_ci return 0; 3838c2ecf20Sopenharmony_ci} 3848c2ecf20Sopenharmony_ci 3858c2ecf20Sopenharmony_cistatic int db1x_pcmcia_sock_suspend(struct pcmcia_socket *skt) 3868c2ecf20Sopenharmony_ci{ 3878c2ecf20Sopenharmony_ci return 0; 3888c2ecf20Sopenharmony_ci} 3898c2ecf20Sopenharmony_ci 3908c2ecf20Sopenharmony_cistatic int au1x00_pcmcia_set_io_map(struct pcmcia_socket *skt, 3918c2ecf20Sopenharmony_ci struct pccard_io_map *map) 3928c2ecf20Sopenharmony_ci{ 3938c2ecf20Sopenharmony_ci struct db1x_pcmcia_sock *sock = to_db1x_socket(skt); 3948c2ecf20Sopenharmony_ci 3958c2ecf20Sopenharmony_ci map->start = (u32)sock->virt_io; 3968c2ecf20Sopenharmony_ci map->stop = map->start + IO_MAP_SIZE; 3978c2ecf20Sopenharmony_ci 3988c2ecf20Sopenharmony_ci return 0; 3998c2ecf20Sopenharmony_ci} 4008c2ecf20Sopenharmony_ci 4018c2ecf20Sopenharmony_cistatic int au1x00_pcmcia_set_mem_map(struct pcmcia_socket *skt, 4028c2ecf20Sopenharmony_ci struct pccard_mem_map *map) 4038c2ecf20Sopenharmony_ci{ 4048c2ecf20Sopenharmony_ci struct db1x_pcmcia_sock *sock = to_db1x_socket(skt); 4058c2ecf20Sopenharmony_ci 4068c2ecf20Sopenharmony_ci if (map->flags & MAP_ATTRIB) 4078c2ecf20Sopenharmony_ci map->static_start = sock->phys_attr + map->card_start; 4088c2ecf20Sopenharmony_ci else 4098c2ecf20Sopenharmony_ci map->static_start = sock->phys_mem + map->card_start; 4108c2ecf20Sopenharmony_ci 4118c2ecf20Sopenharmony_ci return 0; 4128c2ecf20Sopenharmony_ci} 4138c2ecf20Sopenharmony_ci 4148c2ecf20Sopenharmony_cistatic struct pccard_operations db1x_pcmcia_operations = { 4158c2ecf20Sopenharmony_ci .init = db1x_pcmcia_sock_init, 4168c2ecf20Sopenharmony_ci .suspend = db1x_pcmcia_sock_suspend, 4178c2ecf20Sopenharmony_ci .get_status = db1x_pcmcia_get_status, 4188c2ecf20Sopenharmony_ci .set_socket = db1x_pcmcia_configure, 4198c2ecf20Sopenharmony_ci .set_io_map = au1x00_pcmcia_set_io_map, 4208c2ecf20Sopenharmony_ci .set_mem_map = au1x00_pcmcia_set_mem_map, 4218c2ecf20Sopenharmony_ci}; 4228c2ecf20Sopenharmony_ci 4238c2ecf20Sopenharmony_cistatic int db1x_pcmcia_socket_probe(struct platform_device *pdev) 4248c2ecf20Sopenharmony_ci{ 4258c2ecf20Sopenharmony_ci struct db1x_pcmcia_sock *sock; 4268c2ecf20Sopenharmony_ci struct resource *r; 4278c2ecf20Sopenharmony_ci int ret, bid; 4288c2ecf20Sopenharmony_ci 4298c2ecf20Sopenharmony_ci sock = kzalloc(sizeof(struct db1x_pcmcia_sock), GFP_KERNEL); 4308c2ecf20Sopenharmony_ci if (!sock) 4318c2ecf20Sopenharmony_ci return -ENOMEM; 4328c2ecf20Sopenharmony_ci 4338c2ecf20Sopenharmony_ci sock->nr = pdev->id; 4348c2ecf20Sopenharmony_ci 4358c2ecf20Sopenharmony_ci bid = BCSR_WHOAMI_BOARD(bcsr_read(BCSR_WHOAMI)); 4368c2ecf20Sopenharmony_ci switch (bid) { 4378c2ecf20Sopenharmony_ci case BCSR_WHOAMI_PB1500: 4388c2ecf20Sopenharmony_ci case BCSR_WHOAMI_PB1500R2: 4398c2ecf20Sopenharmony_ci case BCSR_WHOAMI_PB1100: 4408c2ecf20Sopenharmony_ci sock->board_type = BOARD_TYPE_PB1100; 4418c2ecf20Sopenharmony_ci break; 4428c2ecf20Sopenharmony_ci case BCSR_WHOAMI_DB1000 ... BCSR_WHOAMI_PB1550_SDR: 4438c2ecf20Sopenharmony_ci sock->board_type = BOARD_TYPE_DEFAULT; 4448c2ecf20Sopenharmony_ci break; 4458c2ecf20Sopenharmony_ci case BCSR_WHOAMI_PB1200 ... BCSR_WHOAMI_DB1200: 4468c2ecf20Sopenharmony_ci sock->board_type = BOARD_TYPE_DB1200; 4478c2ecf20Sopenharmony_ci break; 4488c2ecf20Sopenharmony_ci case BCSR_WHOAMI_DB1300: 4498c2ecf20Sopenharmony_ci sock->board_type = BOARD_TYPE_DB1300; 4508c2ecf20Sopenharmony_ci break; 4518c2ecf20Sopenharmony_ci default: 4528c2ecf20Sopenharmony_ci printk(KERN_INFO "db1xxx-ss: unknown board %d!\n", bid); 4538c2ecf20Sopenharmony_ci ret = -ENODEV; 4548c2ecf20Sopenharmony_ci goto out0; 4558c2ecf20Sopenharmony_ci }; 4568c2ecf20Sopenharmony_ci 4578c2ecf20Sopenharmony_ci /* 4588c2ecf20Sopenharmony_ci * gather resources necessary and optional nice-to-haves to 4598c2ecf20Sopenharmony_ci * operate a socket: 4608c2ecf20Sopenharmony_ci * This includes IRQs for Carddetection/ejection, the card 4618c2ecf20Sopenharmony_ci * itself and optional status change detection. 4628c2ecf20Sopenharmony_ci * Also, the memory areas covered by a socket. For these 4638c2ecf20Sopenharmony_ci * we require the real 36bit addresses (see the au1000.h 4648c2ecf20Sopenharmony_ci * header for more information). 4658c2ecf20Sopenharmony_ci */ 4668c2ecf20Sopenharmony_ci 4678c2ecf20Sopenharmony_ci /* card: irq assigned to the card itself. */ 4688c2ecf20Sopenharmony_ci r = platform_get_resource_byname(pdev, IORESOURCE_IRQ, "card"); 4698c2ecf20Sopenharmony_ci sock->card_irq = r ? r->start : 0; 4708c2ecf20Sopenharmony_ci 4718c2ecf20Sopenharmony_ci /* insert: irq which triggers on card insertion/ejection 4728c2ecf20Sopenharmony_ci * BIG FAT NOTE: on DB1000/1100/1500/1550 we pass a GPIO here! 4738c2ecf20Sopenharmony_ci */ 4748c2ecf20Sopenharmony_ci r = platform_get_resource_byname(pdev, IORESOURCE_IRQ, "insert"); 4758c2ecf20Sopenharmony_ci sock->insert_irq = r ? r->start : -1; 4768c2ecf20Sopenharmony_ci if (sock->board_type == BOARD_TYPE_DEFAULT) { 4778c2ecf20Sopenharmony_ci sock->insert_gpio = r ? r->start : -1; 4788c2ecf20Sopenharmony_ci sock->insert_irq = r ? gpio_to_irq(r->start) : -1; 4798c2ecf20Sopenharmony_ci } 4808c2ecf20Sopenharmony_ci 4818c2ecf20Sopenharmony_ci /* stschg: irq which trigger on card status change (optional) */ 4828c2ecf20Sopenharmony_ci r = platform_get_resource_byname(pdev, IORESOURCE_IRQ, "stschg"); 4838c2ecf20Sopenharmony_ci sock->stschg_irq = r ? r->start : -1; 4848c2ecf20Sopenharmony_ci 4858c2ecf20Sopenharmony_ci /* eject: irq which triggers on ejection (DB1200/PB1200 only) */ 4868c2ecf20Sopenharmony_ci r = platform_get_resource_byname(pdev, IORESOURCE_IRQ, "eject"); 4878c2ecf20Sopenharmony_ci sock->eject_irq = r ? r->start : -1; 4888c2ecf20Sopenharmony_ci 4898c2ecf20Sopenharmony_ci ret = -ENODEV; 4908c2ecf20Sopenharmony_ci 4918c2ecf20Sopenharmony_ci /* 36bit PCMCIA Attribute area address */ 4928c2ecf20Sopenharmony_ci r = platform_get_resource_byname(pdev, IORESOURCE_MEM, "pcmcia-attr"); 4938c2ecf20Sopenharmony_ci if (!r) { 4948c2ecf20Sopenharmony_ci printk(KERN_ERR "pcmcia%d has no 'pseudo-attr' resource!\n", 4958c2ecf20Sopenharmony_ci sock->nr); 4968c2ecf20Sopenharmony_ci goto out0; 4978c2ecf20Sopenharmony_ci } 4988c2ecf20Sopenharmony_ci sock->phys_attr = r->start; 4998c2ecf20Sopenharmony_ci 5008c2ecf20Sopenharmony_ci /* 36bit PCMCIA Memory area address */ 5018c2ecf20Sopenharmony_ci r = platform_get_resource_byname(pdev, IORESOURCE_MEM, "pcmcia-mem"); 5028c2ecf20Sopenharmony_ci if (!r) { 5038c2ecf20Sopenharmony_ci printk(KERN_ERR "pcmcia%d has no 'pseudo-mem' resource!\n", 5048c2ecf20Sopenharmony_ci sock->nr); 5058c2ecf20Sopenharmony_ci goto out0; 5068c2ecf20Sopenharmony_ci } 5078c2ecf20Sopenharmony_ci sock->phys_mem = r->start; 5088c2ecf20Sopenharmony_ci 5098c2ecf20Sopenharmony_ci /* 36bit PCMCIA IO area address */ 5108c2ecf20Sopenharmony_ci r = platform_get_resource_byname(pdev, IORESOURCE_MEM, "pcmcia-io"); 5118c2ecf20Sopenharmony_ci if (!r) { 5128c2ecf20Sopenharmony_ci printk(KERN_ERR "pcmcia%d has no 'pseudo-io' resource!\n", 5138c2ecf20Sopenharmony_ci sock->nr); 5148c2ecf20Sopenharmony_ci goto out0; 5158c2ecf20Sopenharmony_ci } 5168c2ecf20Sopenharmony_ci sock->phys_io = r->start; 5178c2ecf20Sopenharmony_ci 5188c2ecf20Sopenharmony_ci /* 5198c2ecf20Sopenharmony_ci * PCMCIA client drivers use the inb/outb macros to access 5208c2ecf20Sopenharmony_ci * the IO registers. Since mips_io_port_base is added 5218c2ecf20Sopenharmony_ci * to the access address of the mips implementation of 5228c2ecf20Sopenharmony_ci * inb/outb, we need to subtract it here because we want 5238c2ecf20Sopenharmony_ci * to access the I/O or MEM address directly, without 5248c2ecf20Sopenharmony_ci * going through this "mips_io_port_base" mechanism. 5258c2ecf20Sopenharmony_ci */ 5268c2ecf20Sopenharmony_ci sock->virt_io = (void *)(ioremap(sock->phys_io, IO_MAP_SIZE) - 5278c2ecf20Sopenharmony_ci mips_io_port_base); 5288c2ecf20Sopenharmony_ci 5298c2ecf20Sopenharmony_ci if (!sock->virt_io) { 5308c2ecf20Sopenharmony_ci printk(KERN_ERR "pcmcia%d: cannot remap IO area\n", 5318c2ecf20Sopenharmony_ci sock->nr); 5328c2ecf20Sopenharmony_ci ret = -ENOMEM; 5338c2ecf20Sopenharmony_ci goto out0; 5348c2ecf20Sopenharmony_ci } 5358c2ecf20Sopenharmony_ci 5368c2ecf20Sopenharmony_ci sock->socket.ops = &db1x_pcmcia_operations; 5378c2ecf20Sopenharmony_ci sock->socket.owner = THIS_MODULE; 5388c2ecf20Sopenharmony_ci sock->socket.pci_irq = sock->card_irq; 5398c2ecf20Sopenharmony_ci sock->socket.features = SS_CAP_STATIC_MAP | SS_CAP_PCCARD; 5408c2ecf20Sopenharmony_ci sock->socket.map_size = MEM_MAP_SIZE; 5418c2ecf20Sopenharmony_ci sock->socket.io_offset = (unsigned long)sock->virt_io; 5428c2ecf20Sopenharmony_ci sock->socket.dev.parent = &pdev->dev; 5438c2ecf20Sopenharmony_ci sock->socket.resource_ops = &pccard_static_ops; 5448c2ecf20Sopenharmony_ci 5458c2ecf20Sopenharmony_ci platform_set_drvdata(pdev, sock); 5468c2ecf20Sopenharmony_ci 5478c2ecf20Sopenharmony_ci ret = db1x_pcmcia_setup_irqs(sock); 5488c2ecf20Sopenharmony_ci if (ret) { 5498c2ecf20Sopenharmony_ci printk(KERN_ERR "pcmcia%d cannot setup interrupts\n", 5508c2ecf20Sopenharmony_ci sock->nr); 5518c2ecf20Sopenharmony_ci goto out1; 5528c2ecf20Sopenharmony_ci } 5538c2ecf20Sopenharmony_ci 5548c2ecf20Sopenharmony_ci set_stschg(sock, 0); 5558c2ecf20Sopenharmony_ci 5568c2ecf20Sopenharmony_ci ret = pcmcia_register_socket(&sock->socket); 5578c2ecf20Sopenharmony_ci if (ret) { 5588c2ecf20Sopenharmony_ci printk(KERN_ERR "pcmcia%d failed to register\n", sock->nr); 5598c2ecf20Sopenharmony_ci goto out2; 5608c2ecf20Sopenharmony_ci } 5618c2ecf20Sopenharmony_ci 5628c2ecf20Sopenharmony_ci printk(KERN_INFO "Alchemy Db/Pb1xxx pcmcia%d @ io/attr/mem %09llx" 5638c2ecf20Sopenharmony_ci "(%p) %09llx %09llx card/insert/stschg/eject irqs @ %d " 5648c2ecf20Sopenharmony_ci "%d %d %d\n", sock->nr, sock->phys_io, sock->virt_io, 5658c2ecf20Sopenharmony_ci sock->phys_attr, sock->phys_mem, sock->card_irq, 5668c2ecf20Sopenharmony_ci sock->insert_irq, sock->stschg_irq, sock->eject_irq); 5678c2ecf20Sopenharmony_ci 5688c2ecf20Sopenharmony_ci return 0; 5698c2ecf20Sopenharmony_ci 5708c2ecf20Sopenharmony_ciout2: 5718c2ecf20Sopenharmony_ci db1x_pcmcia_free_irqs(sock); 5728c2ecf20Sopenharmony_ciout1: 5738c2ecf20Sopenharmony_ci iounmap((void *)(sock->virt_io + (u32)mips_io_port_base)); 5748c2ecf20Sopenharmony_ciout0: 5758c2ecf20Sopenharmony_ci kfree(sock); 5768c2ecf20Sopenharmony_ci return ret; 5778c2ecf20Sopenharmony_ci} 5788c2ecf20Sopenharmony_ci 5798c2ecf20Sopenharmony_cistatic int db1x_pcmcia_socket_remove(struct platform_device *pdev) 5808c2ecf20Sopenharmony_ci{ 5818c2ecf20Sopenharmony_ci struct db1x_pcmcia_sock *sock = platform_get_drvdata(pdev); 5828c2ecf20Sopenharmony_ci 5838c2ecf20Sopenharmony_ci db1x_pcmcia_free_irqs(sock); 5848c2ecf20Sopenharmony_ci pcmcia_unregister_socket(&sock->socket); 5858c2ecf20Sopenharmony_ci iounmap((void *)(sock->virt_io + (u32)mips_io_port_base)); 5868c2ecf20Sopenharmony_ci kfree(sock); 5878c2ecf20Sopenharmony_ci 5888c2ecf20Sopenharmony_ci return 0; 5898c2ecf20Sopenharmony_ci} 5908c2ecf20Sopenharmony_ci 5918c2ecf20Sopenharmony_cistatic struct platform_driver db1x_pcmcia_socket_driver = { 5928c2ecf20Sopenharmony_ci .driver = { 5938c2ecf20Sopenharmony_ci .name = "db1xxx_pcmcia", 5948c2ecf20Sopenharmony_ci }, 5958c2ecf20Sopenharmony_ci .probe = db1x_pcmcia_socket_probe, 5968c2ecf20Sopenharmony_ci .remove = db1x_pcmcia_socket_remove, 5978c2ecf20Sopenharmony_ci}; 5988c2ecf20Sopenharmony_ci 5998c2ecf20Sopenharmony_cimodule_platform_driver(db1x_pcmcia_socket_driver); 6008c2ecf20Sopenharmony_ci 6018c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 6028c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("PCMCIA Socket Services for Alchemy Db/Pb1x00 boards"); 6038c2ecf20Sopenharmony_ciMODULE_AUTHOR("Manuel Lauss"); 604