| 1 | From 76a102bd5c9d792db19c6c72eafdecea0311a0c9 Mon Sep 17 00:00:00 2001 |
| 2 | From: Craig Hughes <craig@gumstix.com> |
| 3 | Date: Fri, 30 Oct 2009 14:16:27 -0400 |
| 4 | Subject: [PATCH] [ARM] pxa: Gumstix Verdex PCMCIA support |
| 5 | |
| 6 | Needed for the Libertas CS wireless device. |
| 7 | |
| 8 | Signed-off-by: Bobby Powers <bobbypowers@gmail.com> |
| 9 | --- |
| 10 | drivers/pcmcia/Kconfig | 3 +- |
| 11 | drivers/pcmcia/Makefile | 3 + |
| 12 | drivers/pcmcia/pxa2xx_gumstix.c | 194 +++++++++++++++++++++++++++++++++++++++ |
| 13 | 3 files changed, 199 insertions(+), 1 deletions(-) |
| 14 | create mode 100644 drivers/pcmcia/pxa2xx_gumstix.c |
| 15 | |
| 16 | --- a/drivers/pcmcia/Kconfig |
| 17 | +++ b/drivers/pcmcia/Kconfig |
| 18 | @@ -212,7 +212,7 @@ config PCMCIA_PXA2XX |
| 19 | || MACH_ARMCORE || ARCH_PXA_PALM || TRIZEPS_PCMCIA \ |
| 20 | || ARCOM_PCMCIA || ARCH_PXA_ESERIES || MACH_STARGATE2 \ |
| 21 | || MACH_VPAC270 || MACH_BALLOON3 || MACH_COLIBRI \ |
| 22 | - || MACH_COLIBRI320) |
| 23 | + || MACH_COLIBRI320 || ARCH_GUMSTIX) |
| 24 | select PCMCIA_SOC_COMMON |
| 25 | help |
| 26 | Say Y here to include support for the PXA2xx PCMCIA controller |
| 27 | --- a/drivers/pcmcia/Makefile |
| 28 | +++ b/drivers/pcmcia/Makefile |
| 29 | @@ -70,6 +70,9 @@ pxa2xx-obj-$(CONFIG_MACH_BALLOON3) += p |
| 30 | pxa2xx-obj-$(CONFIG_MACH_COLIBRI) += pxa2xx_colibri.o |
| 31 | pxa2xx-obj-$(CONFIG_MACH_COLIBRI320) += pxa2xx_colibri.o |
| 32 | |
| 33 | +pxa2xx-obj-$(CONFIG_MACH_GUMSTIX_VERDEX) += pxa2xx_cs.o |
| 34 | +pxa2xx_cs-objs := pxa2xx_gumstix.o |
| 35 | + |
| 36 | obj-$(CONFIG_PCMCIA_PXA2XX) += pxa2xx_base.o $(pxa2xx-obj-y) |
| 37 | |
| 38 | obj-$(CONFIG_PCMCIA_XXS1500) += xxs1500_ss.o |
| 39 | --- /dev/null |
| 40 | +++ b/drivers/pcmcia/pxa2xx_gumstix.c |
| 41 | @@ -0,0 +1,195 @@ |
| 42 | +/* |
| 43 | + * linux/drivers/pcmcia/pxa2xx_gumstix.c |
| 44 | + * |
| 45 | + * Gumstix PCMCIA specific routines. Based on Mainstone |
| 46 | + * |
| 47 | + * Copyright 2004, Craig Hughes <craig@gumstix.com> |
| 48 | + * |
| 49 | + * This program is free software; you can redistribute it and/or modify |
| 50 | + * it under the terms of the GNU General Public License version 2 as |
| 51 | + * published by the Free Software Foundation. |
| 52 | + */ |
| 53 | + |
| 54 | +#include <linux/module.h> |
| 55 | +#include <linux/init.h> |
| 56 | +#include <linux/kernel.h> |
| 57 | +#include <linux/errno.h> |
| 58 | +#include <linux/interrupt.h> |
| 59 | +#include <linux/irq.h> |
| 60 | +#include <linux/gpio.h> |
| 61 | + |
| 62 | +#include <linux/delay.h> |
| 63 | +#include <linux/platform_device.h> |
| 64 | + |
| 65 | +#include <linux/gpio-pxa.h> |
| 66 | + |
| 67 | +#include <pcmcia/ss.h> |
| 68 | + |
| 69 | +#include <mach/hardware.h> |
| 70 | +#include <asm/mach-types.h> |
| 71 | + |
| 72 | +#include <mach/pxa27x.h> |
| 73 | + |
| 74 | +#include <asm/io.h> |
| 75 | +#include <mach/gpio.h> |
| 76 | +#include <mach/gumstix.h> |
| 77 | +#include "soc_common.h" |
| 78 | + |
| 79 | +#define ARRAY_AND_SIZE(x) (x), ARRAY_SIZE(x) |
| 80 | + |
| 81 | +#define BANK_OFF(n) (((n) < 3) ? (n) << 2 : 0x100 + (((n) - 3) << 2)) |
| 82 | +#define GPLR(x) __REG2(0x40E00000, BANK_OFF((x) >> 5)) |
| 83 | + |
| 84 | +static struct pcmcia_irqs gumstix_pcmcia_irqs0[] = { |
| 85 | + { 0, GUMSTIX_S0_nCD_IRQ, "CF0 nCD" }, |
| 86 | + { 0, GUMSTIX_S0_nSTSCHG_IRQ, "CF0 nSTSCHG" }, |
| 87 | +}; |
| 88 | + |
| 89 | +static struct pcmcia_irqs gumstix_pcmcia_irqs1[] = { |
| 90 | + { 1, GUMSTIX_S1_nCD_IRQ, "CF1 nCD" }, |
| 91 | + { 1, GUMSTIX_S1_nSTSCHG_IRQ, "CF1 nSTSCHG" }, |
| 92 | +}; |
| 93 | + |
| 94 | + |
| 95 | +static int net_cf_vx_mode = 0; |
| 96 | + |
| 97 | +static int gumstix_pcmcia_hw_init(struct soc_pcmcia_socket *skt) |
| 98 | +{ |
| 99 | +/* Note: The verdex_pcmcia_pin_config is moved to gumstix_verdex.c in order to use mfp_pxa2xx_config |
| 100 | + for board-specific pin configuration instead of the old deprecated pxa_gpio_mode function. Thus, |
| 101 | + only the IRQ init is still needed to be done here. */ |
| 102 | + skt->socket.pci_irq = (skt->nr == 0) ? ((net_cf_vx_mode == 0) ? GUMSTIX_S0_PRDY_nBSY_IRQ : GUMSTIX_S0_PRDY_nBSY_OLD_IRQ) : GUMSTIX_S1_PRDY_nBSY_IRQ; |
| 103 | + |
| 104 | + return (skt->nr == 0) ? soc_pcmcia_request_irqs(skt, gumstix_pcmcia_irqs0, ARRAY_SIZE(gumstix_pcmcia_irqs0)) : |
| 105 | + soc_pcmcia_request_irqs(skt, gumstix_pcmcia_irqs1, ARRAY_SIZE(gumstix_pcmcia_irqs1)); |
| 106 | +} |
| 107 | + |
| 108 | +static void gumstix_pcmcia_hw_shutdown(struct soc_pcmcia_socket *skt) |
| 109 | +{ |
| 110 | + if(skt->nr == 0) |
| 111 | + { |
| 112 | + soc_pcmcia_free_irqs(skt, gumstix_pcmcia_irqs0, ARRAY_SIZE(gumstix_pcmcia_irqs0)); |
| 113 | + } else { |
| 114 | + soc_pcmcia_free_irqs(skt, gumstix_pcmcia_irqs1, ARRAY_SIZE(gumstix_pcmcia_irqs1)); |
| 115 | + } |
| 116 | + |
| 117 | + if (net_cf_vx_mode) { |
| 118 | + gpio_free(GPIO_GUMSTIX_CF_OLD_RESET); |
| 119 | + } else { |
| 120 | + gpio_free(GPIO_GUMSTIX_CF_RESET); |
| 121 | + } |
| 122 | + |
| 123 | +} |
| 124 | + |
| 125 | +static void gumstix_pcmcia_socket_state(struct soc_pcmcia_socket *skt, |
| 126 | + struct pcmcia_state *state) |
| 127 | +{ |
| 128 | + unsigned int cd, prdy_nbsy, nbvd1; |
| 129 | + if(skt->nr == 0) |
| 130 | + { |
| 131 | + cd = GPIO_GUMSTIX_nCD_0; |
| 132 | + if(net_cf_vx_mode) |
| 133 | + prdy_nbsy = GPIO_GUMSTIX_PRDY_nBSY_0_OLD; |
| 134 | + else |
| 135 | + prdy_nbsy = GPIO_GUMSTIX_PRDY_nBSY_0; |
| 136 | + nbvd1 = GPIO_GUMSTIX_nBVD1_0; |
| 137 | + } else { |
| 138 | + cd = GPIO_GUMSTIX_nCD_1; |
| 139 | + prdy_nbsy = GPIO_GUMSTIX_PRDY_nBSY_1; |
| 140 | + nbvd1 = GPIO_GUMSTIX_nBVD1_1; |
| 141 | + } |
| 142 | + state->detect = !!gpio_get_value(cd); |
| 143 | + state->ready = !!gpio_get_value(prdy_nbsy); |
| 144 | + state->bvd1 = !!gpio_get_value(nbvd1); |
| 145 | + state->bvd2 = 1; |
| 146 | + state->vs_3v = 0; |
| 147 | + state->vs_Xv = 0; |
| 148 | + state->wrprot = 0; |
| 149 | +} |
| 150 | + |
| 151 | +static int gumstix_pcmcia_configure_socket(struct soc_pcmcia_socket *skt, |
| 152 | + const socket_state_t *state) |
| 153 | +{ |
| 154 | + return 0; |
| 155 | +} |
| 156 | + |
| 157 | +static void gumstix_pcmcia_socket_init(struct soc_pcmcia_socket *skt) |
| 158 | +{ |
| 159 | + if(skt->nr) { |
| 160 | + soc_pcmcia_enable_irqs(skt, gumstix_pcmcia_irqs0, ARRAY_SIZE(gumstix_pcmcia_irqs0)); |
| 161 | + } else { |
| 162 | + soc_pcmcia_enable_irqs(skt, gumstix_pcmcia_irqs1, ARRAY_SIZE(gumstix_pcmcia_irqs1)); |
| 163 | + } |
| 164 | +} |
| 165 | + |
| 166 | +static void gumstix_pcmcia_socket_suspend(struct soc_pcmcia_socket *skt) |
| 167 | +{ |
| 168 | + if(skt->nr) { |
| 169 | + soc_pcmcia_disable_irqs(skt, gumstix_pcmcia_irqs0, ARRAY_SIZE(gumstix_pcmcia_irqs0)); |
| 170 | + } else { |
| 171 | + soc_pcmcia_disable_irqs(skt, gumstix_pcmcia_irqs1, ARRAY_SIZE(gumstix_pcmcia_irqs1)); |
| 172 | + } |
| 173 | +} |
| 174 | + |
| 175 | +static struct pcmcia_low_level gumstix_pcmcia_ops = { |
| 176 | + .owner = THIS_MODULE, |
| 177 | + .hw_init = gumstix_pcmcia_hw_init, |
| 178 | + .hw_shutdown = gumstix_pcmcia_hw_shutdown, |
| 179 | + .socket_state = gumstix_pcmcia_socket_state, |
| 180 | + .configure_socket = gumstix_pcmcia_configure_socket, |
| 181 | + .socket_init = gumstix_pcmcia_socket_init, |
| 182 | + .socket_suspend = gumstix_pcmcia_socket_suspend, |
| 183 | + .nr = 2, |
| 184 | +}; |
| 185 | + |
| 186 | +static struct platform_device *gumstix_pcmcia_device; |
| 187 | + |
| 188 | +extern int __init gumstix_get_cf_cards(void); |
| 189 | + |
| 190 | +#ifdef CONFIG_MACH_GUMSTIX_VERDEX |
| 191 | +extern int __init gumstix_check_if_netCF_vx(void); |
| 192 | +#endif |
| 193 | + |
| 194 | +static int __init gumstix_pcmcia_init(void) |
| 195 | +{ |
| 196 | + int ret; |
| 197 | + |
| 198 | +#ifdef CONFIG_MACH_GUMSTIX_VERDEX |
| 199 | + net_cf_vx_mode = gumstix_check_if_netCF_vx(); |
| 200 | +#endif |
| 201 | + |
| 202 | + gumstix_pcmcia_ops.nr = gumstix_get_cf_cards(); |
| 203 | + |
| 204 | + gumstix_pcmcia_device = platform_device_alloc("pxa2xx-pcmcia", -1); |
| 205 | + if (!gumstix_pcmcia_device) |
| 206 | + return -ENOMEM; |
| 207 | + |
| 208 | + ret = platform_device_add_data(gumstix_pcmcia_device, &gumstix_pcmcia_ops, |
| 209 | + sizeof(gumstix_pcmcia_ops)); |
| 210 | + |
| 211 | + if (ret == 0) { |
| 212 | + printk(KERN_INFO "Registering gumstix PCMCIA interface.\n"); |
| 213 | + ret = platform_device_add(gumstix_pcmcia_device); |
| 214 | + } |
| 215 | + |
| 216 | + if (ret) |
| 217 | + platform_device_put(gumstix_pcmcia_device); |
| 218 | + |
| 219 | + return ret; |
| 220 | +} |
| 221 | + |
| 222 | +static void __exit gumstix_pcmcia_exit(void) |
| 223 | +{ |
| 224 | + /* |
| 225 | + * This call is supposed to free our gumstix_pcmcia_device. |
| 226 | + * Unfortunately platform_device don't have a free method, and |
| 227 | + * we can't assume it's free of any reference at this point so we |
| 228 | + * can't free it either. |
| 229 | + */ |
| 230 | + platform_device_unregister(gumstix_pcmcia_device); |
| 231 | +} |
| 232 | + |
| 233 | +fs_initcall(gumstix_pcmcia_init); |
| 234 | +module_exit(gumstix_pcmcia_exit); |
| 235 | + |
| 236 | +MODULE_LICENSE("GPL"); |
| 237 | |