mirror of
				git://git.openwrt.org/openwrt/openwrt.git
				synced 2025-10-31 05:54:26 -04:00 
			
		
		
		
	
		
			
				
	
	
		
			284 lines
		
	
	
		
			8.6 KiB
		
	
	
	
		
			Diff
		
	
	
	
	
	
			
		
		
	
	
			284 lines
		
	
	
		
			8.6 KiB
		
	
	
	
		
			Diff
		
	
	
	
	
	
| --- a/drivers/pcmcia/Makefile
 | |
| +++ b/drivers/pcmcia/Makefile
 | |
| @@ -69,4 +69,4 @@ sa1100_cs-$(CONFIG_SA1100_SIMPAD)		+= sa
 | |
|  pxa2xx_cs-$(CONFIG_ARCH_LUBBOCK)		+= pxa2xx_lubbock.o sa1111_generic.o
 | |
|  pxa2xx_cs-$(CONFIG_MACH_MAINSTONE)		+= pxa2xx_mainstone.o
 | |
|  pxa2xx_cs-$(CONFIG_PXA_SHARPSL)			+= pxa2xx_sharpsl.o
 | |
| -
 | |
| +pxa2xx_cs-$(CONFIG_ARCH_GUMSTIX)		+= pxa2xx_gumstix.o
 | |
| --- /dev/null
 | |
| +++ b/drivers/pcmcia/pxa2xx_gumstix.c
 | |
| @@ -0,0 +1,272 @@
 | |
