18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * linux/drivers/pcmcia/sa1111_generic.c 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * We implement the generic parts of a SA1111 PCMCIA driver. This 68c2ecf20Sopenharmony_ci * basically means we handle everything except controlling the 78c2ecf20Sopenharmony_ci * power. Power is machine specific... 88c2ecf20Sopenharmony_ci */ 98c2ecf20Sopenharmony_ci#include <linux/module.h> 108c2ecf20Sopenharmony_ci#include <linux/kernel.h> 118c2ecf20Sopenharmony_ci#include <linux/ioport.h> 128c2ecf20Sopenharmony_ci#include <linux/device.h> 138c2ecf20Sopenharmony_ci#include <linux/interrupt.h> 148c2ecf20Sopenharmony_ci#include <linux/init.h> 158c2ecf20Sopenharmony_ci#include <linux/io.h> 168c2ecf20Sopenharmony_ci#include <linux/slab.h> 178c2ecf20Sopenharmony_ci 188c2ecf20Sopenharmony_ci#include <pcmcia/ss.h> 198c2ecf20Sopenharmony_ci 208c2ecf20Sopenharmony_ci#include <mach/hardware.h> 218c2ecf20Sopenharmony_ci#include <asm/hardware/sa1111.h> 228c2ecf20Sopenharmony_ci#include <asm/mach-types.h> 238c2ecf20Sopenharmony_ci#include <asm/irq.h> 248c2ecf20Sopenharmony_ci 258c2ecf20Sopenharmony_ci#include "sa1111_generic.h" 268c2ecf20Sopenharmony_ci 278c2ecf20Sopenharmony_ci/* 288c2ecf20Sopenharmony_ci * These are offsets from the above base. 298c2ecf20Sopenharmony_ci */ 308c2ecf20Sopenharmony_ci#define PCCR 0x0000 318c2ecf20Sopenharmony_ci#define PCSSR 0x0004 328c2ecf20Sopenharmony_ci#define PCSR 0x0008 338c2ecf20Sopenharmony_ci 348c2ecf20Sopenharmony_ci#define PCSR_S0_READY (1<<0) 358c2ecf20Sopenharmony_ci#define PCSR_S1_READY (1<<1) 368c2ecf20Sopenharmony_ci#define PCSR_S0_DETECT (1<<2) 378c2ecf20Sopenharmony_ci#define PCSR_S1_DETECT (1<<3) 388c2ecf20Sopenharmony_ci#define PCSR_S0_VS1 (1<<4) 398c2ecf20Sopenharmony_ci#define PCSR_S0_VS2 (1<<5) 408c2ecf20Sopenharmony_ci#define PCSR_S1_VS1 (1<<6) 418c2ecf20Sopenharmony_ci#define PCSR_S1_VS2 (1<<7) 428c2ecf20Sopenharmony_ci#define PCSR_S0_WP (1<<8) 438c2ecf20Sopenharmony_ci#define PCSR_S1_WP (1<<9) 448c2ecf20Sopenharmony_ci#define PCSR_S0_BVD1 (1<<10) 458c2ecf20Sopenharmony_ci#define PCSR_S0_BVD2 (1<<11) 468c2ecf20Sopenharmony_ci#define PCSR_S1_BVD1 (1<<12) 478c2ecf20Sopenharmony_ci#define PCSR_S1_BVD2 (1<<13) 488c2ecf20Sopenharmony_ci 498c2ecf20Sopenharmony_ci#define PCCR_S0_RST (1<<0) 508c2ecf20Sopenharmony_ci#define PCCR_S1_RST (1<<1) 518c2ecf20Sopenharmony_ci#define PCCR_S0_FLT (1<<2) 528c2ecf20Sopenharmony_ci#define PCCR_S1_FLT (1<<3) 538c2ecf20Sopenharmony_ci#define PCCR_S0_PWAITEN (1<<4) 548c2ecf20Sopenharmony_ci#define PCCR_S1_PWAITEN (1<<5) 558c2ecf20Sopenharmony_ci#define PCCR_S0_PSE (1<<6) 568c2ecf20Sopenharmony_ci#define PCCR_S1_PSE (1<<7) 578c2ecf20Sopenharmony_ci 588c2ecf20Sopenharmony_ci#define PCSSR_S0_SLEEP (1<<0) 598c2ecf20Sopenharmony_ci#define PCSSR_S1_SLEEP (1<<1) 608c2ecf20Sopenharmony_ci 618c2ecf20Sopenharmony_ci#define IDX_IRQ_S0_READY_NINT (0) 628c2ecf20Sopenharmony_ci#define IDX_IRQ_S0_CD_VALID (1) 638c2ecf20Sopenharmony_ci#define IDX_IRQ_S0_BVD1_STSCHG (2) 648c2ecf20Sopenharmony_ci#define IDX_IRQ_S1_READY_NINT (3) 658c2ecf20Sopenharmony_ci#define IDX_IRQ_S1_CD_VALID (4) 668c2ecf20Sopenharmony_ci#define IDX_IRQ_S1_BVD1_STSCHG (5) 678c2ecf20Sopenharmony_ci#define NUM_IRQS (6) 688c2ecf20Sopenharmony_ci 698c2ecf20Sopenharmony_civoid sa1111_pcmcia_socket_state(struct soc_pcmcia_socket *skt, struct pcmcia_state *state) 708c2ecf20Sopenharmony_ci{ 718c2ecf20Sopenharmony_ci struct sa1111_pcmcia_socket *s = to_skt(skt); 728c2ecf20Sopenharmony_ci u32 status = readl_relaxed(s->dev->mapbase + PCSR); 738c2ecf20Sopenharmony_ci 748c2ecf20Sopenharmony_ci switch (skt->nr) { 758c2ecf20Sopenharmony_ci case 0: 768c2ecf20Sopenharmony_ci state->detect = status & PCSR_S0_DETECT ? 0 : 1; 778c2ecf20Sopenharmony_ci state->ready = status & PCSR_S0_READY ? 1 : 0; 788c2ecf20Sopenharmony_ci state->bvd1 = status & PCSR_S0_BVD1 ? 1 : 0; 798c2ecf20Sopenharmony_ci state->bvd2 = status & PCSR_S0_BVD2 ? 1 : 0; 808c2ecf20Sopenharmony_ci state->wrprot = status & PCSR_S0_WP ? 1 : 0; 818c2ecf20Sopenharmony_ci state->vs_3v = status & PCSR_S0_VS1 ? 0 : 1; 828c2ecf20Sopenharmony_ci state->vs_Xv = status & PCSR_S0_VS2 ? 0 : 1; 838c2ecf20Sopenharmony_ci break; 848c2ecf20Sopenharmony_ci 858c2ecf20Sopenharmony_ci case 1: 868c2ecf20Sopenharmony_ci state->detect = status & PCSR_S1_DETECT ? 0 : 1; 878c2ecf20Sopenharmony_ci state->ready = status & PCSR_S1_READY ? 1 : 0; 888c2ecf20Sopenharmony_ci state->bvd1 = status & PCSR_S1_BVD1 ? 1 : 0; 898c2ecf20Sopenharmony_ci state->bvd2 = status & PCSR_S1_BVD2 ? 1 : 0; 908c2ecf20Sopenharmony_ci state->wrprot = status & PCSR_S1_WP ? 1 : 0; 918c2ecf20Sopenharmony_ci state->vs_3v = status & PCSR_S1_VS1 ? 0 : 1; 928c2ecf20Sopenharmony_ci state->vs_Xv = status & PCSR_S1_VS2 ? 0 : 1; 938c2ecf20Sopenharmony_ci break; 948c2ecf20Sopenharmony_ci } 958c2ecf20Sopenharmony_ci} 968c2ecf20Sopenharmony_ci 978c2ecf20Sopenharmony_ciint sa1111_pcmcia_configure_socket(struct soc_pcmcia_socket *skt, const socket_state_t *state) 988c2ecf20Sopenharmony_ci{ 998c2ecf20Sopenharmony_ci struct sa1111_pcmcia_socket *s = to_skt(skt); 1008c2ecf20Sopenharmony_ci u32 pccr_skt_mask, pccr_set_mask, val; 1018c2ecf20Sopenharmony_ci unsigned long flags; 1028c2ecf20Sopenharmony_ci 1038c2ecf20Sopenharmony_ci switch (skt->nr) { 1048c2ecf20Sopenharmony_ci case 0: 1058c2ecf20Sopenharmony_ci pccr_skt_mask = PCCR_S0_RST|PCCR_S0_FLT|PCCR_S0_PWAITEN|PCCR_S0_PSE; 1068c2ecf20Sopenharmony_ci break; 1078c2ecf20Sopenharmony_ci 1088c2ecf20Sopenharmony_ci case 1: 1098c2ecf20Sopenharmony_ci pccr_skt_mask = PCCR_S1_RST|PCCR_S1_FLT|PCCR_S1_PWAITEN|PCCR_S1_PSE; 1108c2ecf20Sopenharmony_ci break; 1118c2ecf20Sopenharmony_ci 1128c2ecf20Sopenharmony_ci default: 1138c2ecf20Sopenharmony_ci return -1; 1148c2ecf20Sopenharmony_ci } 1158c2ecf20Sopenharmony_ci 1168c2ecf20Sopenharmony_ci pccr_set_mask = 0; 1178c2ecf20Sopenharmony_ci 1188c2ecf20Sopenharmony_ci if (state->Vcc != 0) 1198c2ecf20Sopenharmony_ci pccr_set_mask |= PCCR_S0_PWAITEN|PCCR_S1_PWAITEN; 1208c2ecf20Sopenharmony_ci if (state->Vcc == 50) 1218c2ecf20Sopenharmony_ci pccr_set_mask |= PCCR_S0_PSE|PCCR_S1_PSE; 1228c2ecf20Sopenharmony_ci if (state->flags & SS_RESET) 1238c2ecf20Sopenharmony_ci pccr_set_mask |= PCCR_S0_RST|PCCR_S1_RST; 1248c2ecf20Sopenharmony_ci if (state->flags & SS_OUTPUT_ENA) 1258c2ecf20Sopenharmony_ci pccr_set_mask |= PCCR_S0_FLT|PCCR_S1_FLT; 1268c2ecf20Sopenharmony_ci 1278c2ecf20Sopenharmony_ci local_irq_save(flags); 1288c2ecf20Sopenharmony_ci val = readl_relaxed(s->dev->mapbase + PCCR); 1298c2ecf20Sopenharmony_ci val &= ~pccr_skt_mask; 1308c2ecf20Sopenharmony_ci val |= pccr_set_mask & pccr_skt_mask; 1318c2ecf20Sopenharmony_ci writel_relaxed(val, s->dev->mapbase + PCCR); 1328c2ecf20Sopenharmony_ci local_irq_restore(flags); 1338c2ecf20Sopenharmony_ci 1348c2ecf20Sopenharmony_ci return 0; 1358c2ecf20Sopenharmony_ci} 1368c2ecf20Sopenharmony_ci 1378c2ecf20Sopenharmony_ciint sa1111_pcmcia_add(struct sa1111_dev *dev, struct pcmcia_low_level *ops, 1388c2ecf20Sopenharmony_ci int (*add)(struct soc_pcmcia_socket *)) 1398c2ecf20Sopenharmony_ci{ 1408c2ecf20Sopenharmony_ci struct sa1111_pcmcia_socket *s; 1418c2ecf20Sopenharmony_ci struct clk *clk; 1428c2ecf20Sopenharmony_ci int i, ret = 0, irqs[NUM_IRQS]; 1438c2ecf20Sopenharmony_ci 1448c2ecf20Sopenharmony_ci clk = devm_clk_get(&dev->dev, NULL); 1458c2ecf20Sopenharmony_ci if (IS_ERR(clk)) 1468c2ecf20Sopenharmony_ci return PTR_ERR(clk); 1478c2ecf20Sopenharmony_ci 1488c2ecf20Sopenharmony_ci for (i = 0; i < NUM_IRQS; i++) { 1498c2ecf20Sopenharmony_ci irqs[i] = sa1111_get_irq(dev, i); 1508c2ecf20Sopenharmony_ci if (irqs[i] <= 0) 1518c2ecf20Sopenharmony_ci return irqs[i] ? : -ENXIO; 1528c2ecf20Sopenharmony_ci } 1538c2ecf20Sopenharmony_ci 1548c2ecf20Sopenharmony_ci ops->socket_state = sa1111_pcmcia_socket_state; 1558c2ecf20Sopenharmony_ci 1568c2ecf20Sopenharmony_ci for (i = 0; i < ops->nr; i++) { 1578c2ecf20Sopenharmony_ci s = kzalloc(sizeof(*s), GFP_KERNEL); 1588c2ecf20Sopenharmony_ci if (!s) 1598c2ecf20Sopenharmony_ci return -ENOMEM; 1608c2ecf20Sopenharmony_ci 1618c2ecf20Sopenharmony_ci s->soc.nr = ops->first + i; 1628c2ecf20Sopenharmony_ci s->soc.clk = clk; 1638c2ecf20Sopenharmony_ci 1648c2ecf20Sopenharmony_ci soc_pcmcia_init_one(&s->soc, ops, &dev->dev); 1658c2ecf20Sopenharmony_ci s->dev = dev; 1668c2ecf20Sopenharmony_ci if (s->soc.nr) { 1678c2ecf20Sopenharmony_ci s->soc.socket.pci_irq = irqs[IDX_IRQ_S1_READY_NINT]; 1688c2ecf20Sopenharmony_ci s->soc.stat[SOC_STAT_CD].irq = irqs[IDX_IRQ_S1_CD_VALID]; 1698c2ecf20Sopenharmony_ci s->soc.stat[SOC_STAT_CD].name = "SA1111 CF card detect"; 1708c2ecf20Sopenharmony_ci s->soc.stat[SOC_STAT_BVD1].irq = irqs[IDX_IRQ_S1_BVD1_STSCHG]; 1718c2ecf20Sopenharmony_ci s->soc.stat[SOC_STAT_BVD1].name = "SA1111 CF BVD1"; 1728c2ecf20Sopenharmony_ci } else { 1738c2ecf20Sopenharmony_ci s->soc.socket.pci_irq = irqs[IDX_IRQ_S0_READY_NINT]; 1748c2ecf20Sopenharmony_ci s->soc.stat[SOC_STAT_CD].irq = irqs[IDX_IRQ_S0_CD_VALID]; 1758c2ecf20Sopenharmony_ci s->soc.stat[SOC_STAT_CD].name = "SA1111 PCMCIA card detect"; 1768c2ecf20Sopenharmony_ci s->soc.stat[SOC_STAT_BVD1].irq = irqs[IDX_IRQ_S0_BVD1_STSCHG]; 1778c2ecf20Sopenharmony_ci s->soc.stat[SOC_STAT_BVD1].name = "SA1111 PCMCIA BVD1"; 1788c2ecf20Sopenharmony_ci } 1798c2ecf20Sopenharmony_ci 1808c2ecf20Sopenharmony_ci ret = add(&s->soc); 1818c2ecf20Sopenharmony_ci if (ret == 0) { 1828c2ecf20Sopenharmony_ci s->next = dev_get_drvdata(&dev->dev); 1838c2ecf20Sopenharmony_ci dev_set_drvdata(&dev->dev, s); 1848c2ecf20Sopenharmony_ci } else 1858c2ecf20Sopenharmony_ci kfree(s); 1868c2ecf20Sopenharmony_ci } 1878c2ecf20Sopenharmony_ci 1888c2ecf20Sopenharmony_ci return ret; 1898c2ecf20Sopenharmony_ci} 1908c2ecf20Sopenharmony_ci 1918c2ecf20Sopenharmony_cistatic int pcmcia_probe(struct sa1111_dev *dev) 1928c2ecf20Sopenharmony_ci{ 1938c2ecf20Sopenharmony_ci void __iomem *base; 1948c2ecf20Sopenharmony_ci int ret; 1958c2ecf20Sopenharmony_ci 1968c2ecf20Sopenharmony_ci ret = sa1111_enable_device(dev); 1978c2ecf20Sopenharmony_ci if (ret) 1988c2ecf20Sopenharmony_ci return ret; 1998c2ecf20Sopenharmony_ci 2008c2ecf20Sopenharmony_ci dev_set_drvdata(&dev->dev, NULL); 2018c2ecf20Sopenharmony_ci 2028c2ecf20Sopenharmony_ci if (!request_mem_region(dev->res.start, 512, SA1111_DRIVER_NAME(dev))) { 2038c2ecf20Sopenharmony_ci sa1111_disable_device(dev); 2048c2ecf20Sopenharmony_ci return -EBUSY; 2058c2ecf20Sopenharmony_ci } 2068c2ecf20Sopenharmony_ci 2078c2ecf20Sopenharmony_ci base = dev->mapbase; 2088c2ecf20Sopenharmony_ci 2098c2ecf20Sopenharmony_ci /* 2108c2ecf20Sopenharmony_ci * Initialise the suspend state. 2118c2ecf20Sopenharmony_ci */ 2128c2ecf20Sopenharmony_ci writel_relaxed(PCSSR_S0_SLEEP | PCSSR_S1_SLEEP, base + PCSSR); 2138c2ecf20Sopenharmony_ci writel_relaxed(PCCR_S0_FLT | PCCR_S1_FLT, base + PCCR); 2148c2ecf20Sopenharmony_ci 2158c2ecf20Sopenharmony_ci ret = -ENODEV; 2168c2ecf20Sopenharmony_ci#ifdef CONFIG_SA1100_BADGE4 2178c2ecf20Sopenharmony_ci if (machine_is_badge4()) 2188c2ecf20Sopenharmony_ci ret = pcmcia_badge4_init(dev); 2198c2ecf20Sopenharmony_ci#endif 2208c2ecf20Sopenharmony_ci#ifdef CONFIG_SA1100_JORNADA720 2218c2ecf20Sopenharmony_ci if (machine_is_jornada720()) 2228c2ecf20Sopenharmony_ci ret = pcmcia_jornada720_init(dev); 2238c2ecf20Sopenharmony_ci#endif 2248c2ecf20Sopenharmony_ci#ifdef CONFIG_ARCH_LUBBOCK 2258c2ecf20Sopenharmony_ci if (machine_is_lubbock()) 2268c2ecf20Sopenharmony_ci ret = pcmcia_lubbock_init(dev); 2278c2ecf20Sopenharmony_ci#endif 2288c2ecf20Sopenharmony_ci#ifdef CONFIG_ASSABET_NEPONSET 2298c2ecf20Sopenharmony_ci if (machine_is_assabet()) 2308c2ecf20Sopenharmony_ci ret = pcmcia_neponset_init(dev); 2318c2ecf20Sopenharmony_ci#endif 2328c2ecf20Sopenharmony_ci 2338c2ecf20Sopenharmony_ci if (ret) { 2348c2ecf20Sopenharmony_ci release_mem_region(dev->res.start, 512); 2358c2ecf20Sopenharmony_ci sa1111_disable_device(dev); 2368c2ecf20Sopenharmony_ci } 2378c2ecf20Sopenharmony_ci 2388c2ecf20Sopenharmony_ci return ret; 2398c2ecf20Sopenharmony_ci} 2408c2ecf20Sopenharmony_ci 2418c2ecf20Sopenharmony_cistatic int pcmcia_remove(struct sa1111_dev *dev) 2428c2ecf20Sopenharmony_ci{ 2438c2ecf20Sopenharmony_ci struct sa1111_pcmcia_socket *next, *s = dev_get_drvdata(&dev->dev); 2448c2ecf20Sopenharmony_ci 2458c2ecf20Sopenharmony_ci dev_set_drvdata(&dev->dev, NULL); 2468c2ecf20Sopenharmony_ci 2478c2ecf20Sopenharmony_ci for (; s; s = next) { 2488c2ecf20Sopenharmony_ci next = s->next; 2498c2ecf20Sopenharmony_ci soc_pcmcia_remove_one(&s->soc); 2508c2ecf20Sopenharmony_ci kfree(s); 2518c2ecf20Sopenharmony_ci } 2528c2ecf20Sopenharmony_ci 2538c2ecf20Sopenharmony_ci release_mem_region(dev->res.start, 512); 2548c2ecf20Sopenharmony_ci sa1111_disable_device(dev); 2558c2ecf20Sopenharmony_ci return 0; 2568c2ecf20Sopenharmony_ci} 2578c2ecf20Sopenharmony_ci 2588c2ecf20Sopenharmony_cistatic struct sa1111_driver pcmcia_driver = { 2598c2ecf20Sopenharmony_ci .drv = { 2608c2ecf20Sopenharmony_ci .name = "sa1111-pcmcia", 2618c2ecf20Sopenharmony_ci }, 2628c2ecf20Sopenharmony_ci .devid = SA1111_DEVID_PCMCIA, 2638c2ecf20Sopenharmony_ci .probe = pcmcia_probe, 2648c2ecf20Sopenharmony_ci .remove = pcmcia_remove, 2658c2ecf20Sopenharmony_ci}; 2668c2ecf20Sopenharmony_ci 2678c2ecf20Sopenharmony_cistatic int __init sa1111_drv_pcmcia_init(void) 2688c2ecf20Sopenharmony_ci{ 2698c2ecf20Sopenharmony_ci return sa1111_driver_register(&pcmcia_driver); 2708c2ecf20Sopenharmony_ci} 2718c2ecf20Sopenharmony_ci 2728c2ecf20Sopenharmony_cistatic void __exit sa1111_drv_pcmcia_exit(void) 2738c2ecf20Sopenharmony_ci{ 2748c2ecf20Sopenharmony_ci sa1111_driver_unregister(&pcmcia_driver); 2758c2ecf20Sopenharmony_ci} 2768c2ecf20Sopenharmony_ci 2778c2ecf20Sopenharmony_cifs_initcall(sa1111_drv_pcmcia_init); 2788c2ecf20Sopenharmony_cimodule_exit(sa1111_drv_pcmcia_exit); 2798c2ecf20Sopenharmony_ci 2808c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("SA1111 PCMCIA card socket driver"); 2818c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 282