diff options
Diffstat (limited to 'drivers/pcmcia')
-rw-r--r-- | drivers/pcmcia/Kconfig | 3 | ||||
-rw-r--r-- | drivers/pcmcia/Makefile | 5 | ||||
-rw-r--r-- | drivers/pcmcia/pxa2xx_balloon3.c | 11 | ||||
-rw-r--r-- | drivers/pcmcia/pxa2xx_base.c | 64 | ||||
-rw-r--r-- | drivers/pcmcia/pxa2xx_colibri.c | 229 | ||||
-rw-r--r-- | drivers/pcmcia/sa1100_generic.c | 3 | ||||
-rw-r--r-- | drivers/pcmcia/sa1100_generic.h | 1 | ||||
-rw-r--r-- | drivers/pcmcia/sa1100_nanoengine.c | 219 | ||||
-rw-r--r-- | drivers/pcmcia/soc_common.c | 128 | ||||
-rw-r--r-- | drivers/pcmcia/soc_common.h | 3 |
10 files changed, 585 insertions, 81 deletions
diff --git a/drivers/pcmcia/Kconfig b/drivers/pcmcia/Kconfig index c80a7a6e769..de886f3dfd3 100644 --- a/drivers/pcmcia/Kconfig +++ b/drivers/pcmcia/Kconfig @@ -215,7 +215,8 @@ config PCMCIA_PXA2XX depends on (ARCH_LUBBOCK || MACH_MAINSTONE || PXA_SHARPSL \ || MACH_ARMCORE || ARCH_PXA_PALM || TRIZEPS_PCMCIA \ || ARCOM_PCMCIA || ARCH_PXA_ESERIES || MACH_STARGATE2 \ - || MACH_VPAC270 || MACH_BALLOON3) + || MACH_VPAC270 || MACH_BALLOON3 || MACH_COLIBRI \ + || MACH_COLIBRI320) select PCMCIA_SOC_COMMON help Say Y here to include support for the PXA2xx PCMCIA controller diff --git a/drivers/pcmcia/Makefile b/drivers/pcmcia/Makefile index 8d9386a22eb..29935ea921d 100644 --- a/drivers/pcmcia/Makefile +++ b/drivers/pcmcia/Makefile @@ -50,8 +50,9 @@ sa1111_cs-$(CONFIG_SA1100_JORNADA720) += sa1100_jornada720.o sa1100_cs-y += sa1100_generic.o sa1100_cs-$(CONFIG_SA1100_ASSABET) += sa1100_assabet.o sa1100_cs-$(CONFIG_SA1100_CERF) += sa1100_cerf.o -sa1100_cs-$(CONFIG_SA1100_COLLIE) += pxa2xx_sharpsl.o +sa1100_cs-$(CONFIG_SA1100_COLLIE) += pxa2xx_sharpsl.o sa1100_cs-$(CONFIG_SA1100_H3600) += sa1100_h3600.o +sa1100_cs-$(CONFIG_SA1100_NANOENGINE) += sa1100_nanoengine.o sa1100_cs-$(CONFIG_SA1100_SHANNON) += sa1100_shannon.o sa1100_cs-$(CONFIG_SA1100_SIMPAD) += sa1100_simpad.o @@ -70,6 +71,8 @@ pxa2xx-obj-$(CONFIG_MACH_E740) += pxa2xx_e740.o pxa2xx-obj-$(CONFIG_MACH_STARGATE2) += pxa2xx_stargate2.o pxa2xx-obj-$(CONFIG_MACH_VPAC270) += pxa2xx_vpac270.o pxa2xx-obj-$(CONFIG_MACH_BALLOON3) += pxa2xx_balloon3.o +pxa2xx-obj-$(CONFIG_MACH_COLIBRI) += pxa2xx_colibri.o +pxa2xx-obj-$(CONFIG_MACH_COLIBRI320) += pxa2xx_colibri.o obj-$(CONFIG_PCMCIA_PXA2XX) += pxa2xx_base.o $(pxa2xx-obj-y) diff --git a/drivers/pcmcia/pxa2xx_balloon3.c b/drivers/pcmcia/pxa2xx_balloon3.c index dbbdd006320..453c54c9761 100644 --- a/drivers/pcmcia/pxa2xx_balloon3.c +++ b/drivers/pcmcia/pxa2xx_balloon3.c @@ -39,12 +39,10 @@ static struct pcmcia_irqs irqs[] = { static int balloon3_pcmcia_hw_init(struct soc_pcmcia_socket *skt) { uint16_t ver; - int ret; - static void __iomem *fpga_ver; ver = __raw_readw(BALLOON3_FPGA_VER); - if (ver > 0x0201) - pr_warn("The FPGA code, version 0x%04x, is newer than rel-0.3. " + if (ver < 0x4f08) + pr_warn("The FPGA code, version 0x%04x, is too old. " "PCMCIA/CF support might be broken in this version!", ver); @@ -97,8 +95,9 @@ static void balloon3_pcmcia_socket_state(struct soc_pcmcia_socket *skt, static int balloon3_pcmcia_configure_socket(struct soc_pcmcia_socket *skt, const socket_state_t *state) { - __raw_writew((state->flags & SS_RESET) ? BALLOON3_CF_RESET : 0, - BALLOON3_CF_CONTROL_REG); + __raw_writew(BALLOON3_CF_RESET, BALLOON3_CF_CONTROL_REG | + ((state->flags & SS_RESET) ? + BALLOON3_FPGA_SETnCLR : 0)); return 0; } diff --git a/drivers/pcmcia/pxa2xx_base.c b/drivers/pcmcia/pxa2xx_base.c index ae07b4db8a6..3755e7c8c71 100644 --- a/drivers/pcmcia/pxa2xx_base.c +++ b/drivers/pcmcia/pxa2xx_base.c @@ -26,6 +26,7 @@ #include <linux/platform_device.h> #include <mach/hardware.h> +#include <mach/smemc.h> #include <asm/io.h> #include <asm/irq.h> #include <asm/system.h> @@ -116,37 +117,49 @@ static inline u_int pxa2xx_pcmcia_cmd_time(u_int mem_clk_10khz, static int pxa2xx_pcmcia_set_mcmem( int sock, int speed, int clock ) { - MCMEM(sock) = ((pxa2xx_mcxx_setup(speed, clock) + uint32_t val; + + val = ((pxa2xx_mcxx_setup(speed, clock) & MCXX_SETUP_MASK) << MCXX_SETUP_SHIFT) | ((pxa2xx_mcxx_asst(speed, clock) & MCXX_ASST_MASK) << MCXX_ASST_SHIFT) | ((pxa2xx_mcxx_hold(speed, clock) & MCXX_HOLD_MASK) << MCXX_HOLD_SHIFT); + __raw_writel(val, MCMEM(sock)); + return 0; } static int pxa2xx_pcmcia_set_mcio( int sock, int speed, int clock ) { - MCIO(sock) = ((pxa2xx_mcxx_setup(speed, clock) + uint32_t val; + + val = ((pxa2xx_mcxx_setup(speed, clock) & MCXX_SETUP_MASK) << MCXX_SETUP_SHIFT) | ((pxa2xx_mcxx_asst(speed, clock) & MCXX_ASST_MASK) << MCXX_ASST_SHIFT) | ((pxa2xx_mcxx_hold(speed, clock) & MCXX_HOLD_MASK) << MCXX_HOLD_SHIFT); + __raw_writel(val, MCIO(sock)); + return 0; } static int pxa2xx_pcmcia_set_mcatt( int sock, int speed, int clock ) { - MCATT(sock) = ((pxa2xx_mcxx_setup(speed, clock) + uint32_t val; + + val = ((pxa2xx_mcxx_setup(speed, clock) & MCXX_SETUP_MASK) << MCXX_SETUP_SHIFT) | ((pxa2xx_mcxx_asst(speed, clock) & MCXX_ASST_MASK) << MCXX_ASST_SHIFT) | ((pxa2xx_mcxx_hold(speed, clock) & MCXX_HOLD_MASK) << MCXX_HOLD_SHIFT); + __raw_writel(val, MCATT(sock)); + return 0; } @@ -166,8 +179,8 @@ static int pxa2xx_pcmcia_set_mcxx(struct soc_pcmcia_socket *skt, unsigned int cl static int pxa2xx_pcmcia_set_timing(struct soc_pcmcia_socket *skt) { - unsigned int clk = get_memclk_frequency_10khz(); - return pxa2xx_pcmcia_set_mcxx(skt, clk); + unsigned long clk = clk_get_rate(skt->clk); + return pxa2xx_pcmcia_set_mcxx(skt, clk / 10000); } #ifdef CONFIG_CPU_FREQ @@ -205,19 +218,18 @@ pxa2xx_pcmcia_frequency_change(struct soc_pcmcia_socket *skt, static void pxa2xx_configure_sockets(struct device *dev) { struct pcmcia_low_level *ops = dev->platform_data; - /* * We have at least one socket, so set MECR:CIT * (Card Is There) */ - MECR |= MECR_CIT; + uint32_t mecr = MECR_CIT; /* Set MECR:NOS (Number Of Sockets) */ if ((ops->first + ops->nr) > 1 || machine_is_viper() || machine_is_arcom_zeus()) - MECR |= MECR_NOS; - else - MECR &= ~MECR_NOS; + mecr |= MECR_NOS; + + __raw_writel(mecr, MECR); } static const char *skt_names[] = { @@ -270,24 +282,41 @@ static int pxa2xx_drv_pcmcia_probe(struct platform_device *dev) struct pcmcia_low_level *ops; struct skt_dev_info *sinfo; struct soc_pcmcia_socket *skt; + struct clk *clk; ops = (struct pcmcia_low_level *)dev->dev.platform_data; - if (!ops) + if (!ops) { + ret = -ENODEV; + goto err0; + } + + if (cpu_is_pxa320() && ops->nr > 1) { + dev_err(&dev->dev, "pxa320 supports only one pcmcia slot"); + ret = -EINVAL; + goto err0; + } + + clk = clk_get(&dev->dev, NULL); + if (!clk) return -ENODEV; pxa2xx_drv_pcmcia_ops(ops); sinfo = kzalloc(SKT_DEV_INFO_SIZE(ops->nr), GFP_KERNEL); - if (!sinfo) + if (!sinfo) { + clk_put(clk); return -ENOMEM; + } sinfo->nskt = ops->nr; + sinfo->clk = clk; /* Initialize processor specific parameters */ for (i = 0; i < ops->nr; i++) { skt = &sinfo->skt[i]; skt->nr = ops->first + i; + skt->clk = clk; skt->ops = ops; skt->socket.owner = ops->owner; skt->socket.dev.parent = &dev->dev; @@ -295,18 +324,26 @@ static int pxa2xx_drv_pcmcia_probe(struct platform_device *dev) ret = pxa2xx_drv_pcmcia_add_one(skt); if (ret) - break; + goto err1; } if (ret) { while (--i >= 0) soc_pcmcia_remove_one(&sinfo->skt[i]); kfree(sinfo); + clk_put(clk); } else { pxa2xx_configure_sockets(&dev->dev); dev_set_drvdata(&dev->dev, sinfo); } + return 0; + +err1: + while (--i >= 0) + soc_pcmcia_remove_one(&sinfo->skt[i]); + kfree(sinfo); +err0: return ret; } @@ -320,6 +357,7 @@ static int pxa2xx_drv_pcmcia_remove(struct platform_device *dev) for (i = 0; i < sinfo->nskt; i++) soc_pcmcia_remove_one(&sinfo->skt[i]); + clk_put(sinfo->clk); kfree(sinfo); return 0; } diff --git a/drivers/pcmcia/pxa2xx_colibri.c b/drivers/pcmcia/pxa2xx_colibri.c new file mode 100644 index 00000000000..c3f72192af6 --- /dev/null +++ b/drivers/pcmcia/pxa2xx_colibri.c @@ -0,0 +1,229 @@ +/* + * linux/drivers/pcmcia/pxa2xx_colibri.c + * + * Driver for Toradex Colibri PXA270 CF socket + * + * Copyright (C) 2010 Marek Vasut <marek.vasut@gmail.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + */ + +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/delay.h> +#include <linux/gpio.h> + +#include <asm/mach-types.h> + +#include "soc_common.h" + +#define COLIBRI270_RESET_GPIO 53 +#define COLIBRI270_PPEN_GPIO 107 +#define COLIBRI270_BVD1_GPIO 83 +#define COLIBRI270_BVD2_GPIO 82 +#define COLIBRI270_DETECT_GPIO 84 +#define COLIBRI270_READY_GPIO 1 + +#define COLIBRI320_RESET_GPIO 77 +#define COLIBRI320_PPEN_GPIO 57 +#define COLIBRI320_BVD1_GPIO 53 +#define COLIBRI320_BVD2_GPIO 79 +#define COLIBRI320_DETECT_GPIO 81 +#define COLIBRI320_READY_GPIO 29 + +static struct { + int reset_gpio; + int ppen_gpio; + int bvd1_gpio; + int bvd2_gpio; + int detect_gpio; + int ready_gpio; +} colibri_pcmcia_gpio; + +static struct pcmcia_irqs colibri_irqs[] = { + { + .sock = 0, + .str = "PCMCIA CD" + }, +}; + +static int colibri_pcmcia_hw_init(struct soc_pcmcia_socket *skt) +{ + int ret; + + ret = gpio_request(colibri_pcmcia_gpio.detect_gpio, "DETECT"); + if (ret) + goto err1; + ret = gpio_direction_input(colibri_pcmcia_gpio.detect_gpio); + if (ret) + goto err2; + + ret = gpio_request(colibri_pcmcia_gpio.ready_gpio, "READY"); + if (ret) + goto err2; + ret = gpio_direction_input(colibri_pcmcia_gpio.ready_gpio); + if (ret) + goto err3; + + ret = gpio_request(colibri_pcmcia_gpio.bvd1_gpio, "BVD1"); + if (ret) + goto err3; + ret = gpio_direction_input(colibri_pcmcia_gpio.bvd1_gpio); + if (ret) + goto err4; + + ret = gpio_request(colibri_pcmcia_gpio.bvd2_gpio, "BVD2"); + if (ret) + goto err4; + ret = gpio_direction_input(colibri_pcmcia_gpio.bvd2_gpio); + if (ret) + goto err5; + + ret = gpio_request(colibri_pcmcia_gpio.ppen_gpio, "PPEN"); + if (ret) + goto err5; + ret = gpio_direction_output(colibri_pcmcia_gpio.ppen_gpio, 0); + if (ret) + goto err6; + + ret = gpio_request(colibri_pcmcia_gpio.reset_gpio, "RESET"); + if (ret) + goto err6; + ret = gpio_direction_output(colibri_pcmcia_gpio.reset_gpio, 1); + if (ret) + goto err7; + + colibri_irqs[0].irq = gpio_to_irq(colibri_pcmcia_gpio.detect_gpio); + skt->socket.pci_irq = gpio_to_irq(colibri_pcmcia_gpio.ready_gpio); + + return soc_pcmcia_request_irqs(skt, colibri_irqs, + ARRAY_SIZE(colibri_irqs)); + +err7: + gpio_free(colibri_pcmcia_gpio.detect_gpio); +err6: + gpio_free(colibri_pcmcia_gpio.ready_gpio); +err5: + gpio_free(colibri_pcmcia_gpio.bvd1_gpio); +err4: + gpio_free(colibri_pcmcia_gpio.bvd2_gpio); +err3: + gpio_free(colibri_pcmcia_gpio.reset_gpio); +err2: + gpio_free(colibri_pcmcia_gpio.ppen_gpio); +err1: + return ret; +} + +static void colibri_pcmcia_hw_shutdown(struct soc_pcmcia_socket *skt) +{ + gpio_free(colibri_pcmcia_gpio.detect_gpio); + gpio_free(colibri_pcmcia_gpio.ready_gpio); + gpio_free(colibri_pcmcia_gpio.bvd1_gpio); + gpio_free(colibri_pcmcia_gpio.bvd2_gpio); + gpio_free(colibri_pcmcia_gpio.reset_gpio); + gpio_free(colibri_pcmcia_gpio.ppen_gpio); +} + +static void colibri_pcmcia_socket_state(struct soc_pcmcia_socket *skt, + struct pcmcia_state *state) +{ + + state->detect = !!gpio_get_value(colibri_pcmcia_gpio.detect_gpio); + state->ready = !!gpio_get_value(colibri_pcmcia_gpio.ready_gpio); + state->bvd1 = !!gpio_get_value(colibri_pcmcia_gpio.bvd1_gpio); + state->bvd2 = !!gpio_get_value(colibri_pcmcia_gpio.bvd2_gpio); + state->wrprot = 0; + state->vs_3v = 1; + state->vs_Xv = 0; +} + +static int +colibri_pcmcia_configure_socket(struct soc_pcmcia_socket *skt, + const socket_state_t *state) +{ + gpio_set_value(colibri_pcmcia_gpio.ppen_gpio, + !(state->Vcc == 33 && state->Vpp < 50)); + gpio_set_value(colibri_pcmcia_gpio.reset_gpio, state->flags & SS_RESET); + return 0; +} + +static void colibri_pcmcia_socket_init(struct soc_pcmcia_socket *skt) +{ +} + +static void colibri_pcmcia_socket_suspend(struct soc_pcmcia_socket *skt) +{ +} + +static struct pcmcia_low_level colibri_pcmcia_ops = { + .owner = THIS_MODULE, + + .first = 0, + .nr = 1, + + .hw_init = colibri_pcmcia_hw_init, + .hw_shutdown = colibri_pcmcia_hw_shutdown, + + .socket_state = colibri_pcmcia_socket_state, + .configure_socket = colibri_pcmcia_configure_socket, + + .socket_init = colibri_pcmcia_socket_init, + .socket_suspend = colibri_pcmcia_socket_suspend, +}; + +static struct platform_device *colibri_pcmcia_device; + +static int __init colibri_pcmcia_init(void) +{ + int ret; + + colibri_pcmcia_device = platform_device_alloc("pxa2xx-pcmcia", -1); + if (!colibri_pcmcia_device) + return -ENOMEM; + + /* Colibri PXA270 */ + if (machine_is_colibri()) { + colibri_pcmcia_gpio.reset_gpio = COLIBRI270_RESET_GPIO; + colibri_pcmcia_gpio.ppen_gpio = COLIBRI270_PPEN_GPIO; + colibri_pcmcia_gpio.bvd1_gpio = COLIBRI270_BVD1_GPIO; + colibri_pcmcia_gpio.bvd2_gpio = COLIBRI270_BVD2_GPIO; + colibri_pcmcia_gpio.detect_gpio = COLIBRI270_DETECT_GPIO; + colibri_pcmcia_gpio.ready_gpio = COLIBRI270_READY_GPIO; + /* Colibri PXA320 */ + } else if (machine_is_colibri320()) { + colibri_pcmcia_gpio.reset_gpio = COLIBRI320_RESET_GPIO; + colibri_pcmcia_gpio.ppen_gpio = COLIBRI320_PPEN_GPIO; + colibri_pcmcia_gpio.bvd1_gpio = COLIBRI320_BVD1_GPIO; + colibri_pcmcia_gpio.bvd2_gpio = COLIBRI320_BVD2_GPIO; + colibri_pcmcia_gpio.detect_gpio = COLIBRI320_DETECT_GPIO; + colibri_pcmcia_gpio.ready_gpio = COLIBRI320_READY_GPIO; + } + + ret = platform_device_add_data(colibri_pcmcia_device, + &colibri_pcmcia_ops, sizeof(colibri_pcmcia_ops)); + + if (!ret) + ret = platform_device_add(colibri_pcmcia_device); + + if (ret) + platform_device_put(colibri_pcmcia_device); + + return ret; +} + +static void __exit colibri_pcmcia_exit(void) +{ + platform_device_unregister(colibri_pcmcia_device); +} + +module_init(colibri_pcmcia_init); +module_exit(colibri_pcmcia_exit); + +MODULE_AUTHOR("Marek Vasut <marek.vasut@gmail.com>"); +MODULE_DESCRIPTION("PCMCIA support for Toradex Colibri PXA270/PXA320"); +MODULE_ALIAS("platform:pxa2xx-pcmcia"); +MODULE_LICENSE("GPL"); diff --git a/drivers/pcmcia/sa1100_generic.c b/drivers/pcmcia/sa1100_generic.c index 6b228590b3f..fb9740d3e9a 100644 --- a/drivers/pcmcia/sa1100_generic.c +++ b/drivers/pcmcia/sa1100_generic.c @@ -53,6 +53,9 @@ static int (*sa11x0_pcmcia_hw_init[])(struct device *dev) = { #if defined(CONFIG_SA1100_H3100) || defined(CONFIG_SA1100_H3600) pcmcia_h3600_init, #endif +#ifdef CONFIG_SA1100_NANOENGINE + pcmcia_nanoengine_init, +#endif #ifdef CONFIG_SA1100_SHANNON pcmcia_shannon_init, #endif diff --git a/drivers/pcmcia/sa1100_generic.h b/drivers/pcmcia/sa1100_generic.h index 794f96a35bb..adb08dbc723 100644 --- a/drivers/pcmcia/sa1100_generic.h +++ b/drivers/pcmcia/sa1100_generic.h @@ -13,6 +13,7 @@ extern int pcmcia_freebird_init(struct device *); extern int pcmcia_gcplus_init(struct device *); extern int pcmcia_graphicsmaster_init(struct device *); extern int pcmcia_h3600_init(struct device *); +extern int pcmcia_nanoengine_init(struct device *); extern int pcmcia_pangolin_init(struct device *); extern int pcmcia_pfs168_init(struct device *); extern int pcmcia_shannon_init(struct device *); diff --git a/drivers/pcmcia/sa1100_nanoengine.c b/drivers/pcmcia/sa1100_nanoengine.c new file mode 100644 index 00000000000..3d2652e2f5a --- /dev/null +++ b/drivers/pcmcia/sa1100_nanoengine.c @@ -0,0 +1,219 @@ +/* + * drivers/pcmcia/sa1100_nanoengine.c + * + * PCMCIA implementation routines for BSI nanoEngine. + * + * In order to have a fully functional pcmcia subsystem in a BSE nanoEngine + * board you should carefully read this: + * http://cambuca.ldhs.cetuc.puc-rio.br/nanoengine/ + * + * Copyright (C) 2010 Marcelo Roberto Jimenez <mroberto@cpti.cetuc.puc-rio.br> + * + * Based on original work for kernel 2.4 by + * Miguel Freitas <miguel@cpti.cetuc.puc-rio.br> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + */ +#include <linux/device.h> +#include <linux/errno.h> +#include <linux/interrupt.h> +#include <linux/irq.h> +#include <linux/init.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/signal.h> + +#include <asm/mach-types.h> +#include <asm/irq.h> + +#include <mach/hardware.h> +#include <mach/nanoengine.h> + +#include "sa1100_generic.h" + +static struct pcmcia_irqs irqs_skt0[] = { + /* socket, IRQ, name */ + { 0, NANOENGINE_IRQ_GPIO_PC_CD0, "PC CD0" }, +}; + +static struct pcmcia_irqs irqs_skt1[] = { + /* socket, IRQ, name */ + { 1, NANOENGINE_IRQ_GPIO_PC_CD1, "PC CD1" }, +}; + +struct nanoengine_pins { + unsigned input_pins; + unsigned output_pins; + unsigned clear_outputs; + unsigned transition_pins; + unsigned pci_irq; + struct pcmcia_irqs *pcmcia_irqs; + unsigned pcmcia_irqs_size; +}; + +static struct nanoengine_pins nano_skts[] = { + { + .input_pins = GPIO_PC_READY0 | GPIO_PC_CD0, + .output_pins = GPIO_PC_RESET0, + .clear_outputs = GPIO_PC_RESET0, + .transition_pins = NANOENGINE_IRQ_GPIO_PC_CD0, + .pci_irq = NANOENGINE_IRQ_GPIO_PC_READY0, + .pcmcia_irqs = irqs_skt0, + .pcmcia_irqs_size = ARRAY_SIZE(irqs_skt0) + }, { + .input_pins = GPIO_PC_READY1 | GPIO_PC_CD1, + .output_pins = GPIO_PC_RESET1, + .clear_outputs = GPIO_PC_RESET1, + .transition_pins = NANOENGINE_IRQ_GPIO_PC_CD1, + .pci_irq = NANOENGINE_IRQ_GPIO_PC_READY1, + .pcmcia_irqs = irqs_skt1, + .pcmcia_irqs_size = ARRAY_SIZE(irqs_skt1) + } +}; + +unsigned num_nano_pcmcia_sockets = ARRAY_SIZE(nano_skts); + +static int nanoengine_pcmcia_hw_init(struct soc_pcmcia_socket *skt) +{ + unsigned i = skt->nr; + + if (i >= num_nano_pcmcia_sockets) + return -ENXIO; + + GPDR &= ~nano_skts[i].input_pins; + GPDR |= nano_skts[i].output_pins; + GPCR = nano_skts[i].clear_outputs; + set_irq_type(nano_skts[i].transition_pins, IRQ_TYPE_EDGE_BOTH); + skt->socket.pci_irq = nano_skts[i].pci_irq; + + return soc_pcmcia_request_irqs(skt, + nano_skts[i].pcmcia_irqs, nano_skts[i].pcmcia_irqs_size); +} + +/* + * Release all resources. + */ +static void nanoengine_pcmcia_hw_shutdown(struct soc_pcmcia_socket *skt) +{ + unsigned i = skt->nr; + + if (i >= num_nano_pcmcia_sockets) + return; + + soc_pcmcia_free_irqs(skt, + nano_skts[i].pcmcia_irqs, nano_skts[i].pcmcia_irqs_size); +} + +static int nanoengine_pcmcia_configure_socket( + struct soc_pcmcia_socket *skt, const socket_state_t *state) +{ + unsigned reset; + unsigned i = skt->nr; + + if (i >= num_nano_pcmcia_sockets) + return -ENXIO; + + switch (i) { + case 0: + reset = GPIO_PC_RESET0; + break; + case 1: + reset = GPIO_PC_RESET1; + break; + default: + return -ENXIO; + } + + if (state->flags & SS_RESET) + GPSR = reset; + else + GPCR = reset; + + return 0; +} + +static void nanoengine_pcmcia_socket_state( + struct soc_pcmcia_socket *skt, struct pcmcia_state *state) +{ + unsigned long levels = GPLR; + unsigned i = skt->nr; + + if (i >= num_nano_pcmcia_sockets) + return; + + memset(state, 0, sizeof(struct pcmcia_state)); + switch (i) { + case 0: + state->ready = (levels & GPIO_PC_READY0) ? 1 : 0; + state->detect = !(levels & GPIO_PC_CD0) ? 1 : 0; + break; + case 1: + state->ready = (levels & GPIO_PC_READY1) ? 1 : 0; + state->detect = !(levels & GPIO_PC_CD1) ? 1 : 0; + break; + default: + return; + } + state->bvd1 = 1; + state->bvd2 = 1; + state->wrprot = 0; /* Not available */ + state->vs_3v = 1; /* Can only apply 3.3V */ + state->vs_Xv = 0; +} + +/* + * Enable card status IRQs on (re-)initialisation. This can + * be called at initialisation, power management event, or + * pcmcia event. + */ +static void nanoengine_pcmcia_socket_init(struct soc_pcmcia_socket *skt) +{ + unsigned i = skt->nr; + + if (i >= num_nano_pcmcia_sockets) + return; + + soc_pcmcia_enable_irqs(skt, + nano_skts[i].pcmcia_irqs, nano_skts[i].pcmcia_irqs_size); +} + +/* + * Disable card status IRQs on suspend. + */ +static void nanoengine_pcmcia_socket_suspend(struct soc_pcmcia_socket *skt) +{ + unsigned i = skt->nr; + + if (i >= num_nano_pcmcia_sockets) + return; + + soc_pcmcia_disable_irqs(skt, + nano_skts[i].pcmcia_irqs, nano_skts[i].pcmcia_irqs_size); +} + +static struct pcmcia_low_level nanoengine_pcmcia_ops = { + .owner = THIS_MODULE, + + .hw_init = nanoengine_pcmcia_hw_init, + .hw_shutdown = nanoengine_pcmcia_hw_shutdown, + + .configure_socket = nanoengine_pcmcia_configure_socket, + .socket_state = nanoengine_pcmcia_socket_state, + .socket_init = nanoengine_pcmcia_socket_init, + .socket_suspend = nanoengine_pcmcia_socket_suspend, +}; + +int pcmcia_nanoengine_init(struct device *dev) +{ + int ret = -ENODEV; + + if (machine_is_nanoengine()) + ret = sa11xx_drv_pcmcia_probe( + dev, &nanoengine_pcmcia_ops, 0, 2); + + return ret; +} + diff --git a/drivers/pcmcia/soc_common.c b/drivers/pcmcia/soc_common.c index 2fe8cb8e95c..5a9a392eacd 100644 --- a/drivers/pcmcia/soc_common.c +++ b/drivers/pcmcia/soc_common.c @@ -31,20 +31,20 @@ ======================================================================*/ -#include <linux/module.h> -#include <linux/moduleparam.h> +#include <linux/cpufreq.h> #include <linux/init.h> +#include <linux/interrupt.h> +#include <linux/io.h> +#include <linux/irq.h> #include <linux/kernel.h> -#include <linux/timer.h> #include <linux/mm.h> +#include <linux/module.h> +#include <linux/moduleparam.h> #include <linux/mutex.h> -#include <linux/interrupt.h> -#include <linux/irq.h> #include <linux/spinlock.h> -#include <linux/cpufreq.h> +#include <linux/timer.h> #include <mach/hardware.h> -#include <asm/io.h> #include <asm/system.h> #include "soc_common.h" @@ -74,7 +74,8 @@ EXPORT_SYMBOL(soc_pcmcia_debug); #endif -#define to_soc_pcmcia_socket(x) container_of(x, struct soc_pcmcia_socket, socket) +#define to_soc_pcmcia_socket(x) \ + container_of(x, struct soc_pcmcia_socket, socket) static unsigned short calc_speed(unsigned short *spds, int num, unsigned short dflt) @@ -91,11 +92,15 @@ calc_speed(unsigned short *spds, int num, unsigned short dflt) return speed; } -void soc_common_pcmcia_get_timing(struct soc_pcmcia_socket *skt, struct soc_pcmcia_timing *timing) +void soc_common_pcmcia_get_timing(struct soc_pcmcia_socket *skt, + struct soc_pcmcia_timing *timing) { - timing->io = calc_speed(skt->spd_io, MAX_IO_WIN, SOC_PCMCIA_IO_ACCESS); - timing->mem = calc_speed(skt->spd_mem, MAX_WIN, SOC_PCMCIA_3V_MEM_ACCESS); - timing->attr = calc_speed(skt->spd_attr, MAX_WIN, SOC_PCMCIA_3V_MEM_ACCESS); + timing->io = + calc_speed(skt->spd_io, MAX_IO_WIN, SOC_PCMCIA_IO_ACCESS); + timing->mem = + calc_speed(skt->spd_mem, MAX_WIN, SOC_PCMCIA_3V_MEM_ACCESS); + timing->attr = + calc_speed(skt->spd_attr, MAX_WIN, SOC_PCMCIA_3V_MEM_ACCESS); } EXPORT_SYMBOL(soc_common_pcmcia_get_timing); @@ -137,8 +142,8 @@ static unsigned int soc_common_pcmcia_skt_state(struct soc_pcmcia_socket *skt) * * Convert PCMCIA socket state to our socket configure structure. */ -static int -soc_common_pcmcia_config_skt(struct soc_pcmcia_socket *skt, socket_state_t *state) +static int soc_common_pcmcia_config_skt( + struct soc_pcmcia_socket *skt, socket_state_t *state) { int ret; @@ -150,7 +155,8 @@ soc_common_pcmcia_config_skt(struct soc_pcmcia_socket *skt, socket_state_t *stat */ if (skt->irq_state != 1 && state->io_irq) { skt->irq_state = 1; - set_irq_type(skt->socket.pci_irq, IRQ_TYPE_EDGE_FALLING); + set_irq_type(skt->socket.pci_irq, + IRQ_TYPE_EDGE_FALLING); } else if (skt->irq_state == 1 && state->io_irq == 0) { skt->irq_state = 0; set_irq_type(skt->socket.pci_irq, IRQ_TYPE_NONE); @@ -304,24 +310,24 @@ soc_common_pcmcia_get_status(struct pcmcia_socket *sock, unsigned int *status) * of power configuration, reset, &c. We also record the value of * `state' in order to regurgitate it to the PCMCIA core later. */ -static int -soc_common_pcmcia_set_socket(struct pcmcia_socket *sock, socket_state_t *state) +static int soc_common_pcmcia_set_socket( + struct pcmcia_socket *sock, socket_state_t *state) { struct soc_pcmcia_socket *skt = to_soc_pcmcia_socket(sock); - debug(skt, 2, "mask: %s%s%s%s%s%sflags: %s%s%s%s%s%sVcc %d Vpp %d irq %d\n", - (state->csc_mask==0)?"<NONE> ":"", - (state->csc_mask&SS_DETECT)?"DETECT ":"", - (state->csc_mask&SS_READY)?"READY ":"", - (state->csc_mask&SS_BATDEAD)?"BATDEAD ":"", - (state->csc_mask&SS_BATWARN)?"BATWARN ":"", - (state->csc_mask&SS_STSCHG)?"STSCHG ":"", - (state->flags==0)?"<NONE> ":"", - (state->flags&SS_PWR_AUTO)?"PWR_AUTO ":"", - (state->flags&SS_IOCARD)?"IOCARD ":"", - (state->flags&SS_RESET)?"RESET ":"", - (state->flags&SS_SPKR_ENA)?"SPKR_ENA ":"", - (state->flags&SS_OUTPUT_ENA)?"OUTPUT_ENA ":"", + debug(skt, 2, "mask: %s%s%s%s%s%s flags: %s%s%s%s%s%s Vcc %d Vpp %d irq %d\n", + (state->csc_mask == 0) ? "<NONE> " : "", + (state->csc_mask & SS_DETECT) ? "DETECT " : "", + (state->csc_mask & SS_READY) ? "READY " : "", + (state->csc_mask & SS_BATDEAD) ? "BATDEAD " : "", + (state->csc_mask & SS_BATWARN) ? "BATWARN " : "", + (state->csc_mask & SS_STSCHG) ? "STSCHG " : "", + (state->flags == 0) ? "<NONE> " : "", + (state->flags & SS_PWR_AUTO) ? "PWR_AUTO " : "", + (state->flags & SS_IOCARD) ? "IOCARD " : "", + (state->flags & SS_RESET) ? "RESET " : "", + (state->flags & SS_SPKR_ENA) ? "SPKR_ENA " : "", + (state->flags & SS_OUTPUT_ENA) ? "OUTPUT_ENA " : "", state->Vcc, state->Vpp, state->io_irq); return soc_common_pcmcia_config_skt(skt, state); @@ -336,8 +342,8 @@ soc_common_pcmcia_set_socket(struct pcmcia_socket *sock, socket_state_t *state) * * Returns: 0 on success, -1 on error */ -static int -soc_common_pcmcia_set_io_map(struct pcmcia_socket *sock, struct pccard_io_map *map) +static int soc_common_pcmcia_set_io_map( + struct pcmcia_socket *sock, struct pccard_io_map *map) { struct soc_pcmcia_socket *skt = to_soc_pcmcia_socket(sock); unsigned short speed = map->speed; @@ -346,14 +352,14 @@ soc_common_pcmcia_set_io_map(struct pcmcia_socket *sock, struct pccard_io_map *m map->map, map->speed, (unsigned long long)map->start, (unsigned long long)map->stop); debug(skt, 2, "flags: %s%s%s%s%s%s%s%s\n", - (map->flags==0)?"<NONE>":"", - (map->flags&MAP_ACTIVE)?"ACTIVE ":"", - (map->flags&MAP_16BIT)?"16BIT ":"", - (map->flags&MAP_AUTOSZ)?"AUTOSZ ":"", - (map->flags&MAP_0WS)?"0WS ":"", - (map->flags&MAP_WRPROT)?"WRPROT ":"", - (map->flags&MAP_USE_WAIT)?"USE_WAIT ":"", - (map->flags&MAP_PREFETCH)?"PREFETCH ":""); + (map->flags == 0) ? "<NONE>" : "", + (map->flags & MAP_ACTIVE) ? "ACTIVE " : "", + (map->flags & MAP_16BIT) ? "16BIT " : "", + (map->flags & MAP_AUTOSZ) ? "AUTOSZ " : "", + (map->flags & MAP_0WS) ? "0WS " : "", + (map->flags & MAP_WRPROT) ? "WRPROT " : "", + (map->flags & MAP_USE_WAIT) ? "USE_WAIT " : "", + (map->flags & MAP_PREFETCH) ? "PREFETCH " : ""); if (map->map >= MAX_IO_WIN) { printk(KERN_ERR "%s(): map (%d) out of range\n", __func__, @@ -390,8 +396,8 @@ soc_common_pcmcia_set_io_map(struct pcmcia_socket *sock, struct pccard_io_map *m * * Returns: 0 on success, -ERRNO on error */ -static int -soc_common_pcmcia_set_mem_map(struct pcmcia_socket *sock, struct pccard_mem_map *map) +static int soc_common_pcmcia_set_mem_map( + struct pcmcia_socket *sock, struct pccard_mem_map *map) { struct soc_pcmcia_socket *skt = to_soc_pcmcia_socket(sock); struct resource *res; @@ -400,14 +406,14 @@ soc_common_pcmcia_set_mem_map(struct pcmcia_socket *sock, struct pccard_mem_map debug(skt, 2, "map %u speed %u card_start %08x\n", map->map, map->speed, map->card_start); debug(skt, 2, "flags: %s%s%s%s%s%s%s%s\n", - (map->flags==0)?"<NONE>":"", - (map->flags&MAP_ACTIVE)?"ACTIVE ":"", - (map->flags&MAP_16BIT)?"16BIT ":"", - (map->flags&MAP_AUTOSZ)?"AUTOSZ ":"", - (map->flags&MAP_0WS)?"0WS ":"", - (map->flags&MAP_WRPROT)?"WRPROT ":"", - (map->flags&MAP_ATTRIB)?"ATTRIB ":"", - (map->flags&MAP_USE_WAIT)?"USE_WAIT ":""); + (map->flags == 0) ? "<NONE>" : "", + (map->flags & MAP_ACTIVE) ? "ACTIVE " : "", + (map->flags & MAP_16BIT) ? "16BIT " : "", + (map->flags & MAP_AUTOSZ) ? "AUTOSZ " : "", + (map->flags & MAP_0WS) ? "0WS " : "", + (map->flags & MAP_WRPROT) ? "WRPROT " : "", + (map->flags & MAP_ATTRIB) ? "ATTRIB " : "", + (map->flags & MAP_USE_WAIT) ? "USE_WAIT " : ""); if (map->map >= MAX_WIN) return -EINVAL; @@ -462,8 +468,8 @@ static struct bittbl conf_bits[] = { { SS_OUTPUT_ENA, "SS_OUTPUT_ENA" }, }; -static void -dump_bits(char **p, const char *prefix, unsigned int val, struct bittbl *bits, int sz) +static void dump_bits(char **p, const char *prefix, + unsigned int val, struct bittbl *bits, int sz) { char *b = *p; int i; @@ -481,13 +487,14 @@ dump_bits(char **p, const char *prefix, unsigned int val, struct bittbl *bits, i * * Returns: the number of characters added to the buffer */ -static ssize_t show_status(struct device *dev, struct device_attribute *attr, char *buf) +static ssize_t show_status( + struct device *dev, struct device_attribute *attr, char *buf) { struct soc_pcmcia_socket *skt = container_of(dev, struct soc_pcmcia_socket, socket.dev); char *p = buf; - p+=sprintf(p, "slot : %d\n", skt->nr); + p += sprintf(p, "slot : %d\n", skt->nr); dump_bits(&p, "status", skt->status, status_bits, ARRAY_SIZE(status_bits)); @@ -496,12 +503,12 @@ static ssize_t show_status(struct device *dev, struct device_attribute *attr, ch dump_bits(&p, "cs_flags", skt->cs_state.flags, conf_bits, ARRAY_SIZE(conf_bits)); - p+=sprintf(p, "Vcc : %d\n", skt->cs_state.Vcc); - p+=sprintf(p, "Vpp : %d\n", skt->cs_state.Vpp); - p+=sprintf(p, "IRQ : %d (%d)\n", skt->cs_state.io_irq, + p += sprintf(p, "Vcc : %d\n", skt->cs_state.Vcc); + p += sprintf(p, "Vpp : %d\n", skt->cs_state.Vpp); + p += sprintf(p, "IRQ : %d (%d)\n", skt->cs_state.io_irq, skt->socket.pci_irq); if (skt->ops->show_timing) - p+=skt->ops->show_timing(skt, p); + p += skt->ops->show_timing(skt, p); return p-buf; } @@ -594,7 +601,7 @@ soc_pcmcia_notifier(struct notifier_block *nb, unsigned long val, void *data) mutex_lock(&soc_pcmcia_sockets_lock); list_for_each_entry(skt, &soc_pcmcia_sockets, node) - if ( skt->ops->frequency_change ) + if (skt->ops->frequency_change) ret += skt->ops->frequency_change(skt, val, freqs); mutex_unlock(&soc_pcmcia_sockets_lock); @@ -620,7 +627,8 @@ fs_initcall(soc_pcmcia_cpufreq_register); static void soc_pcmcia_cpufreq_unregister(void) { - cpufreq_unregister_notifier(&soc_pcmcia_notifier_block, CPUFREQ_TRANSITION_NOTIFIER); + cpufreq_unregister_notifier(&soc_pcmcia_notifier_block, + CPUFREQ_TRANSITION_NOTIFIER); } module_exit(soc_pcmcia_cpufreq_unregister); diff --git a/drivers/pcmcia/soc_common.h b/drivers/pcmcia/soc_common.h index bbcd5385a22..9daa73615c8 100644 --- a/drivers/pcmcia/soc_common.h +++ b/drivers/pcmcia/soc_common.h @@ -10,6 +10,7 @@ #define _ASM_ARCH_PCMCIA /* include the world */ +#include <linux/clk.h> #include <linux/cpufreq.h> #include <pcmcia/ss.h> #include <pcmcia/cistpl.h> @@ -29,6 +30,7 @@ struct soc_pcmcia_socket { * Info from low level handler */ unsigned int nr; + struct clk *clk; /* * Core PCMCIA state @@ -56,6 +58,7 @@ struct soc_pcmcia_socket { struct skt_dev_info { int nskt; + struct clk *clk; struct soc_pcmcia_socket skt[0]; }; |