| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1 | /* | 
 | 2 |  * linux/drivers/pcmcia/sa1111_generic.c | 
 | 3 |  * | 
 | 4 |  * We implement the generic parts of a SA1111 PCMCIA driver.  This | 
 | 5 |  * basically means we handle everything except controlling the | 
 | 6 |  * power.  Power is machine specific... | 
 | 7 |  */ | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 8 | #include <linux/module.h> | 
 | 9 | #include <linux/kernel.h> | 
 | 10 | #include <linux/ioport.h> | 
 | 11 | #include <linux/device.h> | 
 | 12 | #include <linux/interrupt.h> | 
 | 13 | #include <linux/init.h> | 
| Russell King | 9973022 | 2009-03-25 10:21:35 +0000 | [diff] [blame] | 14 | #include <linux/io.h> | 
| Tejun Heo | 5a0e3ad | 2010-03-24 17:04:11 +0900 | [diff] [blame] | 15 | #include <linux/slab.h> | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 16 |  | 
 | 17 | #include <pcmcia/ss.h> | 
 | 18 |  | 
| Russell King | a09e64f | 2008-08-05 16:14:15 +0100 | [diff] [blame] | 19 | #include <mach/hardware.h> | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 20 | #include <asm/hardware/sa1111.h> | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 21 | #include <asm/irq.h> | 
 | 22 |  | 
 | 23 | #include "sa1111_generic.h" | 
 | 24 |  | 
| Eric Miao | 08fa159 | 2009-12-26 12:32:38 +0800 | [diff] [blame] | 25 | #define IDX_IRQ_S0_READY_NINT	(0) | 
 | 26 | #define IDX_IRQ_S0_CD_VALID	(1) | 
 | 27 | #define IDX_IRQ_S0_BVD1_STSCHG	(2) | 
 | 28 | #define IDX_IRQ_S1_READY_NINT	(3) | 
 | 29 | #define IDX_IRQ_S1_CD_VALID	(4) | 
 | 30 | #define IDX_IRQ_S1_BVD1_STSCHG	(5) | 
 | 31 |  | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 32 | static struct pcmcia_irqs irqs[] = { | 
| Eric Miao | 08fa159 | 2009-12-26 12:32:38 +0800 | [diff] [blame] | 33 | 	{ 0, NO_IRQ, "SA1111 PCMCIA card detect" }, | 
 | 34 | 	{ 0, NO_IRQ, "SA1111 PCMCIA BVD1"        }, | 
 | 35 | 	{ 1, NO_IRQ, "SA1111 CF card detect"     }, | 
 | 36 | 	{ 1, NO_IRQ, "SA1111 CF BVD1"            }, | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 37 | }; | 
 | 38 |  | 
| Russell King - ARM Linux | dabd146 | 2009-03-29 22:35:11 +0100 | [diff] [blame] | 39 | static int sa1111_pcmcia_hw_init(struct soc_pcmcia_socket *skt) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 40 | { | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 41 | 	return soc_pcmcia_request_irqs(skt, irqs, ARRAY_SIZE(irqs)); | 
 | 42 | } | 
 | 43 |  | 
