162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * PCMCIA socket code for the MyCable XXS1500 system. 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (c) 2009 Manuel Lauss <manuel.lauss@gmail.com> 662306a36Sopenharmony_ci * 762306a36Sopenharmony_ci */ 862306a36Sopenharmony_ci 962306a36Sopenharmony_ci#include <linux/delay.h> 1062306a36Sopenharmony_ci#include <linux/gpio.h> 1162306a36Sopenharmony_ci#include <linux/interrupt.h> 1262306a36Sopenharmony_ci#include <linux/io.h> 1362306a36Sopenharmony_ci#include <linux/ioport.h> 1462306a36Sopenharmony_ci#include <linux/mm.h> 1562306a36Sopenharmony_ci#include <linux/module.h> 1662306a36Sopenharmony_ci#include <linux/platform_device.h> 1762306a36Sopenharmony_ci#include <linux/pm.h> 1862306a36Sopenharmony_ci#include <linux/resource.h> 1962306a36Sopenharmony_ci#include <linux/slab.h> 2062306a36Sopenharmony_ci#include <linux/spinlock.h> 2162306a36Sopenharmony_ci 2262306a36Sopenharmony_ci#include <pcmcia/ss.h> 2362306a36Sopenharmony_ci#include <pcmcia/cistpl.h> 2462306a36Sopenharmony_ci 2562306a36Sopenharmony_ci#include <asm/irq.h> 2662306a36Sopenharmony_ci#include <asm/mach-au1x00/au1000.h> 2762306a36Sopenharmony_ci 2862306a36Sopenharmony_ci#define MEM_MAP_SIZE 0x400000 2962306a36Sopenharmony_ci#define IO_MAP_SIZE 0x1000 3062306a36Sopenharmony_ci 3162306a36Sopenharmony_ci 3262306a36Sopenharmony_ci/* 3362306a36Sopenharmony_ci * 3.3V cards only; all interfacing is done via gpios: 3462306a36Sopenharmony_ci * 3562306a36Sopenharmony_ci * 0/1: carddetect (00 = card present, xx = huh) 3662306a36Sopenharmony_ci * 4: card irq 3762306a36Sopenharmony_ci * 204: reset (high-act) 3862306a36Sopenharmony_ci * 205: buffer enable (low-act) 3962306a36Sopenharmony_ci * 208/209: card voltage key (00,01,10,11) 4062306a36Sopenharmony_ci * 210: battwarn 4162306a36Sopenharmony_ci * 211: batdead 4262306a36Sopenharmony_ci * 214: power (low-act) 4362306a36Sopenharmony_ci */ 4462306a36Sopenharmony_ci#define GPIO_CDA 0 4562306a36Sopenharmony_ci#define GPIO_CDB 1 4662306a36Sopenharmony_ci#define GPIO_CARDIRQ 4 4762306a36Sopenharmony_ci#define GPIO_RESET 204 4862306a36Sopenharmony_ci#define GPIO_OUTEN 205 4962306a36Sopenharmony_ci#define GPIO_VSL 208 5062306a36Sopenharmony_ci#define GPIO_VSH 209 5162306a36Sopenharmony_ci#define GPIO_BATTDEAD 210 5262306a36Sopenharmony_ci#define GPIO_BATTWARN 211 5362306a36Sopenharmony_ci#define GPIO_POWER 214 5462306a36Sopenharmony_ci 5562306a36Sopenharmony_cistruct xxs1500_pcmcia_sock { 5662306a36Sopenharmony_ci struct pcmcia_socket socket; 5762306a36Sopenharmony_ci void *virt_io; 5862306a36Sopenharmony_ci 5962306a36Sopenharmony_ci phys_addr_t phys_io; 6062306a36Sopenharmony_ci phys_addr_t phys_attr; 6162306a36Sopenharmony_ci phys_addr_t phys_mem; 6262306a36Sopenharmony_ci 6362306a36Sopenharmony_ci /* previous flags for set_socket() */ 6462306a36Sopenharmony_ci unsigned int old_flags; 6562306a36Sopenharmony_ci}; 6662306a36Sopenharmony_ci 6762306a36Sopenharmony_ci#define to_xxs_socket(x) container_of(x, struct xxs1500_pcmcia_sock, socket) 6862306a36Sopenharmony_ci 6962306a36Sopenharmony_cistatic irqreturn_t cdirq(int irq, void *data) 7062306a36Sopenharmony_ci{ 7162306a36Sopenharmony_ci struct xxs1500_pcmcia_sock *sock = data; 7262306a36Sopenharmony_ci 7362306a36Sopenharmony_ci pcmcia_parse_events(&sock->socket, SS_DETECT); 7462306a36Sopenharmony_ci 7562306a36Sopenharmony_ci return IRQ_HANDLED; 7662306a36Sopenharmony_ci} 7762306a36Sopenharmony_ci 7862306a36Sopenharmony_cistatic int xxs1500_pcmcia_configure(struct pcmcia_socket *skt, 7962306a36Sopenharmony_ci struct socket_state_t *state) 8062306a36Sopenharmony_ci{ 8162306a36Sopenharmony_ci struct xxs1500_pcmcia_sock *sock = to_xxs_socket(skt); 8262306a36Sopenharmony_ci unsigned int changed; 8362306a36Sopenharmony_ci 8462306a36Sopenharmony_ci /* power control */ 8562306a36Sopenharmony_ci switch (state->Vcc) { 8662306a36Sopenharmony_ci case 0: 8762306a36Sopenharmony_ci gpio_set_value(GPIO_POWER, 1); /* power off */ 8862306a36Sopenharmony_ci break; 8962306a36Sopenharmony_ci case 33: 9062306a36Sopenharmony_ci gpio_set_value(GPIO_POWER, 0); /* power on */ 9162306a36Sopenharmony_ci break; 9262306a36Sopenharmony_ci case 50: 9362306a36Sopenharmony_ci default: 9462306a36Sopenharmony_ci return -EINVAL; 9562306a36Sopenharmony_ci } 9662306a36Sopenharmony_ci 9762306a36Sopenharmony_ci changed = state->flags ^ sock->old_flags; 9862306a36Sopenharmony_ci 9962306a36Sopenharmony_ci if (changed & SS_RESET) { 10062306a36Sopenharmony_ci if (state->flags & SS_RESET) { 10162306a36Sopenharmony_ci gpio_set_value(GPIO_RESET, 1); /* assert reset */ 10262306a36Sopenharmony_ci gpio_set_value(GPIO_OUTEN, 1); /* buffers off */ 10362306a36Sopenharmony_ci } else { 10462306a36Sopenharmony_ci gpio_set_value(GPIO_RESET, 0); /* deassert reset */ 10562306a36Sopenharmony_ci gpio_set_value(GPIO_OUTEN, 0); /* buffers on */ 10662306a36Sopenharmony_ci msleep(500); 10762306a36Sopenharmony_ci } 10862306a36Sopenharmony_ci } 10962306a36Sopenharmony_ci 11062306a36Sopenharmony_ci sock->old_flags = state->flags; 11162306a36Sopenharmony_ci 11262306a36Sopenharmony_ci return 0; 11362306a36Sopenharmony_ci} 11462306a36Sopenharmony_ci 11562306a36Sopenharmony_cistatic int xxs1500_pcmcia_get_status(struct pcmcia_socket *skt, 11662306a36Sopenharmony_ci unsigned int *value) 11762306a36Sopenharmony_ci{ 11862306a36Sopenharmony_ci unsigned int status; 11962306a36Sopenharmony_ci int i; 12062306a36Sopenharmony_ci 12162306a36Sopenharmony_ci status = 0; 12262306a36Sopenharmony_ci 12362306a36Sopenharmony_ci /* check carddetects: GPIO[0:1] must both be low */ 12462306a36Sopenharmony_ci if (!gpio_get_value(GPIO_CDA) && !gpio_get_value(GPIO_CDB)) 12562306a36Sopenharmony_ci status |= SS_DETECT; 12662306a36Sopenharmony_ci 12762306a36Sopenharmony_ci /* determine card voltage: GPIO[208:209] binary value */ 12862306a36Sopenharmony_ci i = (!!gpio_get_value(GPIO_VSL)) | ((!!gpio_get_value(GPIO_VSH)) << 1); 12962306a36Sopenharmony_ci 13062306a36Sopenharmony_ci switch (i) { 13162306a36Sopenharmony_ci case 0: 13262306a36Sopenharmony_ci case 1: 13362306a36Sopenharmony_ci case 2: 13462306a36Sopenharmony_ci status |= SS_3VCARD; /* 3V card */ 13562306a36Sopenharmony_ci break; 13662306a36Sopenharmony_ci case 3: /* 5V card, unsupported */ 13762306a36Sopenharmony_ci default: 13862306a36Sopenharmony_ci status |= SS_XVCARD; /* treated as unsupported in core */ 13962306a36Sopenharmony_ci } 14062306a36Sopenharmony_ci 14162306a36Sopenharmony_ci /* GPIO214: low active power switch */ 14262306a36Sopenharmony_ci status |= gpio_get_value(GPIO_POWER) ? 0 : SS_POWERON; 14362306a36Sopenharmony_ci 14462306a36Sopenharmony_ci /* GPIO204: high-active reset line */ 14562306a36Sopenharmony_ci status |= gpio_get_value(GPIO_RESET) ? SS_RESET : SS_READY; 14662306a36Sopenharmony_ci 14762306a36Sopenharmony_ci /* other stuff */ 14862306a36Sopenharmony_ci status |= gpio_get_value(GPIO_BATTDEAD) ? 0 : SS_BATDEAD; 14962306a36Sopenharmony_ci status |= gpio_get_value(GPIO_BATTWARN) ? 0 : SS_BATWARN; 15062306a36Sopenharmony_ci 15162306a36Sopenharmony_ci *value = status; 15262306a36Sopenharmony_ci 15362306a36Sopenharmony_ci return 0; 15462306a36Sopenharmony_ci} 15562306a36Sopenharmony_ci 15662306a36Sopenharmony_cistatic int xxs1500_pcmcia_sock_init(struct pcmcia_socket *skt) 15762306a36Sopenharmony_ci{ 15862306a36Sopenharmony_ci gpio_direction_input(GPIO_CDA); 15962306a36Sopenharmony_ci gpio_direction_input(GPIO_CDB); 16062306a36Sopenharmony_ci gpio_direction_input(GPIO_VSL); 16162306a36Sopenharmony_ci gpio_direction_input(GPIO_VSH); 16262306a36Sopenharmony_ci gpio_direction_input(GPIO_BATTDEAD); 16362306a36Sopenharmony_ci gpio_direction_input(GPIO_BATTWARN); 16462306a36Sopenharmony_ci gpio_direction_output(GPIO_RESET, 1); /* assert reset */ 16562306a36Sopenharmony_ci gpio_direction_output(GPIO_OUTEN, 1); /* disable buffers */ 16662306a36Sopenharmony_ci gpio_direction_output(GPIO_POWER, 1); /* power off */ 16762306a36Sopenharmony_ci 16862306a36Sopenharmony_ci return 0; 16962306a36Sopenharmony_ci} 17062306a36Sopenharmony_ci 17162306a36Sopenharmony_cistatic int xxs1500_pcmcia_sock_suspend(struct pcmcia_socket *skt) 17262306a36Sopenharmony_ci{ 17362306a36Sopenharmony_ci return 0; 17462306a36Sopenharmony_ci} 17562306a36Sopenharmony_ci 17662306a36Sopenharmony_cistatic int au1x00_pcmcia_set_io_map(struct pcmcia_socket *skt, 17762306a36Sopenharmony_ci struct pccard_io_map *map) 17862306a36Sopenharmony_ci{ 17962306a36Sopenharmony_ci struct xxs1500_pcmcia_sock *sock = to_xxs_socket(skt); 18062306a36Sopenharmony_ci 18162306a36Sopenharmony_ci map->start = (u32)sock->virt_io; 18262306a36Sopenharmony_ci map->stop = map->start + IO_MAP_SIZE; 18362306a36Sopenharmony_ci 18462306a36Sopenharmony_ci return 0; 18562306a36Sopenharmony_ci} 18662306a36Sopenharmony_ci 18762306a36Sopenharmony_cistatic int au1x00_pcmcia_set_mem_map(struct pcmcia_socket *skt, 18862306a36Sopenharmony_ci struct pccard_mem_map *map) 18962306a36Sopenharmony_ci{ 19062306a36Sopenharmony_ci struct xxs1500_pcmcia_sock *sock = to_xxs_socket(skt); 19162306a36Sopenharmony_ci 19262306a36Sopenharmony_ci if (map->flags & MAP_ATTRIB) 19362306a36Sopenharmony_ci map->static_start = sock->phys_attr + map->card_start; 19462306a36Sopenharmony_ci else 19562306a36Sopenharmony_ci map->static_start = sock->phys_mem + map->card_start; 19662306a36Sopenharmony_ci 19762306a36Sopenharmony_ci return 0; 19862306a36Sopenharmony_ci} 19962306a36Sopenharmony_ci 20062306a36Sopenharmony_cistatic struct pccard_operations xxs1500_pcmcia_operations = { 20162306a36Sopenharmony_ci .init = xxs1500_pcmcia_sock_init, 20262306a36Sopenharmony_ci .suspend = xxs1500_pcmcia_sock_suspend, 20362306a36Sopenharmony_ci .get_status = xxs1500_pcmcia_get_status, 20462306a36Sopenharmony_ci .set_socket = xxs1500_pcmcia_configure, 20562306a36Sopenharmony_ci .set_io_map = au1x00_pcmcia_set_io_map, 20662306a36Sopenharmony_ci .set_mem_map = au1x00_pcmcia_set_mem_map, 20762306a36Sopenharmony_ci}; 20862306a36Sopenharmony_ci 20962306a36Sopenharmony_cistatic int xxs1500_pcmcia_probe(struct platform_device *pdev) 21062306a36Sopenharmony_ci{ 21162306a36Sopenharmony_ci struct xxs1500_pcmcia_sock *sock; 21262306a36Sopenharmony_ci struct resource *r; 21362306a36Sopenharmony_ci int ret, irq; 21462306a36Sopenharmony_ci 21562306a36Sopenharmony_ci sock = kzalloc(sizeof(struct xxs1500_pcmcia_sock), GFP_KERNEL); 21662306a36Sopenharmony_ci if (!sock) 21762306a36Sopenharmony_ci return -ENOMEM; 21862306a36Sopenharmony_ci 21962306a36Sopenharmony_ci ret = -ENODEV; 22062306a36Sopenharmony_ci 22162306a36Sopenharmony_ci /* 36bit PCMCIA Attribute area address */ 22262306a36Sopenharmony_ci r = platform_get_resource_byname(pdev, IORESOURCE_MEM, "pcmcia-attr"); 22362306a36Sopenharmony_ci if (!r) { 22462306a36Sopenharmony_ci dev_err(&pdev->dev, "missing 'pcmcia-attr' resource!\n"); 22562306a36Sopenharmony_ci goto out0; 22662306a36Sopenharmony_ci } 22762306a36Sopenharmony_ci sock->phys_attr = r->start; 22862306a36Sopenharmony_ci 22962306a36Sopenharmony_ci /* 36bit PCMCIA Memory area address */ 23062306a36Sopenharmony_ci r = platform_get_resource_byname(pdev, IORESOURCE_MEM, "pcmcia-mem"); 23162306a36Sopenharmony_ci if (!r) { 23262306a36Sopenharmony_ci dev_err(&pdev->dev, "missing 'pcmcia-mem' resource!\n"); 23362306a36Sopenharmony_ci goto out0; 23462306a36Sopenharmony_ci } 23562306a36Sopenharmony_ci sock->phys_mem = r->start; 23662306a36Sopenharmony_ci 23762306a36Sopenharmony_ci /* 36bit PCMCIA IO area address */ 23862306a36Sopenharmony_ci r = platform_get_resource_byname(pdev, IORESOURCE_MEM, "pcmcia-io"); 23962306a36Sopenharmony_ci if (!r) { 24062306a36Sopenharmony_ci dev_err(&pdev->dev, "missing 'pcmcia-io' resource!\n"); 24162306a36Sopenharmony_ci goto out0; 24262306a36Sopenharmony_ci } 24362306a36Sopenharmony_ci sock->phys_io = r->start; 24462306a36Sopenharmony_ci 24562306a36Sopenharmony_ci 24662306a36Sopenharmony_ci /* 24762306a36Sopenharmony_ci * PCMCIA client drivers use the inb/outb macros to access 24862306a36Sopenharmony_ci * the IO registers. Since mips_io_port_base is added 24962306a36Sopenharmony_ci * to the access address of the mips implementation of 25062306a36Sopenharmony_ci * inb/outb, we need to subtract it here because we want 25162306a36Sopenharmony_ci * to access the I/O or MEM address directly, without 25262306a36Sopenharmony_ci * going through this "mips_io_port_base" mechanism. 25362306a36Sopenharmony_ci */ 25462306a36Sopenharmony_ci sock->virt_io = (void *)(ioremap(sock->phys_io, IO_MAP_SIZE) - 25562306a36Sopenharmony_ci mips_io_port_base); 25662306a36Sopenharmony_ci 25762306a36Sopenharmony_ci if (!sock->virt_io) { 25862306a36Sopenharmony_ci dev_err(&pdev->dev, "cannot remap IO area\n"); 25962306a36Sopenharmony_ci ret = -ENOMEM; 26062306a36Sopenharmony_ci goto out0; 26162306a36Sopenharmony_ci } 26262306a36Sopenharmony_ci 26362306a36Sopenharmony_ci sock->socket.ops = &xxs1500_pcmcia_operations; 26462306a36Sopenharmony_ci sock->socket.owner = THIS_MODULE; 26562306a36Sopenharmony_ci sock->socket.pci_irq = gpio_to_irq(GPIO_CARDIRQ); 26662306a36Sopenharmony_ci sock->socket.features = SS_CAP_STATIC_MAP | SS_CAP_PCCARD; 26762306a36Sopenharmony_ci sock->socket.map_size = MEM_MAP_SIZE; 26862306a36Sopenharmony_ci sock->socket.io_offset = (unsigned long)sock->virt_io; 26962306a36Sopenharmony_ci sock->socket.dev.parent = &pdev->dev; 27062306a36Sopenharmony_ci sock->socket.resource_ops = &pccard_static_ops; 27162306a36Sopenharmony_ci 27262306a36Sopenharmony_ci platform_set_drvdata(pdev, sock); 27362306a36Sopenharmony_ci 27462306a36Sopenharmony_ci /* setup carddetect irq: use one of the 2 GPIOs as an 27562306a36Sopenharmony_ci * edge detector. 27662306a36Sopenharmony_ci */ 27762306a36Sopenharmony_ci irq = gpio_to_irq(GPIO_CDA); 27862306a36Sopenharmony_ci irq_set_irq_type(irq, IRQ_TYPE_EDGE_BOTH); 27962306a36Sopenharmony_ci ret = request_irq(irq, cdirq, 0, "pcmcia_carddetect", sock); 28062306a36Sopenharmony_ci if (ret) { 28162306a36Sopenharmony_ci dev_err(&pdev->dev, "cannot setup cd irq\n"); 28262306a36Sopenharmony_ci goto out1; 28362306a36Sopenharmony_ci } 28462306a36Sopenharmony_ci 28562306a36Sopenharmony_ci ret = pcmcia_register_socket(&sock->socket); 28662306a36Sopenharmony_ci if (ret) { 28762306a36Sopenharmony_ci dev_err(&pdev->dev, "failed to register\n"); 28862306a36Sopenharmony_ci goto out2; 28962306a36Sopenharmony_ci } 29062306a36Sopenharmony_ci 29162306a36Sopenharmony_ci printk(KERN_INFO "MyCable XXS1500 PCMCIA socket services\n"); 29262306a36Sopenharmony_ci 29362306a36Sopenharmony_ci return 0; 29462306a36Sopenharmony_ci 29562306a36Sopenharmony_ciout2: 29662306a36Sopenharmony_ci free_irq(gpio_to_irq(GPIO_CDA), sock); 29762306a36Sopenharmony_ciout1: 29862306a36Sopenharmony_ci iounmap((void *)(sock->virt_io + (u32)mips_io_port_base)); 29962306a36Sopenharmony_ciout0: 30062306a36Sopenharmony_ci kfree(sock); 30162306a36Sopenharmony_ci return ret; 30262306a36Sopenharmony_ci} 30362306a36Sopenharmony_ci 30462306a36Sopenharmony_cistatic int xxs1500_pcmcia_remove(struct platform_device *pdev) 30562306a36Sopenharmony_ci{ 30662306a36Sopenharmony_ci struct xxs1500_pcmcia_sock *sock = platform_get_drvdata(pdev); 30762306a36Sopenharmony_ci 30862306a36Sopenharmony_ci pcmcia_unregister_socket(&sock->socket); 30962306a36Sopenharmony_ci free_irq(gpio_to_irq(GPIO_CDA), sock); 31062306a36Sopenharmony_ci iounmap((void *)(sock->virt_io + (u32)mips_io_port_base)); 31162306a36Sopenharmony_ci kfree(sock); 31262306a36Sopenharmony_ci 31362306a36Sopenharmony_ci return 0; 31462306a36Sopenharmony_ci} 31562306a36Sopenharmony_ci 31662306a36Sopenharmony_cistatic struct platform_driver xxs1500_pcmcia_socket_driver = { 31762306a36Sopenharmony_ci .driver = { 31862306a36Sopenharmony_ci .name = "xxs1500_pcmcia", 31962306a36Sopenharmony_ci }, 32062306a36Sopenharmony_ci .probe = xxs1500_pcmcia_probe, 32162306a36Sopenharmony_ci .remove = xxs1500_pcmcia_remove, 32262306a36Sopenharmony_ci}; 32362306a36Sopenharmony_ci 32462306a36Sopenharmony_cimodule_platform_driver(xxs1500_pcmcia_socket_driver); 32562306a36Sopenharmony_ci 32662306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 32762306a36Sopenharmony_ciMODULE_DESCRIPTION("PCMCIA Socket Services for MyCable XXS1500 systems"); 32862306a36Sopenharmony_ciMODULE_AUTHOR("Manuel Lauss"); 329