| +/*
 | |
| + * linux/drivers/pcmcia/pxa2xx_gumstix.c
 | |
| + *
 | |
| + * Gumstix PCMCIA specific routines. Based on Mainstone
 | |
| + *
 | |
| + * Copyright 2004, Craig Hughes <craig@gumstix.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/init.h>
 | |
| +#include <linux/kernel.h>
 | |
| +#include <linux/errno.h>
 | |
| +#include <linux/interrupt.h>
 | |
| +#include <linux/device.h>
 | |
| +#include <linux/platform_device.h>
 | |
| +
 | |
| +#include <pcmcia/ss.h>
 | |
| +
 | |
| +#include <asm/hardware.h>
 | |
| +#include <asm/delay.h>
 | |
| +#include <asm/arch/pxa-regs.h>
 | |
| +#include <asm/irq.h>
 | |
| +#include <asm/io.h>
 | |
| +
 | |
| +#include <asm/arch/gumstix.h>
 | |
| +
 | |
| +#include "soc_common.h"
 | |
| +
 | |
| +static struct pcmcia_irqs gumstix_pcmcia_irqs0[] = {
 | |
| +	{ 0, GUMSTIX_S0_nCD_IRQ,	"CF0 nCD"     },
 | |
| +	{ 0, GUMSTIX_S0_nSTSCHG_IRQ,	"CF0 nSTSCHG" },
 | |
| +};
 | |
| +
 | |
| +static struct pcmcia_irqs gumstix_pcmcia_irqs1[] = {
 | |
| +	{ 1, GUMSTIX_S1_nCD_IRQ,	"CF1 nCD"     },
 | |
| +	{ 1, GUMSTIX_S1_nSTSCHG_IRQ,	"CF1 nSTSCHG" },
 | |
| +};
 | |
| +
 | |
| +static int net_cf_vx_mode = 0;
 | |
| +
 | |
| +static int gumstix_pcmcia_hw_init(struct soc_pcmcia_socket *skt)
 | |
| +{
 | |
| +	if(skt->nr == 0)
 | |
| +	{
 | |
| +		pxa_gpio_mode(GPIO_GUMSTIX_nSTSCHG_0_MD);
 | |
| +		pxa_gpio_mode(GPIO_GUMSTIX_nCD_0_MD);
 | |
| +		if(net_cf_vx_mode)
 | |
| +			pxa_gpio_mode(GPIO_GUMSTIX_PRDY_nBSY_0_OLD_MD);
 | |
| +		else
 | |
| +			pxa_gpio_mode(GPIO_GUMSTIX_PRDY_nBSY_0_MD);
 | |
| +	} else {
 | |
| +		pxa_gpio_mode(GPIO_GUMSTIX_nSTSCHG_1_MD);
 | |
| +		pxa_gpio_mode(GPIO_GUMSTIX_nCD_1_MD);
 | |
| +		pxa_gpio_mode(GPIO_GUMSTIX_PRDY_nBSY_1_MD);
 | |
| +	}
 | |
| +
 | |
| +	pxa_gpio_mode(GPIO_GUMSTIX_nPOE_MD);
 | |
| +	pxa_gpio_mode(GPIO_GUMSTIX_nPWE_MD);
 | |
| +	pxa_gpio_mode(GPIO_GUMSTIX_nPIOR_MD);
 | |
| +	pxa_gpio_mode(GPIO_GUMSTIX_nPIOW_MD);
 | |
| +	pxa_gpio_mode(GPIO_GUMSTIX_nPCE_1_MD);
 | |
| +	pxa_gpio_mode(GPIO_GUMSTIX_nPCE_2_MD);
 | |
| +	pxa_gpio_mode(GPIO_GUMSTIX_pSKTSEL_MD);
 | |
| +	pxa_gpio_mode(GPIO_GUMSTIX_nPREG_MD);
 | |
| +	pxa_gpio_mode(GPIO_GUMSTIX_nPWAIT_MD);
 | |
| +	pxa_gpio_mode(GPIO_GUMSTIX_nIOIS16_MD);                                                                                                                                                              
 | |
| +
 | |
| +	skt->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;
 | |
| +
 | |
| +	return (skt->nr == 0) ? soc_pcmcia_request_irqs(skt, gumstix_pcmcia_irqs0, ARRAY_SIZE(gumstix_pcmcia_irqs0)) :
 | |
| +				soc_pcmcia_request_irqs(skt, gumstix_pcmcia_irqs1, ARRAY_SIZE(gumstix_pcmcia_irqs1));
 | |
| +}
 | |
| +
 | |
| +static void gumstix_pcmcia_hw_shutdown(struct soc_pcmcia_socket *skt)
 | |
| +{
 | |
| +	if(skt->nr == 0)
 | |
| +	{
 | |
| +		soc_pcmcia_free_irqs(skt, gumstix_pcmcia_irqs0, ARRAY_SIZE(gumstix_pcmcia_irqs0));
 | |
| +	} else {
 | |
| +		soc_pcmcia_free_irqs(skt, gumstix_pcmcia_irqs1, ARRAY_SIZE(gumstix_pcmcia_irqs1));
 | |
| +	}
 | |
| +}
 | |
| +
 | |
| +static void gumstix_pcmcia_socket_state(struct soc_pcmcia_socket *skt,
 | |
| +				    struct pcmcia_state *state)
 | |
| +{
 | |
| +	unsigned int cd, prdy_nbsy, nbvd1;
 | |
| +	if(skt->nr == 0)
 | |
| +	{
 | |
| +		cd = GPIO_GUMSTIX_nCD_0;
 | |
| +		if(net_cf_vx_mode)
 | |
| +			prdy_nbsy = GPIO_GUMSTIX_PRDY_nBSY_0_OLD;
 | |
| +		else
 | |
| +			prdy_nbsy = GPIO_GUMSTIX_PRDY_nBSY_0;
 | |
| +		nbvd1 = GPIO_GUMSTIX_nBVD1_0;
 | |
| +	} else {
 | |
| +		cd = GPIO_GUMSTIX_nCD_1;
 | |
| +		prdy_nbsy = GPIO_GUMSTIX_PRDY_nBSY_1;
 | |
| +		nbvd1 = GPIO_GUMSTIX_nBVD1_1;
 | |
| +	}
 | |
| +	state->detect = !(GPLR(cd) & GPIO_bit(cd));
 | |
| +	state->ready  = !!(GPLR(prdy_nbsy) & GPIO_bit(prdy_nbsy));
 | |
| +	state->bvd1   = !!(GPLR(nbvd1) & GPIO_bit(nbvd1));
 | |
| +	state->bvd2   = 1;
 | |
| +	state->vs_3v  = 0;
 | |
| +	state->vs_Xv  = 0;
 | |
| +	state->wrprot = 0;
 | |
| +}
 | |
| +
 | |
| +static int gumstix_pcmcia_configure_socket(struct soc_pcmcia_socket *skt,
 | |
| +				       const socket_state_t *state)
 | |
| +{
 | |
| +	return 0;
 | |
| +}
 | |
| +
 | |
| +static void gumstix_pcmcia_socket_init(struct soc_pcmcia_socket *skt)
 | |
| +{
 | |
| +	if(skt->nr) {
 | |
| +		soc_pcmcia_enable_irqs(skt, gumstix_pcmcia_irqs0, ARRAY_SIZE(gumstix_pcmcia_irqs0));
 | |
| +	} else {
 | |
| +		soc_pcmcia_enable_irqs(skt, gumstix_pcmcia_irqs1, ARRAY_SIZE(gumstix_pcmcia_irqs1));
 | |
| +	}
 | |
| +}
 | |
| +
 | |
| +static void gumstix_pcmcia_socket_suspend(struct soc_pcmcia_socket *skt)
 | |
| +{
 | |
| +	if(skt->nr) {
 | |
| +		soc_pcmcia_disable_irqs(skt, gumstix_pcmcia_irqs0, ARRAY_SIZE(gumstix_pcmcia_irqs0));
 | |
| +	} else {
 | |
| +		soc_pcmcia_disable_irqs(skt, gumstix_pcmcia_irqs1, ARRAY_SIZE(gumstix_pcmcia_irqs1));
 | |
| +	}
 | |
| +}
 | |
| +
 | |
| +static struct pcmcia_low_level gumstix_pcmcia_ops = {
 | |
| +	.owner			= THIS_MODULE,
 | |
| +	.hw_init		= gumstix_pcmcia_hw_init,
 | |
| +	.hw_shutdown		= gumstix_pcmcia_hw_shutdown,
 | |
| +	.socket_state		= gumstix_pcmcia_socket_state,
 | |
| +	.configure_socket	= gumstix_pcmcia_configure_socket,
 | |
| +	.socket_init		= gumstix_pcmcia_socket_init,
 | |
| +	.socket_suspend		= gumstix_pcmcia_socket_suspend,
 | |
| +	.nr			= 2,
 | |
| +};
 | |
| +
 | |
| +static struct platform_device *gumstix_pcmcia_device;
 | |
| +
 | |
| +inline void __init gumstix_pcmcia_cpld_clk(void)
 | |
| +{
 | |
| +	GPCR(GPIO_GUMSTIX_nPOE) = GPIO_bit(GPIO_GUMSTIX_nPOE);
 | |
| +	GPSR(GPIO_GUMSTIX_nPOE) = GPIO_bit(GPIO_GUMSTIX_nPOE);
 | |
| +}
 | |
| +
 | |
| +inline unsigned char __init gumstix_pcmcia_cpld_read_bits(int bits)
 | |
| +{
 | |
| +	unsigned char result = 0;
 | |
| +	unsigned int shift = 0;
 | |
| +	while(bits--)
 | |
| +	{
 | |
| +		result |= !!(GPLR(GPIO_GUMSTIX_nCD_0) & GPIO_bit(GPIO_GUMSTIX_nCD_0)) << shift;
 | |
| +		shift ++;
 | |
| +		gumstix_pcmcia_cpld_clk();
 | |
| +	}
 | |
| +	printk("CPLD responded with: %02x\n",result);
 | |
| +	return result;
 | |
| +}
 | |
| +
 | |
| +/* We use the CPLD on the CF-CF card to read a value from a shift register.  If we can read that
 | |
| + * magic sequence, then we have 2 CF cards; otherwise we assume just one
 | |
| + * The CPLD will send the value of the shift register on GPIO11 (the CD line for slot 0)
 | |
| + * when RESET is held in reset.  We use GPIO48 (nPOE) as a clock signal,
 | |
| + * GPIO52/53 (card enable for both cards) to control read/write to the shift register
 | |
| + */
 | |
