diff options
author | John Scherzer <John.Scherzer@bluto.localdomain> | 2008-06-09 11:07:21 -0400 |
---|---|---|
committer | Justin Waters <justin.waters@timesys.com> | 2008-09-16 10:16:08 -0400 |
commit | 7dfe8145a9788da7dd0fc055f4fe0b22511eb357 (patch) | |
tree | 219cd185e6f844f1f42e14405f609f04ab61ddb6 | |
parent | 2831f192f840def7d1f516db64722788bf4d2656 (diff) |
Add sd and cf to acknowledge cards. You still can not mount the sd or cf cards, but the are recognized.
-rw-r--r-- | arch/arm/mach-mx27/devices.c | 18 | ||||
-rw-r--r-- | arch/arm/mach-mx27/mx27lite.c | 2 | ||||
-rw-r--r-- | arch/arm/mach-mx27/mx27lite_gpio.c | 120 | ||||
-rw-r--r-- | arch/arm/tools/mach-types | 2 | ||||
-rw-r--r-- | drivers/net/fec.c | 22 | ||||
-rw-r--r-- | drivers/pcmcia/Kconfig | 8 | ||||
-rw-r--r-- | drivers/pcmcia/Makefile | 1 | ||||
-rw-r--r-- | drivers/pcmcia/mx27lite-pcmcia.c | 1362 | ||||
-rw-r--r-- | drivers/pcmcia/mx27lite-pcmcia.h | 157 |
9 files changed, 1682 insertions, 10 deletions
diff --git a/arch/arm/mach-mx27/devices.c b/arch/arm/mach-mx27/devices.c index e8fec3357487..ae1a35cf42cd 100644 --- a/arch/arm/mach-mx27/devices.c +++ b/arch/arm/mach-mx27/devices.c @@ -527,6 +527,23 @@ static inline void mxc_init_i2c(void) } #endif +#if defined(CONFIG_PCMCIA_MX27LITE) || defined(CONFIG_PCMCIA_MX27LITE_MODULE) + +static struct platform_device mx27lite_device = { + .name = "Mx27lite_pcmcia_soc", + .id = 0, + .dev.release = mxc_nop_release, +}; +static inline void mxc_init_pcmcia(void) +{ + platform_device_register(&mx27lite_device); +} +#else +static inline void mxc_init_pcmcia(void) +{ +} +#endif + #ifdef CONFIG_MXC_VPU /*! Platform Data for MXC VPU */ static struct platform_device mxcvpu_device = { @@ -676,6 +693,7 @@ static int __init mxc_init_devices(void) mxc_init_rtc(); mxc_init_scc(); mxc_init_owire(); + mxc_init_pcmcia(); mxc_init_vpu(); mxc_init_pata(); diff --git a/arch/arm/mach-mx27/mx27lite.c b/arch/arm/mach-mx27/mx27lite.c index 9200116efc6a..04c5e0649240 100644 --- a/arch/arm/mach-mx27/mx27lite.c +++ b/arch/arm/mach-mx27/mx27lite.c @@ -308,7 +308,7 @@ static void __init fixup_mxc_board(struct machine_desc *desc, struct tag *tags, * initialize __mach_desc_MX27LITE data structure. */ /* *INDENT-OFF* */ -MACHINE_START(IMX27LITE, "LogicPD i.MX27LITE") +MACHINE_START(MX27LITE, "LogicPD i.MX27LITE") /* maintainer: TimeSys, Inc. */ .phys_io = AIPI_BASE_ADDR, .io_pg_offst = ((AIPI_BASE_ADDR_VIRT) >> 18) & 0xfffc, diff --git a/arch/arm/mach-mx27/mx27lite_gpio.c b/arch/arm/mach-mx27/mx27lite_gpio.c index 16a921f4de33..cc8a4857e99b 100644 --- a/arch/arm/mach-mx27/mx27lite_gpio.c +++ b/arch/arm/mach-mx27/mx27lite_gpio.c @@ -186,7 +186,6 @@ int gpio_usbh1_active(void) gpio_request_mux(MX27_PIN_USBH1_RXDM, GPIO_MUX_PRIMARY) || gpio_request_mux(MX27_PIN_USBH1_RXDP, GPIO_MUX_PRIMARY)) return -EINVAL; - return 0; } @@ -311,7 +310,6 @@ int gpio_usbotg_hs_active(void) mxc_set_gpio_direction(MX27_PIN_USBH1_TXDM, 0); /* USB1_PWR_nEN */ mxc_set_gpio_direction(MX27_PIN_USBH1_TXDP, 1); /* USB1_nOC */ mxc_set_gpio_dataout(MX27_PIN_USBH1_TXDM, 0); - return 0; } @@ -732,6 +730,7 @@ void gpio_keypad_inactive(void) */ void gpio_ata_active(void) { + printk("\n\nATA active, don't think this is right\n\n"); gpio_request_mux(MX27_PIN_ATA_DATA0, GPIO_MUX_PRIMARY); gpio_request_mux(MX27_PIN_ATA_DATA1, GPIO_MUX_PRIMARY); gpio_request_mux(MX27_PIN_ATA_DATA2, GPIO_MUX_PRIMARY); @@ -1167,6 +1166,121 @@ int sdhc_init_card_det(int id) return ret; } +int gpio_pcmcia_active(void) +{ + printk("Setting all pcmcia pins \n\t0x%08x\n\t0x%08x\n\t0x%08x\n\t0x%08x" + "\n\t0x%08x\n\t0x%08x\n\t0x%08x\n\t0x%08x\n\t0x%08x" + "\n\t0x%08x\n\t0x%08x\n\t0x%08x\n\t0x%08x\n\t0x%08x\n", + mxc_get_gpio_datain(MX27_PIN_PC_CD1_B), + mxc_get_gpio_datain(MX27_PIN_PC_CD2_B), + mxc_get_gpio_datain(MX27_PIN_PC_WAIT_B), + mxc_get_gpio_datain(MX27_PIN_PC_READY), + mxc_get_gpio_datain(MX27_PIN_PC_PWRON), + mxc_get_gpio_datain(MX27_PIN_PC_VS1), + mxc_get_gpio_datain(MX27_PIN_PC_VS1), + mxc_get_gpio_datain(MX27_PIN_PC_VS2), + mxc_get_gpio_datain(MX27_PIN_PC_BVD1), + mxc_get_gpio_datain(MX27_PIN_PC_BVD2), + mxc_get_gpio_datain(MX27_PIN_PC_RST), + mxc_get_gpio_datain(MX27_PIN_IOIS16), + mxc_get_gpio_datain(MX27_PIN_PC_RW_B), + mxc_get_gpio_datain(MX27_PIN_PC_POE)); + + printk("OUTPUT values 0x%08x, 0x%08x, 0x%08x, 0x%08x\n", + mxc_get_gpio_datain(MX27_PIN_TOUT), + mxc_get_gpio_datain(MX27_PIN_TIN), + mxc_get_gpio_datain(MX27_PIN_SSI4_FS), + mxc_get_gpio_datain(MX27_PIN_SSI4_RXDAT) ); + gpio_request_mux(MX27_PIN_SSI4_FS,GPIO_MUX_GPIO); + gpio_request_mux(MX27_PIN_SSI4_RXDAT,GPIO_MUX_GPIO); + mxc_set_gpio_direction( MX27_PIN_SSI4_FS, 0 ); + mxc_set_gpio_direction( MX27_PIN_SSI4_RXDAT, 0 ); + printk("OUTPUT values 0x%08x, 0x%08x, 0x%08x, 0x%08x\n", + mxc_get_gpio_datain(MX27_PIN_TOUT), + mxc_get_gpio_datain(MX27_PIN_TIN), + mxc_get_gpio_datain(MX27_PIN_SSI4_FS), + mxc_get_gpio_datain(MX27_PIN_SSI4_RXDAT) ); + + printk("OUTPUT values 0x%08x, 0x%08x, 0x%08x, 0x%08x\n", + mxc_get_gpio_datain(MX27_PIN_SSI3_FS), + mxc_get_gpio_datain(MX27_PIN_SSI3_RXDAT), + mxc_get_gpio_datain(MX27_PIN_SSI3_TXDAT), + mxc_get_gpio_datain(MX27_PIN_SSI3_CLK) ); + gpio_request_mux( MX27_PIN_SSI3_FS, GPIO_MUX_OUTPUT1); + gpio_request_mux( MX27_PIN_SSI3_RXDAT, GPIO_MUX_OUTPUT1); + gpio_request_mux( MX27_PIN_SSI3_TXDAT, GPIO_MUX_OUTPUT1); + gpio_request_mux( MX27_PIN_SSI3_CLK, GPIO_MUX_OUTPUT1); + mxc_set_gpio_direction( MX27_PIN_SSI3_FS, 1); + mxc_set_gpio_direction( MX27_PIN_SSI3_RXDAT, 1); + mxc_set_gpio_direction( MX27_PIN_SSI3_TXDAT, 1); + mxc_set_gpio_direction( MX27_PIN_SSI3_CLK, 1); + mxc_set_gpio_dataout( MX27_PIN_SSI3_FS, 1); + mxc_set_gpio_dataout( MX27_PIN_SSI3_RXDAT, 1); + mxc_set_gpio_dataout( MX27_PIN_SSI3_TXDAT, 1); + mxc_set_gpio_dataout( MX27_PIN_SSI3_CLK, 1); + printk("OUTPUT values 0x%08x, 0x%08x, 0x%08x, 0x%08x\n", + mxc_get_gpio_datain(MX27_PIN_SSI3_FS), + mxc_get_gpio_datain(MX27_PIN_SSI3_RXDAT), + mxc_get_gpio_datain(MX27_PIN_SSI3_TXDAT), + mxc_get_gpio_datain(MX27_PIN_SSI3_CLK) ); + + if ( + gpio_request_mux(MX27_PIN_PC_CD1_B, GPIO_MUX_PRIMARY) || + gpio_request_mux(MX27_PIN_PC_CD2_B, GPIO_MUX_PRIMARY) || + gpio_request_mux(MX27_PIN_PC_WAIT_B, GPIO_MUX_PRIMARY) || + gpio_request_mux(MX27_PIN_PC_READY, GPIO_MUX_PRIMARY) || + gpio_request_mux(MX27_PIN_PC_PWRON, GPIO_MUX_PRIMARY) || + gpio_request_mux(MX27_PIN_PC_VS1, GPIO_MUX_PRIMARY) || + gpio_request_mux(MX27_PIN_PC_VS2, GPIO_MUX_PRIMARY) || + gpio_request_mux(MX27_PIN_PC_BVD1, GPIO_MUX_PRIMARY) || + gpio_request_mux(MX27_PIN_PC_BVD2, GPIO_MUX_PRIMARY) || + gpio_request_mux(MX27_PIN_PC_RST, GPIO_MUX_PRIMARY) || + gpio_request_mux(MX27_PIN_IOIS16, GPIO_MUX_PRIMARY) || + gpio_request_mux(MX27_PIN_PC_RW_B, GPIO_MUX_PRIMARY) || + gpio_request_mux(MX27_PIN_PC_POE, GPIO_MUX_PRIMARY) + ) + return -EINVAL; + printk("Setting all pcmcia pins \n\t0x%08x\n\t0x%08x\n\t0x%08x\n\t0x%08x" + "\n\t0x%08x\n\t0x%08x\n\t0x%08x\n\t0x%08x\n\t0x%08x" + "\n\t0x%08x\n\t0x%08x\n\t0x%08x\n\t0x%08x\n\t0x%08x\n", + mxc_get_gpio_datain(MX27_PIN_PC_CD1_B), + mxc_get_gpio_datain(MX27_PIN_PC_CD2_B), + mxc_get_gpio_datain(MX27_PIN_PC_WAIT_B), + mxc_get_gpio_datain(MX27_PIN_PC_READY), + mxc_get_gpio_datain(MX27_PIN_PC_PWRON), + mxc_get_gpio_datain(MX27_PIN_PC_VS1), + mxc_get_gpio_datain(MX27_PIN_PC_VS1), + mxc_get_gpio_datain(MX27_PIN_PC_VS2), + mxc_get_gpio_datain(MX27_PIN_PC_BVD1), + mxc_get_gpio_datain(MX27_PIN_PC_BVD2), + mxc_get_gpio_datain(MX27_PIN_PC_RST), + mxc_get_gpio_datain(MX27_PIN_IOIS16), + mxc_get_gpio_datain(MX27_PIN_PC_RW_B), + mxc_get_gpio_datain(MX27_PIN_PC_POE)); + + return 0; +} + +/*! + * Setup GPIO for pcmcia to be inactive + */ +void gpio_pcmcia_inactive(void) +{ + gpio_free_mux(MX27_PIN_IOIS16); + gpio_free_mux(MX27_PIN_PC_BVD1); + gpio_free_mux(MX27_PIN_PC_BVD2); + gpio_free_mux(MX27_PIN_PC_CD1_B); + gpio_free_mux(MX27_PIN_PC_CD2_B); + gpio_free_mux(MX27_PIN_PC_POE); + gpio_free_mux(MX27_PIN_PC_PWRON); + gpio_free_mux(MX27_PIN_PC_READY); + gpio_free_mux(MX27_PIN_PC_RST); + gpio_free_mux(MX27_PIN_PC_RW_B); + gpio_free_mux(MX27_PIN_PC_VS1); + gpio_free_mux(MX27_PIN_PC_VS2); + gpio_free_mux(MX27_PIN_PC_WAIT_B); +} + /* -------------------------------------------------------------------- * Miscellaneous @@ -1233,5 +1347,7 @@ EXPORT_SYMBOL(gpio_sdhc_inactive); EXPORT_SYMBOL(sdhc_get_card_det_status); EXPORT_SYMBOL(sdhc_init_card_det); EXPORT_SYMBOL(board_power_lcd); +EXPORT_SYMBOL(gpio_pcmcia_active); +EXPORT_SYMBOL(gpio_pcmcia_inactive); EXPORT_SYMBOL(gpio_owire_active); EXPORT_SYMBOL(gpio_owire_inactive); diff --git a/arch/arm/tools/mach-types b/arch/arm/tools/mach-types index ca2a8c10be30..305adea1cfaa 100644 --- a/arch/arm/tools/mach-types +++ b/arch/arm/tools/mach-types @@ -1694,7 +1694,7 @@ g900 MACH_G900 G900 1697 apf27 MACH_APF27 APF27 1698 ggus2000 MACH_GGUS2000 GGUS2000 1699 omap_2430_mimic MACH_OMAP_2430_MIMIC OMAP_2430_MIMIC 1700 -imx27lite MACH_IMX27LITE IMX27LITE 1701 +mx27lite MACH_MX27LITE MX27LITE 1701 almex MACH_ALMEX ALMEX 1702 control MACH_CONTROL CONTROL 1703 mba2410 MACH_MBA2410 MBA2410 1704 diff --git a/drivers/net/fec.c b/drivers/net/fec.c index bb518fec9c10..3cd357cd99ac 100644 --- a/drivers/net/fec.c +++ b/drivers/net/fec.c @@ -2618,8 +2618,17 @@ mii_discover_phy3(uint mii_reg, struct net_device *dev) if (phy_info[i]) printk(" -- %s\n", phy_info[i]->name); - else - printk(" -- unknown PHY!\n"); + else { + printk(" -- unknown PHY!, forcing LAN8700\n"); + for(i = 0; phy_info[i]; i++) { + if(phy_info[i]->id == 0x7c0c) + break; + } + if (phy_info[i]) + printk(" -- %s\n", phy_info[i]->name); + else + printk(" Still -- unknown PHY!\n"); + } fep->phy = phy_info[i]; fep->phy_id_done = 1; @@ -2637,9 +2646,8 @@ mii_discover_phy(uint mii_reg, struct net_device *dev) fep = netdev_priv(dev); fecp = fep->hwp; - if (fep->phy_addr < 32) { - if ((phytype = (mii_reg & 0x3fff)) != 0x3fff && phytype != 0) { + if ((phytype = (mii_reg & 0x7fff)) != 0x7fff && phytype != 0) { /* Got first part of ID, now get remainder. */ @@ -2653,9 +2661,11 @@ mii_discover_phy(uint mii_reg, struct net_device *dev) } } else { printk("FEC: No PHY device found.\n"); + mii_queue(dev, mk_mii_read(MII_REG_PHYIR2), + mii_discover_phy3); /* Disable external MII interface */ - fecp->fec_mii_speed = fep->phy_speed = 0; - fec_disable_phy_intr(); + //fecp->fec_mii_speed = fep->phy_speed = 0; + //fec_disable_phy_intr(); } } diff --git a/drivers/pcmcia/Kconfig b/drivers/pcmcia/Kconfig index 2f18b6a7edef..f1f1b58d5d29 100644 --- a/drivers/pcmcia/Kconfig +++ b/drivers/pcmcia/Kconfig @@ -283,6 +283,14 @@ config PCMCIA_MX31ADS This driver is also available as a module called mx31ads_pcmcia. +config PCMCIA_MX27LITE + tristate "MX27LITE PCMCIA support" + depends on ARM && ARCH_MX27 && PCMCIA + help + Say Y here to include support for the Freescale i.MX27 PCMCIA controller. + + This driver is also available as a module called mx27lite_pcmcia. + config PCCARD_NONSTATIC tristate diff --git a/drivers/pcmcia/Makefile b/drivers/pcmcia/Makefile index 8509609b3ba2..bb8363493bf4 100644 --- a/drivers/pcmcia/Makefile +++ b/drivers/pcmcia/Makefile @@ -39,6 +39,7 @@ obj-$(CONFIG_OMAP_CF) += omap_cf.o obj-$(CONFIG_AT91_CF) += at91_cf.o obj-$(CONFIG_ELECTRA_CF) += electra_cf.o obj-$(CONFIG_PCMCIA_MX31ADS) += mx31ads-pcmcia.o +obj-$(CONFIG_PCMCIA_MX27LITE) += mx27lite-pcmcia.o sa11xx_core-y += soc_common.o sa11xx_base.o pxa2xx_core-y += soc_common.o pxa2xx_base.o diff --git a/drivers/pcmcia/mx27lite-pcmcia.c b/drivers/pcmcia/mx27lite-pcmcia.c new file mode 100644 index 000000000000..9d314781fac2 --- /dev/null +++ b/drivers/pcmcia/mx27lite-pcmcia.c @@ -0,0 +1,1362 @@ +/*====================================================================== + drivers/pcmcia/mx27lite-pcmica.c + + Copyright 2005-2007 Freescale Semiconductor, Inc. All Rights Reserved. + + Device driver for the PCMCIA control functionality of i.Mx27 + microprocessors. + + The contents of this file are subject to the Mozilla Public + License Version 1.1 (the "License"); you may not use this file + except in compliance with the License. You may obtain a copy of + the License at http://www.mozilla.org/MPL/ + + Software distributed under the License is distributed on an "AS + IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or + implied. See the License for the specific language governing + rights and limitations under the License. + + The initial developer of the original code is John G. Dorsey + <john+@cs.cmu.edu>. Portions created by John G. Dorsey are + Copyright (C) 1999 John G. Dorsey. All Rights Reserved. + + Alternatively, the contents of this file may be used under the + terms of the GNU Public License version 2 (the "GPL"), in which + case the provisions of the GPL are applicable instead of the + above. If you wish to allow the use of your version of this file + only under the terms of the GPL and not to allow others to use + your version of this file under the MPL, indicate your decision + by deleting the provisions above and replace them with the notice + and other provisions required by the GPL. If you do not delete + the provisions above, a recipient may use your version of this + file under either the MPL or the GPL. + +======================================================================*/ + +#include <linux/module.h> +#include <linux/init.h> +#include <linux/platform_device.h> +#include <linux/clk.h> + +#include <pcmcia/version.h> +#include <pcmcia/cs_types.h> +#include <pcmcia/cs.h> +#include <pcmcia/ss.h> +#include <asm/mach-types.h> +#include <asm/arch/pcmcia.h> +#include <linux/interrupt.h> +#include <linux/delay.h> + +#include <linux/moduleparam.h> +#include <linux/kernel.h> +#include <linux/timer.h> +#include <linux/mm.h> +#include <linux/spinlock.h> + +#include <asm/hardware.h> +#include <asm/io.h> +#include <asm/irq.h> +#include <asm/system.h> + +#include "mx27lite-pcmcia.h" +#include <linux/irq.h> + +#define MX27LITE_PCMCIA_IRQ INT_PCMCIA + +/* + * The mapping of window size to bank size value + */ +static bsize_map_t bsize_map[] = { + /* Window size Bank size */ + {POR_1, POR_BSIZE_1}, + {POR_2, POR_BSIZE_2}, + {POR_4, POR_BSIZE_4}, + {POR_8, POR_BSIZE_8}, + {POR_16, POR_BSIZE_16}, + {POR_32, POR_BSIZE_32}, + {POR_64, POR_BSIZE_64}, + {POR_128, POR_BSIZE_128}, + {POR_256, POR_BSIZE_256}, + {POR_512, POR_BSIZE_512}, + + {POR_1K, POR_BSIZE_1K}, + {POR_2K, POR_BSIZE_2K}, + {POR_4K, POR_BSIZE_4K}, + {POR_8K, POR_BSIZE_8K}, + {POR_16K, POR_BSIZE_16K}, + {POR_32K, POR_BSIZE_32K}, + {POR_64K, POR_BSIZE_64K}, + {POR_128K, POR_BSIZE_128K}, + {POR_256K, POR_BSIZE_256K}, + {POR_512K, POR_BSIZE_512K}, + + {POR_1M, POR_BSIZE_1M}, + {POR_2M, POR_BSIZE_2M}, + {POR_4M, POR_BSIZE_4M}, + {POR_8M, POR_BSIZE_8M}, + {POR_16M, POR_BSIZE_16M}, + {POR_32M, POR_BSIZE_32M}, + {POR_64M, POR_BSIZE_64M} +}; + +#define to_mx27lite_pcmcia_socket(x) container_of(x, struct mx27lite_pcmcia_socket, socket) + +/* mx27lite_pcmcia_find_bsize() + * ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + * + * Find the bsize according to the window size passed in + * + * Return: + */ +static int mx27lite_pcmcia_find_bsize(unsigned long win_size) +{ + int i, nr = sizeof(bsize_map) / sizeof(bsize_map_t); + int bsize = -1; + + for (i = 0; i < nr; i++) { + if (bsize_map[i].win_size == win_size) { + bsize = bsize_map[i].bsize; + break; + } + } + + pr_debug(KERN_INFO "nr = %d bsize = 0x%0x\n", nr, bsize); + if (bsize < 0 || i > nr) { + pr_debug(KERN_INFO "No such bsize\n"); + return -ENODEV; + } + + return bsize; +} + +/* mx27lite_common_pcmcia_sock_init() + * ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + * + * (Re-)Initialise the socket, turning on status interrupts + * and PCMCIA bus. This must wait for power to stabilise + * so that the card status signals report correctly. + * + * Returns: 0 + */ +static int mx27lite_common_pcmcia_sock_init(struct pcmcia_socket *sock) +{ + struct mx27lite_pcmcia_socket *skt = to_mx27lite_pcmcia_socket(sock); + + printk("\n\n\nINIT mx27lite_pcmcia_socket CALLED\n\n\n"); + pr_debug(KERN_INFO "initializing socket\n"); + + skt->ops->socket_init(skt); + return 0; +} + +/* + * mx27lite_common_pcmcia_config_skt + * ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + * + * Convert PCMCIA socket state to our socket configure structure. + */ +static int +mx27lite_common_pcmcia_config_skt(struct mx27lite_pcmcia_socket *skt, + socket_state_t * state) +{ + int ret; + printk("mx27lite_common_pcmcia_config_skt called \n\n"); + ret = skt->ops->configure_socket(skt, state); + if (ret == 0) { + /* + * This really needs a better solution. The IRQ + * may or may not be claimed by the driver. + */ + if (skt->irq_state != 1 && state->io_irq) { + printk("set falling\n"); + skt->irq_state = 1; + set_irq_type(skt->irq, IRQF_TRIGGER_FALLING); + } else if (1){//skt->irq_state == 1 && state->io_irq == 0) { + printk("set rising\n"); + skt->irq_state = 0; + set_irq_type(skt->irq, IRQF_TRIGGER_RISING); + } + printk("maybe nothing\n"); + + skt->cs_state = *state; + } + + if (ret < 0) + pr_debug(KERN_ERR "mx27lite_common_pcmcia: unable to configure" + " socket\n"); + + return ret; +} + +/* + * mx27lite_common_pcmcia_suspend() + * ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + * + * Remove power on the socket, disable IRQs from the card. + * Turn off status interrupts, and disable the PCMCIA bus. + * + * Returns: 0 + */ +static int mx27lite_common_pcmcia_suspend(struct pcmcia_socket *sock) +{ + struct mx27lite_pcmcia_socket *skt = to_mx27lite_pcmcia_socket(sock); + int ret; + + pr_debug(KERN_INFO "suspending socket\n"); + + ret = mx27lite_common_pcmcia_config_skt(skt, &dead_socket); + if (ret == 0) + skt->ops->socket_suspend(skt); + + return ret; +} + +static unsigned int mx27lite_common_pcmcia_skt_state(struct mx27lite_pcmcia_socket + *skt) +{ + struct pcmcia_state state; + unsigned int stat; + + memset(&state, 0, sizeof(struct pcmcia_state)); + + skt->ops->socket_state(skt, &state); + + stat = state.detect ? SS_DETECT : 0; + stat |= state.ready ? SS_READY : 0; + stat |= state.wrprot ? SS_WRPROT : 0; + stat |= state.vs_3v ? SS_3VCARD : 0; + stat |= state.vs_Xv ? SS_XVCARD : 0; + + /* The power status of individual sockets is not available + * explicitly from the hardware, so we just remember the state + * and regurgitate it upon request: + */ + stat |= skt->cs_state.Vcc ? SS_POWERON : 0; + + if (skt->cs_state.flags & SS_IOCARD) + stat |= state.bvd1 ? SS_STSCHG : 0; + else { + if (state.bvd1 == 0) + stat |= SS_BATDEAD; + else if (state.bvd2 == 0) + stat |= SS_BATWARN; + } + + pr_debug(KERN_INFO "stat = 0x%08x\n", stat); + + return stat; +} + +/* + * Implements the get_status() operation for the in-kernel PCMCIA + * service (formerly SS_GetStatus in Card Services). Essentially just + * fills in bits in `status' according to internal driver state or + * the value of the voltage detect chipselect register. + * + * As a debugging note, during card startup, the PCMCIA core issues + * three set_socket() commands in a row the first with RESET deasserted, + * the second with RESET asserted, and the last with RESET deasserted + * again. Following the third set_socket(), a get_status() command will + * be issued. The kernel is looking for the SS_READY flag (see + * setup_socket(), reset_socket(), and unreset_socket() in cs.c). + * + * Returns: 0 + */ +static int mx27lite_common_pcmcia_get_status(struct pcmcia_socket *sock, + unsigned int *status) +{ + struct mx27lite_pcmcia_socket *skt = to_mx27lite_pcmcia_socket(sock); + + skt->status = mx27lite_common_pcmcia_skt_state(skt); + *status = skt->status; + printk("called get_status = %d\n",skt->status); + printk("\nREGISTER VALUES\n\tPIPR=0x%08lx\n\tPSCR=0x%08lx\n\tPER=0x%08lx" + "\n\tPGCR=0x%08lx\n\tPGSR=0x%08lx\n\n", + _reg_PCMCIA_PIPR,_reg_PCMCIA_PSCR,_reg_PCMCIA_PER, + _reg_PCMCIA_PGCR,_reg_PCMCIA_PGSR); + + return 0; +} + +/* + * Implements the set_socket() operation for the in-kernel PCMCIA + * service (formerly SS_SetSocket in Card Services). We more or + * less punt all of this work and let the kernel handle the details + * of power configuration, reset, &c. We also record the value of + * `state' in order to regurgitate it to the PCMCIA core later. + * + * Returns: 0 + */ +static int mx27lite_common_pcmcia_set_socket(struct pcmcia_socket *sock, + socket_state_t * state) +{ + struct mx27lite_pcmcia_socket *skt = to_mx27lite_pcmcia_socket(sock); + + pr_debug(KERN_INFO + "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 " : "", + state->Vcc, state->Vpp, state->io_irq); + + pr_debug(KERN_INFO + "csc_mask: %08x flags: %08x Vcc: %d Vpp: %d io_irq: %d\n", + state->csc_mask, state->flags, state->Vcc, state->Vpp, + state->io_irq); + printk("mx27lite_common_pcmcia_set_socket called\n"); + printk(KERN_INFO + "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 " : "", + state->Vcc, state->Vpp, state->io_irq); + + printk(KERN_INFO + "csc_mask: %08x flags: %08x Vcc: %d Vpp: %d io_irq: %d\n", + state->csc_mask, state->flags, state->Vcc, state->Vpp, + state->io_irq); + + return mx27lite_common_pcmcia_config_skt(skt, state); +} + +/* + * Set address and profile to window registers PBR, POR, POFR + */ +static int mx27lite_pcmcia_set_window_reg(ulong start, ulong end, u_int window) +{ + int bsize; + ulong size = end - start + 1; + + bsize = mx27lite_pcmcia_find_bsize(size); + if (bsize < 0) { + pr_debug("Cannot set the window register\n"); + return -1; + } + /* Disable the window */ + _reg_PCMCIA_POR(window) &= ~PCMCIA_POR_PV; + + /* Set PBR, POR, POFR */ + _reg_PCMCIA_PBR(window) = start; + _reg_PCMCIA_POR(window) &= ~(PCMCIA_POR_PRS_MASK + | PCMCIA_POR_WPEN + | PCMCIA_POR_WP + | PCMCIA_POR_BSIZE_MASK + | PCMCIA_POR_PPS_8); + _reg_PCMCIA_POR(window) |= bsize | PCMCIA_POR_PPS_16; + + switch (window) { + case IO_WINDOW: + _reg_PCMCIA_POR(window) |= PCMCIA_POR_PRS(PCMCIA_POR_PRS_IO); + break; + + case ATTRIBUTE_MEMORY_WINDOW: + _reg_PCMCIA_POR(window) |= + PCMCIA_POR_PRS(PCMCIA_POR_PRS_ATTRIBUTE); + break; + + case COMMON_MEMORY_WINDOW: + _reg_PCMCIA_POR(window) |= + PCMCIA_POR_PRS(PCMCIA_POR_PRS_COMMON); + break; + + default: + pr_debug("Window %d is not support\n", window); + return -1; + } + _reg_PCMCIA_POFR(window) = 0; + + /* Enable the window */ + _reg_PCMCIA_POR(window) |= PCMCIA_POR_PV; + + return 0; +} + +/* + * Implements the set_io_map() operation for the in-kernel PCMCIA + * service (formerly SS_SetIOMap in Card Services). We configure + * the map speed as requested, but override the address ranges + * supplied by Card Services. + * + * Returns: 0 on success, -1 on error + */ +static int +mx27lite_common_pcmcia_set_io_map(struct pcmcia_socket *sock, + struct pccard_io_map *map) +{ + struct mx27lite_pcmcia_socket *skt = to_mx27lite_pcmcia_socket(sock); + unsigned short speed = map->speed; + + pr_debug("map %u speed %u start 0x%08lx stop 0x%08lx\n", + map->map, map->speed, map->start, map->stop); + pr_debug("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 " : ""); + + if (map->map >= MAX_IO_WIN) { + pr_debug(KERN_ERR "%s(): map (%d) out of range\n", __FUNCTION__, + map->map); + return -1; + } + + if (map->flags & MAP_ACTIVE) { + if (speed == 0) + speed = PCMCIA_IO_ACCESS; + } else { + speed = 0; + } + + skt->spd_io[map->map] = speed; + skt->ops->set_timing(skt); + + if (map->stop == 1) + map->stop = PAGE_SIZE - 1; + + skt->socket.io_offset = (unsigned long)skt->virt_io; + map->stop -= map->start; + map->stop += (unsigned long)skt->virt_io; + map->start = (unsigned long)skt->virt_io; + + mx27lite_pcmcia_set_window_reg(skt->res_io.start, skt->res_io.end, + IO_WINDOW); + + pr_debug(KERN_ERR "IO window: _reg_PCMCIA_PBR(%d) = %08x\n", + IO_WINDOW, _reg_PCMCIA_PBR(IO_WINDOW)); + pr_debug(KERN_ERR "IO window: _reg_PCMCIA_POR(%d) = %08x\n", + IO_WINDOW, _reg_PCMCIA_POR(IO_WINDOW)); + + return 0; +} + +/* + * Implements the set_mem_map() operation for the in-kernel PCMCIA + * service (formerly SS_SetMemMap in Card Services). We configure + * the map speed as requested, but override the address ranges + * supplied by Card Services. + * + * Returns: 0 on success, -1 on error + */ +static int +mx27lite_common_pcmcia_set_mem_map(struct pcmcia_socket *sock, + struct pccard_mem_map *map) +{ + struct mx27lite_pcmcia_socket *skt = to_mx27lite_pcmcia_socket(sock); + struct resource *res; + unsigned short speed = map->speed; + + pr_debug + (KERN_INFO + "map %u speed %u card_start %08x flags%08x static_start %08lx\n", + map->map, map->speed, map->card_start, map->flags, + map->static_start); + pr_debug(KERN_INFO "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 " : ""); + + if (map->map >= MAX_WIN) + return -EINVAL; + + if (map->flags & MAP_ACTIVE) { + if (speed == 0) + speed = 300; + } else { + speed = 0; + } + + if (map->flags & MAP_ATTRIB) { + res = &skt->res_attr; + skt->spd_attr[map->map] = speed; + skt->spd_mem[map->map] = 0; + mx27lite_pcmcia_set_window_reg(res->start, res->end, + ATTRIBUTE_MEMORY_WINDOW); + + pr_debug(KERN_INFO "Attr window: _reg_PCMCIA_PBR(%d) = %08x\n", + ATTRIBUTE_MEMORY_WINDOW, + _reg_PCMCIA_PBR(ATTRIBUTE_MEMORY_WINDOW)); + pr_debug(KERN_INFO "_reg_PCMCIA_POR(%d) = %08x\n", + ATTRIBUTE_MEMORY_WINDOW, + _reg_PCMCIA_POR(ATTRIBUTE_MEMORY_WINDOW)); + + } else { + res = &skt->res_mem; + skt->spd_attr[map->map] = 0; + skt->spd_mem[map->map] = speed; + mx27lite_pcmcia_set_window_reg(res->start, res->end, + COMMON_MEMORY_WINDOW); + + pr_debug(KERN_INFO "Com window: _reg_PCMCIA_PBR(%d) = %08x\n", + COMMON_MEMORY_WINDOW, + _reg_PCMCIA_PBR(COMMON_MEMORY_WINDOW)); + pr_debug(KERN_INFO "Com window: _reg_PCMCIA_POR(%d) = %08x\n", + COMMON_MEMORY_WINDOW, + _reg_PCMCIA_POR(COMMON_MEMORY_WINDOW)); + } + + skt->ops->set_timing(skt); + + map->static_start = res->start + map->card_start; + + return 0; +} + +static struct pccard_operations mx27lite_common_pcmcia_operations = { + .init = mx27lite_common_pcmcia_sock_init, + .suspend = mx27lite_common_pcmcia_suspend, + .get_status = mx27lite_common_pcmcia_get_status, + .set_socket = mx27lite_common_pcmcia_set_socket, + .set_io_map = mx27lite_common_pcmcia_set_io_map, + .set_mem_map = mx27lite_common_pcmcia_set_mem_map, +}; + +/* ============================================================================== */ + +static inline void mx27lite_pcmcia_irq_config(void) +{ + printk("\n\nmx27lite_pcmcia_irq_config\n\n"); + /* Setup irq */ + _reg_PCMCIA_PER = + (PCMCIA_PER_ERRINTEN | PCMCIA_PER_CDE1 | PCMCIA_PER_CDE2); +} + +static inline void mx27lite_pcmcia_invalidate_windows(void) +{ + int i; + + for (i = 0; i < PCMCIA_WINDOWS; i++) { + _reg_PCMCIA_PBR(i) = 0; + _reg_PCMCIA_POR(i) = 0; + _reg_PCMCIA_POFR(i) = 0; + } +} + +extern int gpio_pcmcia_active(void); +extern void gpio_pcmcia_inactive(void); + +static int mx27lite_pcmcia_hw_init(struct mx27lite_pcmcia_socket *skt) +{ + printk("\n\nmx27lite_pcmcia_hw_init\n\n"); + /* Configure the pins for PCMCIA */ + if ( gpio_pcmcia_active() ) + { + printk("\n\nGPIO PIN ERROR\n\n"); + } + + /* + * enabling interrupts at this time causes a flood of interrupts + * if a card is present, so wait for configure_socket + * to enable them when requested. + * + * mx27lite_pcmcia_irq_config(); + */ + mx27lite_pcmcia_invalidate_windows(); + + /* Register interrupt. */ + skt->irq = MX27LITE_PCMCIA_IRQ; + + return 0; +} + +static void mx27lite_pcmcia_free_irq(struct mx27lite_pcmcia_socket *skt, + unsigned int irq) +{ + free_irq(irq, skt); +} + +static void mx27lite_pcmcia_hw_shutdown(struct mx27lite_pcmcia_socket *skt) +{ + printk("\n\nmx27lite_pcmcia_hw_shutodwn\n\n"); + mx27lite_pcmcia_invalidate_windows(); + mx27lite_pcmcia_free_irq(skt, MX27LITE_PCMCIA_IRQ); + + /* Disable the pins */ + gpio_pcmcia_inactive(); +} + +/* + * Get the socket state + */ +static void +mx27lite_pcmcia_socket_state(struct mx27lite_pcmcia_socket *skt, + struct pcmcia_state *state) +{ + unsigned long pins; + + pins = _reg_PCMCIA_PIPR; + pr_debug(KERN_INFO "_reg_PCMCIA_PIPR = 0x%08lx\n", pins); + //printk("_reg_PCMCIA_PIPR=0x%08lx, %x, %x, %x,%x,%x,%x\n", + // pins,PCMCIA_PIPR_RDY,PCMCIA_PIPR_BVD2, + // PCMCIA_PIPR_BVD1,PCMCIA_PIPR_CD,PCMCIA_PIPR_POWERON, + // PCMCIA_PIPR_VS_5V); + + state->ready = (pins & PCMCIA_PIPR_RDY) ? 1 : 0; + state->bvd2 = (pins & PCMCIA_PIPR_BVD2) ? 1 : 0; + state->bvd1 = (pins & PCMCIA_PIPR_BVD1) ? 1 : 0; + + if ((pins & PCMCIA_PIPR_CD) == PCMCIA_PIPR_CD) { + state->detect = 0; + skt->cs_state.csc_mask |= SS_INSERTION; + } else { + state->detect = 1; + } + state->detect = (pins & PCMCIA_PIPR_CD) ? 0 : 1; + state->wrprot = (pins & PCMCIA_PIPR_WP) ? 1 : 0; + state->poweron = (pins & PCMCIA_PIPR_POWERON) ? 1 : 0; +#if 0 + if ((pins & PCMCIA_PIPR_CD) == PCMCIA_PIPR_CD) { + state->detect = 0; + skt->cs_state.csc_mask |= SS_INSERTION; + } else { + state->detect = 1; + } + if (pins & PCMCIA_PIPR_VS_5V) { + state->vs_3v = 0; + skt->cs_state.Vcc = 33; + } else { + state->vs_3v = 1; + skt->cs_state.Vcc = 50; + } +#endif + state->vs_3v = (pins & PCMCIA_PIPR_VS_5V) ? 0 : 1; + state->vs_Xv = 0; +} + +static __inline__ void mx27lite_pcmcia_low_power(bool enable) +{ + if (enable) + _reg_PCMCIA_PGCR |= PCMCIA_PGCR_LPMEN; + else + _reg_PCMCIA_PGCR &= ~PCMCIA_PGCR_LPMEN; +} + +static __inline__ void mx27lite_pcmcia_soft_reset(void) +{ + _reg_PCMCIA_PGCR |= PCMCIA_PGCR_RESET; + msleep(2); + + _reg_PCMCIA_PGCR &= ~(PCMCIA_PGCR_RESET | PCMCIA_PGCR_LPMEN); + _reg_PCMCIA_PGCR |= PCMCIA_PGCR_POE; + msleep(2); + pr_debug(KERN_INFO "_reg_PCMCIA_PGCR = %08x\n", _reg_PCMCIA_PGCR); +} + +static int +mx27lite_pcmcia_configure_socket(struct mx27lite_pcmcia_socket *skt, + const socket_state_t * state) +{ + int ret = 0; + + if (state->Vcc != 0 && state->Vcc != 33 && state->Vcc != 50) { + pr_debug(KERN_ERR "mx27lite-pcmcia: unrecognized Vcc %d\n", + state->Vcc); + return -1; + } + + pr_debug(KERN_INFO "PIPR = %x, desired Vcc = %d.%dV\n", + _reg_PCMCIA_PIPR, state->Vcc / 10, state->Vcc % 10); + printk( "PIPR = %x, desired Vcc = %d.%dV\n", + _reg_PCMCIA_PIPR, state->Vcc / 10, state->Vcc % 10); + + if (!(skt->socket.state & SOCKET_PRESENT) && (skt->pre_stat == 1)) { + pr_debug(KERN_INFO "Socket enter low power mode\n"); + skt->pre_stat = 0; + mx27lite_pcmcia_low_power(1); + } + + if (state->flags & SS_RESET) { + printk("soft_reset called from configure_socket\n"); + mx27lite_pcmcia_soft_reset(); + + /* clean out previous tenant's trash */ + _reg_PCMCIA_PGSR = (PCMCIA_PGSR_NWINE + | PCMCIA_PGSR_LPE + | PCMCIA_PGSR_SE + | PCMCIA_PGSR_CDE | PCMCIA_PGSR_WPE); + } + /* enable interrupts if requested, else turn 'em off */ + if (skt->irq) + mx27lite_pcmcia_irq_config(); + else + _reg_PCMCIA_PER = 0; + + if (skt->socket.state & SOCKET_PRESENT) { + skt->pre_stat = 1; + } + return ret; +} + +static void mx27lite_pcmcia_enable_irq(struct mx27lite_pcmcia_socket *skt, + unsigned int irq) +{ + printk("\n\nonable irq=%d\n\n",irq); + set_irq_type(irq, IRQF_TRIGGER_RISING); + //set_irq_type(irq, IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING); +} + +static void mx27lite_pcmcia_disable_irq(struct mx27lite_pcmcia_socket *skt, + unsigned int irq) +{ + printk("\n\nDisable irq=%d\n\n",irq); + set_irq_type(irq, IRQF_TRIGGER_NONE); +} + +/* + * Enable card status IRQs on (re-)initialisation. This can + * be called at initialisation, power management event, or + * pcmcia event. + */ +static void mx27lite_pcmcia_socket_init(struct mx27lite_pcmcia_socket *skt) +{ + printk("soft reset called frmo socket_init\n"); + mx27lite_pcmcia_soft_reset(); + + mx27lite_pcmcia_enable_irq(skt, MX27LITE_PCMCIA_IRQ); +} + +/* + * Disable card status IRQ on suspend. + */ +static void mx27lite_pcmcia_socket_suspend(struct mx27lite_pcmcia_socket *skt) +{ + mx27lite_pcmcia_disable_irq(skt, MX27LITE_PCMCIA_IRQ); + mx27lite_pcmcia_low_power(1); +} + +/* ==================================================================================== */ + +/* + * PCMCIA strobe hold time + */ +static inline u_int mx27lite_pcmcia_por_psht(u_int pcmcia_cycle_ns, + u_int hclk_cycle_ns) +{ + u_int psht; + + return psht = pcmcia_cycle_ns / hclk_cycle_ns; +} + +/* + * PCMCIA strobe set up time + */ +static inline u_int mx27lite_pcmcia_por_psst(u_int pcmcia_cycle_ns, + u_int hclk_cycle_ns) +{ + u_int psst; + + return psst = pcmcia_cycle_ns / hclk_cycle_ns; +} + +/* + * PCMCIA strobe length time + */ +static inline u_int mx27lite_pcmcia_por_pslt(u_int pcmcia_cycle_ns, + u_int hclk_cycle_ns) +{ + u_int pslt; + + return pslt = pcmcia_cycle_ns / hclk_cycle_ns + 2; +} + +/* + * mx27lite_pcmcia_default_mecr_timing + * ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + * + * Calculate MECR clock wait states for given CPU clock + * speed and command wait state. This function can be over- + * written by a board specific version. + * + * The default is to simply calculate the BS values as specified in + * the INTEL SA1100 development manual + * "Expansion Memory (PCMCIA) Configuration Register (MECR)" + * that's section 10.2.5 in _my_ version of the manual ;) + */ +static unsigned int mx27lite_pcmcia_default_mecr_timing(struct + mx27lite_pcmcia_socket + *skt, + unsigned int cpu_speed, + unsigned int cmd_time) +{ + return 0; +} + +/* + * Calculate the timing code + */ +static u_int mx27lite_pcmcia_cal_code(u_int speed_ns, u_int clk_ns) +{ + u_int code; + + code = PCMCIA_POR_PSHT(mx27lite_pcmcia_por_psht(speed_ns, clk_ns)) + | PCMCIA_POR_PSST(mx27lite_pcmcia_por_psst(speed_ns, clk_ns)) + | PCMCIA_POR_PSL(mx27lite_pcmcia_por_pslt(speed_ns, clk_ns)); + + return code; +} + +/* + * set MECR value for socket <sock> based on this sockets + * io, mem and attribute space access speed. + * Call board specific BS value calculation to allow boards + * to tweak the BS values. + */ +static int mx27lite_pcmcia_set_window_timing(u_int speed_ns, u_int window, + u_int clk_ns) +{ + u_int code = 0; + + switch (window) { + case IO_WINDOW: + code = mx27lite_pcmcia_cal_code(speed_ns, clk_ns); + break; + case COMMON_MEMORY_WINDOW: + code = mx27lite_pcmcia_cal_code(speed_ns, clk_ns); + break; + case ATTRIBUTE_MEMORY_WINDOW: + code = mx27lite_pcmcia_cal_code(speed_ns, clk_ns); + break; + default: + break; + } + + /* Disable the window */ + _reg_PCMCIA_POR(window) &= ~PCMCIA_POR_PV; + + /* Clear the register fisrt */ + _reg_PCMCIA_POR(window) &= ~(PCMCIA_POR_PSST_MASK + | PCMCIA_POR_PSL_MASK + | PCMCIA_POR_PSHT_MASK); + /* And then set the register */ + _reg_PCMCIA_POR(window) |= code; + + /* Enable the window */ + _reg_PCMCIA_POR(window) |= PCMCIA_POR_PV; + + return 0; +} + +static unsigned short calc_speed(unsigned short *spds, int num, + unsigned short dflt) +{ + unsigned short speed = 0; + int i; + + for (i = 0; i < num; i++) + if (speed < spds[i]) + speed = spds[i]; + if (speed == 0) + speed = dflt; + + return speed; +} + +static void +mx27lite_common_pcmcia_get_timing(struct mx27lite_pcmcia_socket *skt, + struct mx27lite_pcmcia_timing *timing) +{ + timing->io = calc_speed(skt->spd_io, MAX_IO_WIN, PCMCIA_IO_ACCESS); + timing->mem = calc_speed(skt->spd_mem, MAX_WIN, PCMCIA_3V_MEM_ACCESS); + timing->attr = + calc_speed(skt->spd_attr, MAX_WIN, PCMCIA_ATTR_MEM_ACCESS); +} + +static int mx27lite_pcmcia_set_timing(struct mx27lite_pcmcia_socket *skt) +{ + u_int clk_ns; + struct mx27lite_pcmcia_timing timing; + + /* How many nanoseconds */ + clk_ns = (1000 * 1000 * 1000) / clk_get_rate(skt->clk); + pr_debug(KERN_INFO "clk_ns = %d\n", clk_ns); + + mx27lite_common_pcmcia_get_timing(skt, &timing); + pr_debug(KERN_INFO "timing: io %d, mem %d, attr %d\n", timing.io, + timing.mem, timing.attr); + + mx27lite_pcmcia_set_window_timing(timing.io, IO_WINDOW, clk_ns); + mx27lite_pcmcia_set_window_timing(timing.mem, COMMON_MEMORY_WINDOW, + clk_ns); + mx27lite_pcmcia_set_window_timing(timing.attr, ATTRIBUTE_MEMORY_WINDOW, + clk_ns); + + return 0; +} + +static int mx27lite_pcmcia_show_timing(struct mx27lite_pcmcia_socket *skt, + char *buf) +{ + return 0; +} + +static struct pcmcia_low_level mx27lite_pcmcia_ops = { + .owner = THIS_MODULE, + .hw_init = mx27lite_pcmcia_hw_init, + .hw_shutdown = mx27lite_pcmcia_hw_shutdown, + .socket_state = mx27lite_pcmcia_socket_state, + .configure_socket = mx27lite_pcmcia_configure_socket, + + .socket_init = mx27lite_pcmcia_socket_init, + .socket_suspend = mx27lite_pcmcia_socket_suspend, + + .get_timing = mx27lite_pcmcia_default_mecr_timing, + .set_timing = mx27lite_pcmcia_set_timing, + .show_timing = mx27lite_pcmcia_show_timing, +}; + +/* =================================================================================== */ + +LIST_HEAD(mx27lite_pcmcia_sockets); +DECLARE_MUTEX(mx27lite_pcmcia_sockets_lock); + +static DEFINE_SPINLOCK(status_lock); + +struct bittbl { + unsigned int mask; + const char *name; +}; + +static struct bittbl status_bits[] = { + {SS_WRPROT, "SS_WRPROT"}, + {SS_BATDEAD, "SS_BATDEAD"}, + {SS_BATWARN, "SS_BATWARN"}, + {SS_READY, "SS_READY"}, + {SS_DETECT, "SS_DETECT"}, + {SS_POWERON, "SS_POWERON"}, + {SS_STSCHG, "SS_STSCHG"}, + {SS_3VCARD, "SS_3VCARD"}, + {SS_XVCARD, "SS_XVCARD"}, +}; + +static struct bittbl conf_bits[] = { + {SS_PWR_AUTO, "SS_PWR_AUTO"}, + {SS_IOCARD, "SS_IOCARD"}, + {SS_RESET, "SS_RESET"}, + {SS_DMA_MODE, "SS_DMA_MODE"}, + {SS_SPKR_ENA, "SS_SPKR_ENA"}, + {SS_OUTPUT_ENA, "SS_OUTPUT_ENA"}, +}; + +static void +dump_bits(char **p, const char *prefix, unsigned int val, struct bittbl *bits, + int sz) +{ + char *b = *p; + int i; + + b += sprintf(b, "%-9s:", prefix); + for (i = 0; i < sz; i++) + if (val & bits[i].mask) + b += sprintf(b, " %s", bits[i].name); + *b++ = '\n'; + *p = b; +} + +/* + * Implements the /sys/class/pcmcia_socket/??/status file. + * + * Returns: the number of characters added to the buffer + */ +static ssize_t show_status(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct mx27lite_pcmcia_socket *skt = + container_of(dev, struct mx27lite_pcmcia_socket, socket.dev); + char *p = buf; + + p += sprintf(p, "slot : %d\n", skt->nr); + + dump_bits(&p, "status", skt->status, + status_bits, ARRAY_SIZE(status_bits)); + dump_bits(&p, "csc_mask", skt->cs_state.csc_mask, + status_bits, ARRAY_SIZE(status_bits)); + 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, skt->irq); + if (skt->ops->show_timing) + p += skt->ops->show_timing(skt, p); + + return p - buf; +} + +static DEVICE_ATTR(status, S_IRUGO, show_status, NULL); + +static void mx27lite_common_check_status(struct mx27lite_pcmcia_socket *skt) +{ + unsigned int events; + + pr_debug(KERN_INFO "entering PCMCIA monitoring thread\n"); + + do { + unsigned int status; + unsigned long flags; + + status = mx27lite_common_pcmcia_skt_state(skt); + + spin_lock_irqsave(&status_lock, flags); + events = (status ^ skt->status) & skt->cs_state.csc_mask; + skt->status = status; + spin_unlock_irqrestore(&status_lock, flags); + + pr_debug(KERN_INFO "events: %s%s%s%s%s%s\n", + events == 0 ? "<NONE>" : "", + events & SS_DETECT ? "DETECT " : "", + events & SS_READY ? "READY " : "", + events & SS_BATDEAD ? "BATDEAD " : "", + events & SS_BATWARN ? "BATWARN " : "", + events & SS_STSCHG ? "STSCHG " : ""); + + if (events) + pcmcia_parse_events(&skt->socket, events); + } while (events); +} + +/* + * Service routine for socket driver interrupts (requested by the + * low-level PCMCIA init() operation via mx27lite_common_pcmcia_thread()). + * The actual interrupt-servicing work is performed by + * mx27lite_common_pcmcia_thread(), largely because the Card Services event- + * handling code performs scheduling operations which cannot be + * executed from within an interrupt context. + */ +static irqreturn_t mx27lite_common_pcmcia_interrupt(int irq, void *dev) +{ + struct mx27lite_pcmcia_socket *skt = dev; + volatile u32 pscr, pgsr; + + dev_dbg(dev, "servicing IRQ %d\n", irq); + + /* clear interrupt states */ + pscr = _reg_PCMCIA_PSCR; + _reg_PCMCIA_PSCR = pscr; + + pgsr = _reg_PCMCIA_PGSR; + _reg_PCMCIA_PGSR = pgsr; + printk( "\n\nservicing IRQ %d, 0x%08x, 0x%08x\n", irq, pscr,pgsr); + + mx27lite_common_check_status(skt); + + return IRQ_HANDLED; +} + +/* Let's poll for events in addition to IRQs since IRQ only is unreliable... */ +static void mx27lite_common_pcmcia_poll_event(unsigned long dummy) +{ + struct mx27lite_pcmcia_socket *skt = + (struct mx27lite_pcmcia_socket *)dummy; + pr_debug(KERN_INFO "polling for events\n"); + //printk("polling for events\n"); + //printk("\nREGISTER VALUES\n\tPIPR=0x%08lx\n\tPSCR=0x%08lx\n\tPER=0x%08lx" + // "\n\tPGCR=0x%08lx\n\tPGSR=0x%08lx\n\n", + // _reg_PCMCIA_PIPR,_reg_PCMCIA_PSCR,_reg_PCMCIA_PER, + // _reg_PCMCIA_PGCR,_reg_PCMCIA_PGSR); + + mod_timer(&skt->poll_timer, jiffies + PCMCIA_POLL_PERIOD); + + mx27lite_common_check_status(skt); +} + +#define mx27lite_pcmcia_cpufreq_register() +#define mx27lite_pcmcia_cpufreq_unregister() + +static int mx27lite_common_drv_pcmcia_probe(struct platform_device *pdev, + struct pcmcia_low_level *ops) +{ + struct mx27lite_pcmcia_socket *skt; + int vs, value, ret; + struct pccard_io_map map; + + down(&mx27lite_pcmcia_sockets_lock); + + printk("\n\n\nGETTING PROBED\n\n\n"); + + printk("\nREGISTER VALUES\n\tPIPR=0x%08lx\n\tPSCR=0x%08lx\n\tPER=0x%08lx" + "\n\tPGCR=0x%08lx\n\tPGSR=0x%08lx\n\n", + _reg_PCMCIA_PIPR,_reg_PCMCIA_PSCR,_reg_PCMCIA_PER, + _reg_PCMCIA_PGCR,_reg_PCMCIA_PGSR); + + skt = kmalloc(sizeof(struct mx27lite_pcmcia_socket), GFP_KERNEL); + if (!skt) { + ret = -ENOMEM; + goto out; + } + + memset(skt, 0, sizeof(struct mx27lite_pcmcia_socket)); + + /* + * Initialise the socket structure. + */ + skt->socket.ops = &mx27lite_common_pcmcia_operations; + skt->socket.owner = ops->owner; + skt->socket.driver_data = skt; + + init_timer(&skt->poll_timer); + skt->poll_timer.function = mx27lite_common_pcmcia_poll_event; + skt->poll_timer.data = (unsigned long)skt; + skt->poll_timer.expires = jiffies + PCMCIA_POLL_PERIOD; + + skt->irq = MX27LITE_PCMCIA_IRQ; + skt->socket.dev.parent = pdev->dev.parent; + skt->ops = ops; + + skt->clk = clk_get(NULL, "ahb_clk"); + + skt->res_skt.start = _PCMCIA(0); + skt->res_skt.end = _PCMCIA(0) + PCMCIASp - 1; + skt->res_skt.name = MX27LITE_PCMCIA; + skt->res_skt.flags = IORESOURCE_MEM; + + ret = request_resource(&iomem_resource, &skt->res_skt); + if (ret) + goto out_err_1; + + skt->res_io.start = _PCMCIAIO(0); + skt->res_io.end = _PCMCIAIO(0) + PCMCIAIOSp - 1; + skt->res_io.name = "io"; + skt->res_io.flags = IORESOURCE_MEM | IORESOURCE_BUSY; + + ret = request_resource(&skt->res_skt, &skt->res_io); + if (ret) + goto out_err_2; + + skt->res_mem.start = _PCMCIAMem(0); + skt->res_mem.end = _PCMCIAMem(0) + PCMCIAMemSp - 1; + skt->res_mem.name = "memory"; + skt->res_mem.flags = IORESOURCE_MEM; + + ret = request_resource(&skt->res_skt, &skt->res_mem); + if (ret) + goto out_err_3; + + skt->res_attr.start = _PCMCIAAttr(0); + skt->res_attr.end = _PCMCIAAttr(0) + PCMCIAAttrSp - 1; + skt->res_attr.name = "attribute"; + skt->res_attr.flags = IORESOURCE_MEM; + + ret = request_resource(&skt->res_skt, &skt->res_attr); + if (ret) + goto out_err_4; + + skt->virt_io = ioremap(skt->res_io.start, 0x10000); + if (skt->virt_io == NULL) { + ret = -ENOMEM; + goto out_err_5; + } + + if (list_empty(&mx27lite_pcmcia_sockets)) + mx27lite_pcmcia_cpufreq_register(); + list_add(&skt->node, &mx27lite_pcmcia_sockets); + + /* + * We initialize default socket timing here, because + * we are not guaranteed to see a SetIOMap operation at + * runtime. + */ + ops->set_timing(skt); + + ret = ops->hw_init(skt); + if (ret) + goto out_err_6; + + ret = request_irq(skt->irq, mx27lite_common_pcmcia_interrupt, + IRQF_SHARED | IRQF_DISABLED, "PCMCIA IRQ", skt); + if (ret) + goto out_err_6; + set_irq_type(skt->irq, IRQT_NOEDGE); + + skt->socket.features = SS_CAP_STATIC_MAP | SS_CAP_PCCARD; + skt->socket.resource_ops = &pccard_static_ops; + skt->socket.irq_mask = 0; + skt->socket.map_size = PCMCIAPrtSp; + skt->socket.pci_irq = skt->irq; + skt->socket.io_offset = (unsigned long)skt->virt_io; + + skt->status = mx27lite_common_pcmcia_skt_state(skt); + skt->pre_stat = 0; + ret = pcmcia_register_socket(&skt->socket); + if (ret) + goto out_err_7; + /* FIXED ME workaround for binding with ide-cs. ide usage io port 0x100~0x107 and 0x10e */ + map.map = 0; + map.flags = MAP_ACTIVE | MAP_16BIT; + map.start = 0; + map.stop = PCMCIAIOSp - 1; + map.speed = 0; + mx27lite_common_pcmcia_set_io_map(&skt->socket, &map); + + vs = _reg_PCMCIA_PIPR & PCMCIA_PIPR_VS; + value = vs & PCMCIA_PIPR_VS_5V ? 50 : 33; + dev_dbg(&pdev->dev, "PCMCIA: Voltage the card supports: %d.%dV\n", + value / 10, value % 10); + + add_timer(&skt->poll_timer); + + ret = device_create_file(&skt->socket.dev, &dev_attr_status); + if (ret < 0) + goto out_err_8; + + platform_set_drvdata(pdev, skt); + ret = 0; + goto out; + + out_err_8: + printk("8\n"); + del_timer_sync(&skt->poll_timer); + pcmcia_unregister_socket(&skt->socket); + + out_err_7: + printk("7\n"); + flush_scheduled_work(); + free_irq(skt->irq, skt); + ops->hw_shutdown(skt); + out_err_6: + printk("6\n"); + list_del(&skt->node); + iounmap(skt->virt_io); + out_err_5: + printk("5\n"); + release_resource(&skt->res_attr); + out_err_4: + printk("4\n"); + release_resource(&skt->res_mem); + out_err_3: + printk("3\n"); + release_resource(&skt->res_io); + out_err_2: + printk("2\n"); + release_resource(&skt->res_skt); + out_err_1: + printk("1\n"); + kfree(skt); + out: + up(&mx27lite_pcmcia_sockets_lock); + return ret; +} + +static int mx27lite_drv_pcmcia_remove(struct platform_device *pdev) +{ + struct mx27lite_pcmcia_socket *skt = platform_get_drvdata(pdev); + + platform_set_drvdata(pdev, NULL); + + down(&mx27lite_pcmcia_sockets_lock); + + del_timer_sync(&skt->poll_timer); + + pcmcia_unregister_socket(&skt->socket); + + flush_scheduled_work(); + + skt->ops->hw_shutdown(skt); + + mx27lite_common_pcmcia_config_skt(skt, &dead_socket); + + list_del(&skt->node); + iounmap(skt->virt_io); + skt->virt_io = NULL; + release_resource(&skt->res_attr); + release_resource(&skt->res_mem); + release_resource(&skt->res_io); + release_resource(&skt->res_skt); + + if (list_empty(&mx27lite_pcmcia_sockets)) + mx27lite_pcmcia_cpufreq_unregister(); + + up(&mx27lite_pcmcia_sockets_lock); + + kfree(skt); + + return 0; +} + +static int mx27lite_drv_pcmcia_probe(struct platform_device *pdev) +{ + if (!machine_is_mx27lite()) + return -ENODEV; + + return mx27lite_common_drv_pcmcia_probe(pdev, &mx27lite_pcmcia_ops); +} + +static int mx27lite_drv_pcmcia_suspend(struct platform_device *pdev, + pm_message_t state) +{ + return pcmcia_socket_dev_suspend(&pdev->dev, state); +} + +static int mx27lite_drv_pcmcia_resume(struct platform_device *pdev) +{ + return pcmcia_socket_dev_resume(&pdev->dev); +} + +/* + * Low level functions + */ +static struct platform_driver mx27lite_pcmcia_driver = { + .driver = { + .name = MX27LITE_PCMCIA, + }, + .probe = mx27lite_drv_pcmcia_probe, + .remove = mx27lite_drv_pcmcia_remove, + .suspend = mx27lite_drv_pcmcia_suspend, + .resume = mx27lite_drv_pcmcia_resume, +}; + +/* mx27lite_pcmcia_init() + * + */ +static int __init mx27lite_pcmcia_init(void) +{ + int ret; + //printk("\n\n\nmx27lite_pcmcia_init called\n\n\n"); + if ((ret = platform_driver_register(&mx27lite_pcmcia_driver))){ + printk("Failure during init %d\n\n\n",ret); + return ret; + } + printk(KERN_INFO "PCMCIA: Initialize i.Mx27 pcmcia socket\n"); + + return ret; +} + +/* mx27lite_pcmcia_exit() + * + */ +static void __exit mx27lite_pcmcia_exit(void) +{ + platform_driver_unregister(&mx27lite_pcmcia_driver); +} + +MODULE_AUTHOR("Freescale Semiconductor, Inc."); +MODULE_DESCRIPTION("i.MX27 PCMCIA Socket Controller"); +MODULE_LICENSE("GPL"); + +module_init(mx27lite_pcmcia_init); +module_exit(mx27lite_pcmcia_exit); diff --git a/drivers/pcmcia/mx27lite-pcmcia.h b/drivers/pcmcia/mx27lite-pcmcia.h new file mode 100644 index 000000000000..afa610ebd089 --- /dev/null +++ b/drivers/pcmcia/mx27lite-pcmcia.h @@ -0,0 +1,157 @@ +/* + * linux/drivers/pcmcia/mx27lite-pcmcia.h + * + * Copyright 2005-2007 Freescale Semiconductor, Inc. All Rights Reserved. + * + * This file contains definitions for the PCMCIA support code common to + * integrated SOCs like the i.Mx27 microprocessors. + */ +#ifndef _ASM_ARCH_PCMCIA +#define _ASM_ARCH_PCMCIA + +/* include the world */ +#include <linux/cpufreq.h> +#include <pcmcia/version.h> +#include <pcmcia/cs_types.h> +#include <pcmcia/cs.h> +#include <pcmcia/ss.h> +#include <pcmcia/bulkmem.h> +#include <pcmcia/cistpl.h> +#include "cs_internal.h" + +#define MX27LITE_PCMCIA "Mx27lite_pcmcia_soc"//Name must match device.c + +struct device; +struct pcmcia_low_level; + +/* + * This structure encapsulates per-socket state which we might need to + * use when responding to a Card Services query of some kind. + */ +struct mx27lite_pcmcia_socket { + struct pcmcia_socket socket; + + /* + * Info from low level handler + */ + struct device *dev; + unsigned int nr; + unsigned int irq; + + struct clk *clk; + + /* + * Core PCMCIA state + */ + struct pcmcia_low_level *ops; + + unsigned int status; + unsigned int pre_stat; + socket_state_t cs_state; + + unsigned short spd_io[MAX_IO_WIN]; + unsigned short spd_mem[MAX_WIN]; + unsigned short spd_attr[MAX_WIN]; + + struct resource res_skt; + struct resource res_io; + struct resource res_mem; + struct resource res_attr; + void *virt_io; + + unsigned int irq_state; + + struct timer_list poll_timer; + struct list_head node; +}; + +struct pcmcia_state { + unsigned detect:1, + ready:1, bvd1:1, bvd2:1, wrprot:1, vs_3v:1, vs_Xv:1, poweron:1; +}; + +struct pcmcia_low_level { + struct module *owner; + + /* first socket in system */ + int first; + /* nr of sockets */ + int nr; + + int (*hw_init) (struct mx27lite_pcmcia_socket *); + void (*hw_shutdown) (struct mx27lite_pcmcia_socket *); + + void (*socket_state) (struct mx27lite_pcmcia_socket *, + struct pcmcia_state *); + int (*configure_socket) (struct mx27lite_pcmcia_socket *, + const socket_state_t *); + + /* + * Enable card status IRQs on (re-)initialisation. This can + * be called at initialisation, power management event, or + * pcmcia event. + */ + void (*socket_init) (struct mx27lite_pcmcia_socket *); + + /* + * Disable card status IRQs and PCMCIA bus on suspend. + */ + void (*socket_suspend) (struct mx27lite_pcmcia_socket *); + + /* + * Hardware specific timing routines. + * If provided, the get_timing routine overrides the SOC default. + */ + unsigned int (*get_timing) (struct mx27lite_pcmcia_socket *, + unsigned int, unsigned int); + int (*set_timing) (struct mx27lite_pcmcia_socket *); + int (*show_timing) (struct mx27lite_pcmcia_socket *, char *); + +#ifdef CONFIG_CPU_FREQ + /* + * CPUFREQ support. + */ + int (*frequency_change) (struct mx27lite_pcmcia_socket *, unsigned long, + struct cpufreq_freqs *); +#endif +}; + +struct mx27lite_pcmcia_timing { + unsigned short io; + unsigned short mem; + unsigned short attr; +}; + +typedef struct { + ulong win_size; + int bsize; +} bsize_map_t; + +/* + * The PC Card Standard, Release 7, section 4.13.4, says that twIORD + * has a minimum value of 165ns. Section 4.13.5 says that twIOWR has + * a minimum value of 165ns, as well. Section 4.7.2 (describing + * common and attribute memory write timing) says that twWE has a + * minimum value of 150ns for a 250ns cycle time (for 5V operation; + * see section 4.7.4), or 300ns for a 600ns cycle time (for 3.3V + * operation, also section 4.7.4). Section 4.7.3 says that taOE + * has a maximum value of 150ns for a 300ns cycle time (for 5V + * operation), or 300ns for a 600ns cycle time (for 3.3V operation). + * + * When configuring memory maps, Card Services appears to adopt the policy + * that a memory access time of "0" means "use the default." The default + * PCMCIA I/O command width time is 165ns. The default PCMCIA 5V attribute + * and memory command width time is 150ns; the PCMCIA 3.3V attribute and + * memory command width time is 300ns. + */ +#define PCMCIA_IO_ACCESS (165) +#define PCMCIA_5V_MEM_ACCESS (150) +#define PCMCIA_3V_MEM_ACCESS (300) +#define PCMCIA_ATTR_MEM_ACCESS (300) + +/* + * The socket driver actually works nicely in interrupt-driven form, + * so the (relatively infrequent) polling is "just to be sure." + */ +#define PCMCIA_POLL_PERIOD (2*HZ) +#endif |