| Marek Vasut | addff0f | 2010-03-10 04:16:28 +0100 | [diff] [blame] | 1 | /* | 
 | 2 |  * linux/drivers/pcmcia/pxa2xx_vpac270.c | 
 | 3 |  * | 
 | 4 |  * Driver for Voipac PXA270 PCMCIA and CF sockets | 
 | 5 |  * | 
 | 6 |  * Copyright (C) 2010 | 
 | 7 |  * Marek Vasut <marek.vasut@gmail.com> | 
 | 8 |  * | 
 | 9 |  * This program is free software; you can redistribute it and/or modify | 
 | 10 |  * it under the terms of the GNU General Public License version 2 as | 
 | 11 |  * published by the Free Software Foundation. | 
 | 12 |  * | 
 | 13 |  */ | 
 | 14 |  | 
 | 15 | #include <linux/module.h> | 
 | 16 | #include <linux/platform_device.h> | 
 | 17 |  | 
 | 18 | #include <asm/mach-types.h> | 
 | 19 |  | 
 | 20 | #include <mach/gpio.h> | 
 | 21 | #include <mach/vpac270.h> | 
 | 22 |  | 
 | 23 | #include "soc_common.h" | 
 | 24 |  | 
 | 25 | static struct pcmcia_irqs cd_irqs[] = { | 
 | 26 | 	{ | 
 | 27 | 		.sock = 0, | 
 | 28 | 		.irq  = IRQ_GPIO(GPIO84_VPAC270_PCMCIA_CD), | 
 | 29 | 		.str  = "PCMCIA CD" | 
 | 30 | 	}, | 
 | 31 | 	{ | 
 | 32 | 		.sock = 1, | 
 | 33 | 		.irq  = IRQ_GPIO(GPIO17_VPAC270_CF_CD), | 
 | 34 | 		.str  = "CF CD" | 
 | 35 | 	}, | 
 | 36 | }; | 
 | 37 |  | 
 | 38 | static int vpac270_pcmcia_hw_init(struct soc_pcmcia_socket *skt) | 
 | 39 | { | 
 | 40 | 	int ret; | 
 | 41 |  | 
 | 42 | 	if (skt->nr == 0) { | 
 | 43 | 		ret = gpio_request(GPIO84_VPAC270_PCMCIA_CD, "PCMCIA CD"); | 
 | 44 | 		if (ret) | 
 | 45 | 			goto err1; | 
 | 46 | 		ret = gpio_direction_input(GPIO84_VPAC270_PCMCIA_CD); | 
 | 47 | 		if (ret) | 
 | 48 | 			goto err2; | 
 | 49 |  | 
 | 50 | 		ret = gpio_request(GPIO35_VPAC270_PCMCIA_RDY, "PCMCIA RDY"); | 
 | 51 | 		if (ret) | 
 | 52 | 			goto err2; | 
 | 53 | 		ret = gpio_direction_input(GPIO35_VPAC270_PCMCIA_RDY); | 
 | 54 | 		if (ret) | 
 | 55 | 			goto err3; | 
 | 56 |  | 
 | 57 | 		ret = gpio_request(GPIO107_VPAC270_PCMCIA_PPEN, "PCMCIA PPEN"); | 
 | 58 | 		if (ret) | 
 | 59 | 			goto err3; | 
 | 60 | 		ret = gpio_direction_output(GPIO107_VPAC270_PCMCIA_PPEN, 0); | 
 | 61 | 		if (ret) | 
 | 62 | 			goto err4; | 
 | 63 |  | 
 | 64 | 		ret = gpio_request(GPIO11_VPAC270_PCMCIA_RESET, "PCMCIA RESET"); | 
 | 65 | 		if (ret) | 
 | 66 | 			goto err4; | 
 | 67 | 		ret = gpio_direction_output(GPIO11_VPAC270_PCMCIA_RESET, 0); | 
 | 68 | 		if (ret) | 
 | 69 | 			goto err5; | 
 | 70 |  | 
 | 71 | 		skt->socket.pci_irq = gpio_to_irq(GPIO35_VPAC270_PCMCIA_RDY); | 
 | 72 |  | 
 | 73 | 		return soc_pcmcia_request_irqs(skt, &cd_irqs[0], 1); | 
 | 74 |  | 
 | 75 | err5: | 
 | 76 | 		gpio_free(GPIO11_VPAC270_PCMCIA_RESET); | 
 | 77 | err4: | 
 | 78 | 		gpio_free(GPIO107_VPAC270_PCMCIA_PPEN); | 
 | 79 | err3: | 
 | 80 | 		gpio_free(GPIO35_VPAC270_PCMCIA_RDY); | 
 | 81 | err2: | 
 | 82 | 		gpio_free(GPIO84_VPAC270_PCMCIA_CD); | 
 | 83 | err1: | 
 | 84 | 		return ret; | 
 | 85 |  | 
 | 86 | 	} else { | 
 | 87 | 		ret = gpio_request(GPIO17_VPAC270_CF_CD, "CF CD"); | 
 | 88 | 		if (ret) | 
 | 89 | 			goto err6; | 
 | 90 | 		ret = gpio_direction_input(GPIO17_VPAC270_CF_CD); | 
 | 91 | 		if (ret) | 
 | 92 | 			goto err7; | 
 | 93 |  | 
 | 94 | 		ret = gpio_request(GPIO12_VPAC270_CF_RDY, "CF RDY"); | 
 | 95 | 		if (ret) | 
 | 96 | 			goto err7; | 
 | 97 | 		ret = gpio_direction_input(GPIO12_VPAC270_CF_RDY); | 
 | 98 | 		if (ret) | 
 | 99 | 			goto err8; | 
 | 100 |  | 
 | 101 | 		ret = gpio_request(GPIO16_VPAC270_CF_RESET, "CF RESET"); | 
 | 102 | 		if (ret) | 
 | 103 | 			goto err8; | 
 | 104 | 		ret = gpio_direction_output(GPIO16_VPAC270_CF_RESET, 0); | 
 | 105 | 		if (ret) | 
 | 106 | 			goto err9; | 
 | 107 |  | 
 | 108 | 		skt->socket.pci_irq = gpio_to_irq(GPIO12_VPAC270_CF_RDY); | 
 | 109 |  | 
 | 110 | 		return soc_pcmcia_request_irqs(skt, &cd_irqs[1], 1); | 
 | 111 |  | 
 | 112 | err9: | 
 | 113 | 		gpio_free(GPIO16_VPAC270_CF_RESET); | 
 | 114 | err8: | 
 | 115 | 		gpio_free(GPIO12_VPAC270_CF_RDY); | 
 | 116 | err7: | 
 | 117 | 		gpio_free(GPIO17_VPAC270_CF_CD); | 
 | 118 | err6: | 
 | 119 | 		return ret; | 
 | 120 |  | 
 | 121 | 	} | 
 | 122 | } | 
 | 123 |  | 
 | 124 | static void vpac270_pcmcia_hw_shutdown(struct soc_pcmcia_socket *skt) | 
 | 125 | { | 
 | 126 | 	gpio_free(GPIO11_VPAC270_PCMCIA_RESET); | 
 | 127 | 	gpio_free(GPIO107_VPAC270_PCMCIA_PPEN); | 
 | 128 | 	gpio_free(GPIO35_VPAC270_PCMCIA_RDY); | 
 | 129 | 	gpio_free(GPIO84_VPAC270_PCMCIA_CD); | 
 | 130 | 	gpio_free(GPIO16_VPAC270_CF_RESET); | 
 | 131 | 	gpio_free(GPIO12_VPAC270_CF_RDY); | 
 | 132 | 	gpio_free(GPIO17_VPAC270_CF_CD); | 
 | 133 | } | 
 | 134 |  | 
 | 135 | static void vpac270_pcmcia_socket_state(struct soc_pcmcia_socket *skt, | 
 | 136 | 					struct pcmcia_state *state) | 
 | 137 | { | 
 | 138 | 	if (skt->nr == 0) { | 
 | 139 | 		state->detect = !gpio_get_value(GPIO84_VPAC270_PCMCIA_CD); | 
 | 140 | 		state->ready  = !!gpio_get_value(GPIO35_VPAC270_PCMCIA_RDY); | 
 | 141 | 	} else { | 
 | 142 | 		state->detect = !gpio_get_value(GPIO17_VPAC270_CF_CD); | 
 | 143 | 		state->ready  = !!gpio_get_value(GPIO12_VPAC270_CF_RDY); | 
 | 144 | 	} | 
 | 145 | 	state->bvd1   = 1; | 
 | 146 | 	state->bvd2   = 1; | 
 | 147 | 	state->wrprot = 0; | 
 | 148 | 	state->vs_3v  = 1; | 
 | 149 | 	state->vs_Xv  = 0; | 
 | 150 | } | 
 | 151 |  | 
 | 152 | static int | 
 | 153 | vpac270_pcmcia_configure_socket(struct soc_pcmcia_socket *skt, | 
 | 154 | 				const socket_state_t *state) | 
 | 155 | { | 
 | 156 | 	if (skt->nr == 0) { | 
 | 157 | 		gpio_set_value(GPIO11_VPAC270_PCMCIA_RESET, | 
 | 158 | 			(state->flags & SS_RESET)); | 
 | 159 | 		gpio_set_value(GPIO107_VPAC270_PCMCIA_PPEN, | 
 | 160 | 			!(state->Vcc == 33 || state->Vcc == 50)); | 
 | 161 | 	} else { | 
 | 162 | 		gpio_set_value(GPIO16_VPAC270_CF_RESET, | 
 | 163 | 			(state->flags & SS_RESET)); | 
 | 164 | 	} | 
 | 165 |  | 
 | 166 | 	return 0; | 
 | 167 | } | 
 | 168 |  | 
 | 169 | static void vpac270_pcmcia_socket_init(struct soc_pcmcia_socket *skt) | 
 | 170 | { | 
 | 171 | } | 
 | 172 |  | 
 | 173 | static void vpac270_pcmcia_socket_suspend(struct soc_pcmcia_socket *skt) | 
 | 174 | { | 
 | 175 | } | 
 | 176 |  | 
 | 177 | static struct pcmcia_low_level vpac270_pcmcia_ops = { | 
 | 178 | 	.owner			= THIS_MODULE, | 
 | 179 |  | 
 | 180 | 	.first			= 0, | 
 | 181 | 	.nr			= 2, | 
 | 182 |  | 
 | 183 | 	.hw_init		= vpac270_pcmcia_hw_init, | 
 | 184 | 	.hw_shutdown		= vpac270_pcmcia_hw_shutdown, | 
 | 185 |  | 
 | 186 | 	.socket_state		= vpac270_pcmcia_socket_state, | 
 | 187 | 	.configure_socket	= vpac270_pcmcia_configure_socket, | 
 | 188 |  | 
 | 189 | 	.socket_init		= vpac270_pcmcia_socket_init, | 
 | 190 | 	.socket_suspend		= vpac270_pcmcia_socket_suspend, | 
 | 191 | }; | 
 | 192 |  | 
 | 193 | static struct platform_device *vpac270_pcmcia_device; | 
 | 194 |  | 
 | 195 | static int __init vpac270_pcmcia_init(void) | 
 | 196 | { | 
 | 197 | 	int ret; | 
 | 198 |  | 
 | 199 | 	if (!machine_is_vpac270()) | 
 | 200 | 		return -ENODEV; | 
 | 201 |  | 
 | 202 | 	vpac270_pcmcia_device = platform_device_alloc("pxa2xx-pcmcia", -1); | 
 | 203 | 	if (!vpac270_pcmcia_device) | 
 | 204 | 		return -ENOMEM; | 
 | 205 |  | 
 | 206 | 	ret = platform_device_add_data(vpac270_pcmcia_device, | 
 | 207 | 		&vpac270_pcmcia_ops, sizeof(vpac270_pcmcia_ops)); | 
 | 208 |  | 
 | 209 | 	if (!ret) | 
 | 210 | 		ret = platform_device_add(vpac270_pcmcia_device); | 
 | 211 |  | 
 | 212 | 	if (ret) | 
 | 213 | 		platform_device_put(vpac270_pcmcia_device); | 
 | 214 |  | 
 | 215 | 	return ret; | 
 | 216 | } | 
 | 217 |  | 
 | 218 | static void __exit vpac270_pcmcia_exit(void) | 
 | 219 | { | 
 | 220 | 	platform_device_unregister(vpac270_pcmcia_device); | 
 | 221 | } | 
 | 222 |  | 
 | 223 | module_init(vpac270_pcmcia_init); | 
 | 224 | module_exit(vpac270_pcmcia_exit); | 
 | 225 |  | 
 | 226 | MODULE_AUTHOR("Marek Vasut <marek.vasut@gmail.com>"); | 
 | 227 | MODULE_DESCRIPTION("PCMCIA support for Voipac PXA270"); | 
 | 228 | MODULE_ALIAS("platform:pxa2xx-pcmcia"); | 
 | 229 | MODULE_LICENSE("GPL"); |