| Russell King - ARM Linux | dabd146 | 2009-03-29 22:35:11 +0100 | [diff] [blame] | 44 | static void sa1111_pcmcia_hw_shutdown(struct soc_pcmcia_socket *skt) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 45 | { | 
 | 46 | 	soc_pcmcia_free_irqs(skt, irqs, ARRAY_SIZE(irqs)); | 
 | 47 | } | 
 | 48 |  | 
 | 49 | void sa1111_pcmcia_socket_state(struct soc_pcmcia_socket *skt, struct pcmcia_state *state) | 
 | 50 | { | 
| Russell King - ARM Linux | 701a5dc | 2009-03-29 19:42:44 +0100 | [diff] [blame] | 51 | 	struct sa1111_pcmcia_socket *s = to_skt(skt); | 
 | 52 | 	unsigned long status = sa1111_readl(s->dev->mapbase + SA1111_PCSR); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 53 |  | 
 | 54 | 	switch (skt->nr) { | 
 | 55 | 	case 0: | 
 | 56 | 		state->detect = status & PCSR_S0_DETECT ? 0 : 1; | 
 | 57 | 		state->ready  = status & PCSR_S0_READY  ? 1 : 0; | 
 | 58 | 		state->bvd1   = status & PCSR_S0_BVD1   ? 1 : 0; | 
 | 59 | 		state->bvd2   = status & PCSR_S0_BVD2   ? 1 : 0; | 
 | 60 | 		state->wrprot = status & PCSR_S0_WP     ? 1 : 0; | 
 | 61 | 		state->vs_3v  = status & PCSR_S0_VS1    ? 0 : 1; | 
 | 62 | 		state->vs_Xv  = status & PCSR_S0_VS2    ? 0 : 1; | 
 | 63 | 		break; | 
 | 64 |  | 
 | 65 | 	case 1: | 
 | 66 | 		state->detect = status & PCSR_S1_DETECT ? 0 : 1; | 
 | 67 | 		state->ready  = status & PCSR_S1_READY  ? 1 : 0; | 
 | 68 | 		state->bvd1   = status & PCSR_S1_BVD1   ? 1 : 0; | 
 | 69 | 		state->bvd2   = status & PCSR_S1_BVD2   ? 1 : 0; | 
 | 70 | 		state->wrprot = status & PCSR_S1_WP     ? 1 : 0; | 
 | 71 | 		state->vs_3v  = status & PCSR_S1_VS1    ? 0 : 1; | 
 | 72 | 		state->vs_Xv  = status & PCSR_S1_VS2    ? 0 : 1; | 
 | 73 | 		break; | 
 | 74 | 	} | 
 | 75 | } | 
 | 76 |  | 
 | 77 | int sa1111_pcmcia_configure_socket(struct soc_pcmcia_socket *skt, const socket_state_t *state) | 
 | 78 | { | 
| Russell King - ARM Linux | 701a5dc | 2009-03-29 19:42:44 +0100 | [diff] [blame] | 79 | 	struct sa1111_pcmcia_socket *s = to_skt(skt); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 80 | 	unsigned int pccr_skt_mask, pccr_set_mask, val; | 
 | 81 | 	unsigned long flags; | 
 | 82 |  | 
 | 83 | 	switch (skt->nr) { | 
 | 84 | 	case 0: | 
 | 85 | 		pccr_skt_mask = PCCR_S0_RST|PCCR_S0_FLT|PCCR_S0_PWAITEN|PCCR_S0_PSE; | 
 | 86 | 		break; | 
 | 87 |  | 
 | 88 | 	case 1: | 
 | 89 | 		pccr_skt_mask = PCCR_S1_RST|PCCR_S1_FLT|PCCR_S1_PWAITEN|PCCR_S1_PSE; | 
 | 90 | 		break; | 
 | 91 |  | 
 | 92 | 	default: | 
 | 93 | 		return -1; | 
 | 94 | 	} | 
 | 95 |  | 
 | 96 | 	pccr_set_mask = 0; | 
 | 97 |  | 
 | 98 | 	if (state->Vcc != 0) | 
 | 99 | 		pccr_set_mask |= PCCR_S0_PWAITEN|PCCR_S1_PWAITEN; | 
 | 100 | 	if (state->Vcc == 50) | 
 | 101 | 		pccr_set_mask |= PCCR_S0_PSE|PCCR_S1_PSE; | 
 | 102 | 	if (state->flags & SS_RESET) | 
 | 103 | 		pccr_set_mask |= PCCR_S0_RST|PCCR_S1_RST; | 
 | 104 | 	if (state->flags & SS_OUTPUT_ENA) | 
 | 105 | 		pccr_set_mask |= PCCR_S0_FLT|PCCR_S1_FLT; | 
 | 106 |  | 
 | 107 | 	local_irq_save(flags); | 
| Russell King - ARM Linux | 701a5dc | 2009-03-29 19:42:44 +0100 | [diff] [blame] | 108 | 	val = sa1111_readl(s->dev->mapbase + SA1111_PCCR); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 109 | 	val &= ~pccr_skt_mask; | 
 | 110 | 	val |= pccr_set_mask & pccr_skt_mask; | 
| Russell King - ARM Linux | 701a5dc | 2009-03-29 19:42:44 +0100 | [diff] [blame] | 111 | 	sa1111_writel(val, s->dev->mapbase + SA1111_PCCR); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 112 | 	local_irq_restore(flags); | 
 | 113 |  | 
 | 114 | 	return 0; | 
 | 115 | } | 
 | 116 |  | 
 | 117 | void sa1111_pcmcia_socket_init(struct soc_pcmcia_socket *skt) | 
 | 118 | { | 
 | 119 | 	soc_pcmcia_enable_irqs(skt, irqs, ARRAY_SIZE(irqs)); | 
 | 120 | } | 
 | 121 |  | 