| +static void __init gumstix_count_cards(void)
 | |
| +{
 | |
| +	pxa_gpio_mode(GPIO_GUMSTIX_nPOE | GPIO_OUT);
 | |
| +	pxa_gpio_mode(GPIO_GUMSTIX_nPCE_1 | GPIO_OUT);
 | |
| +	pxa_gpio_mode(GPIO_GUMSTIX_nPCE_2 | GPIO_OUT);
 | |
| +	pxa_gpio_mode(GPIO_GUMSTIX_nCD_0 | GPIO_IN);
 | |
| +	if(net_cf_vx_mode)
 | |
| +		pxa_gpio_mode(GPIO_GUMSTIX_CF_OLD_RESET | GPIO_OUT);
 | |
| +	else
 | |
| +		pxa_gpio_mode(GPIO_GUMSTIX_CF_RESET  | GPIO_OUT);
 | |
| +
 | |
| +	// Enter reset
 | |
| +	if(net_cf_vx_mode)
 | |
| +		GPSR(GPIO_GUMSTIX_CF_OLD_RESET) = GPIO_bit(GPIO_GUMSTIX_CF_OLD_RESET);
 | |
| +	else
 | |
| +		GPSR(GPIO_GUMSTIX_CF_RESET) = GPIO_bit(GPIO_GUMSTIX_CF_RESET);
 | |
| +
 | |
| +	// Setup the shift register
 | |
| +	GPSR(GPIO_GUMSTIX_nPCE_1) = GPIO_bit(GPIO_GUMSTIX_nPCE_1);
 | |
| +	GPCR(GPIO_GUMSTIX_nPCE_2) = GPIO_bit(GPIO_GUMSTIX_nPCE_2);
 | |
| +
 | |
| +	// Tick the clock to program the shift register
 | |
| +	gumstix_pcmcia_cpld_clk();
 | |
| +
 | |
| +	// Now set shift register into read mode
 | |
| +	GPCR(GPIO_GUMSTIX_nPCE_1) = GPIO_bit(GPIO_GUMSTIX_nPCE_1);
 | |
| +	GPSR(GPIO_GUMSTIX_nPCE_2) = GPIO_bit(GPIO_GUMSTIX_nPCE_2);
 | |
| +
 | |
| +	// We can read the bits now -- 0xC2 means "Dual compact flash"
 | |
| +	if(gumstix_pcmcia_cpld_read_bits(8) != 0xC2)
 | |
| +	{
 | |
| +		// We do not have 2 CF slots
 | |
| +		gumstix_pcmcia_ops.nr = 1;
 | |
| +	}
 | |
| +}
 | |
