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