18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Copyright (C) 2007 PA Semi, Inc 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Maintained by: Olof Johansson <olof@lixom.net> 68c2ecf20Sopenharmony_ci * 78c2ecf20Sopenharmony_ci * Based on drivers/pcmcia/omap_cf.c 88c2ecf20Sopenharmony_ci */ 98c2ecf20Sopenharmony_ci 108c2ecf20Sopenharmony_ci#include <linux/module.h> 118c2ecf20Sopenharmony_ci#include <linux/kernel.h> 128c2ecf20Sopenharmony_ci#include <linux/sched.h> 138c2ecf20Sopenharmony_ci#include <linux/platform_device.h> 148c2ecf20Sopenharmony_ci#include <linux/errno.h> 158c2ecf20Sopenharmony_ci#include <linux/init.h> 168c2ecf20Sopenharmony_ci#include <linux/delay.h> 178c2ecf20Sopenharmony_ci#include <linux/interrupt.h> 188c2ecf20Sopenharmony_ci#include <linux/mm.h> 198c2ecf20Sopenharmony_ci#include <linux/vmalloc.h> 208c2ecf20Sopenharmony_ci#include <linux/of_address.h> 218c2ecf20Sopenharmony_ci#include <linux/of_irq.h> 228c2ecf20Sopenharmony_ci#include <linux/of_platform.h> 238c2ecf20Sopenharmony_ci#include <linux/slab.h> 248c2ecf20Sopenharmony_ci 258c2ecf20Sopenharmony_ci#include <pcmcia/ss.h> 268c2ecf20Sopenharmony_ci 278c2ecf20Sopenharmony_cistatic const char driver_name[] = "electra-cf"; 288c2ecf20Sopenharmony_ci 298c2ecf20Sopenharmony_cistruct electra_cf_socket { 308c2ecf20Sopenharmony_ci struct pcmcia_socket socket; 318c2ecf20Sopenharmony_ci 328c2ecf20Sopenharmony_ci struct timer_list timer; 338c2ecf20Sopenharmony_ci unsigned present:1; 348c2ecf20Sopenharmony_ci unsigned active:1; 358c2ecf20Sopenharmony_ci 368c2ecf20Sopenharmony_ci struct platform_device *ofdev; 378c2ecf20Sopenharmony_ci unsigned long mem_phys; 388c2ecf20Sopenharmony_ci void __iomem *mem_base; 398c2ecf20Sopenharmony_ci unsigned long mem_size; 408c2ecf20Sopenharmony_ci void __iomem *io_virt; 418c2ecf20Sopenharmony_ci unsigned int io_base; 428c2ecf20Sopenharmony_ci unsigned int io_size; 438c2ecf20Sopenharmony_ci u_int irq; 448c2ecf20Sopenharmony_ci struct resource iomem; 458c2ecf20Sopenharmony_ci void __iomem *gpio_base; 468c2ecf20Sopenharmony_ci int gpio_detect; 478c2ecf20Sopenharmony_ci int gpio_vsense; 488c2ecf20Sopenharmony_ci int gpio_3v; 498c2ecf20Sopenharmony_ci int gpio_5v; 508c2ecf20Sopenharmony_ci}; 518c2ecf20Sopenharmony_ci 528c2ecf20Sopenharmony_ci#define POLL_INTERVAL (2 * HZ) 538c2ecf20Sopenharmony_ci 548c2ecf20Sopenharmony_ci 558c2ecf20Sopenharmony_cistatic int electra_cf_present(struct electra_cf_socket *cf) 568c2ecf20Sopenharmony_ci{ 578c2ecf20Sopenharmony_ci unsigned int gpio; 588c2ecf20Sopenharmony_ci 598c2ecf20Sopenharmony_ci gpio = in_le32(cf->gpio_base+0x40); 608c2ecf20Sopenharmony_ci return !(gpio & (1 << cf->gpio_detect)); 618c2ecf20Sopenharmony_ci} 628c2ecf20Sopenharmony_ci 638c2ecf20Sopenharmony_cistatic int electra_cf_ss_init(struct pcmcia_socket *s) 648c2ecf20Sopenharmony_ci{ 658c2ecf20Sopenharmony_ci return 0; 668c2ecf20Sopenharmony_ci} 678c2ecf20Sopenharmony_ci 688c2ecf20Sopenharmony_ci/* the timer is primarily to kick this socket's pccardd */ 698c2ecf20Sopenharmony_cistatic void electra_cf_timer(struct timer_list *t) 708c2ecf20Sopenharmony_ci{ 718c2ecf20Sopenharmony_ci struct electra_cf_socket *cf = from_timer(cf, t, timer); 728c2ecf20Sopenharmony_ci int present = electra_cf_present(cf); 738c2ecf20Sopenharmony_ci 748c2ecf20Sopenharmony_ci if (present != cf->present) { 758c2ecf20Sopenharmony_ci cf->present = present; 768c2ecf20Sopenharmony_ci pcmcia_parse_events(&cf->socket, SS_DETECT); 778c2ecf20Sopenharmony_ci } 788c2ecf20Sopenharmony_ci 798c2ecf20Sopenharmony_ci if (cf->active) 808c2ecf20Sopenharmony_ci mod_timer(&cf->timer, jiffies + POLL_INTERVAL); 818c2ecf20Sopenharmony_ci} 828c2ecf20Sopenharmony_ci 838c2ecf20Sopenharmony_cistatic irqreturn_t electra_cf_irq(int irq, void *_cf) 848c2ecf20Sopenharmony_ci{ 858c2ecf20Sopenharmony_ci struct electra_cf_socket *cf = _cf; 868c2ecf20Sopenharmony_ci 878c2ecf20Sopenharmony_ci electra_cf_timer(&cf->timer); 888c2ecf20Sopenharmony_ci return IRQ_HANDLED; 898c2ecf20Sopenharmony_ci} 908c2ecf20Sopenharmony_ci 918c2ecf20Sopenharmony_cistatic int electra_cf_get_status(struct pcmcia_socket *s, u_int *sp) 928c2ecf20Sopenharmony_ci{ 938c2ecf20Sopenharmony_ci struct electra_cf_socket *cf; 948c2ecf20Sopenharmony_ci 958c2ecf20Sopenharmony_ci if (!sp) 968c2ecf20Sopenharmony_ci return -EINVAL; 978c2ecf20Sopenharmony_ci 988c2ecf20Sopenharmony_ci cf = container_of(s, struct electra_cf_socket, socket); 998c2ecf20Sopenharmony_ci 1008c2ecf20Sopenharmony_ci /* NOTE CF is always 3VCARD */ 1018c2ecf20Sopenharmony_ci if (electra_cf_present(cf)) { 1028c2ecf20Sopenharmony_ci *sp = SS_READY | SS_DETECT | SS_POWERON | SS_3VCARD; 1038c2ecf20Sopenharmony_ci 1048c2ecf20Sopenharmony_ci s->pci_irq = cf->irq; 1058c2ecf20Sopenharmony_ci } else 1068c2ecf20Sopenharmony_ci *sp = 0; 1078c2ecf20Sopenharmony_ci return 0; 1088c2ecf20Sopenharmony_ci} 1098c2ecf20Sopenharmony_ci 1108c2ecf20Sopenharmony_cistatic int electra_cf_set_socket(struct pcmcia_socket *sock, 1118c2ecf20Sopenharmony_ci struct socket_state_t *s) 1128c2ecf20Sopenharmony_ci{ 1138c2ecf20Sopenharmony_ci unsigned int gpio; 1148c2ecf20Sopenharmony_ci unsigned int vcc; 1158c2ecf20Sopenharmony_ci struct electra_cf_socket *cf; 1168c2ecf20Sopenharmony_ci 1178c2ecf20Sopenharmony_ci cf = container_of(sock, struct electra_cf_socket, socket); 1188c2ecf20Sopenharmony_ci 1198c2ecf20Sopenharmony_ci /* "reset" means no power in our case */ 1208c2ecf20Sopenharmony_ci vcc = (s->flags & SS_RESET) ? 0 : s->Vcc; 1218c2ecf20Sopenharmony_ci 1228c2ecf20Sopenharmony_ci switch (vcc) { 1238c2ecf20Sopenharmony_ci case 0: 1248c2ecf20Sopenharmony_ci gpio = 0; 1258c2ecf20Sopenharmony_ci break; 1268c2ecf20Sopenharmony_ci case 33: 1278c2ecf20Sopenharmony_ci gpio = (1 << cf->gpio_3v); 1288c2ecf20Sopenharmony_ci break; 1298c2ecf20Sopenharmony_ci case 5: 1308c2ecf20Sopenharmony_ci gpio = (1 << cf->gpio_5v); 1318c2ecf20Sopenharmony_ci break; 1328c2ecf20Sopenharmony_ci default: 1338c2ecf20Sopenharmony_ci return -EINVAL; 1348c2ecf20Sopenharmony_ci } 1358c2ecf20Sopenharmony_ci 1368c2ecf20Sopenharmony_ci gpio |= 1 << (cf->gpio_3v + 16); /* enwr */ 1378c2ecf20Sopenharmony_ci gpio |= 1 << (cf->gpio_5v + 16); /* enwr */ 1388c2ecf20Sopenharmony_ci out_le32(cf->gpio_base+0x90, gpio); 1398c2ecf20Sopenharmony_ci 1408c2ecf20Sopenharmony_ci pr_debug("%s: Vcc %d, io_irq %d, flags %04x csc %04x\n", 1418c2ecf20Sopenharmony_ci driver_name, s->Vcc, s->io_irq, s->flags, s->csc_mask); 1428c2ecf20Sopenharmony_ci 1438c2ecf20Sopenharmony_ci return 0; 1448c2ecf20Sopenharmony_ci} 1458c2ecf20Sopenharmony_ci 1468c2ecf20Sopenharmony_cistatic int electra_cf_set_io_map(struct pcmcia_socket *s, 1478c2ecf20Sopenharmony_ci struct pccard_io_map *io) 1488c2ecf20Sopenharmony_ci{ 1498c2ecf20Sopenharmony_ci return 0; 1508c2ecf20Sopenharmony_ci} 1518c2ecf20Sopenharmony_ci 1528c2ecf20Sopenharmony_cistatic int electra_cf_set_mem_map(struct pcmcia_socket *s, 1538c2ecf20Sopenharmony_ci struct pccard_mem_map *map) 1548c2ecf20Sopenharmony_ci{ 1558c2ecf20Sopenharmony_ci struct electra_cf_socket *cf; 1568c2ecf20Sopenharmony_ci 1578c2ecf20Sopenharmony_ci if (map->card_start) 1588c2ecf20Sopenharmony_ci return -EINVAL; 1598c2ecf20Sopenharmony_ci cf = container_of(s, struct electra_cf_socket, socket); 1608c2ecf20Sopenharmony_ci map->static_start = cf->mem_phys; 1618c2ecf20Sopenharmony_ci map->flags &= MAP_ACTIVE|MAP_ATTRIB; 1628c2ecf20Sopenharmony_ci if (!(map->flags & MAP_ATTRIB)) 1638c2ecf20Sopenharmony_ci map->static_start += 0x800; 1648c2ecf20Sopenharmony_ci return 0; 1658c2ecf20Sopenharmony_ci} 1668c2ecf20Sopenharmony_ci 1678c2ecf20Sopenharmony_cistatic struct pccard_operations electra_cf_ops = { 1688c2ecf20Sopenharmony_ci .init = electra_cf_ss_init, 1698c2ecf20Sopenharmony_ci .get_status = electra_cf_get_status, 1708c2ecf20Sopenharmony_ci .set_socket = electra_cf_set_socket, 1718c2ecf20Sopenharmony_ci .set_io_map = electra_cf_set_io_map, 1728c2ecf20Sopenharmony_ci .set_mem_map = electra_cf_set_mem_map, 1738c2ecf20Sopenharmony_ci}; 1748c2ecf20Sopenharmony_ci 1758c2ecf20Sopenharmony_cistatic int electra_cf_probe(struct platform_device *ofdev) 1768c2ecf20Sopenharmony_ci{ 1778c2ecf20Sopenharmony_ci struct device *device = &ofdev->dev; 1788c2ecf20Sopenharmony_ci struct device_node *np = ofdev->dev.of_node; 1798c2ecf20Sopenharmony_ci struct electra_cf_socket *cf; 1808c2ecf20Sopenharmony_ci struct resource mem, io; 1818c2ecf20Sopenharmony_ci int status = -ENOMEM; 1828c2ecf20Sopenharmony_ci const unsigned int *prop; 1838c2ecf20Sopenharmony_ci int err; 1848c2ecf20Sopenharmony_ci 1858c2ecf20Sopenharmony_ci err = of_address_to_resource(np, 0, &mem); 1868c2ecf20Sopenharmony_ci if (err) 1878c2ecf20Sopenharmony_ci return -EINVAL; 1888c2ecf20Sopenharmony_ci 1898c2ecf20Sopenharmony_ci err = of_address_to_resource(np, 1, &io); 1908c2ecf20Sopenharmony_ci if (err) 1918c2ecf20Sopenharmony_ci return -EINVAL; 1928c2ecf20Sopenharmony_ci 1938c2ecf20Sopenharmony_ci cf = kzalloc(sizeof(*cf), GFP_KERNEL); 1948c2ecf20Sopenharmony_ci if (!cf) 1958c2ecf20Sopenharmony_ci return -ENOMEM; 1968c2ecf20Sopenharmony_ci 1978c2ecf20Sopenharmony_ci timer_setup(&cf->timer, electra_cf_timer, 0); 1988c2ecf20Sopenharmony_ci cf->irq = 0; 1998c2ecf20Sopenharmony_ci 2008c2ecf20Sopenharmony_ci cf->ofdev = ofdev; 2018c2ecf20Sopenharmony_ci cf->mem_phys = mem.start; 2028c2ecf20Sopenharmony_ci cf->mem_size = PAGE_ALIGN(resource_size(&mem)); 2038c2ecf20Sopenharmony_ci cf->mem_base = ioremap(cf->mem_phys, cf->mem_size); 2048c2ecf20Sopenharmony_ci if (!cf->mem_base) 2058c2ecf20Sopenharmony_ci goto out_free_cf; 2068c2ecf20Sopenharmony_ci cf->io_size = PAGE_ALIGN(resource_size(&io)); 2078c2ecf20Sopenharmony_ci cf->io_virt = ioremap_phb(io.start, cf->io_size); 2088c2ecf20Sopenharmony_ci if (!cf->io_virt) 2098c2ecf20Sopenharmony_ci goto out_unmap_mem; 2108c2ecf20Sopenharmony_ci 2118c2ecf20Sopenharmony_ci cf->gpio_base = ioremap(0xfc103000, 0x1000); 2128c2ecf20Sopenharmony_ci if (!cf->gpio_base) 2138c2ecf20Sopenharmony_ci goto out_unmap_virt; 2148c2ecf20Sopenharmony_ci dev_set_drvdata(device, cf); 2158c2ecf20Sopenharmony_ci 2168c2ecf20Sopenharmony_ci cf->io_base = (unsigned long)cf->io_virt - VMALLOC_END; 2178c2ecf20Sopenharmony_ci cf->iomem.start = (unsigned long)cf->mem_base; 2188c2ecf20Sopenharmony_ci cf->iomem.end = (unsigned long)cf->mem_base + (mem.end - mem.start); 2198c2ecf20Sopenharmony_ci cf->iomem.flags = IORESOURCE_MEM; 2208c2ecf20Sopenharmony_ci 2218c2ecf20Sopenharmony_ci cf->irq = irq_of_parse_and_map(np, 0); 2228c2ecf20Sopenharmony_ci 2238c2ecf20Sopenharmony_ci status = request_irq(cf->irq, electra_cf_irq, IRQF_SHARED, 2248c2ecf20Sopenharmony_ci driver_name, cf); 2258c2ecf20Sopenharmony_ci if (status < 0) { 2268c2ecf20Sopenharmony_ci dev_err(device, "request_irq failed\n"); 2278c2ecf20Sopenharmony_ci goto fail1; 2288c2ecf20Sopenharmony_ci } 2298c2ecf20Sopenharmony_ci 2308c2ecf20Sopenharmony_ci cf->socket.pci_irq = cf->irq; 2318c2ecf20Sopenharmony_ci 2328c2ecf20Sopenharmony_ci prop = of_get_property(np, "card-detect-gpio", NULL); 2338c2ecf20Sopenharmony_ci if (!prop) 2348c2ecf20Sopenharmony_ci goto fail1; 2358c2ecf20Sopenharmony_ci cf->gpio_detect = *prop; 2368c2ecf20Sopenharmony_ci 2378c2ecf20Sopenharmony_ci prop = of_get_property(np, "card-vsense-gpio", NULL); 2388c2ecf20Sopenharmony_ci if (!prop) 2398c2ecf20Sopenharmony_ci goto fail1; 2408c2ecf20Sopenharmony_ci cf->gpio_vsense = *prop; 2418c2ecf20Sopenharmony_ci 2428c2ecf20Sopenharmony_ci prop = of_get_property(np, "card-3v-gpio", NULL); 2438c2ecf20Sopenharmony_ci if (!prop) 2448c2ecf20Sopenharmony_ci goto fail1; 2458c2ecf20Sopenharmony_ci cf->gpio_3v = *prop; 2468c2ecf20Sopenharmony_ci 2478c2ecf20Sopenharmony_ci prop = of_get_property(np, "card-5v-gpio", NULL); 2488c2ecf20Sopenharmony_ci if (!prop) 2498c2ecf20Sopenharmony_ci goto fail1; 2508c2ecf20Sopenharmony_ci cf->gpio_5v = *prop; 2518c2ecf20Sopenharmony_ci 2528c2ecf20Sopenharmony_ci cf->socket.io_offset = cf->io_base; 2538c2ecf20Sopenharmony_ci 2548c2ecf20Sopenharmony_ci /* reserve chip-select regions */ 2558c2ecf20Sopenharmony_ci if (!request_mem_region(cf->mem_phys, cf->mem_size, driver_name)) { 2568c2ecf20Sopenharmony_ci status = -ENXIO; 2578c2ecf20Sopenharmony_ci dev_err(device, "Can't claim memory region\n"); 2588c2ecf20Sopenharmony_ci goto fail1; 2598c2ecf20Sopenharmony_ci } 2608c2ecf20Sopenharmony_ci 2618c2ecf20Sopenharmony_ci if (!request_region(cf->io_base, cf->io_size, driver_name)) { 2628c2ecf20Sopenharmony_ci status = -ENXIO; 2638c2ecf20Sopenharmony_ci dev_err(device, "Can't claim I/O region\n"); 2648c2ecf20Sopenharmony_ci goto fail2; 2658c2ecf20Sopenharmony_ci } 2668c2ecf20Sopenharmony_ci 2678c2ecf20Sopenharmony_ci cf->socket.owner = THIS_MODULE; 2688c2ecf20Sopenharmony_ci cf->socket.dev.parent = &ofdev->dev; 2698c2ecf20Sopenharmony_ci cf->socket.ops = &electra_cf_ops; 2708c2ecf20Sopenharmony_ci cf->socket.resource_ops = &pccard_static_ops; 2718c2ecf20Sopenharmony_ci cf->socket.features = SS_CAP_PCCARD | SS_CAP_STATIC_MAP | 2728c2ecf20Sopenharmony_ci SS_CAP_MEM_ALIGN; 2738c2ecf20Sopenharmony_ci cf->socket.map_size = 0x800; 2748c2ecf20Sopenharmony_ci 2758c2ecf20Sopenharmony_ci status = pcmcia_register_socket(&cf->socket); 2768c2ecf20Sopenharmony_ci if (status < 0) { 2778c2ecf20Sopenharmony_ci dev_err(device, "pcmcia_register_socket failed\n"); 2788c2ecf20Sopenharmony_ci goto fail3; 2798c2ecf20Sopenharmony_ci } 2808c2ecf20Sopenharmony_ci 2818c2ecf20Sopenharmony_ci dev_info(device, "at mem 0x%lx io 0x%llx irq %d\n", 2828c2ecf20Sopenharmony_ci cf->mem_phys, io.start, cf->irq); 2838c2ecf20Sopenharmony_ci 2848c2ecf20Sopenharmony_ci cf->active = 1; 2858c2ecf20Sopenharmony_ci electra_cf_timer(&cf->timer); 2868c2ecf20Sopenharmony_ci return 0; 2878c2ecf20Sopenharmony_ci 2888c2ecf20Sopenharmony_cifail3: 2898c2ecf20Sopenharmony_ci release_region(cf->io_base, cf->io_size); 2908c2ecf20Sopenharmony_cifail2: 2918c2ecf20Sopenharmony_ci release_mem_region(cf->mem_phys, cf->mem_size); 2928c2ecf20Sopenharmony_cifail1: 2938c2ecf20Sopenharmony_ci if (cf->irq) 2948c2ecf20Sopenharmony_ci free_irq(cf->irq, cf); 2958c2ecf20Sopenharmony_ci 2968c2ecf20Sopenharmony_ci iounmap(cf->gpio_base); 2978c2ecf20Sopenharmony_ciout_unmap_virt: 2988c2ecf20Sopenharmony_ci device_init_wakeup(&ofdev->dev, 0); 2998c2ecf20Sopenharmony_ci iounmap(cf->io_virt); 3008c2ecf20Sopenharmony_ciout_unmap_mem: 3018c2ecf20Sopenharmony_ci iounmap(cf->mem_base); 3028c2ecf20Sopenharmony_ciout_free_cf: 3038c2ecf20Sopenharmony_ci kfree(cf); 3048c2ecf20Sopenharmony_ci return status; 3058c2ecf20Sopenharmony_ci 3068c2ecf20Sopenharmony_ci} 3078c2ecf20Sopenharmony_ci 3088c2ecf20Sopenharmony_cistatic int electra_cf_remove(struct platform_device *ofdev) 3098c2ecf20Sopenharmony_ci{ 3108c2ecf20Sopenharmony_ci struct device *device = &ofdev->dev; 3118c2ecf20Sopenharmony_ci struct electra_cf_socket *cf; 3128c2ecf20Sopenharmony_ci 3138c2ecf20Sopenharmony_ci cf = dev_get_drvdata(device); 3148c2ecf20Sopenharmony_ci 3158c2ecf20Sopenharmony_ci cf->active = 0; 3168c2ecf20Sopenharmony_ci pcmcia_unregister_socket(&cf->socket); 3178c2ecf20Sopenharmony_ci free_irq(cf->irq, cf); 3188c2ecf20Sopenharmony_ci del_timer_sync(&cf->timer); 3198c2ecf20Sopenharmony_ci 3208c2ecf20Sopenharmony_ci iounmap(cf->io_virt); 3218c2ecf20Sopenharmony_ci iounmap(cf->mem_base); 3228c2ecf20Sopenharmony_ci iounmap(cf->gpio_base); 3238c2ecf20Sopenharmony_ci release_mem_region(cf->mem_phys, cf->mem_size); 3248c2ecf20Sopenharmony_ci release_region(cf->io_base, cf->io_size); 3258c2ecf20Sopenharmony_ci 3268c2ecf20Sopenharmony_ci kfree(cf); 3278c2ecf20Sopenharmony_ci 3288c2ecf20Sopenharmony_ci return 0; 3298c2ecf20Sopenharmony_ci} 3308c2ecf20Sopenharmony_ci 3318c2ecf20Sopenharmony_cistatic const struct of_device_id electra_cf_match[] = { 3328c2ecf20Sopenharmony_ci { 3338c2ecf20Sopenharmony_ci .compatible = "electra-cf", 3348c2ecf20Sopenharmony_ci }, 3358c2ecf20Sopenharmony_ci {}, 3368c2ecf20Sopenharmony_ci}; 3378c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(of, electra_cf_match); 3388c2ecf20Sopenharmony_ci 3398c2ecf20Sopenharmony_cistatic struct platform_driver electra_cf_driver = { 3408c2ecf20Sopenharmony_ci .driver = { 3418c2ecf20Sopenharmony_ci .name = driver_name, 3428c2ecf20Sopenharmony_ci .of_match_table = electra_cf_match, 3438c2ecf20Sopenharmony_ci }, 3448c2ecf20Sopenharmony_ci .probe = electra_cf_probe, 3458c2ecf20Sopenharmony_ci .remove = electra_cf_remove, 3468c2ecf20Sopenharmony_ci}; 3478c2ecf20Sopenharmony_ci 3488c2ecf20Sopenharmony_cimodule_platform_driver(electra_cf_driver); 3498c2ecf20Sopenharmony_ci 3508c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 3518c2ecf20Sopenharmony_ciMODULE_AUTHOR("Olof Johansson <olof@lixom.net>"); 3528c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("PA Semi Electra CF driver"); 353