353 lines · c
1// SPDX-License-Identifier: GPL-2.0-or-later2/*3 * Copyright (C) 2007 PA Semi, Inc4 *5 * Maintained by: Olof Johansson <olof@lixom.net>6 *7 * Based on drivers/pcmcia/omap_cf.c8 */9 10#include <linux/module.h>11#include <linux/kernel.h>12#include <linux/sched.h>13#include <linux/platform_device.h>14#include <linux/errno.h>15#include <linux/init.h>16#include <linux/delay.h>17#include <linux/interrupt.h>18#include <linux/mm.h>19#include <linux/vmalloc.h>20#include <linux/of_address.h>21#include <linux/of_irq.h>22#include <linux/of_platform.h>23#include <linux/slab.h>24 25#include <pcmcia/ss.h>26 27static const char driver_name[] = "electra-cf";28 29struct electra_cf_socket {30 struct pcmcia_socket socket;31 32 struct timer_list timer;33 unsigned present:1;34 unsigned active:1;35 36 struct platform_device *ofdev;37 unsigned long mem_phys;38 void __iomem *mem_base;39 unsigned long mem_size;40 void __iomem *io_virt;41 unsigned int io_base;42 unsigned int io_size;43 u_int irq;44 struct resource iomem;45 void __iomem *gpio_base;46 int gpio_detect;47 int gpio_vsense;48 int gpio_3v;49 int gpio_5v;50};51 52#define POLL_INTERVAL (2 * HZ)53 54 55static int electra_cf_present(struct electra_cf_socket *cf)56{57 unsigned int gpio;58 59 gpio = in_le32(cf->gpio_base+0x40);60 return !(gpio & (1 << cf->gpio_detect));61}62 63static int electra_cf_ss_init(struct pcmcia_socket *s)64{65 return 0;66}67 68/* the timer is primarily to kick this socket's pccardd */69static void electra_cf_timer(struct timer_list *t)70{71 struct electra_cf_socket *cf = from_timer(cf, t, timer);72 int present = electra_cf_present(cf);73 74 if (present != cf->present) {75 cf->present = present;76 pcmcia_parse_events(&cf->socket, SS_DETECT);77 }78 79 if (cf->active)80 mod_timer(&cf->timer, jiffies + POLL_INTERVAL);81}82 83static irqreturn_t electra_cf_irq(int irq, void *_cf)84{85 struct electra_cf_socket *cf = _cf;86 87 electra_cf_timer(&cf->timer);88 return IRQ_HANDLED;89}90 91static int electra_cf_get_status(struct pcmcia_socket *s, u_int *sp)92{93 struct electra_cf_socket *cf;94 95 if (!sp)96 return -EINVAL;97 98 cf = container_of(s, struct electra_cf_socket, socket);99 100 /* NOTE CF is always 3VCARD */101 if (electra_cf_present(cf)) {102 *sp = SS_READY | SS_DETECT | SS_POWERON | SS_3VCARD;103 104 s->pci_irq = cf->irq;105 } else106 *sp = 0;107 return 0;108}109 110static int electra_cf_set_socket(struct pcmcia_socket *sock,111 struct socket_state_t *s)112{113 unsigned int gpio;114 unsigned int vcc;115 struct electra_cf_socket *cf;116 117 cf = container_of(sock, struct electra_cf_socket, socket);118 119 /* "reset" means no power in our case */120 vcc = (s->flags & SS_RESET) ? 0 : s->Vcc;121 122 switch (vcc) {123 case 0:124 gpio = 0;125 break;126 case 33:127 gpio = (1 << cf->gpio_3v);128 break;129 case 5:130 gpio = (1 << cf->gpio_5v);131 break;132 default:133 return -EINVAL;134 }135 136 gpio |= 1 << (cf->gpio_3v + 16); /* enwr */137 gpio |= 1 << (cf->gpio_5v + 16); /* enwr */138 out_le32(cf->gpio_base+0x90, gpio);139 140 pr_debug("%s: Vcc %d, io_irq %d, flags %04x csc %04x\n",141 driver_name, s->Vcc, s->io_irq, s->flags, s->csc_mask);142 143 return 0;144}145 146static int electra_cf_set_io_map(struct pcmcia_socket *s,147 struct pccard_io_map *io)148{149 return 0;150}151 152static int electra_cf_set_mem_map(struct pcmcia_socket *s,153 struct pccard_mem_map *map)154{155 struct electra_cf_socket *cf;156 157 if (map->card_start)158 return -EINVAL;159 cf = container_of(s, struct electra_cf_socket, socket);160 map->static_start = cf->mem_phys;161 map->flags &= MAP_ACTIVE|MAP_ATTRIB;162 if (!(map->flags & MAP_ATTRIB))163 map->static_start += 0x800;164 return 0;165}166 167static struct pccard_operations electra_cf_ops = {168 .init = electra_cf_ss_init,169 .get_status = electra_cf_get_status,170 .set_socket = electra_cf_set_socket,171 .set_io_map = electra_cf_set_io_map,172 .set_mem_map = electra_cf_set_mem_map,173};174 175static int electra_cf_probe(struct platform_device *ofdev)176{177 struct device *device = &ofdev->dev;178 struct device_node *np = ofdev->dev.of_node;179 struct electra_cf_socket *cf;180 struct resource mem, io;181 int status = -ENOMEM;182 const unsigned int *prop;183 int err;184 185 err = of_address_to_resource(np, 0, &mem);186 if (err)187 return -EINVAL;188 189 err = of_address_to_resource(np, 1, &io);190 if (err)191 return -EINVAL;192 193 cf = kzalloc(sizeof(*cf), GFP_KERNEL);194 if (!cf)195 return -ENOMEM;196 197 timer_setup(&cf->timer, electra_cf_timer, 0);198 cf->irq = 0;199 200 cf->ofdev = ofdev;201 cf->mem_phys = mem.start;202 cf->mem_size = PAGE_ALIGN(resource_size(&mem));203 cf->mem_base = ioremap(cf->mem_phys, cf->mem_size);204 if (!cf->mem_base)205 goto out_free_cf;206 cf->io_size = PAGE_ALIGN(resource_size(&io));207 cf->io_virt = ioremap_phb(io.start, cf->io_size);208 if (!cf->io_virt)209 goto out_unmap_mem;210 211 cf->gpio_base = ioremap(0xfc103000, 0x1000);212 if (!cf->gpio_base)213 goto out_unmap_virt;214 dev_set_drvdata(device, cf);215 216 cf->io_base = (unsigned long)cf->io_virt - VMALLOC_END;217 cf->iomem.start = (unsigned long)cf->mem_base;218 cf->iomem.end = (unsigned long)cf->mem_base + (mem.end - mem.start);219 cf->iomem.flags = IORESOURCE_MEM;220 221 cf->irq = irq_of_parse_and_map(np, 0);222 223 status = request_irq(cf->irq, electra_cf_irq, IRQF_SHARED,224 driver_name, cf);225 if (status < 0) {226 dev_err(device, "request_irq failed\n");227 goto fail1;228 }229 230 cf->socket.pci_irq = cf->irq;231 232 status = -EINVAL;233 234 prop = of_get_property(np, "card-detect-gpio", NULL);235 if (!prop)236 goto fail1;237 cf->gpio_detect = *prop;238 239 prop = of_get_property(np, "card-vsense-gpio", NULL);240 if (!prop)241 goto fail1;242 cf->gpio_vsense = *prop;243 244 prop = of_get_property(np, "card-3v-gpio", NULL);245 if (!prop)246 goto fail1;247 cf->gpio_3v = *prop;248 249 prop = of_get_property(np, "card-5v-gpio", NULL);250 if (!prop)251 goto fail1;252 cf->gpio_5v = *prop;253 254 cf->socket.io_offset = cf->io_base;255 256 /* reserve chip-select regions */257 if (!request_mem_region(cf->mem_phys, cf->mem_size, driver_name)) {258 status = -ENXIO;259 dev_err(device, "Can't claim memory region\n");260 goto fail1;261 }262 263 if (!request_region(cf->io_base, cf->io_size, driver_name)) {264 status = -ENXIO;265 dev_err(device, "Can't claim I/O region\n");266 goto fail2;267 }268 269 cf->socket.owner = THIS_MODULE;270 cf->socket.dev.parent = &ofdev->dev;271 cf->socket.ops = &electra_cf_ops;272 cf->socket.resource_ops = &pccard_static_ops;273 cf->socket.features = SS_CAP_PCCARD | SS_CAP_STATIC_MAP |274 SS_CAP_MEM_ALIGN;275 cf->socket.map_size = 0x800;276 277 status = pcmcia_register_socket(&cf->socket);278 if (status < 0) {279 dev_err(device, "pcmcia_register_socket failed\n");280 goto fail3;281 }282 283 dev_info(device, "at mem 0x%lx io 0x%llx irq %d\n",284 cf->mem_phys, io.start, cf->irq);285 286 cf->active = 1;287 electra_cf_timer(&cf->timer);288 return 0;289 290fail3:291 release_region(cf->io_base, cf->io_size);292fail2:293 release_mem_region(cf->mem_phys, cf->mem_size);294fail1:295 if (cf->irq)296 free_irq(cf->irq, cf);297 298 iounmap(cf->gpio_base);299out_unmap_virt:300 device_init_wakeup(&ofdev->dev, 0);301 iounmap(cf->io_virt);302out_unmap_mem:303 iounmap(cf->mem_base);304out_free_cf:305 kfree(cf);306 return status;307 308}309 310static void electra_cf_remove(struct platform_device *ofdev)311{312 struct device *device = &ofdev->dev;313 struct electra_cf_socket *cf;314 315 cf = dev_get_drvdata(device);316 317 cf->active = 0;318 pcmcia_unregister_socket(&cf->socket);319 free_irq(cf->irq, cf);320 timer_shutdown_sync(&cf->timer);321 322 iounmap(cf->io_virt);323 iounmap(cf->mem_base);324 iounmap(cf->gpio_base);325 release_mem_region(cf->mem_phys, cf->mem_size);326 release_region(cf->io_base, cf->io_size);327 328 kfree(cf);329}330 331static const struct of_device_id electra_cf_match[] = {332 {333 .compatible = "electra-cf",334 },335 {},336};337MODULE_DEVICE_TABLE(of, electra_cf_match);338 339static struct platform_driver electra_cf_driver = {340 .driver = {341 .name = driver_name,342 .of_match_table = electra_cf_match,343 },344 .probe = electra_cf_probe,345 .remove_new = electra_cf_remove,346};347 348module_platform_driver(electra_cf_driver);349 350MODULE_LICENSE("GPL");351MODULE_AUTHOR("Olof Johansson <olof@lixom.net>");352MODULE_DESCRIPTION("PA Semi Electra CF driver");353