brintos

brintos / linux-shallow public Read only

0
0
Text · 7.7 KiB · 5ae826e Raw
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