| Russell King - ARM Linux | dabd146 | 2009-03-29 22:35:11 +0100 | [diff] [blame] | 122 | static void sa1111_pcmcia_socket_suspend(struct soc_pcmcia_socket *skt) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 123 | { | 
 | 124 | 	soc_pcmcia_disable_irqs(skt, irqs, ARRAY_SIZE(irqs)); | 
 | 125 | } | 
 | 126 |  | 
| Russell King - ARM Linux | 701a5dc | 2009-03-29 19:42:44 +0100 | [diff] [blame] | 127 | int sa1111_pcmcia_add(struct sa1111_dev *dev, struct pcmcia_low_level *ops, | 
 | 128 | 	int (*add)(struct soc_pcmcia_socket *)) | 
 | 129 | { | 
 | 130 | 	struct sa1111_pcmcia_socket *s; | 
 | 131 | 	int i, ret = 0; | 
 | 132 |  | 
| Russell King - ARM Linux | dabd146 | 2009-03-29 22:35:11 +0100 | [diff] [blame] | 133 | 	ops->hw_init = sa1111_pcmcia_hw_init; | 
 | 134 | 	ops->hw_shutdown = sa1111_pcmcia_hw_shutdown; | 
 | 135 | 	ops->socket_state = sa1111_pcmcia_socket_state; | 
 | 136 | 	ops->socket_suspend = sa1111_pcmcia_socket_suspend; | 
 | 137 |  | 
| Russell King - ARM Linux | 701a5dc | 2009-03-29 19:42:44 +0100 | [diff] [blame] | 138 | 	for (i = 0; i < ops->nr; i++) { | 
 | 139 | 		s = kzalloc(sizeof(*s), GFP_KERNEL); | 
 | 140 | 		if (!s) | 
 | 141 | 			return -ENOMEM; | 
 | 142 |  | 
 | 143 | 		s->soc.nr = ops->first + i; | 
| Russell King - ARM Linux | 701a5dc | 2009-03-29 19:42:44 +0100 | [diff] [blame] | 144 | 		s->soc.ops = ops; | 
 | 145 | 		s->soc.socket.owner = ops->owner; | 
 | 146 | 		s->soc.socket.dev.parent = &dev->dev; | 
| Eric Miao | 08fa159 | 2009-12-26 12:32:38 +0800 | [diff] [blame] | 147 | 		s->soc.socket.pci_irq = s->soc.nr ? | 
 | 148 | 				dev->irq[IDX_IRQ_S0_READY_NINT] : | 
 | 149 | 				dev->irq[IDX_IRQ_S1_READY_NINT]; | 
| Russell King - ARM Linux | 701a5dc | 2009-03-29 19:42:44 +0100 | [diff] [blame] | 150 | 		s->dev = dev; | 
 | 151 |  | 
 | 152 | 		ret = add(&s->soc); | 
 | 153 | 		if (ret == 0) { | 
 | 154 | 			s->next = dev_get_drvdata(&dev->dev); | 
 | 155 | 			dev_set_drvdata(&dev->dev, s); | 
 | 156 | 		} else | 
 | 157 | 			kfree(s); | 
 | 158 | 	} | 
 | 159 |  | 
 | 160 | 	return ret; | 
 | 161 | } | 
 | 162 |  | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 163 | static int pcmcia_probe(struct sa1111_dev *dev) | 
 | 164 | { | 
| Russell King | f339ab3 | 2005-10-28 14:29:43 +0100 | [diff] [blame] | 165 | 	void __iomem *base; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 166 |  | 
| Russell King - ARM Linux | 701a5dc | 2009-03-29 19:42:44 +0100 | [diff] [blame] | 167 | 	dev_set_drvdata(&dev->dev, NULL); | 
 | 168 |  | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 169 | 	if (!request_mem_region(dev->res.start, 512, | 
 | 170 | 				SA1111_DRIVER_NAME(dev))) | 
 | 171 | 		return -EBUSY; | 
 | 172 |  | 
 | 173 | 	base = dev->mapbase; | 
 | 174 |  | 
| Eric Miao | 08fa159 | 2009-12-26 12:32:38 +0800 | [diff] [blame] | 175 | 	/* Initialize PCMCIA IRQs */ | 
 | 176 | 	irqs[0].irq = dev->irq[IDX_IRQ_S0_CD_VALID]; | 
 | 177 | 	irqs[1].irq = dev->irq[IDX_IRQ_S0_BVD1_STSCHG]; | 
 | 178 | 	irqs[2].irq = dev->irq[IDX_IRQ_S1_CD_VALID]; | 
 | 179 | 	irqs[3].irq = dev->irq[IDX_IRQ_S1_BVD1_STSCHG]; | 
 | 180 |  | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 181 | 	/* | 
 | 182 | 	 * Initialise the suspend state. | 
 | 183 | 	 */ | 
 | 184 | 	sa1111_writel(PCSSR_S0_SLEEP | PCSSR_S1_SLEEP, base + SA1111_PCSSR); | 
 | 185 | 	sa1111_writel(PCCR_S0_FLT | PCCR_S1_FLT, base + SA1111_PCCR); | 
 | 186 |  | 
 | 187 | #ifdef CONFIG_SA1100_BADGE4 | 
 | 188 | 	pcmcia_badge4_init(&dev->dev); | 
 | 189 | #endif | 
 | 190 | #ifdef CONFIG_SA1100_JORNADA720 | 
 | 191 | 	pcmcia_jornada720_init(&dev->dev); | 
 | 192 | #endif | 
 | 193 | #ifdef CONFIG_ARCH_LUBBOCK | 
 | 194 | 	pcmcia_lubbock_init(dev); | 
 | 195 | #endif | 
 | 196 | #ifdef CONFIG_ASSABET_NEPONSET | 
 | 197 | 	pcmcia_neponset_init(dev); | 
 | 198 | #endif | 
 | 199 | 	return 0; | 
 | 200 | } | 
 | 201 |  | 
 | 202 | static int __devexit pcmcia_remove(struct sa1111_dev *dev) | 
 | 203 | { | 
| Russell King - ARM Linux | 701a5dc | 2009-03-29 19:42:44 +0100 | [diff] [blame] | 204 | 	struct sa1111_pcmcia_socket *next, *s = dev_get_drvdata(&dev->dev); | 
| Russell King - ARM Linux | be85458 | 2009-03-26 22:21:18 +0000 | [diff] [blame] | 205 |  | 
 | 206 | 	dev_set_drvdata(&dev->dev, NULL); | 
 | 207 |  | 
| Russell King - ARM Linux | 701a5dc | 2009-03-29 19:42:44 +0100 | [diff] [blame] | 208 | 	for (; next = s->next, s; s = next) { | 
 | 209 | 		soc_pcmcia_remove_one(&s->soc); | 
 | 210 | 		kfree(s); | 
 | 211 | 	} | 
| Russell King - ARM Linux | be85458 | 2009-03-26 22:21:18 +0000 | [diff] [blame] | 212 |  | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 213 | 	release_mem_region(dev->res.start, 512); | 
 | 214 | 	return 0; | 
 | 215 | } | 
 | 216 |  | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 217 | static struct sa1111_driver pcmcia_driver = { | 
 | 218 | 	.drv = { | 
 | 219 | 		.name	= "sa1111-pcmcia", | 
 | 220 | 	}, | 
 | 221 | 	.devid		= SA1111_DEVID_PCMCIA, | 
 | 222 | 	.probe		= pcmcia_probe, | 
 | 223 | 	.remove		= __devexit_p(pcmcia_remove), | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 224 | }; | 
 | 225 |  | 
 | 226 | static int __init sa1111_drv_pcmcia_init(void) | 
 | 227 | { | 
 | 228 | 	return sa1111_driver_register(&pcmcia_driver); | 
 | 229 | } | 
 | 230 |  | 
 | 231 | static void __exit sa1111_drv_pcmcia_exit(void) | 
 | 232 | { | 
 | 233 | 	sa1111_driver_unregister(&pcmcia_driver); | 
 | 234 | } | 
 | 235 |  | 
| Richard Purdie | f36598a | 2005-09-03 19:39:25 +0100 | [diff] [blame] | 236 | fs_initcall(sa1111_drv_pcmcia_init); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 237 | module_exit(sa1111_drv_pcmcia_exit); | 
 | 238 |  | 
 | 239 | MODULE_DESCRIPTION("SA1111 PCMCIA card socket driver"); | 
 | 240 | MODULE_LICENSE("GPL"); |