162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Copyright (C) 2007 PA Semi, Inc 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Maintained by: Olof Johansson <olof@lixom.net> 662306a36Sopenharmony_ci * 762306a36Sopenharmony_ci * Based on drivers/pcmcia/omap_cf.c 862306a36Sopenharmony_ci */ 962306a36Sopenharmony_ci 1062306a36Sopenharmony_ci#include <linux/module.h> 1162306a36Sopenharmony_ci#include <linux/kernel.h> 1262306a36Sopenharmony_ci#include <linux/sched.h> 1362306a36Sopenharmony_ci#include <linux/platform_device.h> 1462306a36Sopenharmony_ci#include <linux/errno.h> 1562306a36Sopenharmony_ci#include <linux/init.h> 1662306a36Sopenharmony_ci#include <linux/delay.h> 1762306a36Sopenharmony_ci#include <linux/interrupt.h> 1862306a36Sopenharmony_ci#include <linux/mm.h> 1962306a36Sopenharmony_ci#include <linux/vmalloc.h> 2062306a36Sopenharmony_ci#include <linux/of_address.h> 2162306a36Sopenharmony_ci#include <linux/of_irq.h> 2262306a36Sopenharmony_ci#include <linux/of_platform.h> 2362306a36Sopenharmony_ci#include <linux/slab.h> 2462306a36Sopenharmony_ci 2562306a36Sopenharmony_ci#include <pcmcia/ss.h> 2662306a36Sopenharmony_ci 2762306a36Sopenharmony_cistatic const char driver_name[] = "electra-cf"; 2862306a36Sopenharmony_ci 2962306a36Sopenharmony_cistruct electra_cf_socket { 3062306a36Sopenharmony_ci struct pcmcia_socket socket; 3162306a36Sopenharmony_ci 3262306a36Sopenharmony_ci struct timer_list timer; 3362306a36Sopenharmony_ci unsigned present:1; 3462306a36Sopenharmony_ci unsigned active:1; 3562306a36Sopenharmony_ci 3662306a36Sopenharmony_ci struct platform_device *ofdev; 3762306a36Sopenharmony_ci unsigned long mem_phys; 3862306a36Sopenharmony_ci void __iomem *mem_base; 3962306a36Sopenharmony_ci unsigned long mem_size; 4062306a36Sopenharmony_ci void __iomem *io_virt; 4162306a36Sopenharmony_ci unsigned int io_base; 4262306a36Sopenharmony_ci unsigned int io_size; 4362306a36Sopenharmony_ci u_int irq; 4462306a36Sopenharmony_ci struct resource iomem; 4562306a36Sopenharmony_ci void __iomem *gpio_base; 4662306a36Sopenharmony_ci int gpio_detect; 4762306a36Sopenharmony_ci int gpio_vsense; 4862306a36Sopenharmony_ci int gpio_3v; 4962306a36Sopenharmony_ci int gpio_5v; 5062306a36Sopenharmony_ci}; 5162306a36Sopenharmony_ci 5262306a36Sopenharmony_ci#define POLL_INTERVAL (2 * HZ) 5362306a36Sopenharmony_ci 5462306a36Sopenharmony_ci 5562306a36Sopenharmony_cistatic int electra_cf_present(struct electra_cf_socket *cf) 5662306a36Sopenharmony_ci{ 5762306a36Sopenharmony_ci unsigned int gpio; 5862306a36Sopenharmony_ci 5962306a36Sopenharmony_ci gpio = in_le32(cf->gpio_base+0x40); 6062306a36Sopenharmony_ci return !(gpio & (1 << cf->gpio_detect)); 6162306a36Sopenharmony_ci} 6262306a36Sopenharmony_ci 6362306a36Sopenharmony_cistatic int electra_cf_ss_init(struct pcmcia_socket *s) 6462306a36Sopenharmony_ci{ 6562306a36Sopenharmony_ci return 0; 6662306a36Sopenharmony_ci} 6762306a36Sopenharmony_ci 6862306a36Sopenharmony_ci/* the timer is primarily to kick this socket's pccardd */ 6962306a36Sopenharmony_cistatic void electra_cf_timer(struct timer_list *t) 7062306a36Sopenharmony_ci{ 7162306a36Sopenharmony_ci struct electra_cf_socket *cf = from_timer(cf, t, timer); 7262306a36Sopenharmony_ci int present = electra_cf_present(cf); 7362306a36Sopenharmony_ci 7462306a36Sopenharmony_ci if (present != cf->present) { 7562306a36Sopenharmony_ci cf->present = present; 7662306a36Sopenharmony_ci pcmcia_parse_events(&cf->socket, SS_DETECT); 7762306a36Sopenharmony_ci } 7862306a36Sopenharmony_ci 7962306a36Sopenharmony_ci if (cf->active) 8062306a36Sopenharmony_ci mod_timer(&cf->timer, jiffies + POLL_INTERVAL); 8162306a36Sopenharmony_ci} 8262306a36Sopenharmony_ci 8362306a36Sopenharmony_cistatic irqreturn_t electra_cf_irq(int irq, void *_cf) 8462306a36Sopenharmony_ci{ 8562306a36Sopenharmony_ci struct electra_cf_socket *cf = _cf; 8662306a36Sopenharmony_ci 8762306a36Sopenharmony_ci electra_cf_timer(&cf->timer); 8862306a36Sopenharmony_ci return IRQ_HANDLED; 8962306a36Sopenharmony_ci} 9062306a36Sopenharmony_ci 9162306a36Sopenharmony_cistatic int electra_cf_get_status(struct pcmcia_socket *s, u_int *sp) 9262306a36Sopenharmony_ci{ 9362306a36Sopenharmony_ci struct electra_cf_socket *cf; 9462306a36Sopenharmony_ci 9562306a36Sopenharmony_ci if (!sp) 9662306a36Sopenharmony_ci return -EINVAL; 9762306a36Sopenharmony_ci 9862306a36Sopenharmony_ci cf = container_of(s, struct electra_cf_socket, socket); 9962306a36Sopenharmony_ci 10062306a36Sopenharmony_ci /* NOTE CF is always 3VCARD */ 10162306a36Sopenharmony_ci if (electra_cf_present(cf)) { 10262306a36Sopenharmony_ci *sp = SS_READY | SS_DETECT | SS_POWERON | SS_3VCARD; 10362306a36Sopenharmony_ci 10462306a36Sopenharmony_ci s->pci_irq = cf->irq; 10562306a36Sopenharmony_ci } else 10662306a36Sopenharmony_ci *sp = 0; 10762306a36Sopenharmony_ci return 0; 10862306a36Sopenharmony_ci} 10962306a36Sopenharmony_ci 11062306a36Sopenharmony_cistatic int electra_cf_set_socket(struct pcmcia_socket *sock, 11162306a36Sopenharmony_ci struct socket_state_t *s) 11262306a36Sopenharmony_ci{ 11362306a36Sopenharmony_ci unsigned int gpio; 11462306a36Sopenharmony_ci unsigned int vcc; 11562306a36Sopenharmony_ci struct electra_cf_socket *cf; 11662306a36Sopenharmony_ci 11762306a36Sopenharmony_ci cf = container_of(sock, struct electra_cf_socket, socket); 11862306a36Sopenharmony_ci 11962306a36Sopenharmony_ci /* "reset" means no power in our case */ 12062306a36Sopenharmony_ci vcc = (s->flags & SS_RESET) ? 0 : s->Vcc; 12162306a36Sopenharmony_ci 12262306a36Sopenharmony_ci switch (vcc) { 12362306a36Sopenharmony_ci case 0: 12462306a36Sopenharmony_ci gpio = 0; 12562306a36Sopenharmony_ci break; 12662306a36Sopenharmony_ci case 33: 12762306a36Sopenharmony_ci gpio = (1 << cf->gpio_3v); 12862306a36Sopenharmony_ci break; 12962306a36Sopenharmony_ci case 5: 13062306a36Sopenharmony_ci gpio = (1 << cf->gpio_5v); 13162306a36Sopenharmony_ci break; 13262306a36Sopenharmony_ci default: 13362306a36Sopenharmony_ci return -EINVAL; 13462306a36Sopenharmony_ci } 13562306a36Sopenharmony_ci 13662306a36Sopenharmony_ci gpio |= 1 << (cf->gpio_3v + 16); /* enwr */ 13762306a36Sopenharmony_ci gpio |= 1 << (cf->gpio_5v + 16); /* enwr */ 13862306a36Sopenharmony_ci out_le32(cf->gpio_base+0x90, gpio); 13962306a36Sopenharmony_ci 14062306a36Sopenharmony_ci pr_debug("%s: Vcc %d, io_irq %d, flags %04x csc %04x\n", 14162306a36Sopenharmony_ci driver_name, s->Vcc, s->io_irq, s->flags, s->csc_mask); 14262306a36Sopenharmony_ci 14362306a36Sopenharmony_ci return 0; 14462306a36Sopenharmony_ci} 14562306a36Sopenharmony_ci 14662306a36Sopenharmony_cistatic int electra_cf_set_io_map(struct pcmcia_socket *s, 14762306a36Sopenharmony_ci struct pccard_io_map *io) 14862306a36Sopenharmony_ci{ 14962306a36Sopenharmony_ci return 0; 15062306a36Sopenharmony_ci} 15162306a36Sopenharmony_ci 15262306a36Sopenharmony_cistatic int electra_cf_set_mem_map(struct pcmcia_socket *s, 15362306a36Sopenharmony_ci struct pccard_mem_map *map) 15462306a36Sopenharmony_ci{ 15562306a36Sopenharmony_ci struct electra_cf_socket *cf; 15662306a36Sopenharmony_ci 15762306a36Sopenharmony_ci if (map->card_start) 15862306a36Sopenharmony_ci return -EINVAL; 15962306a36Sopenharmony_ci cf = container_of(s, struct electra_cf_socket, socket); 16062306a36Sopenharmony_ci map->static_start = cf->mem_phys; 16162306a36Sopenharmony_ci map->flags &= MAP_ACTIVE|MAP_ATTRIB; 16262306a36Sopenharmony_ci if (!(map->flags & MAP_ATTRIB)) 16362306a36Sopenharmony_ci map->static_start += 0x800; 16462306a36Sopenharmony_ci return 0; 16562306a36Sopenharmony_ci} 16662306a36Sopenharmony_ci 16762306a36Sopenharmony_cistatic struct pccard_operations electra_cf_ops = { 16862306a36Sopenharmony_ci .init = electra_cf_ss_init, 16962306a36Sopenharmony_ci .get_status = electra_cf_get_status, 17062306a36Sopenharmony_ci .set_socket = electra_cf_set_socket, 17162306a36Sopenharmony_ci .set_io_map = electra_cf_set_io_map, 17262306a36Sopenharmony_ci .set_mem_map = electra_cf_set_mem_map, 17362306a36Sopenharmony_ci}; 17462306a36Sopenharmony_ci 17562306a36Sopenharmony_cistatic int electra_cf_probe(struct platform_device *ofdev) 17662306a36Sopenharmony_ci{ 17762306a36Sopenharmony_ci struct device *device = &ofdev->dev; 17862306a36Sopenharmony_ci struct device_node *np = ofdev->dev.of_node; 17962306a36Sopenharmony_ci struct electra_cf_socket *cf; 18062306a36Sopenharmony_ci struct resource mem, io; 18162306a36Sopenharmony_ci int status = -ENOMEM; 18262306a36Sopenharmony_ci const unsigned int *prop; 18362306a36Sopenharmony_ci int err; 18462306a36Sopenharmony_ci 18562306a36Sopenharmony_ci err = of_address_to_resource(np, 0, &mem); 18662306a36Sopenharmony_ci if (err) 18762306a36Sopenharmony_ci return -EINVAL; 18862306a36Sopenharmony_ci 18962306a36Sopenharmony_ci err = of_address_to_resource(np, 1, &io); 19062306a36Sopenharmony_ci if (err) 19162306a36Sopenharmony_ci return -EINVAL; 19262306a36Sopenharmony_ci 19362306a36Sopenharmony_ci cf = kzalloc(sizeof(*cf), GFP_KERNEL); 19462306a36Sopenharmony_ci if (!cf) 19562306a36Sopenharmony_ci return -ENOMEM; 19662306a36Sopenharmony_ci 19762306a36Sopenharmony_ci timer_setup(&cf->timer, electra_cf_timer, 0); 19862306a36Sopenharmony_ci cf->irq = 0; 19962306a36Sopenharmony_ci 20062306a36Sopenharmony_ci cf->ofdev = ofdev; 20162306a36Sopenharmony_ci cf->mem_phys = mem.start; 20262306a36Sopenharmony_ci cf->mem_size = PAGE_ALIGN(resource_size(&mem)); 20362306a36Sopenharmony_ci cf->mem_base = ioremap(cf->mem_phys, cf->mem_size); 20462306a36Sopenharmony_ci if (!cf->mem_base) 20562306a36Sopenharmony_ci goto out_free_cf; 20662306a36Sopenharmony_ci cf->io_size = PAGE_ALIGN(resource_size(&io)); 20762306a36Sopenharmony_ci cf->io_virt = ioremap_phb(io.start, cf->io_size); 20862306a36Sopenharmony_ci if (!cf->io_virt) 20962306a36Sopenharmony_ci goto out_unmap_mem; 21062306a36Sopenharmony_ci 21162306a36Sopenharmony_ci cf->gpio_base = ioremap(0xfc103000, 0x1000); 21262306a36Sopenharmony_ci if (!cf->gpio_base) 21362306a36Sopenharmony_ci goto out_unmap_virt; 21462306a36Sopenharmony_ci dev_set_drvdata(device, cf); 21562306a36Sopenharmony_ci 21662306a36Sopenharmony_ci cf->io_base = (unsigned long)cf->io_virt - VMALLOC_END; 21762306a36Sopenharmony_ci cf->iomem.start = (unsigned long)cf->mem_base; 21862306a36Sopenharmony_ci cf->iomem.end = (unsigned long)cf->mem_base + (mem.end - mem.start); 21962306a36Sopenharmony_ci cf->iomem.flags = IORESOURCE_MEM; 22062306a36Sopenharmony_ci 22162306a36Sopenharmony_ci cf->irq = irq_of_parse_and_map(np, 0); 22262306a36Sopenharmony_ci 22362306a36Sopenharmony_ci status = request_irq(cf->irq, electra_cf_irq, IRQF_SHARED, 22462306a36Sopenharmony_ci driver_name, cf); 22562306a36Sopenharmony_ci if (status < 0) { 22662306a36Sopenharmony_ci dev_err(device, "request_irq failed\n"); 22762306a36Sopenharmony_ci goto fail1; 22862306a36Sopenharmony_ci } 22962306a36Sopenharmony_ci 23062306a36Sopenharmony_ci cf->socket.pci_irq = cf->irq; 23162306a36Sopenharmony_ci 23262306a36Sopenharmony_ci status = -EINVAL; 23362306a36Sopenharmony_ci 23462306a36Sopenharmony_ci prop = of_get_property(np, "card-detect-gpio", NULL); 23562306a36Sopenharmony_ci if (!prop) 23662306a36Sopenharmony_ci goto fail1; 23762306a36Sopenharmony_ci cf->gpio_detect = *prop; 23862306a36Sopenharmony_ci 23962306a36Sopenharmony_ci prop = of_get_property(np, "card-vsense-gpio", NULL); 24062306a36Sopenharmony_ci if (!prop) 24162306a36Sopenharmony_ci goto fail1; 24262306a36Sopenharmony_ci cf->gpio_vsense = *prop; 24362306a36Sopenharmony_ci 24462306a36Sopenharmony_ci prop = of_get_property(np, "card-3v-gpio", NULL); 24562306a36Sopenharmony_ci if (!prop) 24662306a36Sopenharmony_ci goto fail1; 24762306a36Sopenharmony_ci cf->gpio_3v = *prop; 24862306a36Sopenharmony_ci 24962306a36Sopenharmony_ci prop = of_get_property(np, "card-5v-gpio", NULL); 25062306a36Sopenharmony_ci if (!prop) 25162306a36Sopenharmony_ci goto fail1; 25262306a36Sopenharmony_ci cf->gpio_5v = *prop; 25362306a36Sopenharmony_ci 25462306a36Sopenharmony_ci cf->socket.io_offset = cf->io_base; 25562306a36Sopenharmony_ci 25662306a36Sopenharmony_ci /* reserve chip-select regions */ 25762306a36Sopenharmony_ci if (!request_mem_region(cf->mem_phys, cf->mem_size, driver_name)) { 25862306a36Sopenharmony_ci status = -ENXIO; 25962306a36Sopenharmony_ci dev_err(device, "Can't claim memory region\n"); 26062306a36Sopenharmony_ci goto fail1; 26162306a36Sopenharmony_ci } 26262306a36Sopenharmony_ci 26362306a36Sopenharmony_ci if (!request_region(cf->io_base, cf->io_size, driver_name)) { 26462306a36Sopenharmony_ci status = -ENXIO; 26562306a36Sopenharmony_ci dev_err(device, "Can't claim I/O region\n"); 26662306a36Sopenharmony_ci goto fail2; 26762306a36Sopenharmony_ci } 26862306a36Sopenharmony_ci 26962306a36Sopenharmony_ci cf->socket.owner = THIS_MODULE; 27062306a36Sopenharmony_ci cf->socket.dev.parent = &ofdev->dev; 27162306a36Sopenharmony_ci cf->socket.ops = &electra_cf_ops; 27262306a36Sopenharmony_ci cf->socket.resource_ops = &pccard_static_ops; 27362306a36Sopenharmony_ci cf->socket.features = SS_CAP_PCCARD | SS_CAP_STATIC_MAP | 27462306a36Sopenharmony_ci SS_CAP_MEM_ALIGN; 27562306a36Sopenharmony_ci cf->socket.map_size = 0x800; 27662306a36Sopenharmony_ci 27762306a36Sopenharmony_ci status = pcmcia_register_socket(&cf->socket); 27862306a36Sopenharmony_ci if (status < 0) { 27962306a36Sopenharmony_ci dev_err(device, "pcmcia_register_socket failed\n"); 28062306a36Sopenharmony_ci goto fail3; 28162306a36Sopenharmony_ci } 28262306a36Sopenharmony_ci 28362306a36Sopenharmony_ci dev_info(device, "at mem 0x%lx io 0x%llx irq %d\n", 28462306a36Sopenharmony_ci cf->mem_phys, io.start, cf->irq); 28562306a36Sopenharmony_ci 28662306a36Sopenharmony_ci cf->active = 1; 28762306a36Sopenharmony_ci electra_cf_timer(&cf->timer); 28862306a36Sopenharmony_ci return 0; 28962306a36Sopenharmony_ci 29062306a36Sopenharmony_cifail3: 29162306a36Sopenharmony_ci release_region(cf->io_base, cf->io_size); 29262306a36Sopenharmony_cifail2: 29362306a36Sopenharmony_ci release_mem_region(cf->mem_phys, cf->mem_size); 29462306a36Sopenharmony_cifail1: 29562306a36Sopenharmony_ci if (cf->irq) 29662306a36Sopenharmony_ci free_irq(cf->irq, cf); 29762306a36Sopenharmony_ci 29862306a36Sopenharmony_ci iounmap(cf->gpio_base); 29962306a36Sopenharmony_ciout_unmap_virt: 30062306a36Sopenharmony_ci device_init_wakeup(&ofdev->dev, 0); 30162306a36Sopenharmony_ci iounmap(cf->io_virt); 30262306a36Sopenharmony_ciout_unmap_mem: 30362306a36Sopenharmony_ci iounmap(cf->mem_base); 30462306a36Sopenharmony_ciout_free_cf: 30562306a36Sopenharmony_ci kfree(cf); 30662306a36Sopenharmony_ci return status; 30762306a36Sopenharmony_ci 30862306a36Sopenharmony_ci} 30962306a36Sopenharmony_ci 31062306a36Sopenharmony_cistatic int electra_cf_remove(struct platform_device *ofdev) 31162306a36Sopenharmony_ci{ 31262306a36Sopenharmony_ci struct device *device = &ofdev->dev; 31362306a36Sopenharmony_ci struct electra_cf_socket *cf; 31462306a36Sopenharmony_ci 31562306a36Sopenharmony_ci cf = dev_get_drvdata(device); 31662306a36Sopenharmony_ci 31762306a36Sopenharmony_ci cf->active = 0; 31862306a36Sopenharmony_ci pcmcia_unregister_socket(&cf->socket); 31962306a36Sopenharmony_ci free_irq(cf->irq, cf); 32062306a36Sopenharmony_ci timer_shutdown_sync(&cf->timer); 32162306a36Sopenharmony_ci 32262306a36Sopenharmony_ci iounmap(cf->io_virt); 32362306a36Sopenharmony_ci iounmap(cf->mem_base); 32462306a36Sopenharmony_ci iounmap(cf->gpio_base); 32562306a36Sopenharmony_ci release_mem_region(cf->mem_phys, cf->mem_size); 32662306a36Sopenharmony_ci release_region(cf->io_base, cf->io_size); 32762306a36Sopenharmony_ci 32862306a36Sopenharmony_ci kfree(cf); 32962306a36Sopenharmony_ci 33062306a36Sopenharmony_ci return 0; 33162306a36Sopenharmony_ci} 33262306a36Sopenharmony_ci 33362306a36Sopenharmony_cistatic const struct of_device_id electra_cf_match[] = { 33462306a36Sopenharmony_ci { 33562306a36Sopenharmony_ci .compatible = "electra-cf", 33662306a36Sopenharmony_ci }, 33762306a36Sopenharmony_ci {}, 33862306a36Sopenharmony_ci}; 33962306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, electra_cf_match); 34062306a36Sopenharmony_ci 34162306a36Sopenharmony_cistatic struct platform_driver electra_cf_driver = { 34262306a36Sopenharmony_ci .driver = { 34362306a36Sopenharmony_ci .name = driver_name, 34462306a36Sopenharmony_ci .of_match_table = electra_cf_match, 34562306a36Sopenharmony_ci }, 34662306a36Sopenharmony_ci .probe = electra_cf_probe, 34762306a36Sopenharmony_ci .remove = electra_cf_remove, 34862306a36Sopenharmony_ci}; 34962306a36Sopenharmony_ci 35062306a36Sopenharmony_cimodule_platform_driver(electra_cf_driver); 35162306a36Sopenharmony_ci 35262306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 35362306a36Sopenharmony_ciMODULE_AUTHOR("Olof Johansson <olof@lixom.net>"); 35462306a36Sopenharmony_ciMODULE_DESCRIPTION("PA Semi Electra CF driver"); 355