| +
 | |
| +#ifdef CONFIG_ARCH_GUMSTIX_VERDEX
 | |
| +static void __init gumstix_check_if_netCF_vx(void)
 | |
| +{
 | |
| +	void *network_controller_memory = ioremap(0x04000300,16);
 | |
| +	// Look for the special 91c111 value in the bank select register
 | |
| +	if((0xff00 & readw(network_controller_memory+0x0e)) == 0x3300) {
 | |
| +		printk("Detected netCF-vx board: using older GPIO configuration\n");
 | |
| +		net_cf_vx_mode = 1;
 | |
| +	} else {
 | |
| +		printk("Not netCF-vx board: using newer GPIO configuration\n");
 | |
| +		net_cf_vx_mode = 0;
 | |
| +	}
 | |
| +	iounmap(network_controller_memory);
 | |
| +}
 | |
| +#endif
 | |
| +
 | |
| +static int __init gumstix_pcmcia_init(void)
 | |
| +{
 | |
| +	int ret;
 | |
| +
 | |
| +#ifdef CONFIG_ARCH_GUMSTIX_VERDEX
 | |
| +	gumstix_check_if_netCF_vx();
 | |
| +#endif
 | |
| +
 | |
| +	gumstix_count_cards();
 | |
| +
 | |
| +	udelay(50);
 | |
| +	if(net_cf_vx_mode)
 | |
| +		GPCR(GPIO_GUMSTIX_CF_OLD_RESET) = GPIO_bit(GPIO_GUMSTIX_CF_OLD_RESET);
 | |
| +	else
 | |
| +		GPCR(GPIO_GUMSTIX_CF_RESET) = GPIO_bit(GPIO_GUMSTIX_CF_RESET);
 | |
| +
 | |
| +	gumstix_pcmcia_device = platform_device_alloc("pxa2xx-pcmcia", -1);
 | |
| +	if (!gumstix_pcmcia_device)
 | |
| +		return -ENOMEM;
 | |
| +
 | |
| +	gumstix_pcmcia_device->dev.platform_data = &gumstix_pcmcia_ops;
 | |
| +
 | |
| +	ret = platform_device_add(gumstix_pcmcia_device);
 | |
| +	if (ret)
 | |
| +		platform_device_put(gumstix_pcmcia_device);
 | |
| +
 | |
| +	return ret;
 | |
| +}
 | |
| +
 | |
| +static void __exit gumstix_pcmcia_exit(void)
 | |
| +{
 | |
| +	/*
 | |
| +	 * This call is supposed to free our gumstix_pcmcia_device.
 | |
| +	 * Unfortunately platform_device don't have a free method, and
 | |
| +	 * we can't assume it's free of any reference at this point so we
 | |
| +	 * can't free it either.
 | |
| +	 */
 | |
| +	platform_device_unregister(gumstix_pcmcia_device);
 | |
| +}
 | |
| +
 | |
| +fs_initcall(gumstix_pcmcia_init);
 | |
| +module_exit(gumstix_pcmcia_exit);
 | |
| +
 | |
| +MODULE_LICENSE("GPL");
 |