Commit ce1e6b9f authored by Russell King's avatar Russell King

Fix up SA1100 PCMCIA for IRQ handling changes.

Major SA1100 generic DMA cleanup.
Fix suspend/resume bugs.
Provide and use new SA1111 generic driver for SA1111-based devices.
parent e51b59f6
...@@ -24,5 +24,6 @@ if [ "$CONFIG_PCMCIA" != "n" ]; then ...@@ -24,5 +24,6 @@ if [ "$CONFIG_PCMCIA" != "n" ]; then
dep_tristate ' HD64465 host bridge support' CONFIG_HD64465_PCMCIA $CONFIG_PCMCIA dep_tristate ' HD64465 host bridge support' CONFIG_HD64465_PCMCIA $CONFIG_PCMCIA
fi fi
fi fi
dep_tristate ' SA1100 support' CONFIG_PCMCIA_SA1100 $CONFIG_ARCH_SA1100 $CONFIG_PCMCIA
endmenu endmenu
...@@ -62,22 +62,24 @@ endif ...@@ -62,22 +62,24 @@ endif
obj-$(CONFIG_PCMCIA_SA1100) += sa1100_cs.o obj-$(CONFIG_PCMCIA_SA1100) += sa1100_cs.o
sa1100_cs-objs-y := sa1100_generic.o sa1100_cs-objs-y := sa1100_generic.o
sa1100_cs-objs-$(CONFIG_SA1100_ADSBITSY) += sa1100_adsbitsy.o sa1111_generic.o
sa1100_cs-objs-$(CONFIG_SA1100_ASSABET) += sa1100_assabet.o sa1100_cs-objs-$(CONFIG_SA1100_ASSABET) += sa1100_assabet.o
sa1100_cs-objs-$(CONFIG_ASSABET_NEPONSET) += sa1100_neponset.o sa1100_cs-objs-$(CONFIG_ASSABET_NEPONSET) += sa1100_neponset.o sa1111_generic.o
sa1100_cs-objs-$(CONFIG_SA1100_H3600) += sa1100_h3600.o sa1100_cs-objs-$(CONFIG_SA1100_BADGE4) += sa1100_badge4.o sa1111_generic.o
sa1100_cs-objs-$(CONFIG_SA1100_CERF) += sa1100_cerf.o sa1100_cs-objs-$(CONFIG_SA1100_CERF) += sa1100_cerf.o
sa1100_cs-objs-$(CONFIG_SA1100_FLEXANET) += sa1100_flexanet.o
sa1100_cs-objs-$(CONFIG_SA1100_FREEBIRD) += sa1100_freebird.o
sa1100_cs-objs-$(CONFIG_SA1100_GRAPHICSMASTER) += sa1100_graphicsmaster.o sa1111_generic.o
sa1100_cs-objs-$(CONFIG_SA1100_GRAPHICSCLIENT) += sa1100_graphicsclient.o sa1100_cs-objs-$(CONFIG_SA1100_GRAPHICSCLIENT) += sa1100_graphicsclient.o
sa1100_cs-objs-$(CONFIG_SA1100_XP860) += sa1100_xp860.o sa1100_cs-objs-$(CONFIG_SA1100_H3600) += sa1100_h3600.o
sa1100_cs-objs-$(CONFIG_SA1100_JORNADA720) += sa1100_jornada720.o sa1111_generic.o
sa1100_cs-objs-$(CONFIG_SA1100_PANGOLIN) += sa1100_pangolin.o sa1100_cs-objs-$(CONFIG_SA1100_PANGOLIN) += sa1100_pangolin.o
sa1100_cs-objs-$(CONFIG_SA1100_YOPY) += sa1100_yopy.o sa1100_cs-objs-$(CONFIG_SA1100_PFS168) += sa1100_pfs168.o sa1111_generic.o
sa1100_cs-objs-$(CONFIG_SA1100_FREEBIRD) += sa1100_freebird.o sa1100_cs-objs-$(CONFIG_SA1100_SHANNON) += sa1100_shannon.o
sa1100_cs-objs-$(CONFIG_SA1100_PFS168) += sa1100_pfs168.o
sa1100_cs-objs-$(CONFIG_SA1100_JORNADA720) += sa1100_jornada720.o
sa1100_cs-objs-$(CONFIG_SA1100_FLEXANET) += sa1100_flexanet.o
sa1100_cs-objs-$(CONFIG_SA1100_SIMPAD) += sa1100_simpad.o sa1100_cs-objs-$(CONFIG_SA1100_SIMPAD) += sa1100_simpad.o
sa1100_cs-objs-$(CONFIG_SA1100_GRAPHICSMASTER) += sa1100_graphicsmaster.o
sa1100_cs-objs-$(CONFIG_SA1100_ADSBITSY) += sa1100_adsbitsy.o
sa1100_cs-objs-$(CONFIG_SA1100_STORK) += sa1100_stork.o sa1100_cs-objs-$(CONFIG_SA1100_STORK) += sa1100_stork.o
sa1100_cs-objs-$(CONFIG_SA1100_XP860) += sa1100_xp860.o sa1111_generic.o
sa1100_cs-objs-$(CONFIG_SA1100_YOPY) += sa1100_yopy.o
include $(TOPDIR)/Rules.make include $(TOPDIR)/Rules.make
...@@ -85,7 +87,7 @@ pcmcia_core.o: $(pcmcia_core-objs) ...@@ -85,7 +87,7 @@ pcmcia_core.o: $(pcmcia_core-objs)
$(LD) $(LD_RFLAG) -r -o $@ $(pcmcia_core-objs) $(LD) $(LD_RFLAG) -r -o $@ $(pcmcia_core-objs)
sa1100_cs.o: $(sa1100_cs-objs-y) sa1100_cs.o: $(sa1100_cs-objs-y)
$(LD) -r -o $@ $(sa1100_cs-objs-y) $(LD) -r -o $@ $(sort $(sa1100_cs-objs-y))
yenta_socket.o: $(yenta_socket-objs) yenta_socket.o: $(yenta_socket-objs)
$(LD) $(LD_RFLAG) -r -o $@ $(yenta_socket-objs) $(LD) $(LD_RFLAG) -r -o $@ $(yenta_socket-objs)
...@@ -38,9 +38,7 @@ ...@@ -38,9 +38,7 @@
#include <pcmcia/bulkmem.h> #include <pcmcia/bulkmem.h>
#include <pcmcia/cistpl.h> #include <pcmcia/cistpl.h>
#include "cs_internal.h" #include "cs_internal.h"
#include "sa1100_generic.h"
#include <asm/arch/pcmcia.h>
/* MECR: Expansion Memory Configuration Register /* MECR: Expansion Memory Configuration Register
* (SA-1100 Developers Manual, p.10-13; SA-1110 Developers Manual, p.10-24) * (SA-1100 Developers Manual, p.10-13; SA-1110 Developers Manual, p.10-24)
...@@ -157,15 +155,24 @@ static inline unsigned int sa1100_pcmcia_cmd_time(unsigned int cpu_clock_khz, ...@@ -157,15 +155,24 @@ static inline unsigned int sa1100_pcmcia_cmd_time(unsigned int cpu_clock_khz,
* use when responding to a Card Services query of some kind. * use when responding to a Card Services query of some kind.
*/ */
struct sa1100_pcmcia_socket { struct sa1100_pcmcia_socket {
/*
* Core PCMCIA state
*/
socket_state_t cs_state; socket_state_t cs_state;
struct pcmcia_state k_state;
unsigned int irq;
void (*handler)(void *, unsigned int);
void *handler_info;
pccard_io_map io_map[MAX_IO_WIN]; pccard_io_map io_map[MAX_IO_WIN];
pccard_mem_map mem_map[MAX_WIN]; pccard_mem_map mem_map[MAX_WIN];
ioaddr_t virt_io, phys_attr, phys_mem; void (*handler)(void *, unsigned int);
void *handler_info;
struct pcmcia_state k_state;
ioaddr_t phys_attr, phys_mem;
void *virt_io;
unsigned short speed_io, speed_attr, speed_mem; unsigned short speed_io, speed_attr, speed_mem;
/*
* Info from low level handler
*/
unsigned int irq;
}; };
...@@ -180,23 +187,57 @@ struct sa1100_pcmcia_socket { ...@@ -180,23 +187,57 @@ struct sa1100_pcmcia_socket {
/* /*
* Declaration for all implementation specific low_level operations. * Declaration for all machine specific init/exit functions.
*/ */
extern struct pcmcia_low_level assabet_pcmcia_ops; extern int pcmcia_adsbitsy_init(void);
extern struct pcmcia_low_level neponset_pcmcia_ops; extern void pcmcia_adsbitsy_exit(void);
extern struct pcmcia_low_level h3600_pcmcia_ops;
extern struct pcmcia_low_level cerf_pcmcia_ops; extern int pcmcia_assabet_init(void);
extern struct pcmcia_low_level gcplus_pcmcia_ops; extern void pcmcia_assabet_exit(void);
extern struct pcmcia_low_level xp860_pcmcia_ops;
extern struct pcmcia_low_level yopy_pcmcia_ops; extern int pcmcia_badge4_init(void);
extern struct pcmcia_low_level pangolin_pcmcia_ops; extern void pcmcia_badge4_exit(void);
extern struct pcmcia_low_level freebird_pcmcia_ops;
extern struct pcmcia_low_level pfs168_pcmcia_ops; extern int pcmcia_cerf_init(void);
extern struct pcmcia_low_level jornada720_pcmcia_ops; extern void pcmcia_cerf_exit(void);
extern struct pcmcia_low_level flexanet_pcmcia_ops;
extern struct pcmcia_low_level simpad_pcmcia_ops; extern int pcmcia_flexanet_init(void);
extern struct pcmcia_low_level graphicsmaster_pcmcia_ops; extern void pcmcia_flexanet_exit(void);
extern struct pcmcia_low_level adsbitsy_pcmcia_ops;
extern struct pcmcia_low_level stork_pcmcia_ops; extern int pcmcia_freebird_init(void);
extern void pcmcia_freebird_exit(void);
extern int pcmcia_gcplus_init(void);
extern void pcmcia_gcplus_exit(void);
extern int pcmcia_graphicsmaster_init(void);
extern void pcmcia_graphicsmaster_exit(void);
extern int pcmcia_jornada720_init(void);
extern void pcmcia_jornada720_exit(void);
extern int pcmcia_neponset_init(void);
extern void pcmcia_neponset_exit(void);
extern int pcmcia_pangolin_init(void);
extern void pcmcia_pangolin_exit(void);
extern int pcmcia_pfs168_init(void);
extern void pcmcia_pfs168_exit(void);
extern int pcmcia_shannon_init(void);
extern void pcmcia_shannon_exit(void);
extern int pcmcia_simpad_init(void);
extern void pcmcia_simpad_exit(void);
extern int pcmcia_stork_init(void);
extern void pcmcia_stork_exit(void);
extern int pcmcia_xp860_init(void);
extern void pcmcia_xp860_exit(void);
extern int pcmcia_yopy_init(void);
extern void pcmcia_yopy_exit(void);
#endif /* !defined(_PCMCIA_SA1100_H) */ #endif /* !defined(_PCMCIA_SA1100_H) */
...@@ -11,206 +11,97 @@ ...@@ -11,206 +11,97 @@
*/ */
#include <linux/kernel.h> #include <linux/kernel.h>
#include <linux/sched.h> #include <linux/sched.h>
#include <linux/init.h>
#include <asm/delay.h>
#include <asm/hardware.h> #include <asm/hardware.h>
#include <asm/irq.h>
#include <asm/arch/pcmcia.h> #include "sa1100_generic.h"
#include "sa1111_generic.h"
static int adsbitsy_pcmcia_init(struct pcmcia_init *init) static int adsbitsy_pcmcia_init(struct pcmcia_init *init)
{ {
int return_val=0;
/* Set GPIO_A<3:0> to be outputs for PCMCIA/CF power controller: */ /* Set GPIO_A<3:0> to be outputs for PCMCIA/CF power controller: */
PA_DDR &= ~(GPIO_GPIO0 | GPIO_GPIO1 | GPIO_GPIO2 | GPIO_GPIO3); PA_DDR &= ~(GPIO_GPIO0 | GPIO_GPIO1 | GPIO_GPIO2 | GPIO_GPIO3);
/* Disable Power 3.3V/5V for PCMCIA/CF */ /* Disable Power 3.3V/5V for PCMCIA/CF */
PA_DWR |= GPIO_GPIO0 | GPIO_GPIO1 | GPIO_GPIO2 | GPIO_GPIO3; PA_DWR |= GPIO_GPIO0 | GPIO_GPIO1 | GPIO_GPIO2 | GPIO_GPIO3;
INTPOL1 |= (1 << (S0_READY_NINT - SA1111_IRQ(32))) | /* Why? */
(1 << (S1_READY_NINT - SA1111_IRQ(32))) |
(1 << (S0_CD_VALID - SA1111_IRQ(32))) |
(1 << (S1_CD_VALID - SA1111_IRQ(32))) |
(1 << (S0_BVD1_STSCHG - SA1111_IRQ(32))) |
(1 << (S1_BVD1_STSCHG - SA1111_IRQ(32)));
return_val+=request_irq(S0_CD_VALID, init->handler, SA_INTERRUPT,
"GC Master PCMCIA (0) CD", NULL);
return_val+=request_irq(S1_CD_VALID, init->handler, SA_INTERRUPT,
"GC Master CF (1) CD", NULL);
return_val+=request_irq(S0_BVD1_STSCHG, init->handler, SA_INTERRUPT,
"GC Master PCMCIA (0) BVD1", NULL);
return_val+=request_irq(S1_BVD1_STSCHG, init->handler, SA_INTERRUPT,
"GC Master CF (1) BVD1", NULL);
MECR = 0x09430943; MECR = 0x09430943;
return (return_val<0) ? -1 : 2; return sa1111_pcmcia_init(init);
}
static int adsbitsy_pcmcia_shutdown(void)
{
free_irq(S0_CD_VALID, NULL);
free_irq(S1_CD_VALID, NULL);
free_irq(S0_BVD1_STSCHG, NULL);
free_irq(S1_BVD1_STSCHG, NULL);
INTPOL1 &= ~((1 << (S0_CD_VALID - SA1111_IRQ(32))) |
(1 << (S1_CD_VALID - SA1111_IRQ(32))) |
(1 << (S0_BVD1_STSCHG - SA1111_IRQ(32))) |
(1 << (S1_BVD1_STSCHG - SA1111_IRQ(32))));
return 0;
} }
static int adsbitsy_pcmcia_socket_state(struct pcmcia_state_array *state_array) static int
adsbitsy_pcmcia_configure_socket(const struct pcmcia_configure *conf)
{ {
unsigned long status; unsigned int pa_dwr_mask, pa_dwr_set;
int return_val=1; int ret;
if(state_array->size<2) return -1;
memset(state_array->state, 0,
(state_array->size)*sizeof(struct pcmcia_state));
status=PCSR;
state_array->state[0].detect=((status & PCSR_S0_DETECT)==0)?1:0;
state_array->state[0].ready=((status & PCSR_S0_READY)==0)?0:1;
state_array->state[0].bvd1=((status & PCSR_S0_BVD1)==0)?0:1;
state_array->state[0].bvd2=((status & PCSR_S0_BVD2)==0)?0:1;
state_array->state[0].wrprot=((status & PCSR_S0_WP)==0)?0:1;
state_array->state[0].vs_3v=((status & PCSR_S0_VS1)==0)?1:0;
state_array->state[0].vs_Xv=((status & PCSR_S0_VS2)==0)?1:0; switch (conf->sock) {
state_array->state[1].detect=((status & PCSR_S1_DETECT)==0)?1:0;
state_array->state[1].ready=((status & PCSR_S1_READY)==0)?0:1;
state_array->state[1].bvd1=((status & PCSR_S1_BVD1)==0)?0:1;
state_array->state[1].bvd2=((status & PCSR_S1_BVD2)==0)?0:1;
state_array->state[1].wrprot=((status & PCSR_S1_WP)==0)?0:1;
state_array->state[1].vs_3v=((status & PCSR_S1_VS1)==0)?1:0;
state_array->state[1].vs_Xv=((status & PCSR_S1_VS2)==0)?1:0;
return return_val;
}
static int adsbitsy_pcmcia_get_irq_info(struct pcmcia_irq_info *info)
{
switch(info->sock){
case 0: case 0:
info->irq=S0_READY_NINT; pa_dwr_mask = GPIO_GPIO0 | GPIO_GPIO1;
break;
case 1:
info->irq=S1_READY_NINT;
break;
switch (conf->vcc) {
default: default:
return -1; case 0: pa_dwr_set = GPIO_GPIO0 | GPIO_GPIO1; break;
case 33: pa_dwr_set = GPIO_GPIO1; break;
case 50: pa_dwr_set = GPIO_GPIO0; break;
} }
return 0;
}
static int adsbitsy_pcmcia_configure_socket(const struct pcmcia_configure *configure)
{
unsigned long pccr=PCCR, gpio=PA_DWR;
switch(configure->sock){
case 0:
switch(configure->vcc){
case 0:
pccr = (pccr & ~PCCR_S0_FLT);
gpio |= GPIO_GPIO0 | GPIO_GPIO1;
break;
case 33:
pccr = (pccr & ~PCCR_S0_PSE) | PCCR_S0_FLT | PCCR_S0_PWAITEN;
gpio &= ~(GPIO_GPIO0 | GPIO_GPIO1);
gpio &= ~GPIO_GPIO0;
break; break;
case 50: case 1:
pccr = (pccr | PCCR_S0_PSE | PCCR_S0_FLT | PCCR_S0_PWAITEN); pa_dwr_mask = GPIO_GPIO2 | GPIO_GPIO3;
gpio &= ~(GPIO_GPIO0 | GPIO_GPIO1);
gpio |= GPIO_GPIO0;
break;
switch (conf->vcc) {
default: default:
printk(KERN_ERR "%s(): unrecognized Vcc %u\n", __FUNCTION__, case 0: pa_dwr_set = 0; break;
configure->vcc); case 33: pa_dwr_set = GPIO_GPIO2; break;
return -1; case 50: pa_dwr_set = GPIO_GPIO3; break;
} }
pccr=(configure->reset)?(pccr | PCCR_S0_RST):(pccr & ~PCCR_S0_RST);
break;
case 1:
switch(configure->vcc){
case 0:
pccr = (pccr & ~PCCR_S1_FLT);
gpio &= ~(GPIO_GPIO2 | GPIO_GPIO3);
break;
case 33:
pccr = (pccr & ~PCCR_S1_PSE) | PCCR_S1_FLT | PCCR_S1_PWAITEN;
gpio &= ~(GPIO_GPIO2 | GPIO_GPIO3);
gpio |= GPIO_GPIO2;
break;
case 50:
pccr = (pccr | PCCR_S1_PSE | PCCR_S1_FLT | PCCR_S1_PWAITEN);
gpio &= ~(GPIO_GPIO2 | GPIO_GPIO3);
gpio |= GPIO_GPIO3;
break;
default: default:
printk(KERN_ERR "%s(): unrecognized Vcc %u\n", __FUNCTION__,
configure->vcc);
return -1; return -1;
} }
if(configure->vpp!=configure->vcc && configure->vpp!=0){ if (conf->vpp != conf->vcc && conf->vpp != 0) {
printk(KERN_ERR "%s(): CF slot cannot support Vpp %u\n", __FUNCTION__, printk(KERN_ERR "%s(): CF slot cannot support VPP %u\n",
configure->vpp); __FUNCTION__, conf->vpp);
return -1; return -1;
} }
pccr=(configure->reset)?(pccr | PCCR_S1_RST):(pccr & ~PCCR_S1_RST); ret = sa1111_pcmcia_configure_socket(conf);
if (ret == 0) {
break; unsigned long flags;
default: local_irq_save(flags);
return -1; PA_DWR = (PA_DWR & ~pa_dwr_mask) | pa_dwr_set;
local_irq_restore(flags);
} }
PCCR = pccr; return ret;
PA_DWR = gpio;
return 0;
} }
struct pcmcia_low_level adsbitsy_pcmcia_ops = { static struct pcmcia_low_level adsbitsy_pcmcia_ops = {
adsbitsy_pcmcia_init, init: adsbitsy_pcmcia_init,
adsbitsy_pcmcia_shutdown, shutdown: sa1111_pcmcia_shutdown,
adsbitsy_pcmcia_socket_state, socket_state: sa1111_pcmcia_socket_state,
adsbitsy_pcmcia_get_irq_info, get_irq_info: sa1111_pcmcia_get_irq_info,
adsbitsy_pcmcia_configure_socket configure_socket: adsbitsy_pcmcia_configure_socket,
socket_init: sa1111_pcmcia_socket_init,
socket_suspend: sa1111_pcmcia_socket_suspend,
}; };
int __init pcmcia_adsbitsy_init(void)
{
int ret = -ENODEV;
if (machine_is_adsbitsy())
ret = sa1100_register_pcmcia(&adsbitsy_pcmcia_ops);
return ret;
}
void __exit pcmcia_adsbitsy_exit(void)
{
sa1100_unregister_pcmcia(&adsbitsy_pcmcia_ops);
}
...@@ -4,113 +4,114 @@ ...@@ -4,113 +4,114 @@
* PCMCIA implementation routines for Assabet * PCMCIA implementation routines for Assabet
* *
*/ */
#include <linux/config.h>
#include <linux/kernel.h> #include <linux/kernel.h>
#include <linux/sched.h> #include <linux/sched.h>
#include <linux/init.h>
#include <asm/hardware.h> #include <asm/hardware.h>
#include <asm/irq.h> #include <asm/irq.h>
#include <asm/arch/pcmcia.h>
#include <asm/arch/assabet.h> #include <asm/arch/assabet.h>
static int assabet_pcmcia_init(struct pcmcia_init *init){ #include "sa1100_generic.h"
int irq, res;
/* Enable CF bus: */ static struct irqs {
ASSABET_BCR_clear(ASSABET_BCR_CF_BUS_OFF); int irq;
const char *str;
} irqs[] = {
{ ASSABET_IRQ_GPIO_CF_CD, "CF_CD" },
{ ASSABET_IRQ_GPIO_CF_BVD2, "CF_BVD2" },
{ ASSABET_IRQ_GPIO_CF_BVD1, "CF_BVD1" },
};
/* All those are inputs */ static int assabet_pcmcia_init(struct pcmcia_init *init)
GPDR &= ~(ASSABET_GPIO_CF_CD | ASSABET_GPIO_CF_BVD2 | ASSABET_GPIO_CF_BVD1 | ASSABET_GPIO_CF_IRQ); {
int i, res;
/* Set transition detect */ /* Set transition detect */
set_GPIO_IRQ_edge( ASSABET_GPIO_CF_CD|ASSABET_GPIO_CF_BVD2|ASSABET_GPIO_CF_BVD1, GPIO_BOTH_EDGES ); set_irq_type(ASSABET_IRQ_GPIO_CF_IRQ, IRQT_FALLING);
set_GPIO_IRQ_edge( ASSABET_GPIO_CF_IRQ, GPIO_FALLING_EDGE );
/* Register interrupts */ /* Register interrupts */
irq = ASSABET_IRQ_GPIO_CF_CD; for (i = 0; i < ARRAY_SIZE(irqs); i++) {
res = request_irq( irq, init->handler, SA_INTERRUPT, "CF_CD", NULL ); set_irq_type(irqs[i].irq, IRQT_NOEDGE);
if( res < 0 ) goto irq_err; res = request_irq(irqs[i].irq, init->handler, SA_INTERRUPT,
irq = ASSABET_IRQ_GPIO_CF_BVD2; irqs[i].str, NULL);
res = request_irq( irq, init->handler, SA_INTERRUPT, "CF_BVD2", NULL ); if (res)
if( res < 0 ) goto irq_err; goto irq_err;
irq = ASSABET_IRQ_GPIO_CF_BVD1; }
res = request_irq( irq, init->handler, SA_INTERRUPT, "CF_BVD1", NULL );
if( res < 0 ) goto irq_err;
/* There's only one slot, but it's "Slot 1": */ /* There's only one slot, but it's "Slot 1": */
return 2; return 2;
irq_err: irq_err:
printk( KERN_ERR "%s: Request for IRQ %u failed\n", __FUNCTION__, irq ); printk(KERN_ERR "%s: request for IRQ%d failed (%d)\n",
return -1; __FUNCTION__, irqs[i].irq, res);
while (i--)
free_irq(irqs[i].irq, NULL);
return res;
} }
/*
* Release all resources.
*/
static int assabet_pcmcia_shutdown(void) static int assabet_pcmcia_shutdown(void)
{ {
/* disable IRQs */ int i;
free_irq( ASSABET_IRQ_GPIO_CF_CD, NULL );
free_irq( ASSABET_IRQ_GPIO_CF_BVD2, NULL );
free_irq( ASSABET_IRQ_GPIO_CF_BVD1, NULL );
/* Disable CF bus: */ for (i = 0; i < ARRAY_SIZE(irqs); i++)
ASSABET_BCR_set(ASSABET_BCR_CF_BUS_OFF); free_irq(irqs[i].irq, NULL);
return 0; return 0;
} }
static int assabet_pcmcia_socket_state(struct pcmcia_state_array static int
*state_array){ assabet_pcmcia_socket_state(struct pcmcia_state_array *state_array)
{
unsigned long levels; unsigned long levels;
if(state_array->size<2) return -1; if (state_array->size < 2)
return -1;
memset(state_array->state, 0,
(state_array->size)*sizeof(struct pcmcia_state));
levels=GPLR;
state_array->state[1].detect=((levels & ASSABET_GPIO_CF_CD)==0)?1:0;
state_array->state[1].ready=(levels & ASSABET_GPIO_CF_IRQ)?1:0;
state_array->state[1].bvd1=(levels & ASSABET_GPIO_CF_BVD1)?1:0;
state_array->state[1].bvd2=(levels & ASSABET_GPIO_CF_BVD2)?1:0;
state_array->state[1].wrprot=0; /* Not available on Assabet. */
state_array->state[1].vs_3v=1; /* Can only apply 3.3V on Assabet. */ levels = GPLR;
state_array->state[1].vs_Xv=0; state_array->state[1].detect = (levels & ASSABET_GPIO_CF_CD) ? 0 : 1;
state_array->state[1].ready = (levels & ASSABET_GPIO_CF_IRQ) ? 1 : 0;
state_array->state[1].bvd1 = (levels & ASSABET_GPIO_CF_BVD1) ? 1 : 0;
state_array->state[1].bvd2 = (levels & ASSABET_GPIO_CF_BVD2) ? 1 : 0;
state_array->state[1].wrprot = 0; /* Not available on Assabet. */
state_array->state[1].vs_3v = 1; /* Can only apply 3.3V on Assabet. */
state_array->state[1].vs_Xv = 0;
return 1; return 1;
} }
static int assabet_pcmcia_get_irq_info(struct pcmcia_irq_info *info){ static int assabet_pcmcia_get_irq_info(struct pcmcia_irq_info *info)
{
if(info->sock>1) return -1; if (info->sock > 1)
return -1;
if(info->sock==1) if (info->sock == 1)
info->irq=ASSABET_IRQ_GPIO_CF_IRQ; info->irq = ASSABET_IRQ_GPIO_CF_IRQ;
return 0; return 0;
} }
static int assabet_pcmcia_configure_socket(const struct pcmcia_configure static int
*configure) assabet_pcmcia_configure_socket(const struct pcmcia_configure *configure)
{ {
unsigned long value, flags; unsigned int mask;
if(configure->sock>1) return -1;
if(configure->sock==0) return 0;
save_flags_cli(flags); if (configure->sock > 1)
return -1;
value = BCR_value; if (configure->sock == 0)
return 0;
switch(configure->vcc){ switch (configure->vcc) {
case 0: case 0:
value &= ~ASSABET_BCR_CF_PWR; mask = 0;
break; break;
case 50: case 50:
...@@ -118,32 +119,106 @@ static int assabet_pcmcia_configure_socket(const struct pcmcia_configure ...@@ -118,32 +119,106 @@ static int assabet_pcmcia_configure_socket(const struct pcmcia_configure
__FUNCTION__); __FUNCTION__);
case 33: /* Can only apply 3.3V to the CF slot. */ case 33: /* Can only apply 3.3V to the CF slot. */
value |= ASSABET_BCR_CF_PWR; mask = ASSABET_BCR_CF_PWR;
break; break;
default: default:
printk(KERN_ERR "%s(): unrecognized Vcc %u\n", __FUNCTION__, printk(KERN_ERR "%s(): unrecognized Vcc %u\n", __FUNCTION__,
configure->vcc); configure->vcc);
restore_flags(flags);
return -1; return -1;
} }
value = (configure->reset) ? (value | ASSABET_BCR_CF_RST) : (value & ~ASSABET_BCR_CF_RST);
/* Silently ignore Vpp, output enable, speaker enable. */ /* Silently ignore Vpp, output enable, speaker enable. */
ASSABET_BCR = BCR_value = value; if (configure->reset)
mask |= ASSABET_BCR_CF_RST;
restore_flags(flags); ASSABET_BCR_frob(ASSABET_BCR_CF_RST | ASSABET_BCR_CF_PWR, mask);
/*
* Handle suspend mode properly. This prevents a
* flood of IRQs from the CF device.
*/
if (configure->irq)
enable_irq(ASSABET_IRQ_GPIO_CF_IRQ);
else
disable_irq(ASSABET_IRQ_GPIO_CF_IRQ);
return 0; return 0;
} }
struct pcmcia_low_level assabet_pcmcia_ops = { /*
assabet_pcmcia_init, * Enable card status IRQs on (re-)initialisation. This can
assabet_pcmcia_shutdown, * be called at initialisation, power management event, or
assabet_pcmcia_socket_state, * pcmcia event.
assabet_pcmcia_get_irq_info, */
assabet_pcmcia_configure_socket static int assabet_pcmcia_socket_init(int sock)
{
int i;
if (sock == 1) {
/*
* Enable CF bus
*/
ASSABET_BCR_clear(ASSABET_BCR_CF_BUS_OFF);
for (i = 0; i < ARRAY_SIZE(irqs); i++)
set_irq_type(irqs[i].irq, IRQT_BOTHEDGE);
}
return 0;
}
/*
* Disable card status IRQs on suspend.
*/
static int assabet_pcmcia_socket_suspend(int sock)
{
int i;
if (sock == 1) {
for (i = 0; i < ARRAY_SIZE(irqs); i++)
set_irq_type(irqs[i].irq, IRQT_NOEDGE);
/*
* Tristate the CF bus signals. Also assert CF
* reset as per user guide page 4-11.
*/
ASSABET_BCR_set(ASSABET_BCR_CF_BUS_OFF | ASSABET_BCR_CF_RST);
}
return 0;
}
static struct pcmcia_low_level assabet_pcmcia_ops = {
init: assabet_pcmcia_init,
shutdown: assabet_pcmcia_shutdown,
socket_state: assabet_pcmcia_socket_state,
get_irq_info: assabet_pcmcia_get_irq_info,
configure_socket: assabet_pcmcia_configure_socket,
socket_init: assabet_pcmcia_socket_init,
socket_suspend: assabet_pcmcia_socket_suspend,
}; };
int __init pcmcia_assabet_init(void)
{
int ret = -ENODEV;
if (machine_is_assabet()) {
if (!machine_has_neponset())
ret = sa1100_register_pcmcia(&assabet_pcmcia_ops);
#ifndef CONFIG_ASSABET_NEPONSET
else
printk(KERN_ERR "Card Services disabled: missing "
"Neponset support\n");
#endif
}
return ret;
}
void __exit pcmcia_assabet_exit(void)
{
sa1100_unregister_pcmcia(&assabet_pcmcia_ops);
}
/*
* linux/drivers/pcmcia/sa1100_badge4.c
*
* BadgePAD 4 PCMCIA specific routines
*
* Christopher Hoover <ch@hpl.hp.com>
*
* Copyright (C) 2002 Hewlett-Packard Company
*
* 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/kernel.h>
#include <linux/sched.h>
#include <linux/init.h>
#include <asm/hardware.h>
#include <asm/arch/badge4.h>
#include <asm/hardware/sa1111.h>
#include "sa1100_generic.h"
#include "sa1111_generic.h"
/*
* BadgePAD 4 Details
*
* PCM Vcc:
*
* PCM Vcc on BadgePAD 4 can be jumpered for 3.3V (short pins 1 and 3
* on JP6) or 5V (short pins 3 and 5 on JP6). N.B., 5V supply rail
* is enabled by the SA-1110's BADGE4_GPIO_PCMEN5V (GPIO 24).
*
* PCM Vpp:
*
* PCM Vpp on BadgePAD 4 can be jumpered for 12V (short pins 2 and 4
* on JP6) or tied to PCM Vcc (short pins 4 and 6 on JP6). N.B., 12V
* operation requires that the power supply actually supply 12V.
*
* CF Vcc:
*
* CF Vcc on BadgePAD 4 can be jumpered either for 3.3V (short pins 1
* and 2 on JP10) or 5V (short pins 2 and 3 on JP10). The note above
* about the 5V supply rail applies.
*
* There's no way programmatically to determine how a given board is
* jumpered. This code assumes a default jumpering: 5V PCM Vcc (pins
* 3 and 5 shorted) and PCM Vpp = PCM Vcc (pins 4 and 6 shorted) and
* no jumpering for CF Vcc. If this isn't correct, Override these
* defaults with a pcmv setup argument: pcmv=<pcm vcc>,<pcm vpp>,<cf
* vcc>. E.g. pcmv=33,120,50 indicates 3.3V PCM Vcc, 12.0V PCM Vpp,
* and 5.0V CF Vcc.
*
*/
static int badge4_pcmvcc = 50;
static int badge4_pcmvpp = 50;
static int badge4_cfvcc = 0;
static int badge4_pcmcia_init(struct pcmcia_init *init)
{
printk(KERN_INFO __FUNCTION__
": badge4_pcmvcc=%d, badge4_pcmvpp=%d, badge4_cfvcc=%d\n",
badge4_pcmvcc, badge4_pcmvpp, badge4_cfvcc);
return sa1111_pcmcia_init(init);
}
static int badge4_pcmcia_shutdown(void)
{
int rc = sa1111_pcmcia_shutdown();
/* be sure to disable 5V use */
badge4_set_5V(BADGE4_5V_PCMCIA_SOCK0, 0);
badge4_set_5V(BADGE4_5V_PCMCIA_SOCK1, 0);
return rc;
}
static void complain_about_jumpering(const char *whom,
const char *supply,
int given, int wanted)
{
printk(KERN_ERR
"%s: %s %d.%dV wanted but board is jumpered for %s %d.%dV operation"
"; re-jumper the board and/or use pcmv=xx,xx,xx\n",
whom, supply,
wanted / 10, wanted % 10,
supply,
given / 10, given % 10);
}
static unsigned badge4_need_5V_bitmap = 0;
static int
badge4_pcmcia_configure_socket(const struct pcmcia_configure *conf)
{
int ret;
switch (conf->sock) {
case 0:
if ((conf->vcc != 0) &&
(conf->vcc != badge4_pcmvcc)) {
complain_about_jumpering(__FUNCTION__, "pcmvcc",
badge4_pcmvcc, conf->vcc);
return -1;
}
if ((conf->vpp != 0) &&
(conf->vpp != badge4_pcmvpp)) {
complain_about_jumpering(__FUNCTION__, "pcmvpp",
badge4_pcmvpp, conf->vpp);
return -1;
}
break;
case 1:
if ((conf->vcc != 0) &&
(conf->vcc != badge4_cfvcc)) {
complain_about_jumpering(__FUNCTION__, "cfvcc",
badge4_cfvcc, conf->vcc);
return -1;
}
break;
default:
return -1;
}
ret = sa1111_pcmcia_configure_socket(conf);
if (ret == 0) {
unsigned long flags;
int need5V;
local_irq_save(flags);
need5V = ((conf->vcc == 50) || (conf->vpp == 50));
badge4_set_5V(BADGE4_5V_PCMCIA_SOCK(conf->sock), need5V);
local_irq_restore(flags);
}
return 0;
}
static struct pcmcia_low_level badge4_pcmcia_ops = {
init: badge4_pcmcia_init,
shutdown: badge4_pcmcia_shutdown,
socket_state: sa1111_pcmcia_socket_state,
get_irq_info: sa1111_pcmcia_get_irq_info,
configure_socket: badge4_pcmcia_configure_socket,
socket_init: sa1111_pcmcia_socket_init,
socket_suspend: sa1111_pcmcia_socket_suspend,
};
int __init pcmcia_badge4_init(void)
{
int ret = -ENODEV;
if (machine_is_badge4())
ret = sa1100_register_pcmcia(&badge4_pcmcia_ops);
return ret;
}
void __exit pcmcia_badge4_exit(void)
{
sa1100_unregister_pcmcia(&badge4_pcmcia_ops);
}
static int __init pcmv_setup(char *s)
{
int v[4];
s = get_options(s, ARRAY_SIZE(v), v);
if (v[0] >= 1) badge4_pcmvcc = v[1];
if (v[0] >= 2) badge4_pcmvpp = v[2];
if (v[0] >= 3) badge4_cfvcc = v[3];
return 1;
}
__setup("pcmv=", pcmv_setup);
...@@ -5,45 +5,62 @@ ...@@ -5,45 +5,62 @@
* Based off the Assabet. * Based off the Assabet.
* *
*/ */
#include <linux/config.h>
#include <linux/kernel.h> #include <linux/kernel.h>
#include <linux/sched.h> #include <linux/sched.h>
#include <linux/init.h>
#include <asm/hardware.h> #include <asm/hardware.h>
#include <asm/irq.h> #include <asm/irq.h>
#include <asm/arch/pcmcia.h> #include "sa1100_generic.h"
#ifdef CONFIG_SA1100_CERF_CPLD
#define CERF_SOCKET 0
#else
#define CERF_SOCKET 1
#endif
static int cerf_pcmcia_init(struct pcmcia_init *init){ static struct irqs {
int irq, res; int irq;
const char *str;
} irqs[] = {
{ IRQ_GPIO_CF_CD, "CF_CD" },
{ IRQ_GPIO_CF_BVD2, "CF_BVD2" },
{ IRQ_GPIO_CF_BVD1, "CF_BVD1" }
};
GPDR &= ~(GPIO_CF_CD | GPIO_CF_BVD2 | GPIO_CF_BVD1 | GPIO_CF_IRQ); static int cerf_pcmcia_init(struct pcmcia_init *init)
GPDR |= (GPIO_CF_RESET); {
int i, res;
set_GPIO_IRQ_edge( GPIO_CF_CD|GPIO_CF_BVD2|GPIO_CF_BVD1, GPIO_BOTH_EDGES ); set_irq_type(IRQ_GPIO_CF_IRQ, IRQT_FALLING);
set_GPIO_IRQ_edge( GPIO_CF_IRQ, GPIO_FALLING_EDGE );
irq = IRQ_GPIO_CF_CD; for (i = 0; i < ARRAY_SIZE(irqs); i++) {
res = request_irq( irq, init->handler, SA_INTERRUPT, "CF_CD", NULL ); set_irq_type(irqs[i].irq, IRQT_NOEDGE);
if( res < 0 ) goto irq_err; res = request_irq(irqs[i].irq, init->handler, SA_INTERRUPT,
irq = IRQ_GPIO_CF_BVD2; irqs[i].str, NULL);
res = request_irq( irq, init->handler, SA_INTERRUPT, "CF_BVD2", NULL ); if (res)
if( res < 0 ) goto irq_err; goto irq_err;
irq = IRQ_GPIO_CF_BVD1; }
res = request_irq( irq, init->handler, SA_INTERRUPT, "CF_BVD1", NULL );
if( res < 0 ) goto irq_err;
return 2; return 2;
irq_err: irq_err:
printk( KERN_ERR "%s: Request for IRQ %lu failed\n", __FUNCTION__, irq ); printk(KERN_ERR "%s: request for IRQ%d failed (%d)\n",
return -1; __FUNCTION__, irqs[i].irq, res);
while (i--)
free_irq(irqs[i].irq, NULL);
return res;
} }
static int cerf_pcmcia_shutdown(void) static int cerf_pcmcia_shutdown(void)
{ {
free_irq( IRQ_GPIO_CF_CD, NULL ); int i;
free_irq( IRQ_GPIO_CF_BVD2, NULL );
free_irq( IRQ_GPIO_CF_BVD1, NULL ); for (i = 0; i < ARRAY_SIZE(irqs); i++)
free_irq(irqs[i].irq, NULL);
return 0; return 0;
} }
...@@ -51,31 +68,18 @@ static int cerf_pcmcia_shutdown(void) ...@@ -51,31 +68,18 @@ static int cerf_pcmcia_shutdown(void)
static int cerf_pcmcia_socket_state(struct pcmcia_state_array static int cerf_pcmcia_socket_state(struct pcmcia_state_array
*state_array){ *state_array){
unsigned long levels; unsigned long levels;
#ifdef CONFIG_SA1100_CERF_CPLD int i = CERF_SOCKET;
int i = 0;
#else
int i = 1;
#endif
if(state_array->size<2) return -1; if(state_array->size<2) return -1;
memset(state_array->state, 0,
(state_array->size)*sizeof(struct pcmcia_state));
levels=GPLR; levels=GPLR;
state_array->state[i].detect=((levels & GPIO_CF_CD)==0)?1:0; state_array->state[i].detect=((levels & GPIO_CF_CD)==0)?1:0;
state_array->state[i].ready=(levels & GPIO_CF_IRQ)?1:0; state_array->state[i].ready=(levels & GPIO_CF_IRQ)?1:0;
state_array->state[i].bvd1=(levels & GPIO_CF_BVD1)?1:0; state_array->state[i].bvd1=(levels & GPIO_CF_BVD1)?1:0;
state_array->state[i].bvd2=(levels & GPIO_CF_BVD2)?1:0; state_array->state[i].bvd2=(levels & GPIO_CF_BVD2)?1:0;
state_array->state[i].wrprot=0; state_array->state[i].wrprot=0;
state_array->state[i].vs_3v=1; state_array->state[i].vs_3v=1;
state_array->state[i].vs_Xv=0; state_array->state[i].vs_Xv=0;
return 1; return 1;
...@@ -85,11 +89,7 @@ static int cerf_pcmcia_get_irq_info(struct pcmcia_irq_info *info){ ...@@ -85,11 +89,7 @@ static int cerf_pcmcia_get_irq_info(struct pcmcia_irq_info *info){
if(info->sock>1) return -1; if(info->sock>1) return -1;
#ifdef CONFIG_SA1100_CERF_CPLD if (info->sock == CERF_SOCKET)
if(info->sock==0)
#else
if(info->sock==1)
#endif
info->irq=IRQ_GPIO_CF_IRQ; info->irq=IRQ_GPIO_CF_IRQ;
return 0; return 0;
...@@ -98,20 +98,12 @@ static int cerf_pcmcia_get_irq_info(struct pcmcia_irq_info *info){ ...@@ -98,20 +98,12 @@ static int cerf_pcmcia_get_irq_info(struct pcmcia_irq_info *info){
static int cerf_pcmcia_configure_socket(const struct pcmcia_configure static int cerf_pcmcia_configure_socket(const struct pcmcia_configure
*configure) *configure)
{ {
unsigned long flags;
if(configure->sock>1) if(configure->sock>1)
return -1; return -1;
#ifdef CONFIG_SA1100_CERF_CPLD if (configure->sock != CERF_SOCKET)
if(configure->sock==1)
#else
if(configure->sock==0)
#endif
return 0; return 0;
save_flags_cli(flags);
switch(configure->vcc){ switch(configure->vcc){
case 0: case 0:
break; break;
...@@ -119,43 +111,76 @@ static int cerf_pcmcia_configure_socket(const struct pcmcia_configure ...@@ -119,43 +111,76 @@ static int cerf_pcmcia_configure_socket(const struct pcmcia_configure
case 50: case 50:
case 33: case 33:
#ifdef CONFIG_SA1100_CERF_CPLD #ifdef CONFIG_SA1100_CERF_CPLD
GPDR |= GPIO_PWR_SHUTDOWN; GPCR = GPIO_PWR_SHUTDOWN;
GPCR |= GPIO_PWR_SHUTDOWN;
#endif #endif
break; break;
default: default:
printk(KERN_ERR "%s(): unrecognized Vcc %u\n", __FUNCTION__, printk(KERN_ERR "%s(): unrecognized Vcc %u\n", __FUNCTION__,
configure->vcc); configure->vcc);
restore_flags(flags);
return -1; return -1;
} }
if(configure->reset) if(configure->reset)
{ {
#ifdef CONFIG_SA1100_CERF_CPLD #ifdef CONFIG_SA1100_CERF_CPLD
GPDR |= GPIO_CF_RESET; GPSR = GPIO_CF_RESET;
GPSR |= GPIO_CF_RESET;
#endif #endif
} }
else else
{ {
#ifdef CONFIG_SA1100_CERF_CPLD #ifdef CONFIG_SA1100_CERF_CPLD
GPDR |= GPIO_CF_RESET; GPCR = GPIO_CF_RESET;
GPCR |= GPIO_CF_RESET;
#endif #endif
} }
restore_flags(flags); return 0;
}
static int cerf_pcmcia_socket_init(int sock)
{
int i;
if (sock == CERF_SOCKET)
for (i = 0; i < ARRAY_SIZE(irqs); i++)
set_irq_type(irqs[i].irq, IRQT_BOTHEDGE);
return 0; return 0;
} }
struct pcmcia_low_level cerf_pcmcia_ops = { static int cerf_pcmcia_socket_suspend(int sock)
cerf_pcmcia_init, {
cerf_pcmcia_shutdown, int i;
cerf_pcmcia_socket_state,
cerf_pcmcia_get_irq_info, if (sock == CERF_SOCKET)
cerf_pcmcia_configure_socket for (i = 0; i < ARRAY_SIZE(irqs); i++)
set_irq_type(irqs[i].irq, IRQT_NOEDGE);
return 0;
}
static struct pcmcia_low_level cerf_pcmcia_ops = {
init: cerf_pcmcia_init,
shutdown: cerf_pcmcia_shutdown,
socket_state: cerf_pcmcia_socket_state,
get_irq_info: cerf_pcmcia_get_irq_info,
configure_socket: cerf_pcmcia_configure_socket,
socket_init: cerf_pcmcia_socket_init,
socket_suspend: cerf_pcmcia_socket_suspend,
}; };
int __init pcmcia_cerf_init(void)
{
int ret = -ENODEV;
if (machine_is_cerf())
ret = sa1100_register_pcmcia(&cerf_pcmcia_ops);
return ret;
}
void __exit pcmcia_cerf_exit(void)
{
sa1100_unregister_pcmcia(&cerf_pcmcia_ops);
}
...@@ -4,16 +4,25 @@ ...@@ -4,16 +4,25 @@
* PCMCIA implementation routines for Flexanet. * PCMCIA implementation routines for Flexanet.
* by Jordi Colomer, 09/05/2001 * by Jordi Colomer, 09/05/2001
* *
* Yet to be defined.
*/ */
#include <linux/kernel.h> #include <linux/kernel.h>
#include <linux/sched.h> #include <linux/sched.h>
#include <linux/init.h>
#include <asm/hardware.h> #include <asm/hardware.h>
#include <asm/irq.h> #include <asm/irq.h>
#include <asm/arch/pcmcia.h> #include "sa1100_generic.h"
static struct {
int irq;
const char *name;
} irqs[] = {
{ IRQ_GPIO_CF1_CD, "CF1_CD" },
{ IRQ_GPIO_CF1_BVD1, "CF1_BVD1" },
{ IRQ_GPIO_CF2_CD, "CF2_CD" },
{ IRQ_GPIO_CF2_BVD1, "CF2_BVD1" }
};
/* /*
* Socket initialization. * Socket initialization.
...@@ -22,9 +31,37 @@ ...@@ -22,9 +31,37 @@
* Must return the number of slots. * Must return the number of slots.
* *
*/ */
static int flexanet_pcmcia_init(struct pcmcia_init *init){ static int flexanet_pcmcia_init(struct pcmcia_init *init)
{
return 0; int i, res;
/* Configure the GPIOs as inputs (BVD2 is not implemented) */
GPDR &= ~(GPIO_CF1_NCD | GPIO_CF1_BVD1 | GPIO_CF1_IRQ |
GPIO_CF2_NCD | GPIO_CF2_BVD1 | GPIO_CF2_IRQ );
/* Set IRQ edge */
set_irq_type(IRQ_GPIO_CF1_IRQ, IRQT_FALLING);
set_irq_type(IRQ_GPIO_CF2_IRQ, IRQT_FALLING);
/* Register the socket interrupts (not the card interrupts) */
for (i = 0; i < ARRAY_SIZE(irqs); i++) {
set_irq_type(irqs[i].irq, IRQT_NOEDGE);
res = request_irq(irqs[i].irq, init->handler, SA_INTERRUPT,
irqs[i].name, NULL);
if (res < 0)
break;
}
/* If we failed, then free all interrupts requested thus far. */
if (res < 0) {
printk(KERN_ERR "%s: request for IRQ%d failed: %d\n",
__FUNCTION__, irqs[i].irq, res);
while (i--)
free_irq(irqs[i].irq, NULL);
return res;
}
return 2;
} }
...@@ -34,6 +71,12 @@ static int flexanet_pcmcia_init(struct pcmcia_init *init){ ...@@ -34,6 +71,12 @@ static int flexanet_pcmcia_init(struct pcmcia_init *init){
*/ */
static int flexanet_pcmcia_shutdown(void) static int flexanet_pcmcia_shutdown(void)
{ {
int i;
/* disable IRQs */
for (i = 0; i < ARRAY_SIZE(irqs); i++)
free_irq(irqs[i].irq, NULL);
return 0; return 0;
} }
...@@ -46,7 +89,33 @@ static int flexanet_pcmcia_shutdown(void) ...@@ -46,7 +89,33 @@ static int flexanet_pcmcia_shutdown(void)
*/ */
static int flexanet_pcmcia_socket_state(struct pcmcia_state_array static int flexanet_pcmcia_socket_state(struct pcmcia_state_array
*state_array){ *state_array){
unsigned long levels;
if (state_array->size < 2)
return -1; return -1;
/* Sense the GPIOs, asynchronously */
levels = GPLR;
/* Socket 0 */
state_array->state[0].detect = ((levels & GPIO_CF1_NCD)==0)?1:0;
state_array->state[0].ready = (levels & GPIO_CF1_IRQ)?1:0;
state_array->state[0].bvd1 = (levels & GPIO_CF1_BVD1)?1:0;
state_array->state[0].bvd2 = 1;
state_array->state[0].wrprot = 0;
state_array->state[0].vs_3v = 1;
state_array->state[0].vs_Xv = 0;
/* Socket 1 */
state_array->state[1].detect = ((levels & GPIO_CF2_NCD)==0)?1:0;
state_array->state[1].ready = (levels & GPIO_CF2_IRQ)?1:0;
state_array->state[1].bvd1 = (levels & GPIO_CF2_BVD1)?1:0;
state_array->state[1].bvd2 = 1;
state_array->state[1].wrprot = 0;
state_array->state[1].vs_3v = 1;
state_array->state[1].vs_Xv = 0;
return 1;
} }
...@@ -56,7 +125,16 @@ static int flexanet_pcmcia_socket_state(struct pcmcia_state_array ...@@ -56,7 +125,16 @@ static int flexanet_pcmcia_socket_state(struct pcmcia_state_array
*/ */
static int flexanet_pcmcia_get_irq_info(struct pcmcia_irq_info *info){ static int flexanet_pcmcia_get_irq_info(struct pcmcia_irq_info *info){
/* check the socket index */
if (info->sock > 1)
return -1; return -1;
if (info->sock == 0)
info->irq = IRQ_GPIO_CF1_IRQ;
else if (info->sock == 1)
info->irq = IRQ_GPIO_CF2_IRQ;
return 0;
} }
...@@ -66,19 +144,105 @@ static int flexanet_pcmcia_get_irq_info(struct pcmcia_irq_info *info){ ...@@ -66,19 +144,105 @@ static int flexanet_pcmcia_get_irq_info(struct pcmcia_irq_info *info){
static int flexanet_pcmcia_configure_socket(const struct pcmcia_configure static int flexanet_pcmcia_configure_socket(const struct pcmcia_configure
*configure) *configure)
{ {
unsigned long value, flags, mask;
if (configure->sock > 1)
return -1; return -1;
/* Ignore the VCC level since it is 3.3V and always on */
switch (configure->vcc)
{
case 0:
printk(KERN_WARNING "%s(): CS asked to power off.\n", __FUNCTION__);
break;
case 50:
printk(KERN_WARNING "%s(): CS asked for 5V, applying 3.3V...\n",
__FUNCTION__);
case 33:
break;
default:
printk(KERN_ERR "%s(): unrecognized Vcc %u\n", __FUNCTION__,
configure->vcc);
return -1;
}
/* Reset the slot(s) using the controls in the BCR */
mask = 0;
switch (configure->sock)
{
case 0 : mask = FHH_BCR_CF1_RST; break;
case 1 : mask = FHH_BCR_CF2_RST; break;
}
local_irq_save(flags);
value = flexanet_BCR;
value = (configure->reset) ? (value | mask) : (value & ~mask);
FHH_BCR = flexanet_BCR = value;
local_irq_restore(flags);
return 0;
} }
static int flexanet_pcmcia_socket_init(int sock)
{
if (sock == 0) {
set_irq_type(IRQ_GPIO_CF1_CD, IRQT_BOTHEDGE);
set_irq_type(IRQ_GPIO_CF1_BVD1, IRQT_BOTHEDGE);
} else if (sock == 1) {
set_irq_type(IRQ_GPIO_CF2_CD, IRQT_BOTHEDGE);
set_irq_type(IRQ_GPIO_CF2_BVD1, IRQT_BOTHEDGE);
}
return 0;
}
static int flexanet_pcmcia_socket_suspend(int sock)
{
if (sock == 0) {
set_irq_type(IRQ_GPIO_CF1_CD, IRQT_NOEDGE);
set_irq_type(IRQ_GPIO_CF1_BVD1, IRQT_NOEDGE);
} else if (sock == 1) {
set_irq_type(IRQ_GPIO_CF2_CD, IRQT_NOEDGE);
set_irq_type(IRQ_GPIO_CF2_BVD1, IRQT_NOEDGE);
}
return 0;
}
/* /*
* The set of socket operations * The set of socket operations
* *
*/ */
struct pcmcia_low_level flexanet_pcmcia_ops = { static struct pcmcia_low_level flexanet_pcmcia_ops = {
flexanet_pcmcia_init, init: flexanet_pcmcia_init,
flexanet_pcmcia_shutdown, shutdown: flexanet_pcmcia_shutdown,
flexanet_pcmcia_socket_state, socket_state: flexanet_pcmcia_socket_state,
flexanet_pcmcia_get_irq_info, get_irq_info: flexanet_pcmcia_get_irq_info,
flexanet_pcmcia_configure_socket configure_socket: flexanet_pcmcia_configure_socket,
socket_init: flexanet_pcmcia_socket_init,
socket_suspend: flexanet_pcmcia_socket_suspend,
}; };
int __init pcmcia_flexanet_init(void)
{
int ret = -ENODEV;
if (machine_is_flexanet())
ret = sa1100_register_pcmcia(&flexanet_pcmcia_ops);
return ret;
}
void __exit pcmcia_flexanet_exit(void)
{
sa1100_unregister_pcmcia(&flexanet_pcmcia_ops);
}
...@@ -6,14 +6,22 @@ ...@@ -6,14 +6,22 @@
*/ */
#include <linux/kernel.h> #include <linux/kernel.h>
#include <linux/sched.h> #include <linux/sched.h>
#include <linux/init.h>
#include <asm/hardware.h> #include <asm/hardware.h>
#include <asm/irq.h> #include <asm/irq.h>
#include <asm/arch/pcmcia.h> #include "sa1100_generic.h"
static struct irqs {
int irq;
const char *str;
} irqs[] = {
{ IRQ_GPIO_FREEBIRD_CF_CD, "CF_CD" },
{ IRQ_GPIO_FREEBIRD_CF_BVD, "CF_BVD1" },
};
static int freebird_pcmcia_init(struct pcmcia_init *init){ static int freebird_pcmcia_init(struct pcmcia_init *init){
int irq, res; int i, res;
/* Enable Linkup CF card */ /* Enable Linkup CF card */
LINKUP_PRC = 0xc0; LINKUP_PRC = 0xc0;
...@@ -26,37 +34,38 @@ static int freebird_pcmcia_init(struct pcmcia_init *init){ ...@@ -26,37 +34,38 @@ static int freebird_pcmcia_init(struct pcmcia_init *init){
mdelay(100); mdelay(100);
LINKUP_PRC = 0xc0; LINKUP_PRC = 0xc0;
/* All those are inputs */
////GPDR &= ~(GPIO_CF_CD | GPIO_CF_BVD2 | GPIO_CF_BVD1 | GPIO_CF_IRQ);
GPDR &= ~(GPIO_FREEBIRD_CF_CD | GPIO_FREEBIRD_CF_IRQ | GPIO_FREEBIRD_CF_BVD);
/* Set transition detect */ /* Set transition detect */
//set_GPIO_IRQ_edge( GPIO_CF_CD|GPIO_CF_BVD2|GPIO_CF_BVD1, GPIO_BOTH_EDGES ); set_irq_type(IRQ_GPIO_FREEBIRD_CF_IRQ, IRQT_FALLING);
//set_GPIO_IRQ_edge( GPIO_CF_IRQ, GPIO_FALLING_EDGE );
set_GPIO_IRQ_edge(GPIO_FREEBIRD_CF_CD|GPIO_FREEBIRD_CF_BVD,GPIO_BOTH_EDGES);
set_GPIO_IRQ_edge(GPIO_FREEBIRD_CF_IRQ, GPIO_FALLING_EDGE);
/* Register interrupts */ /* Register interrupts */
irq = IRQ_GPIO_FREEBIRD_CF_CD; for (i = 0; i < ARRAY_SIZE(irqs); i++) {
res = request_irq( irq, init->handler, SA_INTERRUPT, "CF_CD", NULL ); set_irq_type(irqs[i].irq, IRQT_NOEDGE);
if( res < 0 ) goto irq_err; res = request_irq(irqs[i].irq, init->handler, SA_INTERRUPT,
irq = IRQ_GPIO_FREEBIRD_CF_BVD; irqs[i].str, NULL);
res = request_irq( irq, init->handler, SA_INTERRUPT, "CF_BVD1", NULL ); if (res)
if( res < 0 ) goto irq_err; goto irq_err;
}
/* There's only one slot, but it's "Slot 1": */ /* There's only one slot, but it's "Slot 1": */
return 2; return 2;
irq_err: irq_err:
printk( KERN_ERR "%s: Request for IRQ %lu failed\n", __FUNCTION__, irq ); printk(KERN_ERR "%s: request for IRQ%d failed (%d)\n",
return -1; __FUNCTION__, irqs[i].irq, res);
while (i--)
free_irq(irqs[i].irq, NULL);
return res;
} }
static int freebird_pcmcia_shutdown(void) static int freebird_pcmcia_shutdown(void)
{ {
int i;
/* disable IRQs */ /* disable IRQs */
free_irq( IRQ_GPIO_FREEBIRD_CF_CD, NULL ); for (i = 0; i < ARRAY_SIZE(irqs); i++)
free_irq( IRQ_GPIO_FREEBIRD_CF_BVD, NULL ); free_irq(irqs[i].irq, NULL);
/* Disable CF card */ /* Disable CF card */
LINKUP_PRC = 0x40; /* SSP=1 SOE=0 */ LINKUP_PRC = 0x40; /* SSP=1 SOE=0 */
...@@ -75,7 +84,7 @@ static int freebird_pcmcia_socket_state(struct pcmcia_state_array ...@@ -75,7 +84,7 @@ static int freebird_pcmcia_socket_state(struct pcmcia_state_array
(state_array->size)*sizeof(struct pcmcia_state)); (state_array->size)*sizeof(struct pcmcia_state));
levels = LINKUP_PRS; levels = LINKUP_PRS;
//printk("LINKUP_PRS=%x \n",levels); //printk("LINKUP_PRS=%x\n",levels);
state_array->state[0].detect= state_array->state[0].detect=
((levels & (LINKUP_CD1 | LINKUP_CD2))==0)?1:0; ((levels & (LINKUP_CD1 | LINKUP_CD2))==0)?1:0;
...@@ -114,7 +123,7 @@ static int freebird_pcmcia_configure_socket(const struct pcmcia_configure ...@@ -114,7 +123,7 @@ static int freebird_pcmcia_configure_socket(const struct pcmcia_configure
if(configure->sock==1) return 0; if(configure->sock==1) return 0;
save_flags_cli(flags); local_irq_save(flags);
value = 0xc0; /* SSP=1 SOE=1 CFE=1 */ value = 0xc0; /* SSP=1 SOE=1 CFE=1 */
...@@ -134,7 +143,7 @@ static int freebird_pcmcia_configure_socket(const struct pcmcia_configure ...@@ -134,7 +143,7 @@ static int freebird_pcmcia_configure_socket(const struct pcmcia_configure
default: default:
printk(KERN_ERR "%s(): unrecognized Vcc %u\n", __FUNCTION__, printk(KERN_ERR "%s(): unrecognized Vcc %u\n", __FUNCTION__,
configure->vcc); configure->vcc);
restore_flags(flags); local_irq_restore(flags);
return -1; return -1;
} }
...@@ -145,16 +154,51 @@ static int freebird_pcmcia_configure_socket(const struct pcmcia_configure ...@@ -145,16 +154,51 @@ static int freebird_pcmcia_configure_socket(const struct pcmcia_configure
LINKUP_PRC = value; LINKUP_PRC = value;
//printk("LINKUP_PRC=%x\n",value); //printk("LINKUP_PRC=%x\n",value);
restore_flags(flags); local_irq_restore(flags);
return 0;
}
static int freebird_pcmcia_socket_init(int sock)
{
if (sock == 1) {
set_irq_type(IRQ_GPIO_FREEBIRD_CF_CD, IRQT_BOTHEDGE);
set_irq_type(IRQ_GPIO_FREEBIRD_CF_BVD, IRQT_BOTHEDGE);
}
return 0;
}
static int freebird_pcmcia_socket_suspend(int sock)
{
if (sock == 1) {
set_irq_type(IRQ_GPIO_FREEBIRD_CF_CD, IRQT_NOEDGE);
set_irq_type(IRQ_GPIO_FREEBIRD_CF_BVD, IRQT_NOEDGE);
}
return 0; return 0;
} }
struct pcmcia_low_level freebird_pcmcia_ops = { static struct pcmcia_low_level freebird_pcmcia_ops = {
freebird_pcmcia_init, init: freebird_pcmcia_init,
freebird_pcmcia_shutdown, shutdown: freebird_pcmcia_shutdown,
freebird_pcmcia_socket_state, socket_state: freebird_pcmcia_socket_state,
freebird_pcmcia_get_irq_info, get_irq_info: freebird_pcmcia_get_irq_info,
freebird_pcmcia_configure_socket configure_socket: freebird_pcmcia_configure_socket,
socket_init: freebird_pcmcia_socket_init,
socket_suspend: freebird_pcmcia_socket_suspend,
}; };
int __init pcmcia_freebird_init(void)
{
int ret = -ENODEV;
if (machine_is_freebird())
ret = sa1100_register_pcmcia(&freebird_pcmcia_ops);
return ret;
}
void __exit pcmcia_freebird_exit(void)
{
sa1100_unregister_pcmcia(&freebird_pcmcia_ops);
}
...@@ -29,6 +29,10 @@ ...@@ -29,6 +29,10 @@
file under either the MPL or the GPL. file under either the MPL or the GPL.
======================================================================*/ ======================================================================*/
/*
* Please see linux/Documentation/arm/SA1100/PCMCIA for more information
* on the low-level kernel interface.
*/
#include <linux/module.h> #include <linux/module.h>
#include <linux/init.h> #include <linux/init.h>
...@@ -43,6 +47,7 @@ ...@@ -43,6 +47,7 @@
#include <linux/notifier.h> #include <linux/notifier.h>
#include <linux/proc_fs.h> #include <linux/proc_fs.h>
#include <linux/version.h> #include <linux/version.h>
#include <linux/cpufreq.h>
#include <pcmcia/version.h> #include <pcmcia/version.h>
#include <pcmcia/cs_types.h> #include <pcmcia/cs_types.h>
...@@ -62,338 +67,94 @@ ...@@ -62,338 +67,94 @@
static int pc_debug; static int pc_debug;
#endif #endif
MODULE_AUTHOR("John Dorsey <john+@cs.cmu.edu>");
MODULE_DESCRIPTION("Linux PCMCIA Card Services: SA-1100 Socket Controller");
/* This structure maintains housekeeping state for each socket, such /* This structure maintains housekeeping state for each socket, such
* as the last known values of the card detect pins, or the Card Services * as the last known values of the card detect pins, or the Card Services
* callback value associated with the socket: * callback value associated with the socket:
*/ */
static struct sa1100_pcmcia_socket
sa1100_pcmcia_socket[SA1100_PCMCIA_MAX_SOCK];
static int sa1100_pcmcia_socket_count; static int sa1100_pcmcia_socket_count;
static struct sa1100_pcmcia_socket sa1100_pcmcia_socket[SA1100_PCMCIA_MAX_SOCK];
#define PCMCIA_SOCKET(x) (sa1100_pcmcia_socket + (x))
/* Returned by the low-level PCMCIA interface: */ /* Returned by the low-level PCMCIA interface: */
static struct pcmcia_low_level *pcmcia_low_level; static struct pcmcia_low_level *pcmcia_low_level;
/* Event poll timer structure */
static struct timer_list poll_timer; static struct timer_list poll_timer;
/* Prototypes for routines which are used internally: */
static int sa1100_pcmcia_driver_init(void);
static void sa1100_pcmcia_driver_shutdown(void);
static void sa1100_pcmcia_task_handler(void *data);
static void sa1100_pcmcia_poll_event(unsigned long data);
static void sa1100_pcmcia_interrupt(int irq, void *dev,
struct pt_regs *regs);
static struct tq_struct sa1100_pcmcia_task; static struct tq_struct sa1100_pcmcia_task;
#ifdef CONFIG_PROC_FS /*
static int sa1100_pcmcia_proc_status(char *buf, char **start, off_t pos, * sa1100_pcmcia_state_to_config
int count, int *eof, void *data); * ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
#endif
/* Prototypes for operations which are exported to the
* new-and-impr^H^H^H^H^H^H^H^H^H^H in-kernel PCMCIA core:
*/
static int sa1100_pcmcia_init(unsigned int sock);
static int sa1100_pcmcia_suspend(unsigned int sock);
static int sa1100_pcmcia_register_callback(unsigned int sock,
void (*handler)(void *,
unsigned int),
void *info);
static int sa1100_pcmcia_inquire_socket(unsigned int sock,
socket_cap_t *cap);
static int sa1100_pcmcia_get_status(unsigned int sock, u_int *value);
static int sa1100_pcmcia_get_socket(unsigned int sock,
socket_state_t *state);
static int sa1100_pcmcia_set_socket(unsigned int sock,
socket_state_t *state);
static int sa1100_pcmcia_get_io_map(unsigned int sock,
struct pccard_io_map *io);
static int sa1100_pcmcia_set_io_map(unsigned int sock,
struct pccard_io_map *io);
static int sa1100_pcmcia_get_mem_map(unsigned int sock,
struct pccard_mem_map *mem);
static int sa1100_pcmcia_set_mem_map(unsigned int sock,
struct pccard_mem_map *mem);
#ifdef CONFIG_PROC_FS
static void sa1100_pcmcia_proc_setup(unsigned int sock,
struct proc_dir_entry *base);
#endif
static struct pccard_operations sa1100_pcmcia_operations = {
sa1100_pcmcia_init,
sa1100_pcmcia_suspend,
sa1100_pcmcia_register_callback,
sa1100_pcmcia_inquire_socket,
sa1100_pcmcia_get_status,
sa1100_pcmcia_get_socket,
sa1100_pcmcia_set_socket,
sa1100_pcmcia_get_io_map,
sa1100_pcmcia_set_io_map,
sa1100_pcmcia_get_mem_map,
sa1100_pcmcia_set_mem_map,
#ifdef CONFIG_PROC_FS
sa1100_pcmcia_proc_setup
#endif
};
#ifdef CONFIG_CPU_FREQ
/* forward declaration */
static struct notifier_block sa1100_pcmcia_notifier_block;
#endif
/* sa1100_pcmcia_driver_init()
* ^^^^^^^^^^^^^^^^^^^^^^^^^^^
* *
* This routine performs a basic sanity check to ensure that this * Convert PCMCIA socket state to our socket configure structure.
* kernel has been built with the appropriate board-specific low-level
* PCMCIA support, performs low-level PCMCIA initialization, registers
* this socket driver with Card Services, and then spawns the daemon
* thread which is the real workhorse of the socket driver.
*
* Please see linux/Documentation/arm/SA1100/PCMCIA for more information
* on the low-level kernel interface.
*
* Returns: 0 on success, -1 on error
*/
static int __init sa1100_pcmcia_driver_init(void){
servinfo_t info;
struct pcmcia_init pcmcia_init;
struct pcmcia_state state[SA1100_PCMCIA_MAX_SOCK];
struct pcmcia_state_array state_array;
unsigned int i, clock;
unsigned long mecr;
printk(KERN_INFO "SA-1100 PCMCIA (CS release %s)\n", CS_RELEASE);
CardServices(GetCardServicesInfo, &info);
if(info.Revision!=CS_RELEASE_CODE){
printk(KERN_ERR "Card Services release codes do not match\n");
return -1;
}
if(machine_is_assabet()){
#ifdef CONFIG_SA1100_ASSABET
if(machine_has_neponset()){
#ifdef CONFIG_ASSABET_NEPONSET
pcmcia_low_level=&neponset_pcmcia_ops;
#else
printk(KERN_ERR "Card Services disabled: missing Neponset support\n");
return -1;
#endif
}else{
pcmcia_low_level=&assabet_pcmcia_ops;
}
#endif
} else if (machine_is_freebird()) {
#ifdef CONFIG_SA1100_FREEBIRD
pcmcia_low_level = &freebird_pcmcia_ops;
#endif
} else if (machine_is_h3600()) {
#ifdef CONFIG_SA1100_H3600
pcmcia_low_level = &h3600_pcmcia_ops;
#endif
} else if (machine_is_cerf()) {
#ifdef CONFIG_SA1100_CERF
pcmcia_low_level = &cerf_pcmcia_ops;
#endif
} else if (machine_is_graphicsclient()) {
#ifdef CONFIG_SA1100_GRAPHICSCLIENT
pcmcia_low_level = &gcplus_pcmcia_ops;
#endif
} else if (machine_is_xp860()) {
#ifdef CONFIG_SA1100_XP860
pcmcia_low_level = &xp860_pcmcia_ops;
#endif
} else if (machine_is_yopy()) {
#ifdef CONFIG_SA1100_YOPY
pcmcia_low_level = &yopy_pcmcia_ops;
#endif
} else if (machine_is_pangolin()) {
#ifdef CONFIG_SA1100_PANGOLIN
pcmcia_low_level = &pangolin_pcmcia_ops;
#endif
} else if (machine_is_jornada720()) {
#ifdef CONFIG_SA1100_JORNADA720
pcmcia_low_level = &jornada720_pcmcia_ops;
#endif
} else if(machine_is_pfs168()){
#ifdef CONFIG_SA1100_PFS168
pcmcia_low_level=&pfs168_pcmcia_ops;
#endif
} else if(machine_is_flexanet()){
#ifdef CONFIG_SA1100_FLEXANET
pcmcia_low_level=&flexanet_pcmcia_ops;
#endif
} else if(machine_is_simpad()){
#ifdef CONFIG_SA1100_SIMPAD
pcmcia_low_level=&simpad_pcmcia_ops;
#endif
} else if(machine_is_graphicsmaster()) {
#ifdef CONFIG_SA1100_GRAPHICSMASTER
pcmcia_low_level=&graphicsmaster_pcmcia_ops;
#endif
} else if(machine_is_adsbitsy()) {
#ifdef CONFIG_SA1100_ADSBITSY
pcmcia_low_level=&adsbitsy_pcmcia_ops;
#endif
} else if(machine_is_stork()) {
#ifdef CONFIG_SA1100_STORK
pcmcia_low_level=&stork_pcmcia_ops;
#endif
}
if (!pcmcia_low_level) {
printk(KERN_ERR "This hardware is not supported by the SA1100 Card Service driver\n");
return -ENODEV;
}
pcmcia_init.handler=sa1100_pcmcia_interrupt;
if((sa1100_pcmcia_socket_count=pcmcia_low_level->init(&pcmcia_init))<0){
printk(KERN_ERR "Unable to initialize kernel PCMCIA service.\n");
return -EIO;
}
state_array.size=sa1100_pcmcia_socket_count;
state_array.state=state;
if(pcmcia_low_level->socket_state(&state_array)<0){
printk(KERN_ERR "Unable to get PCMCIA status from kernel.\n");
return -EIO;
}
/* We initialize the MECR to default values here, because we are
* not guaranteed to see a SetIOMap operation at runtime.
*/
mecr=0;
clock = get_cclk_frequency() * 100;
for(i=0; i<sa1100_pcmcia_socket_count; ++i){
sa1100_pcmcia_socket[i].k_state=state[i];
/* This is an interim fix. Apparently, SetSocket is no longer
* called to initialize each socket (prior to the first detect
* event). For now, we'll just manually set up the mask.
*/
sa1100_pcmcia_socket[i].cs_state.csc_mask=SS_DETECT;
sa1100_pcmcia_socket[i].virt_io=(i==0)?PCMCIA_IO_0_BASE:PCMCIA_IO_1_BASE;
sa1100_pcmcia_socket[i].phys_attr=_PCMCIAAttr(i);
sa1100_pcmcia_socket[i].phys_mem=_PCMCIAMem(i);
MECR_FAST_SET(mecr, i, 0);
MECR_BSIO_SET(mecr, i,
sa1100_pcmcia_mecr_bs(SA1100_PCMCIA_IO_ACCESS, clock));
MECR_BSA_SET(mecr, i,
sa1100_pcmcia_mecr_bs(SA1100_PCMCIA_5V_MEM_ACCESS, clock));
MECR_BSM_SET(mecr, i,
sa1100_pcmcia_mecr_bs(SA1100_PCMCIA_5V_MEM_ACCESS, clock));
sa1100_pcmcia_socket[i].speed_io=SA1100_PCMCIA_IO_ACCESS;
sa1100_pcmcia_socket[i].speed_attr=SA1100_PCMCIA_5V_MEM_ACCESS;
sa1100_pcmcia_socket[i].speed_mem=SA1100_PCMCIA_5V_MEM_ACCESS;
}
MECR=mecr;
#ifdef CONFIG_CPU_FREQ
if(cpufreq_register_notifier(&sa1100_pcmcia_notifier_block) < 0){
printk(KERN_ERR "Unable to register CPU frequency change notifier\n");
return -ENXIO;
}
#endif
/* Only advertise as many sockets as we can detect: */
if(register_ss_entry(sa1100_pcmcia_socket_count,
&sa1100_pcmcia_operations)<0){
printk(KERN_ERR "Unable to register socket service routine\n");
return -ENXIO;
}
/* Start the event poll timer. It will reschedule by itself afterwards. */
sa1100_pcmcia_poll_event(0);
DEBUG(1, "sa1100: initialization complete\n");
return 0;
} /* sa1100_pcmcia_driver_init() */
module_init(sa1100_pcmcia_driver_init);
/* sa1100_pcmcia_driver_shutdown()
* ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
* Invokes the low-level kernel service to free IRQs associated with this
* socket controller and reset GPIO edge detection.
*/ */
static void __exit sa1100_pcmcia_driver_shutdown(void){ static struct pcmcia_configure
sa1100_pcmcia_state_to_config(unsigned int sock, socket_state_t *state)
del_timer_sync(&poll_timer); {
unregister_ss_entry(&sa1100_pcmcia_operations); struct pcmcia_configure conf;
#ifdef CONFIG_CPU_FREQ
cpufreq_unregister_notifier(&sa1100_pcmcia_notifier_block);
#endif
pcmcia_low_level->shutdown();
flush_scheduled_tasks();
DEBUG(1, "sa1100: shutdown complete\n"); conf.sock = sock;
conf.vcc = state->Vcc;
conf.vpp = state->Vpp;
conf.output = state->flags & SS_OUTPUT_ENA ? 1 : 0;
conf.speaker = state->flags & SS_SPKR_ENA ? 1 : 0;
conf.reset = state->flags & SS_RESET ? 1 : 0;
conf.irq = state->io_irq != 0;
return conf;
} }
module_exit(sa1100_pcmcia_driver_shutdown); /* sa1100_pcmcia_sock_init()
* ^^^^^^^^^^^^^^^^^^^^^^^^^
*
/* sa1100_pcmcia_init() * (Re-)Initialise the socket, turning on status interrupts
* ^^^^^^^^^^^^^^^^^^^^ * and PCMCIA bus. This must wait for power to stabilise
* We perform all of the interesting initialization tasks in * so that the card status signals report correctly.
* sa1100_pcmcia_driver_init().
* *
* Returns: 0 * Returns: 0
*/ */
static int sa1100_pcmcia_init(unsigned int sock){ static int sa1100_pcmcia_sock_init(unsigned int sock)
{
struct sa1100_pcmcia_socket *skt = PCMCIA_SOCKET(sock);
struct pcmcia_configure conf;
DEBUG(2, "%s(): initializing socket %u\n", __FUNCTION__, sock); DEBUG(2, "%s(): initializing socket %u\n", __FUNCTION__, sock);
return 0; skt->cs_state = dead_socket;
conf = sa1100_pcmcia_state_to_config(sock, &dead_socket);
pcmcia_low_level->configure_socket(&conf);
return pcmcia_low_level->socket_init(sock);
} }
/* sa1100_pcmcia_suspend() /*
* sa1100_pcmcia_suspend()
* ^^^^^^^^^^^^^^^^^^^^^^^ * ^^^^^^^^^^^^^^^^^^^^^^^
* We don't currently perform any actions on a suspend. *
* Remove power on the socket, disable IRQs from the card.
* Turn off status interrupts, and disable the PCMCIA bus.
* *
* Returns: 0 * Returns: 0
*/ */
static int sa1100_pcmcia_suspend(unsigned int sock) static int sa1100_pcmcia_suspend(unsigned int sock)
{ {
struct sa1100_pcmcia_socket *skt = PCMCIA_SOCKET(sock);
struct pcmcia_configure conf; struct pcmcia_configure conf;
int ret; int ret;
DEBUG(2, "%s(): suspending socket %u\n", __FUNCTION__, sock); DEBUG(2, "%s(): suspending socket %u\n", __FUNCTION__, sock);
conf.sock = sock; conf = sa1100_pcmcia_state_to_config(sock, &dead_socket);
conf.vcc = 0;
conf.vpp = 0;
conf.output = 0;
conf.speaker = 0;
conf.reset = 1;
ret = pcmcia_low_level->configure_socket(&conf); ret = pcmcia_low_level->configure_socket(&conf);
if (ret == 0) if (ret == 0) {
sa1100_pcmcia_socket[sock].cs_state = dead_socket; skt->cs_state = dead_socket;
ret = pcmcia_low_level->socket_suspend(sock);
}
return ret; return ret;
} }
...@@ -407,52 +168,50 @@ static int sa1100_pcmcia_suspend(unsigned int sock) ...@@ -407,52 +168,50 @@ static int sa1100_pcmcia_suspend(unsigned int sock)
* *
* Returns: an event mask for the given socket state. * Returns: an event mask for the given socket state.
*/ */
static inline unsigned sa1100_pcmcia_events(struct pcmcia_state *state, static inline unsigned int
sa1100_pcmcia_events(struct pcmcia_state *state,
struct pcmcia_state *prev_state, struct pcmcia_state *prev_state,
unsigned int mask, unsigned int mask, unsigned int flags)
unsigned int flags){ {
unsigned int events=0; unsigned int events = 0;
if(state->detect!=prev_state->detect){
DEBUG(2, "%s(): card detect value %u\n", __FUNCTION__, state->detect); if (state->detect != prev_state->detect) {
DEBUG(3, "%s(): card detect value %u\n", __FUNCTION__, state->detect);
events|=mask&SS_DETECT; events |= SS_DETECT;
} }
if(state->ready!=prev_state->ready){ if (state->ready != prev_state->ready) {
DEBUG(3, "%s(): card ready value %u\n", __FUNCTION__, state->ready);
DEBUG(2, "%s(): card ready value %u\n", __FUNCTION__, state->ready); events |= flags & SS_IOCARD ? 0 : SS_READY;
events|=mask&((flags&SS_IOCARD)?0:SS_READY);
} }
if(state->bvd1!=prev_state->bvd1){ if (state->bvd1 != prev_state->bvd1) {
DEBUG(3, "%s(): card BVD1 value %u\n", __FUNCTION__, state->bvd1);
DEBUG(2, "%s(): card BVD1 value %u\n", __FUNCTION__, state->bvd1);
events|=mask&(flags&SS_IOCARD)?SS_STSCHG:SS_BATDEAD; events |= flags & SS_IOCARD ? SS_STSCHG : SS_BATDEAD;
} }
if(state->bvd2!=prev_state->bvd2){ if (state->bvd2 != prev_state->bvd2) {
DEBUG(3, "%s(): card BVD2 value %u\n", __FUNCTION__, state->bvd2);
DEBUG(2, "%s(): card BVD2 value %u\n", __FUNCTION__, state->bvd2); events |= flags & SS_IOCARD ? 0 : SS_BATWARN;
events|=mask&(flags&SS_IOCARD)?0:SS_BATWARN;
} }
DEBUG(2, "events: %s%s%s%s%s%s\n", *prev_state = *state;
(events==0)?"<NONE>":"",
(events&SS_DETECT)?"DETECT ":"",
(events&SS_READY)?"READY ":"",
(events&SS_BATDEAD)?"BATDEAD ":"",
(events&SS_BATWARN)?"BATWARN ":"",
(events&SS_STSCHG)?"STSCHG ":"");
*prev_state=*state; events &= mask;
return events; DEBUG(2, "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 " : "");
return events;
} /* sa1100_pcmcia_events() */ } /* sa1100_pcmcia_events() */
...@@ -464,38 +223,43 @@ static inline unsigned sa1100_pcmcia_events(struct pcmcia_state *state, ...@@ -464,38 +223,43 @@ static inline unsigned sa1100_pcmcia_events(struct pcmcia_state *state,
* callback) occurs in this thread rather than in the actual interrupt * callback) occurs in this thread rather than in the actual interrupt
* handler due to the use of scheduling operations in the PCMCIA core. * handler due to the use of scheduling operations in the PCMCIA core.
*/ */
static void sa1100_pcmcia_task_handler(void *data) { static void sa1100_pcmcia_task_handler(void *data)
{
struct pcmcia_state state[SA1100_PCMCIA_MAX_SOCK]; struct pcmcia_state state[SA1100_PCMCIA_MAX_SOCK];
struct pcmcia_state_array state_array; struct pcmcia_state_array state_array;
int i, events, all_events, irq_status; unsigned int all_events;
DEBUG(2, "%s(): entering PCMCIA monitoring thread\n", __FUNCTION__); DEBUG(4, "%s(): entering PCMCIA monitoring thread\n", __FUNCTION__);
state_array.size=sa1100_pcmcia_socket_count; state_array.size = sa1100_pcmcia_socket_count;
state_array.state=state; state_array.state = state;
do { do {
unsigned int events;
int ret, i;
DEBUG(3, "%s(): interrogating low-level PCMCIA service\n", __FUNCTION__); memset(state, 0, sizeof(state));
if((irq_status=pcmcia_low_level->socket_state(&state_array))<0) DEBUG(4, "%s(): interrogating low-level PCMCIA service\n", __FUNCTION__);
printk(KERN_ERR "Error in kernel low-level PCMCIA service.\n");
all_events=0; ret = pcmcia_low_level->socket_state(&state_array);
if (ret < 0) {
printk(KERN_ERR "sa1100_pcmcia: unable to read socket status\n");
break;
}
if(irq_status>0){ all_events = 0;
for(i=0; i<state_array.size; ++i, all_events|=events) for (i = 0; i < state_array.size; i++, all_events |= events) {
if((events= struct sa1100_pcmcia_socket *skt = PCMCIA_SOCKET(i);
sa1100_pcmcia_events(&state[i],
&sa1100_pcmcia_socket[i].k_state,
sa1100_pcmcia_socket[i].cs_state.csc_mask,
sa1100_pcmcia_socket[i].cs_state.flags)))
if(sa1100_pcmcia_socket[i].handler!=NULL)
sa1100_pcmcia_socket[i].handler(sa1100_pcmcia_socket[i].handler_info,
events);
}
events = sa1100_pcmcia_events(&state[i], &skt->k_state,
skt->cs_state.csc_mask,
skt->cs_state.flags);
if (events && sa1100_pcmcia_socket[i].handler != NULL)
skt->handler(skt->handler_info, events);
}
} while(all_events); } while(all_events);
} /* sa1100_pcmcia_task_handler() */ } /* sa1100_pcmcia_task_handler() */
...@@ -510,7 +274,7 @@ static struct tq_struct sa1100_pcmcia_task = { ...@@ -510,7 +274,7 @@ static struct tq_struct sa1100_pcmcia_task = {
*/ */
static void sa1100_pcmcia_poll_event(unsigned long dummy) static void sa1100_pcmcia_poll_event(unsigned long dummy)
{ {
DEBUG(3, "%s(): polling for events\n", __FUNCTION__); DEBUG(4, "%s(): polling for events\n", __FUNCTION__);
poll_timer.function = sa1100_pcmcia_poll_event; poll_timer.function = sa1100_pcmcia_poll_event;
poll_timer.expires = jiffies + SA1100_PCMCIA_POLL_PERIOD; poll_timer.expires = jiffies + SA1100_PCMCIA_POLL_PERIOD;
add_timer(&poll_timer); add_timer(&poll_timer);
...@@ -527,7 +291,8 @@ static void sa1100_pcmcia_poll_event(unsigned long dummy) ...@@ -527,7 +291,8 @@ static void sa1100_pcmcia_poll_event(unsigned long dummy)
* handling code performs scheduling operations which cannot be * handling code performs scheduling operations which cannot be
* executed from within an interrupt context. * executed from within an interrupt context.
*/ */
static void sa1100_pcmcia_interrupt(int irq, void *dev, struct pt_regs *regs){ static void sa1100_pcmcia_interrupt(int irq, void *dev, struct pt_regs *regs)
{
DEBUG(3, "%s(): servicing IRQ %d\n", __FUNCTION__, irq); DEBUG(3, "%s(): servicing IRQ %d\n", __FUNCTION__, irq);
schedule_task(&sa1100_pcmcia_task); schedule_task(&sa1100_pcmcia_task);
} }
...@@ -546,17 +311,20 @@ static void sa1100_pcmcia_interrupt(int irq, void *dev, struct pt_regs *regs){ ...@@ -546,17 +311,20 @@ static void sa1100_pcmcia_interrupt(int irq, void *dev, struct pt_regs *regs){
* *
* Returns: 0 * Returns: 0
*/ */
static int sa1100_pcmcia_register_callback(unsigned int sock, static int
void (*handler)(void *, sa1100_pcmcia_register_callback(unsigned int sock,
unsigned int), void (*handler)(void *, unsigned int),
void *info){ void *info)
if(handler==NULL){ {
sa1100_pcmcia_socket[sock].handler=NULL; struct sa1100_pcmcia_socket *skt = PCMCIA_SOCKET(sock);
if (handler == NULL) {
skt->handler = NULL;
MOD_DEC_USE_COUNT; MOD_DEC_USE_COUNT;
} else { } else {
MOD_INC_USE_COUNT; MOD_INC_USE_COUNT;
sa1100_pcmcia_socket[sock].handler=handler; skt->handler_info = info;
sa1100_pcmcia_socket[sock].handler_info=info; skt->handler = handler;
} }
return 0; return 0;
...@@ -580,20 +348,7 @@ static int sa1100_pcmcia_register_callback(unsigned int sock, ...@@ -580,20 +348,7 @@ static int sa1100_pcmcia_register_callback(unsigned int sock,
* an offset which is applied to client-requested base I/O addresses * an offset which is applied to client-requested base I/O addresses
* in alloc_io_space(). * in alloc_io_space().
* *
* Returns: 0 on success, -1 if no pin has been configured for `sock' * SS_CAP_PAGE_REGS: used by setup_cis_mem() in cistpl.c to set the
*/
static int sa1100_pcmcia_inquire_socket(unsigned int sock,
socket_cap_t *cap){
struct pcmcia_irq_info irq_info;
DEBUG(3, "%s() for sock %u\n", __FUNCTION__, sock);
if(sock>=sa1100_pcmcia_socket_count){
printk(KERN_ERR "sa1100: socket %u not configured\n", sock);
return -1;
}
/* SS_CAP_PAGE_REGS: used by setup_cis_mem() in cistpl.c to set the
* force_low argument to validate_mem() in rsrc_mgr.c -- since in * force_low argument to validate_mem() in rsrc_mgr.c -- since in
* general, the mapped * addresses of the PCMCIA memory regions * general, the mapped * addresses of the PCMCIA memory regions
* will not be within 0xffff, setting force_low would be * will not be within 0xffff, setting force_low would be
...@@ -605,26 +360,29 @@ static int sa1100_pcmcia_inquire_socket(unsigned int sock, ...@@ -605,26 +360,29 @@ static int sa1100_pcmcia_inquire_socket(unsigned int sock,
* *
* SS_CAP_PCCARD: we can deal with 16-bit PCMCIA & CF cards, but * SS_CAP_PCCARD: we can deal with 16-bit PCMCIA & CF cards, but
* not 32-bit CardBus devices. * not 32-bit CardBus devices.
*
* Return value is irrelevant; the pcmcia subsystem ignores it.
*/ */
cap->features=(SS_CAP_PAGE_REGS | SS_CAP_STATIC_MAP | SS_CAP_PCCARD); static int
sa1100_pcmcia_inquire_socket(unsigned int sock, socket_cap_t *cap)
irq_info.sock=sock; {
irq_info.irq=-1; struct sa1100_pcmcia_socket *skt = PCMCIA_SOCKET(sock);
int ret = -1;
if(pcmcia_low_level->get_irq_info(&irq_info)<0){ DEBUG(2, "%s() for sock %u\n", __FUNCTION__, sock);
printk(KERN_ERR "Error obtaining IRQ info from kernel for socket %u\n",
sock);
return -1;
}
cap->irq_mask=0; if (sock < sa1100_pcmcia_socket_count) {
cap->map_size=PAGE_SIZE; cap->features = SS_CAP_PAGE_REGS | SS_CAP_STATIC_MAP | SS_CAP_PCCARD;
cap->pci_irq=irq_info.irq; cap->irq_mask = 0;
cap->io_offset=sa1100_pcmcia_socket[sock].virt_io; cap->map_size = PAGE_SIZE;
cap->pci_irq = skt->irq;
cap->io_offset = (unsigned long)skt->virt_io;
return 0; ret = 0;
}
} /* sa1100_pcmcia_inquire_socket() */ return ret;
}
/* sa1100_pcmcia_get_status() /* sa1100_pcmcia_get_status()
...@@ -643,58 +401,61 @@ static int sa1100_pcmcia_inquire_socket(unsigned int sock, ...@@ -643,58 +401,61 @@ static int sa1100_pcmcia_inquire_socket(unsigned int sock,
* *
* Returns: 0 * Returns: 0
*/ */
static int sa1100_pcmcia_get_status(unsigned int sock, static int
unsigned int *status){ sa1100_pcmcia_get_status(unsigned int sock, unsigned int *status)
{
struct sa1100_pcmcia_socket *skt = PCMCIA_SOCKET(sock);
struct pcmcia_state state[SA1100_PCMCIA_MAX_SOCK]; struct pcmcia_state state[SA1100_PCMCIA_MAX_SOCK];
struct pcmcia_state_array state_array; struct pcmcia_state_array state_array;
unsigned int stat;
DEBUG(3, "%s() for sock %u\n", __FUNCTION__, sock); DEBUG(2, "%s() for sock %u\n", __FUNCTION__, sock);
state_array.size=sa1100_pcmcia_socket_count; state_array.size = sa1100_pcmcia_socket_count;
state_array.state=state; state_array.state = state;
if((pcmcia_low_level->socket_state(&state_array))<0){ memset(state, 0, sizeof(state));
printk(KERN_ERR "Unable to get PCMCIA status from kernel.\n");
if ((pcmcia_low_level->socket_state(&state_array)) < 0) {
printk(KERN_ERR "sa1100_pcmcia: unable to get socket status\n");
return -1; return -1;
} }
sa1100_pcmcia_socket[sock].k_state=state[sock]; skt->k_state = state[sock];
*status=state[sock].detect?SS_DETECT:0;
*status|=state[sock].ready?SS_READY:0; stat = state[sock].detect ? SS_DETECT : 0;
stat |= state[sock].ready ? SS_READY : 0;
stat |= state[sock].vs_3v ? SS_3VCARD : 0;
stat |= state[sock].vs_Xv ? SS_XVCARD : 0;
/* The power status of individual sockets is not available /* The power status of individual sockets is not available
* explicitly from the hardware, so we just remember the state * explicitly from the hardware, so we just remember the state
* and regurgitate it upon request: * and regurgitate it upon request:
*/ */
*status|=sa1100_pcmcia_socket[sock].cs_state.Vcc?SS_POWERON:0; stat |= skt->cs_state.Vcc ? SS_POWERON : 0;
if(sa1100_pcmcia_socket[sock].cs_state.flags&SS_IOCARD) if (skt->cs_state.flags & SS_IOCARD)
*status|=state[sock].bvd1?SS_STSCHG:0; stat |= state[sock].bvd1 ? SS_STSCHG : 0;
else { else {
if(state[sock].bvd1==0) if (state[sock].bvd1 == 0)
*status|=SS_BATDEAD; stat |= SS_BATDEAD;
else if(state[sock].bvd2==0) else if (state[sock].bvd2 == 0)
*status|=SS_BATWARN; stat |= SS_BATWARN;
} }
*status|=state[sock].vs_3v?SS_3VCARD:0;
*status|=state[sock].vs_Xv?SS_XVCARD:0;
DEBUG(3, "\tstatus: %s%s%s%s%s%s%s%s\n", DEBUG(3, "\tstatus: %s%s%s%s%s%s%s%s\n",
(*status&SS_DETECT)?"DETECT ":"", stat & SS_DETECT ? "DETECT " : "",
(*status&SS_READY)?"READY ":"", stat & SS_READY ? "READY " : "",
(*status&SS_BATDEAD)?"BATDEAD ":"", stat & SS_BATDEAD ? "BATDEAD " : "",
(*status&SS_BATWARN)?"BATWARN ":"", stat & SS_BATWARN ? "BATWARN " : "",
(*status&SS_POWERON)?"POWERON ":"", stat & SS_POWERON ? "POWERON " : "",
(*status&SS_STSCHG)?"STSCHG ":"", stat & SS_STSCHG ? "STSCHG " : "",
(*status&SS_3VCARD)?"3VCARD ":"", stat & SS_3VCARD ? "3VCARD " : "",
(*status&SS_XVCARD)?"XVCARD ":""); stat & SS_XVCARD ? "XVCARD " : "");
return 0; *status = stat;
return 0;
} /* sa1100_pcmcia_get_status() */ } /* sa1100_pcmcia_get_status() */
...@@ -706,20 +467,18 @@ static int sa1100_pcmcia_get_status(unsigned int sock, ...@@ -706,20 +467,18 @@ static int sa1100_pcmcia_get_status(unsigned int sock,
* *
* Returns: 0 * Returns: 0
*/ */
static int sa1100_pcmcia_get_socket(unsigned int sock, static int
socket_state_t *state){ sa1100_pcmcia_get_socket(unsigned int sock, socket_state_t *state)
{
struct sa1100_pcmcia_socket *skt = PCMCIA_SOCKET(sock);
DEBUG(3, "%s() for sock %u\n", __FUNCTION__, sock); DEBUG(2, "%s() for sock %u\n", __FUNCTION__, sock);
/* This information was given to us in an earlier call to set_socket(), *state = skt->cs_state;
* so we're just regurgitating it here:
*/
*state=sa1100_pcmcia_socket[sock].cs_state;
return 0; return 0;
} }
/* sa1100_pcmcia_set_socket() /* sa1100_pcmcia_set_socket()
* ^^^^^^^^^^^^^^^^^^^^^^^^^^ * ^^^^^^^^^^^^^^^^^^^^^^^^^^
* Implements the set_socket() operation for the in-kernel PCMCIA * Implements the set_socket() operation for the in-kernel PCMCIA
...@@ -730,14 +489,15 @@ static int sa1100_pcmcia_get_socket(unsigned int sock, ...@@ -730,14 +489,15 @@ static int sa1100_pcmcia_get_socket(unsigned int sock,
* *
* Returns: 0 * Returns: 0
*/ */
static int sa1100_pcmcia_set_socket(unsigned int sock, static int
socket_state_t *state){ sa1100_pcmcia_set_socket(unsigned int sock, socket_state_t *state)
struct pcmcia_configure configure; {
struct sa1100_pcmcia_socket *skt = PCMCIA_SOCKET(sock);
struct pcmcia_configure conf;
DEBUG(3, "%s() for sock %u\n", __FUNCTION__, sock); DEBUG(2, "%s() for sock %u\n", __FUNCTION__, sock);
DEBUG(3, "\tmask: %s%s%s%s%s%s\n\tflags: %s%s%s%s%s%s\n" DEBUG(3, "\tmask: %s%s%s%s%s%s\n\tflags: %s%s%s%s%s%s\n",
"\tVcc %d Vpp %d irq %d\n",
(state->csc_mask==0)?"<NONE>":"", (state->csc_mask==0)?"<NONE>":"",
(state->csc_mask&SS_DETECT)?"DETECT ":"", (state->csc_mask&SS_DETECT)?"DETECT ":"",
(state->csc_mask&SS_READY)?"READY ":"", (state->csc_mask&SS_READY)?"READY ":"",
...@@ -749,25 +509,20 @@ static int sa1100_pcmcia_set_socket(unsigned int sock, ...@@ -749,25 +509,20 @@ static int sa1100_pcmcia_set_socket(unsigned int sock,
(state->flags&SS_IOCARD)?"IOCARD ":"", (state->flags&SS_IOCARD)?"IOCARD ":"",
(state->flags&SS_RESET)?"RESET ":"", (state->flags&SS_RESET)?"RESET ":"",
(state->flags&SS_SPKR_ENA)?"SPKR_ENA ":"", (state->flags&SS_SPKR_ENA)?"SPKR_ENA ":"",
(state->flags&SS_OUTPUT_ENA)?"OUTPUT_ENA ":"", (state->flags&SS_OUTPUT_ENA)?"OUTPUT_ENA ":"");
DEBUG(3, "\tVcc %d Vpp %d irq %d\n",
state->Vcc, state->Vpp, state->io_irq); state->Vcc, state->Vpp, state->io_irq);
configure.sock=sock; conf = sa1100_pcmcia_state_to_config(sock, state);
configure.vcc=state->Vcc;
configure.vpp=state->Vpp;
configure.output=(state->flags&SS_OUTPUT_ENA)?1:0;
configure.speaker=(state->flags&SS_SPKR_ENA)?1:0;
configure.reset=(state->flags&SS_RESET)?1:0;
if(pcmcia_low_level->configure_socket(&configure)<0){ if (pcmcia_low_level->configure_socket(&conf) < 0) {
printk(KERN_ERR "Unable to configure socket %u\n", sock); printk(KERN_ERR "sa1100_pcmcia: unable to configure socket %d\n", sock);
return -1; return -1;
} }
sa1100_pcmcia_socket[sock].cs_state=*state; skt->cs_state = *state;
return 0; return 0;
} /* sa1100_pcmcia_set_socket() */ } /* sa1100_pcmcia_set_socket() */
...@@ -779,20 +534,20 @@ static int sa1100_pcmcia_set_socket(unsigned int sock, ...@@ -779,20 +534,20 @@ static int sa1100_pcmcia_set_socket(unsigned int sock,
* *
* Returns: 0 on success, -1 if the map index was out of range * Returns: 0 on success, -1 if the map index was out of range
*/ */
static int sa1100_pcmcia_get_io_map(unsigned int sock, static int
struct pccard_io_map *map){ sa1100_pcmcia_get_io_map(unsigned int sock, struct pccard_io_map *map)
{
struct sa1100_pcmcia_socket *skt = PCMCIA_SOCKET(sock);
int ret = -1;
DEBUG(4, "%s() for sock %u\n", __FUNCTION__, sock); DEBUG(2, "%s() for sock %u\n", __FUNCTION__, sock);
if(map->map>=MAX_IO_WIN){ if (map->map < MAX_IO_WIN) {
printk(KERN_ERR "%s(): map (%d) out of range\n", __FUNCTION__, *map = skt->io_map[map->map];
map->map); ret = 0;
return -1;
} }
*map=sa1100_pcmcia_socket[sock].io_map[map->map]; return ret;
return 0;
} }
...@@ -805,16 +560,16 @@ static int sa1100_pcmcia_get_io_map(unsigned int sock, ...@@ -805,16 +560,16 @@ static int sa1100_pcmcia_get_io_map(unsigned int sock,
* *
* Returns: 0 on success, -1 on error * Returns: 0 on success, -1 on error
*/ */
static int sa1100_pcmcia_set_io_map(unsigned int sock, static int
struct pccard_io_map *map){ sa1100_pcmcia_set_io_map(unsigned int sock, struct pccard_io_map *map)
unsigned int clock, speed; {
unsigned long mecr, start; struct sa1100_pcmcia_socket *skt = PCMCIA_SOCKET(sock);
DEBUG(4, "%s() for sock %u\n", __FUNCTION__, sock); DEBUG(2, "%s() for sock %u\n", __FUNCTION__, sock);
DEBUG(4, "\tmap %u speed %u\n\tstart 0x%08lx stop 0x%08lx\n" DEBUG(3, "\tmap %u speed %u\n\tstart 0x%08x stop 0x%08x\n",
"\tflags: %s%s%s%s%s%s%s%s\n", map->map, map->speed, map->start, map->stop);
map->map, map->speed, map->start, map->stop, DEBUG(3, "\tflags: %s%s%s%s%s%s%s%s\n",
(map->flags==0)?"<NONE>":"", (map->flags==0)?"<NONE>":"",
(map->flags&MAP_ACTIVE)?"ACTIVE ":"", (map->flags&MAP_ACTIVE)?"ACTIVE ":"",
(map->flags&MAP_16BIT)?"16BIT ":"", (map->flags&MAP_16BIT)?"16BIT ":"",
...@@ -824,45 +579,45 @@ static int sa1100_pcmcia_set_io_map(unsigned int sock, ...@@ -824,45 +579,45 @@ static int sa1100_pcmcia_set_io_map(unsigned int sock,
(map->flags&MAP_USE_WAIT)?"USE_WAIT ":"", (map->flags&MAP_USE_WAIT)?"USE_WAIT ":"",
(map->flags&MAP_PREFETCH)?"PREFETCH ":""); (map->flags&MAP_PREFETCH)?"PREFETCH ":"");
if(map->map>=MAX_IO_WIN){ if (map->map >= MAX_IO_WIN) {
printk(KERN_ERR "%s(): map (%d) out of range\n", __FUNCTION__, printk(KERN_ERR "%s(): map (%d) out of range\n", __FUNCTION__,
map->map); map->map);
return -1; return -1;
} }
if(map->flags&MAP_ACTIVE){ if (map->flags & MAP_ACTIVE) {
unsigned int clock, speed = map->speed;
unsigned long mecr;
speed=(map->speed>0)?map->speed:SA1100_PCMCIA_IO_ACCESS; if (speed == 0)
speed = SA1100_PCMCIA_IO_ACCESS;
clock = get_cclk_frequency() * 100; clock = cpufreq_get(0);
mecr=MECR; mecr = MECR;
MECR_BSIO_SET(mecr, sock, sa1100_pcmcia_mecr_bs(speed, clock)); MECR_BSIO_SET(mecr, sock, sa1100_pcmcia_mecr_bs(speed, clock));
sa1100_pcmcia_socket[sock].speed_io=speed; skt->speed_io = speed;
DEBUG(4, "%s(): FAST%u %lx BSM%u %lx BSA%u %lx BSIO%u %lx\n", DEBUG(4, "%s(): FAST%u %lx BSM%u %lx BSA%u %lx BSIO%u %lx\n",
__FUNCTION__, sock, MECR_FAST_GET(mecr, sock), sock, __FUNCTION__, sock, MECR_FAST_GET(mecr, sock), sock,
MECR_BSM_GET(mecr, sock), sock, MECR_BSA_GET(mecr, sock), MECR_BSM_GET(mecr, sock), sock, MECR_BSA_GET(mecr, sock),
sock, MECR_BSIO_GET(mecr, sock)); sock, MECR_BSIO_GET(mecr, sock));
MECR=mecr; MECR = mecr;
} }
start=map->start; if (map->stop == 1)
map->stop = PAGE_SIZE-1;
if(map->stop==1)
map->stop=PAGE_SIZE-1;
map->start=sa1100_pcmcia_socket[sock].virt_io; map->stop -= map->start;
map->stop=map->start+(map->stop-start); map->stop += (unsigned long)skt->virt_io;
map->start = (unsigned long)skt->virt_io;
sa1100_pcmcia_socket[sock].io_map[map->map]=*map; skt->io_map[map->map] = *map;
return 0; return 0;
} /* sa1100_pcmcia_set_io_map() */ } /* sa1100_pcmcia_set_io_map() */
...@@ -875,20 +630,20 @@ static int sa1100_pcmcia_set_io_map(unsigned int sock, ...@@ -875,20 +630,20 @@ static int sa1100_pcmcia_set_io_map(unsigned int sock,
* *
* Returns: 0 on success, -1 if the map index was out of range * Returns: 0 on success, -1 if the map index was out of range
*/ */
static int sa1100_pcmcia_get_mem_map(unsigned int sock, static int
struct pccard_mem_map *map){ sa1100_pcmcia_get_mem_map(unsigned int sock, struct pccard_mem_map *map)
{
struct sa1100_pcmcia_socket *skt = PCMCIA_SOCKET(sock);
int ret = -1;
DEBUG(4, "%s() for sock %u\n", __FUNCTION__, sock); DEBUG(2, "%s() for sock %u\n", __FUNCTION__, sock);
if(map->map>=MAX_WIN){ if (map->map < MAX_WIN) {
printk(KERN_ERR "%s(): map (%d) out of range\n", __FUNCTION__, *map = skt->mem_map[map->map];
map->map); ret = 0;
return -1;
} }
*map=sa1100_pcmcia_socket[sock].mem_map[map->map]; return ret;
return 0;
} }
...@@ -901,18 +656,18 @@ static int sa1100_pcmcia_get_mem_map(unsigned int sock, ...@@ -901,18 +656,18 @@ static int sa1100_pcmcia_get_mem_map(unsigned int sock,
* *
* Returns: 0 on success, -1 on error * Returns: 0 on success, -1 on error
*/ */
static int sa1100_pcmcia_set_mem_map(unsigned int sock, static int
struct pccard_mem_map *map){ sa1100_pcmcia_set_mem_map(unsigned int sock, struct pccard_mem_map *map)
unsigned int clock, speed; {
unsigned long mecr, start; struct sa1100_pcmcia_socket *skt = PCMCIA_SOCKET(sock);
unsigned long start;
DEBUG(4, "%s() for sock %u\n", __FUNCTION__, sock); DEBUG(2, "%s() for sock %u\n", __FUNCTION__, sock);
DEBUG(4, "\tmap %u speed %u\n\tsys_start %#lx\n" DEBUG(3, "\tmap %u speed %u sys_start %08lx sys_stop %08lx card_start %08x\n",
"\tsys_stop %#lx\n\tcard_start %#x\n" map->map, map->speed, map->sys_start, map->sys_stop, map->card_start);
"\tflags: %s%s%s%s%s%s%s%s\n", DEBUG(3, "\tflags: %s%s%s%s%s%s%s%s\n",
map->map, map->speed, map->sys_start, map->sys_stop, (map->flags==0)?"<NONE>":"",
map->card_start, (map->flags==0)?"<NONE>":"",
(map->flags&MAP_ACTIVE)?"ACTIVE ":"", (map->flags&MAP_ACTIVE)?"ACTIVE ":"",
(map->flags&MAP_16BIT)?"16BIT ":"", (map->flags&MAP_16BIT)?"16BIT ":"",
(map->flags&MAP_AUTOSZ)?"AUTOSZ ":"", (map->flags&MAP_AUTOSZ)?"AUTOSZ ":"",
...@@ -921,42 +676,38 @@ static int sa1100_pcmcia_set_mem_map(unsigned int sock, ...@@ -921,42 +676,38 @@ static int sa1100_pcmcia_set_mem_map(unsigned int sock,
(map->flags&MAP_ATTRIB)?"ATTRIB ":"", (map->flags&MAP_ATTRIB)?"ATTRIB ":"",
(map->flags&MAP_USE_WAIT)?"USE_WAIT ":""); (map->flags&MAP_USE_WAIT)?"USE_WAIT ":"");
if(map->map>=MAX_WIN){ if (map->map >= MAX_WIN){
printk(KERN_ERR "%s(): map (%d) out of range\n", __FUNCTION__, printk(KERN_ERR "%s(): map (%d) out of range\n", __FUNCTION__,
map->map); map->map);
return -1; return -1;
} }
if(map->flags&MAP_ACTIVE){ if (map->flags & MAP_ACTIVE) {
unsigned int clock, speed = map->speed;
unsigned long mecr;
/* When clients issue RequestMap, the access speed is not always /*
* properly configured: * When clients issue RequestMap, the access speed is not always
* properly configured. Choose some sensible defaults.
*/ */
if(map->speed > 0) if (speed == 0) {
speed = map->speed; if (skt->cs_state.Vcc == 33)
else
switch(sa1100_pcmcia_socket[sock].cs_state.Vcc){
case 33:
speed = SA1100_PCMCIA_3V_MEM_ACCESS; speed = SA1100_PCMCIA_3V_MEM_ACCESS;
break; else
default:
speed = SA1100_PCMCIA_5V_MEM_ACCESS; speed = SA1100_PCMCIA_5V_MEM_ACCESS;
} }
clock = get_cclk_frequency() * 100; clock = cpufreq_get(0);
mecr=MECR; /* Fixme: MECR is not pre-empt safe. */
mecr = MECR;
if(map->flags&MAP_ATTRIB){
if (map->flags & MAP_ATTRIB) {
MECR_BSA_SET(mecr, sock, sa1100_pcmcia_mecr_bs(speed, clock)); MECR_BSA_SET(mecr, sock, sa1100_pcmcia_mecr_bs(speed, clock));
sa1100_pcmcia_socket[sock].speed_attr=speed; skt->speed_attr = speed;
} else { } else {
MECR_BSM_SET(mecr, sock, sa1100_pcmcia_mecr_bs(speed, clock)); MECR_BSM_SET(mecr, sock, sa1100_pcmcia_mecr_bs(speed, clock));
sa1100_pcmcia_socket[sock].speed_mem=speed; skt->speed_mem = speed;
} }
DEBUG(4, "%s(): FAST%u %lx BSM%u %lx BSA%u %lx BSIO%u %lx\n", DEBUG(4, "%s(): FAST%u %lx BSM%u %lx BSA%u %lx BSIO%u %lx\n",
...@@ -964,134 +715,136 @@ static int sa1100_pcmcia_set_mem_map(unsigned int sock, ...@@ -964,134 +715,136 @@ static int sa1100_pcmcia_set_mem_map(unsigned int sock,
MECR_BSM_GET(mecr, sock), sock, MECR_BSA_GET(mecr, sock), MECR_BSM_GET(mecr, sock), sock, MECR_BSA_GET(mecr, sock),
sock, MECR_BSIO_GET(mecr, sock)); sock, MECR_BSIO_GET(mecr, sock));
MECR=mecr; MECR = mecr;
} }
start=map->sys_start; start = (map->flags & MAP_ATTRIB) ? skt->phys_attr : skt->phys_mem;
if(map->sys_stop==0)
map->sys_stop=PAGE_SIZE-1;
map->sys_start=(map->flags & MAP_ATTRIB)?\ if (map->sys_stop == 0)
sa1100_pcmcia_socket[sock].phys_attr:\ map->sys_stop = PAGE_SIZE-1;
sa1100_pcmcia_socket[sock].phys_mem;
map->sys_stop=map->sys_start+(map->sys_stop-start); map->sys_stop -= map->sys_start;
map->sys_stop += start;
map->sys_start = start;
sa1100_pcmcia_socket[sock].mem_map[map->map]=*map; skt->mem_map[map->map] = *map;
return 0; return 0;
} /* sa1100_pcmcia_set_mem_map() */ } /* sa1100_pcmcia_set_mem_map() */
#if defined(CONFIG_PROC_FS) #if defined(CONFIG_PROC_FS)
/* sa1100_pcmcia_proc_setup()
* ^^^^^^^^^^^^^^^^^^^^^^^^^^
* Implements the proc_setup() operation for the in-kernel PCMCIA
* service (formerly SS_ProcSetup in Card Services).
*
* Returns: 0 on success, -1 on error
*/
static void sa1100_pcmcia_proc_setup(unsigned int sock,
struct proc_dir_entry *base){
struct proc_dir_entry *entry;
DEBUG(4, "%s() for sock %u\n", __FUNCTION__, sock);
if((entry=create_proc_entry("status", 0, base))==NULL){
printk(KERN_ERR "Unable to install \"status\" procfs entry\n");
return;
}
entry->read_proc=sa1100_pcmcia_proc_status;
entry->data=(void *)sock;
}
/* sa1100_pcmcia_proc_status() /* sa1100_pcmcia_proc_status()
* ^^^^^^^^^^^^^^^^^^^^^^^^^^^ * ^^^^^^^^^^^^^^^^^^^^^^^^^^^
* Implements the /proc/bus/pccard/??/status file. * Implements the /proc/bus/pccard/??/status file.
* *
* Returns: the number of characters added to the buffer * Returns: the number of characters added to the buffer
*/ */
static int sa1100_pcmcia_proc_status(char *buf, char **start, off_t pos, static int
int count, int *eof, void *data){ sa1100_pcmcia_proc_status(char *buf, char **start, off_t pos,
char *p=buf; int count, int *eof, void *data)
unsigned int sock=(unsigned int)data; {
unsigned int clock = get_cclk_frequency() * 100; unsigned int sock = (unsigned int)data;
unsigned int clock = cpufreq_get(0);
struct sa1100_pcmcia_socket *skt = PCMCIA_SOCKET(sock);
unsigned long mecr = MECR; unsigned long mecr = MECR;
char *p = buf;
p+=sprintf(p, "k_flags : %s%s%s%s%s%s%s\n", p+=sprintf(p, "k_state : %s%s%s%s%s%s%s\n",
sa1100_pcmcia_socket[sock].k_state.detect?"detect ":"", skt->k_state.detect ? "detect " : "",
sa1100_pcmcia_socket[sock].k_state.ready?"ready ":"", skt->k_state.ready ? "ready " : "",
sa1100_pcmcia_socket[sock].k_state.bvd1?"bvd1 ":"", skt->k_state.bvd1 ? "bvd1 " : "",
sa1100_pcmcia_socket[sock].k_state.bvd2?"bvd2 ":"", skt->k_state.bvd2 ? "bvd2 " : "",
sa1100_pcmcia_socket[sock].k_state.wrprot?"wrprot ":"", skt->k_state.wrprot ? "wrprot " : "",
sa1100_pcmcia_socket[sock].k_state.vs_3v?"vs_3v ":"", skt->k_state.vs_3v ? "vs_3v " : "",
sa1100_pcmcia_socket[sock].k_state.vs_Xv?"vs_Xv ":""); skt->k_state.vs_Xv ? "vs_Xv " : "");
p+=sprintf(p, "status : %s%s%s%s%s%s%s%s%s\n", p+=sprintf(p, "status : %s%s%s%s%s%s%s%s%s\n",
sa1100_pcmcia_socket[sock].k_state.detect?"SS_DETECT ":"", skt->k_state.detect ? "SS_DETECT " : "",
sa1100_pcmcia_socket[sock].k_state.ready?"SS_READY ":"", skt->k_state.ready ? "SS_READY " : "",
sa1100_pcmcia_socket[sock].cs_state.Vcc?"SS_POWERON ":"", skt->cs_state.Vcc ? "SS_POWERON " : "",
sa1100_pcmcia_socket[sock].cs_state.flags&SS_IOCARD?\ skt->cs_state.flags & SS_IOCARD ? "SS_IOCARD " : "",
"SS_IOCARD ":"", (skt->cs_state.flags & SS_IOCARD &&
(sa1100_pcmcia_socket[sock].cs_state.flags&SS_IOCARD && skt->k_state.bvd1) ? "SS_STSCHG " : "",
sa1100_pcmcia_socket[sock].k_state.bvd1)?"SS_STSCHG ":"", ((skt->cs_state.flags & SS_IOCARD)==0 &&
((sa1100_pcmcia_socket[sock].cs_state.flags&SS_IOCARD)==0 && (skt->k_state.bvd1==0)) ? "SS_BATDEAD " : "",
(sa1100_pcmcia_socket[sock].k_state.bvd1==0))?"SS_BATDEAD ":"", ((skt->cs_state.flags & SS_IOCARD)==0 &&
((sa1100_pcmcia_socket[sock].cs_state.flags&SS_IOCARD)==0 && (skt->k_state.bvd2==0)) ? "SS_BATWARN " : "",
(sa1100_pcmcia_socket[sock].k_state.bvd2==0))?"SS_BATWARN ":"", skt->k_state.vs_3v ? "SS_3VCARD " : "",
sa1100_pcmcia_socket[sock].k_state.vs_3v?"SS_3VCARD ":"", skt->k_state.vs_Xv ? "SS_XVCARD " : "");
sa1100_pcmcia_socket[sock].k_state.vs_Xv?"SS_XVCARD ":"");
p+=sprintf(p, "mask : %s%s%s%s%s\n", p+=sprintf(p, "mask : %s%s%s%s%s\n",
sa1100_pcmcia_socket[sock].cs_state.csc_mask&SS_DETECT?\ skt->cs_state.csc_mask & SS_DETECT ? "SS_DETECT " : "",
"SS_DETECT ":"", skt->cs_state.csc_mask & SS_READY ? "SS_READY " : "",
sa1100_pcmcia_socket[sock].cs_state.csc_mask&SS_READY?\ skt->cs_state.csc_mask & SS_BATDEAD ? "SS_BATDEAD " : "",
"SS_READY ":"", skt->cs_state.csc_mask & SS_BATWARN ? "SS_BATWARN " : "",
sa1100_pcmcia_socket[sock].cs_state.csc_mask&SS_BATDEAD?\ skt->cs_state.csc_mask & SS_STSCHG ? "SS_STSCHG " : "");
"SS_BATDEAD ":"",
sa1100_pcmcia_socket[sock].cs_state.csc_mask&SS_BATWARN?\
"SS_BATWARN ":"",
sa1100_pcmcia_socket[sock].cs_state.csc_mask&SS_STSCHG?\
"SS_STSCHG ":"");
p+=sprintf(p, "cs_flags : %s%s%s%s%s\n", p+=sprintf(p, "cs_flags : %s%s%s%s%s\n",
sa1100_pcmcia_socket[sock].cs_state.flags&SS_PWR_AUTO?\ skt->cs_state.flags & SS_PWR_AUTO ? "SS_PWR_AUTO " : "",
"SS_PWR_AUTO ":"", skt->cs_state.flags & SS_IOCARD ? "SS_IOCARD " : "",
sa1100_pcmcia_socket[sock].cs_state.flags&SS_IOCARD?\ skt->cs_state.flags & SS_RESET ? "SS_RESET " : "",
"SS_IOCARD ":"", skt->cs_state.flags & SS_SPKR_ENA ? "SS_SPKR_ENA " : "",
sa1100_pcmcia_socket[sock].cs_state.flags&SS_RESET?\ skt->cs_state.flags & SS_OUTPUT_ENA ? "SS_OUTPUT_ENA " : "");
"SS_RESET ":"",
sa1100_pcmcia_socket[sock].cs_state.flags&SS_SPKR_ENA?\
"SS_SPKR_ENA ":"",
sa1100_pcmcia_socket[sock].cs_state.flags&SS_OUTPUT_ENA?\
"SS_OUTPUT_ENA ":"");
p+=sprintf(p, "Vcc : %d\n", sa1100_pcmcia_socket[sock].cs_state.Vcc); 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\n", skt->cs_state.io_irq);
p+=sprintf(p, "Vpp : %d\n", sa1100_pcmcia_socket[sock].cs_state.Vpp); p+=sprintf(p, "I/O : %u (%u)\n", skt->speed_io,
p+=sprintf(p, "irq : %d\n", sa1100_pcmcia_socket[sock].cs_state.io_irq);
p+=sprintf(p, "I/O : %u (%u)\n", sa1100_pcmcia_socket[sock].speed_io,
sa1100_pcmcia_cmd_time(clock, MECR_BSIO_GET(mecr, sock))); sa1100_pcmcia_cmd_time(clock, MECR_BSIO_GET(mecr, sock)));
p+=sprintf(p, "attribute: %u (%u)\n", sa1100_pcmcia_socket[sock].speed_attr, p+=sprintf(p, "attribute: %u (%u)\n", skt->speed_attr,
sa1100_pcmcia_cmd_time(clock, MECR_BSA_GET(mecr, sock))); sa1100_pcmcia_cmd_time(clock, MECR_BSA_GET(mecr, sock)));
p+=sprintf(p, "common : %u (%u)\n", sa1100_pcmcia_socket[sock].speed_mem, p+=sprintf(p, "common : %u (%u)\n", skt->speed_mem,
sa1100_pcmcia_cmd_time(clock, MECR_BSM_GET(mecr, sock))); sa1100_pcmcia_cmd_time(clock, MECR_BSM_GET(mecr, sock)));
return p-buf; return p-buf;
} }
/* sa1100_pcmcia_proc_setup()
* ^^^^^^^^^^^^^^^^^^^^^^^^^^
* Implements the proc_setup() operation for the in-kernel PCMCIA
* service (formerly SS_ProcSetup in Card Services).
*
* Returns: 0 on success, -1 on error
*/
static void
sa1100_pcmcia_proc_setup(unsigned int sock, struct proc_dir_entry *base)
{
struct proc_dir_entry *entry;
DEBUG(4, "%s() for sock %u\n", __FUNCTION__, sock);
if ((entry = create_proc_entry("status", 0, base)) == NULL){
printk(KERN_ERR "unable to install \"status\" procfs entry\n");
return;
}
entry->read_proc = sa1100_pcmcia_proc_status;
entry->data = (void *)sock;
}
#endif /* defined(CONFIG_PROC_FS) */ #endif /* defined(CONFIG_PROC_FS) */
static struct pccard_operations sa1100_pcmcia_operations = {
init: sa1100_pcmcia_sock_init,
suspend: sa1100_pcmcia_suspend,
register_callback: sa1100_pcmcia_register_callback,
inquire_socket: sa1100_pcmcia_inquire_socket,
get_status: sa1100_pcmcia_get_status,
get_socket: sa1100_pcmcia_get_socket,
set_socket: sa1100_pcmcia_set_socket,
get_io_map: sa1100_pcmcia_get_io_map,
set_io_map: sa1100_pcmcia_set_io_map,
get_mem_map: sa1100_pcmcia_get_mem_map,
set_mem_map: sa1100_pcmcia_set_mem_map,
#ifdef CONFIG_PROC_FS
proc_setup: sa1100_pcmcia_proc_setup
#endif
};
#ifdef CONFIG_CPU_FREQ #ifdef CONFIG_CPU_FREQ
...@@ -1101,25 +854,23 @@ static int sa1100_pcmcia_proc_status(char *buf, char **start, off_t pos, ...@@ -1101,25 +854,23 @@ static int sa1100_pcmcia_proc_status(char *buf, char **start, off_t pos,
* to a core clock frequency change) is needed, this routine establishes * to a core clock frequency change) is needed, this routine establishes
* new BS_xx values consistent with the clock speed `clock'. * new BS_xx values consistent with the clock speed `clock'.
*/ */
static void sa1100_pcmcia_update_mecr(unsigned int clock){ static void sa1100_pcmcia_update_mecr(unsigned int clock)
{
unsigned int sock; unsigned int sock;
unsigned long mecr = MECR; unsigned long mecr = MECR;
for(sock = 0; sock < SA1100_PCMCIA_MAX_SOCK; ++sock){ for(sock = 0; sock < SA1100_PCMCIA_MAX_SOCK; ++sock){
struct sa1100_pcmcia_socket *skt = PCMCIA_SOCKET(sock);
MECR_BSIO_SET(mecr, sock, MECR_BSIO_SET(mecr, sock,
sa1100_pcmcia_mecr_bs(sa1100_pcmcia_socket[sock].speed_io, sa1100_pcmcia_mecr_bs(skt->speed_io, clock));
clock));
MECR_BSA_SET(mecr, sock, MECR_BSA_SET(mecr, sock,
sa1100_pcmcia_mecr_bs(sa1100_pcmcia_socket[sock].speed_attr, sa1100_pcmcia_mecr_bs(skt->speed_attr, clock));
clock));
MECR_BSM_SET(mecr, sock, MECR_BSM_SET(mecr, sock,
sa1100_pcmcia_mecr_bs(sa1100_pcmcia_socket[sock].speed_mem, sa1100_pcmcia_mecr_bs(skt->speed_mem, clock));
clock));
} }
MECR = mecr; MECR = mecr;
} }
/* sa1100_pcmcia_notifier() /* sa1100_pcmcia_notifier()
...@@ -1132,53 +883,367 @@ static void sa1100_pcmcia_update_mecr(unsigned int clock){ ...@@ -1132,53 +883,367 @@ static void sa1100_pcmcia_update_mecr(unsigned int clock){
* *
* Returns: 0 on success, -1 on error * Returns: 0 on success, -1 on error
*/ */
static int sa1100_pcmcia_notifier(struct notifier_block *nb, static int
unsigned long val, void *data){ sa1100_pcmcia_notifier(struct notifier_block *nb, unsigned long val,
void *data)
{
struct cpufreq_info *ci = data; struct cpufreq_info *ci = data;
switch(val){ switch (val) {
case CPUFREQ_MINMAX:
break;
case CPUFREQ_PRECHANGE: case CPUFREQ_PRECHANGE:
if (ci->new_freq > ci->old_freq) {
if(ci->new_freq > ci->old_freq){
DEBUG(2, "%s(): new frequency %u.%uMHz > %u.%uMHz, pre-updating\n", DEBUG(2, "%s(): new frequency %u.%uMHz > %u.%uMHz, pre-updating\n",
__FUNCTION__, __FUNCTION__,
ci->new_freq / 1000, (ci->new_freq / 100) % 10, ci->new_freq / 1000, (ci->new_freq / 100) % 10,
ci->old_freq / 1000, (ci->old_freq / 100) % 10); ci->old_freq / 1000, (ci->old_freq / 100) % 10);
sa1100_pcmcia_update_mecr(ci->new_freq); sa1100_pcmcia_update_mecr(ci->new_freq);
} }
break; break;
case CPUFREQ_POSTCHANGE: case CPUFREQ_POSTCHANGE:
if (ci->new_freq < ci->old_freq) {
if(ci->new_freq < ci->old_freq){
DEBUG(2, "%s(): new frequency %u.%uMHz < %u.%uMHz, post-updating\n", DEBUG(2, "%s(): new frequency %u.%uMHz < %u.%uMHz, post-updating\n",
__FUNCTION__, __FUNCTION__,
ci->new_freq / 1000, (ci->new_freq / 100) % 10, ci->new_freq / 1000, (ci->new_freq / 100) % 10,
ci->old_freq / 1000, (ci->old_freq / 100) % 10); ci->old_freq / 1000, (ci->old_freq / 100) % 10);
sa1100_pcmcia_update_mecr(ci->new_freq); sa1100_pcmcia_update_mecr(ci->new_freq);
} }
break; break;
default:
printk(KERN_ERR "%s(): unknown CPU frequency event %lx\n", __FUNCTION__,
val);
return -1;
} }
return 0; return 0;
} }
static struct notifier_block sa1100_pcmcia_notifier_block = { static struct notifier_block sa1100_pcmcia_notifier_block = {
notifier_call: sa1100_pcmcia_notifier notifier_call: sa1100_pcmcia_notifier
}; };
#endif
/* sa1100_register_pcmcia()
* ^^^^^^^^^^^^^^^^^^^^^^^^
*
* Register an SA1100 PCMCIA low level driver with the SA1100 core.
*/
int sa1100_register_pcmcia(struct pcmcia_low_level *ops)
{
struct pcmcia_init pcmcia_init;
struct pcmcia_state state[SA1100_PCMCIA_MAX_SOCK];
struct pcmcia_state_array state_array;
unsigned int i, clock;
unsigned long mecr;
int ret;
/*
* Refuse to replace an existing driver.
*/
if (pcmcia_low_level)
return -EBUSY;
pcmcia_low_level = ops;
pcmcia_init.handler = sa1100_pcmcia_interrupt;
ret = ops->init(&pcmcia_init);
if (ret < 0) {
printk(KERN_ERR "Unable to initialize kernel PCMCIA service (%d).\n", ret);
if (ret == -1)
ret = -EIO;
goto out;
}
sa1100_pcmcia_socket_count = ret;
state_array.size = ret;
state_array.state = state;
memset(state, 0, sizeof(state));
if (ops->socket_state(&state_array) < 0) {
printk(KERN_ERR "Unable to get PCMCIA status driver.\n");
ret = -EIO;
goto shutdown;
}
/*
* We initialize the MECR to default values here, because we are
* not guaranteed to see a SetIOMap operation at runtime.
*/
mecr = 0;
clock = cpufreq_get(0);
for (i = 0; i < sa1100_pcmcia_socket_count; i++) {
struct sa1100_pcmcia_socket *skt = PCMCIA_SOCKET(i);
struct pcmcia_irq_info irq_info;
if (!request_mem_region(_PCMCIA(i), PCMCIASp, "PCMCIA")) {
ret = -EBUSY;
goto out_err;
}
irq_info.sock = i;
irq_info.irq = -1;
ret = ops->get_irq_info(&irq_info);
if (ret < 0)
printk(KERN_ERR "Unable to get IRQ for socket %u (%d)\n", i, ret);
skt->irq = irq_info.irq;
skt->k_state = state[i];
skt->speed_io = SA1100_PCMCIA_IO_ACCESS;
skt->speed_attr = SA1100_PCMCIA_5V_MEM_ACCESS;
skt->speed_mem = SA1100_PCMCIA_5V_MEM_ACCESS;
skt->phys_attr = _PCMCIAAttr(i);
skt->phys_mem = _PCMCIAMem(i);
skt->virt_io = ioremap(_PCMCIAIO(i), 0x10000);
if (skt->virt_io == NULL) {
ret = -ENOMEM;
goto out_err;
}
MECR_FAST_SET(mecr, i, 0);
MECR_BSIO_SET(mecr, i, sa1100_pcmcia_mecr_bs(skt->speed_io, clock));
MECR_BSA_SET(mecr, i, sa1100_pcmcia_mecr_bs(skt->speed_attr, clock));
MECR_BSM_SET(mecr, i, sa1100_pcmcia_mecr_bs(skt->speed_mem, clock));
}
MECR = mecr;
/* Only advertise as many sockets as we can detect */
ret = register_ss_entry(sa1100_pcmcia_socket_count,
&sa1100_pcmcia_operations);
if (ret < 0) {
printk(KERN_ERR "Unable to register sockets\n");
goto out_err;
}
/*
* Start the event poll timer. It will reschedule by itself afterwards.
*/
sa1100_pcmcia_poll_event(0);
return 0;
out_err:
for (i = 0; i < sa1100_pcmcia_socket_count; i++) {
struct sa1100_pcmcia_socket *skt = PCMCIA_SOCKET(i);
iounmap(skt->virt_io);
skt->virt_io = NULL;
release_mem_region(_PCMCIA(i), PCMCIASp);
}
shutdown:
ops->shutdown();
out:
pcmcia_low_level = NULL;
return ret;
}
/* sa1100_unregister_pcmcia()
* ^^^^^^^^^^^^^^^^^^^^^^^^^^
*
* Unregister a previously registered pcmcia driver
*/
void sa1100_unregister_pcmcia(struct pcmcia_low_level *ops)
{
int i;
if (!ops)
return;
if (ops != pcmcia_low_level) {
printk(KERN_DEBUG "PCMCIA: Trying to unregister wrong "
"low-level driver (%p != %p)", ops,
pcmcia_low_level);
return;
}
del_timer_sync(&poll_timer);
unregister_ss_entry(&sa1100_pcmcia_operations);
for (i = 0; i < sa1100_pcmcia_socket_count; i++) {
struct sa1100_pcmcia_socket *skt = PCMCIA_SOCKET(i);
iounmap(skt->virt_io);
skt->virt_io = NULL;
release_mem_region(_PCMCIA(i), PCMCIASp);
}
ops->shutdown();
flush_scheduled_tasks();
pcmcia_low_level = NULL;
}
/* sa1100_pcmcia_init()
* ^^^^^^^^^^^^^^^^^^^^
*
* This routine performs a basic sanity check to ensure that this
* kernel has been built with the appropriate board-specific low-level
* PCMCIA support, performs low-level PCMCIA initialization, registers
* this socket driver with Card Services, and then spawns the daemon
* thread which is the real workhorse of the socket driver.
*
* Returns: 0 on success, -1 on error
*/
static int __init sa1100_pcmcia_init(void)
{
servinfo_t info;
int ret, i;
printk(KERN_INFO "SA-1100 PCMCIA (CS release %s)\n", CS_RELEASE);
CardServices(GetCardServicesInfo, &info);
if (info.Revision != CS_RELEASE_CODE) {
printk(KERN_ERR "Card Services release codes do not match\n");
return -EINVAL;
}
for (i = 0; i < SA1100_PCMCIA_MAX_SOCK; i++) {
struct sa1100_pcmcia_socket *skt = PCMCIA_SOCKET(i);
skt->speed_io = SA1100_PCMCIA_IO_ACCESS;
skt->speed_attr = SA1100_PCMCIA_5V_MEM_ACCESS;
skt->speed_mem = SA1100_PCMCIA_5V_MEM_ACCESS;
}
#ifdef CONFIG_CPU_FREQ
ret = cpufreq_register_notifier(&sa1100_pcmcia_notifier_block);
if (ret < 0) {
printk(KERN_ERR "Unable to register CPU frequency change "
"notifier (%d)\n", ret);
return ret;
}
#endif
#ifdef CONFIG_SA1100_ADSBITSY
pcmcia_adsbitsy_init();
#endif
#ifdef CONFIG_SA1100_ASSABET
pcmcia_assabet_init();
#endif
#ifdef CONFIG_SA1100_BADGE4
pcmcia_badge4_init();
#endif
#ifdef CONFIG_SA1100_CERF
pcmcia_cerf_init();
#endif
#ifdef CONFIG_SA1100_FLEXANET
pcmcia_flexanet_init();
#endif
#ifdef CONFIG_SA1100_FREEBIRD
pcmcia_freebird_init();
#endif
#ifdef CONFIG_SA1100_GRAPHICSCLIENT
pcmcia_gcplus_init();
#endif
#ifdef CONFIG_SA1100_GRAPHICSMASTER
pcmcia_graphicsmaster_init();
#endif
#ifdef CONFIG_SA1100_JORNADA720
pcmcia_jornada720_init();
#endif
#ifdef CONFIG_ASSABET_NEPONSET
pcmcia_neponset_init();
#endif
#ifdef CONFIG_SA1100_PANGOLIN
pcmcia_pangolin_init();
#endif
#ifdef CONFIG_SA1100_PFS168
pcmcia_pfs_init();
#endif
#ifdef CONFIG_SA1100_SHANNON
pcmcia_shannon_init();
#endif #endif
#ifdef CONFIG_SA1100_SIMPAD
pcmcia_simpad_init();
#endif
#ifdef CONFIG_SA1100_STORK
pcmcia_stork_init();
#endif
#ifdef CONFIG_SA1100_XP860
pcmcia_xp860_init();
#endif
#ifdef CONFIG_SA1100_YOPY
pcmcia_yopy_init();
#endif
return 0;
}
/* sa1100_pcmcia_exit()
* ^^^^^^^^^^^^^^^^^^^^
* Invokes the low-level kernel service to free IRQs associated with this
* socket controller and reset GPIO edge detection.
*/
static void __exit sa1100_pcmcia_exit(void)
{
#ifdef CONFIG_SA1100_ADSBITSY
pcmcia_adsbitsy_exit();
#endif
#ifdef CONFIG_SA1100_ASSABET
pcmcia_assabet_exit();
#endif
#ifdef CONFIG_SA1100_BADGE4
pcmcia_badge4_exit();
#endif
#ifdef CONFIG_SA1100_CERF
pcmcia_cerf_exit();
#endif
#ifdef CONFIG_SA1100_FLEXANET
pcmcia_flexanet_exit();
#endif
#ifdef CONFIG_SA1100_FREEBIRD
pcmcia_freebird_exit();
#endif
#ifdef CONFIG_SA1100_GRAPHICSCLIENT
pcmcia_gcplus_exit();
#endif
#ifdef CONFIG_SA1100_GRAPHICSMASTER
pcmcia_graphicsmaster_exit();
#endif
#ifdef CONFIG_SA1100_JORNADA720
pcmcia_jornada720_exit();
#endif
#ifdef CONFIG_ASSABET_NEPONSET
pcmcia_neponset_exit();
#endif
#ifdef CONFIG_SA1100_PANGOLIN
pcmcia_pangolin_exit();
#endif
#ifdef CONFIG_SA1100_PFS168
pcmcia_pfs_exit();
#endif
#ifdef CONFIG_SA1100_SHANNON
pcmcia_shannon_exit();
#endif
#ifdef CONFIG_SA1100_SIMPAD
pcmcia_simpad_exit();
#endif
#ifdef CONFIG_SA1100_STORK
pcmcia_stork_exit();
#endif
#ifdef CONFIG_SA1100_XP860
pcmcia_xp860_exit();
#endif
#ifdef CONFIG_SA1100_YOPY
pcmcia_yopy_exit();
#endif
if (pcmcia_low_level) {
printk(KERN_ERR "PCMCIA: low level driver still registered\n");
sa1100_unregister_pcmcia(pcmcia_low_level);
}
#ifdef CONFIG_CPU_FREQ
cpufreq_unregister_notifier(&sa1100_pcmcia_notifier_block);
#endif
}
MODULE_AUTHOR("John Dorsey <john+@cs.cmu.edu>");
MODULE_DESCRIPTION("Linux PCMCIA Card Services: SA-1100 Socket Controller");
MODULE_LICENSE("Dual MPL/GPL");
module_init(sa1100_pcmcia_init);
module_exit(sa1100_pcmcia_exit);
/*
* linux/include/asm/arch/pcmcia.h
*
* Copyright (C) 2000 John G Dorsey <john+@cs.cmu.edu>
*
* This file contains definitions for the low-level SA-1100 kernel PCMCIA
* interface. Please see linux/Documentation/arm/SA1100/PCMCIA for details.
*/
#ifndef _ASM_ARCH_PCMCIA
#define _ASM_ARCH_PCMCIA
/* Ideally, we'd support up to MAX_SOCK sockets, but the SA-1100 only
* has support for two. This shows up in lots of hardwired ways, such
* as the fact that MECR only has enough bits to configure two sockets.
* Since it's so entrenched in the hardware, limiting the software
* in this way doesn't seem too terrible.
*/
#define SA1100_PCMCIA_MAX_SOCK (2)
struct pcmcia_init {
void (*handler)(int irq, void *dev, struct pt_regs *regs);
};
struct pcmcia_state {
unsigned detect: 1,
ready: 1,
bvd1: 1,
bvd2: 1,
wrprot: 1,
vs_3v: 1,
vs_Xv: 1;
};
struct pcmcia_state_array {
unsigned int size;
struct pcmcia_state *state;
};
struct pcmcia_configure {
unsigned sock: 8,
vcc: 8,
vpp: 8,
output: 1,
speaker: 1,
reset: 1,
irq: 1;
};
struct pcmcia_irq_info {
unsigned int sock;
unsigned int irq;
};
struct pcmcia_low_level {
int (*init)(struct pcmcia_init *);
int (*shutdown)(void);
int (*socket_state)(struct pcmcia_state_array *);
int (*get_irq_info)(struct pcmcia_irq_info *);
int (*configure_socket)(const struct pcmcia_configure *);
/*
* Enable card status IRQs on (re-)initialisation. This can
* be called at initialisation, power management event, or
* pcmcia event.
*/
int (*socket_init)(int sock);
/*
* Disable card status IRQs and PCMCIA bus on suspend.
*/
int (*socket_suspend)(int sock);
};
extern int sa1100_register_pcmcia(struct pcmcia_low_level *);
extern void sa1100_unregister_pcmcia(struct pcmcia_low_level *);
#endif
...@@ -14,10 +14,13 @@ ...@@ -14,10 +14,13 @@
#include <linux/kernel.h> #include <linux/kernel.h>
#include <linux/sched.h> #include <linux/sched.h>
#include <linux/delay.h> #include <linux/delay.h>
#include <linux/init.h>
#include <asm/hardware.h> #include <asm/hardware.h>
#include <asm/irq.h> #include <asm/irq.h>
#include <asm/arch/pcmcia.h> #include "sa1100_generic.h"
#error This is broken!
#define S0_CD_IRQ 60 // Socket 0 Card Detect IRQ #define S0_CD_IRQ 60 // Socket 0 Card Detect IRQ
#define S0_STS_IRQ 55 // Socket 0 PCMCIA IRQ #define S0_STS_IRQ 55 // Socket 0 PCMCIA IRQ
...@@ -47,8 +50,9 @@ static int gcplus_pcmcia_init(struct pcmcia_init *init) ...@@ -47,8 +50,9 @@ static int gcplus_pcmcia_init(struct pcmcia_init *init)
irq = S0_CD_IRQ; irq = S0_CD_IRQ;
res = request_irq(irq, init->handler, SA_INTERRUPT, "PCMCIA 0 CD", NULL); res = request_irq(irq, init->handler, SA_INTERRUPT, "PCMCIA 0 CD", NULL);
if (res < 0) { if (res < 0) {
printk(KERN_ERR "%s: Request for IRQ %lu failed\n", __FUNCTION__, irq); printk(KERN_ERR "%s: request for IRQ%d failed (%d)\n",
return -1; __FUNCTION__, irq, res);
return res;
} }
return 1; // 1 PCMCIA Slot return 1; // 1 PCMCIA Slot
...@@ -106,7 +110,7 @@ static int gcplus_pcmcia_configure_socket(const struct pcmcia_configure ...@@ -106,7 +110,7 @@ static int gcplus_pcmcia_configure_socket(const struct pcmcia_configure
if(configure->sock>1) return -1; if(configure->sock>1) return -1;
save_flags_cli(flags); local_irq_save(flags);
switch (configure->vcc) { switch (configure->vcc) {
case 0: case 0:
...@@ -126,7 +130,7 @@ static int gcplus_pcmcia_configure_socket(const struct pcmcia_configure ...@@ -126,7 +130,7 @@ static int gcplus_pcmcia_configure_socket(const struct pcmcia_configure
default: default:
printk(KERN_ERR "%s(): unrecognized Vcc %u\n", __FUNCTION__, printk(KERN_ERR "%s(): unrecognized Vcc %u\n", __FUNCTION__,
configure->vcc); configure->vcc);
restore_flags(flags); local_irq_restore(flags);
return -1; return -1;
} }
...@@ -139,16 +143,44 @@ static int gcplus_pcmcia_configure_socket(const struct pcmcia_configure ...@@ -139,16 +143,44 @@ static int gcplus_pcmcia_configure_socket(const struct pcmcia_configure
*PCMCIA_Power |= ADS_CS_PR_A_RESET; *PCMCIA_Power |= ADS_CS_PR_A_RESET;
mdelay(30); mdelay(30);
restore_flags(flags); local_irq_restore(flags);
return 0;
}
static int gcplus_pcmcia_socket_init(int sock)
{
return 0;
}
static int gcplus_pcmcia_socket_suspend(int sock)
{
return 0; return 0;
} }
struct pcmcia_low_level gcplus_pcmcia_ops = { static struct pcmcia_low_level gcplus_pcmcia_ops = {
gcplus_pcmcia_init, init: gcplus_pcmcia_init,
gcplus_pcmcia_shutdown, shutdown: gcplus_pcmcia_shutdown,
gcplus_pcmcia_socket_state, socket_state: gcplus_pcmcia_socket_state,
gcplus_pcmcia_get_irq_info, get_irq_info: gcplus_pcmcia_get_irq_info,
gcplus_pcmcia_configure_socket configure_socket: gcplus_pcmcia_configure_socket,
socket_init: gcplus_pcmcia_socket_init,
socket_suspend: gcplus_pcmcia_socket_suspend,
}; };
int __init pcmcia_gcplus_init(void)
{
int ret = -ENODEV;
if (machine_is_gcplus())
ret = sa1100_register_pcmcia(&gcplus_pcmcia_ops);
return ret;
}
void __exit pcmcia_gcplus_exit(void)
{
sa1100_unregister_pcmcia(&gcplus_pcmcia_ops);
}
...@@ -10,11 +10,12 @@ ...@@ -10,11 +10,12 @@
*/ */
#include <linux/kernel.h> #include <linux/kernel.h>
#include <linux/sched.h> #include <linux/sched.h>
#include <linux/init.h>
#include <asm/delay.h>
#include <asm/hardware.h> #include <asm/hardware.h>
#include <asm/irq.h>
#include <asm/arch/pcmcia.h> #include "sa1100_generic.h"
#include "sa1111_generic.h"
static int graphicsmaster_pcmcia_init(struct pcmcia_init *init) static int graphicsmaster_pcmcia_init(struct pcmcia_init *init)
{ {
...@@ -26,190 +27,82 @@ static int graphicsmaster_pcmcia_init(struct pcmcia_init *init) ...@@ -26,190 +27,82 @@ static int graphicsmaster_pcmcia_init(struct pcmcia_init *init)
/* Disable Power 3.3V/5V for PCMCIA/CF */ /* Disable Power 3.3V/5V for PCMCIA/CF */
PA_DWR |= GPIO_GPIO0 | GPIO_GPIO1 | GPIO_GPIO2 | GPIO_GPIO3; PA_DWR |= GPIO_GPIO0 | GPIO_GPIO1 | GPIO_GPIO2 | GPIO_GPIO3;
INTPOL1 |= (1 << (S0_READY_NINT - SA1111_IRQ(32))) | /* why? */
(1 << (S1_READY_NINT - SA1111_IRQ(32))) |
(1 << (S0_CD_VALID - SA1111_IRQ(32))) |
(1 << (S1_CD_VALID - SA1111_IRQ(32))) |
(1 << (S0_BVD1_STSCHG - SA1111_IRQ(32))) |
(1 << (S1_BVD1_STSCHG - SA1111_IRQ(32)));
return_val+=request_irq(S0_CD_VALID, init->handler, SA_INTERRUPT,
"GC Master PCMCIA (0) CD", NULL);
return_val+=request_irq(S1_CD_VALID, init->handler, SA_INTERRUPT,
"GC Master CF (1) CD", NULL);
return_val+=request_irq(S0_BVD1_STSCHG, init->handler, SA_INTERRUPT,
"GC Master PCMCIA (0) BVD1", NULL);
return_val+=request_irq(S1_BVD1_STSCHG, init->handler, SA_INTERRUPT,
"GC Master CF (1) BVD1", NULL);
MECR = 0x09430943; MECR = 0x09430943;
return (return_val<0) ? -1 : 2; return sa1111_pcmcia_init(init);
}
static int graphicsmaster_pcmcia_shutdown(void)
{
free_irq(S0_CD_VALID, NULL);
free_irq(S1_CD_VALID, NULL);
free_irq(S0_BVD1_STSCHG, NULL);
free_irq(S1_BVD1_STSCHG, NULL);
INTPOL1 &= ~((1 << (S0_CD_VALID - SA1111_IRQ(32))) |
(1 << (S1_CD_VALID - SA1111_IRQ(32))) |
(1 << (S0_BVD1_STSCHG - SA1111_IRQ(32))) |
(1 << (S1_BVD1_STSCHG - SA1111_IRQ(32))));
return 0;
}
static int graphicsmaster_pcmcia_socket_state(struct pcmcia_state_array *state_array)
{
unsigned long status;
int return_val=1;
if(state_array->size<2) return -1;
memset(state_array->state, 0,
(state_array->size)*sizeof(struct pcmcia_state));
status=PCSR;
state_array->state[0].detect=((status & PCSR_S0_DETECT)==0)?1:0;
state_array->state[0].ready=((status & PCSR_S0_READY)==0)?0:1;
state_array->state[0].bvd1=((status & PCSR_S0_BVD1)==0)?0:1;
state_array->state[0].bvd2=((status & PCSR_S0_BVD2)==0)?0:1;
state_array->state[0].wrprot=((status & PCSR_S0_WP)==0)?0:1;
state_array->state[0].vs_3v=((status & PCSR_S0_VS1)==0)?1:0;
state_array->state[0].vs_Xv=((status & PCSR_S0_VS2)==0)?1:0;
state_array->state[1].detect=((status & PCSR_S1_DETECT)==0)?1:0;
state_array->state[1].ready=((status & PCSR_S1_READY)==0)?0:1;
state_array->state[1].bvd1=((status & PCSR_S1_BVD1)==0)?0:1;
state_array->state[1].bvd2=((status & PCSR_S1_BVD2)==0)?0:1;
state_array->state[1].wrprot=((status & PCSR_S1_WP)==0)?0:1;
state_array->state[1].vs_3v=((status & PCSR_S1_VS1)==0)?1:0;
state_array->state[1].vs_Xv=((status & PCSR_S1_VS2)==0)?1:0;
return return_val;
} }
static int graphicsmaster_pcmcia_get_irq_info(struct pcmcia_irq_info *info) static int
graphicsmaster_pcmcia_configure_socket(const struct pcmcia_configure *conf)
{ {
unsigned int pa_dwr_mask, pa_dwr_set;
int ret;
switch(info->sock){ switch (conf->sock) {
case 0: case 0:
info->irq=S0_READY_NINT; pa_dwr_mask = GPIO_GPIO0 | GPIO_GPIO1;
break;
case 1:
info->irq=S1_READY_NINT;
break;
switch (conf->vcc) {
default: default:
return -1; case 0: pa_dwr_set = GPIO_GPIO0 | GPIO_GPIO1; break;
case 33: pa_dwr_set = GPIO_GPIO1; break;
case 50: pa_dwr_set = GPIO_GPIO0; break;
} }
return 0;
}
static int graphicsmaster_pcmcia_configure_socket(const struct pcmcia_configure *configure)
{
unsigned long pccr=PCCR, gpio=PA_DWR;
switch(configure->sock){
case 0:
switch(configure->vcc){
case 0:
pccr = (pccr & ~PCCR_S0_FLT);
gpio |= GPIO_GPIO0 | GPIO_GPIO1;
break;
case 33:
pccr = (pccr & ~PCCR_S0_PSE) | PCCR_S0_FLT | PCCR_S0_PWAITEN;
gpio &= ~(GPIO_GPIO0 | GPIO_GPIO1);
gpio &= ~GPIO_GPIO0;
break;
case 50:
pccr = (pccr | PCCR_S0_PSE | PCCR_S0_FLT | PCCR_S0_PWAITEN);
gpio &= ~(GPIO_GPIO0 | GPIO_GPIO1);
gpio |= GPIO_GPIO0;
break;
default:
printk(KERN_ERR "%s(): unrecognized Vcc %u\n", __FUNCTION__,
configure->vcc);
return -1;
}
pccr=(configure->reset)?(pccr | PCCR_S0_RST):(pccr & ~PCCR_S0_RST);
break; break;
case 1: case 1:
switch(configure->vcc){ pa_dwr_mask = GPIO_GPIO2 | GPIO_GPIO3;
case 0:
pccr = (pccr & ~PCCR_S1_FLT);
gpio |= GPIO_GPIO2 | GPIO_GPIO3;
break;
case 33:
pccr = (pccr & ~PCCR_S1_PSE) | PCCR_S1_FLT | PCCR_S1_PWAITEN;
gpio &= ~(GPIO_GPIO2 | GPIO_GPIO3);
gpio &= ~GPIO_GPIO2;
break;
case 50:
pccr = (pccr | PCCR_S1_PSE | PCCR_S1_FLT | PCCR_S1_PWAITEN);
gpio &= ~(GPIO_GPIO2 | GPIO_GPIO3);
gpio |= GPIO_GPIO2;
break;
switch (conf->vcc) {
default: default:
printk(KERN_ERR "%s(): unrecognized Vcc %u\n", __FUNCTION__, case 0: pa_dwr_set = GPIO_GPIO2 | GPIO_GPIO3; break;
configure->vcc); case 33: pa_dwr_set = GPIO_GPIO3; break;
return -1; case 50: pa_dwr_set = GPIO_GPIO2; break;
}
} }
if(configure->vpp!=configure->vcc && configure->vpp!=0){ if (conf->vpp != conf->vcc && conf->vpp != 0) {
printk(KERN_ERR "%s(): CF slot cannot support Vpp %u\n", __FUNCTION__, printk(KERN_ERR "%s(): CF slot cannot support Vpp %u\n", __FUNCTION__,
configure->vpp); conf->vpp);
return -1; return -1;
} }
pccr=(configure->reset)?(pccr | PCCR_S1_RST):(pccr & ~PCCR_S1_RST); ret = sa1111_pcmcia_configure_socket(conf);
if (ret == 0) {
unsigned long flags;
break; local_irq_save(flags);
PA_DWR = (PA_DWR & ~pa_dwr_mask) | pa_dwr_set;
default: local_irq_restore(flags);
return -1;
} }
PCCR = pccr; return ret;
PA_DWR = gpio;
return 0;
} }
struct pcmcia_low_level graphicsmaster_pcmcia_ops = { static struct pcmcia_low_level graphicsmaster_pcmcia_ops = {
graphicsmaster_pcmcia_init, init: graphicsmaster_pcmcia_init,
graphicsmaster_pcmcia_shutdown, shutdown: sa1111_pcmcia_shutdown,
graphicsmaster_pcmcia_socket_state, socket_state: sa1111_pcmcia_socket_state,
graphicsmaster_pcmcia_get_irq_info, get_irq_info: sa1111_pcmcia_get_irq_info,
graphicsmaster_pcmcia_configure_socket configure_socket: graphicsmaster_pcmcia_configure_socket,
socket_init: sa1111_pcmcia_socket_init,
socket_suspend: sa1111_pcmcia_socket_suspend,
}; };
int __init pcmcia_graphicsmaster_init(void)
{
int ret = -ENODEV;
if (machine_is_graphicsmaster())
ret = sa1100_register_pcmcia(&graphicsmaster_pcmcia_ops);
return ret;
}
void __exit pcmcia_graphicsmaster_exit(void)
{
sa1100_unregister_pcmcia(&graphicsmaster_pcmcia_ops);
}
...@@ -6,92 +6,106 @@ ...@@ -6,92 +6,106 @@
*/ */
#include <linux/kernel.h> #include <linux/kernel.h>
#include <linux/sched.h> #include <linux/sched.h>
#include <linux/init.h>
#include <asm/hardware.h> #include <asm/hardware.h>
#include <asm/irq.h> #include <asm/irq.h>
#include <asm/arch/pcmcia.h> #include "sa1100_generic.h"
static struct irqs {
static int h3600_pcmcia_init(struct pcmcia_init *init){ int irq;
int irq, res; const char *str;
} irqs[] = {
{ IRQ_GPIO_H3600_PCMCIA_CD0, "PCMCIA CD0" },
{ IRQ_GPIO_H3600_PCMCIA_CD1, "PCMCIA CD1" }
};
/* Enable CF bus: */ static int h3600_pcmcia_init(struct pcmcia_init *init)
set_h3600_egpio(EGPIO_H3600_OPT_NVRAM_ON); {
clr_h3600_egpio(EGPIO_H3600_OPT_RESET); int i, res;
/* All those are inputs */ /*
GPDR &= ~(GPIO_H3600_PCMCIA_CD0 | GPIO_H3600_PCMCIA_CD1 | GPIO_H3600_PCMCIA_IRQ0| GPIO_H3600_PCMCIA_IRQ1); * Set transition detect
*/
set_irq_type(IRQ_GPIO_H3600_PCMCIA_IRQ0, IRQT_FALLING);
set_irq_type(IRQ_GPIO_H3600_PCMCIA_IRQ1, IRQT_FALLING);
/* Set transition detect */ /*
set_GPIO_IRQ_edge( GPIO_H3600_PCMCIA_CD0 | GPIO_H3600_PCMCIA_CD1, GPIO_BOTH_EDGES ); * Register interrupts
set_GPIO_IRQ_edge( GPIO_H3600_PCMCIA_IRQ0| GPIO_H3600_PCMCIA_IRQ1, GPIO_FALLING_EDGE ); */
for (i = res = 0; i < ARRAY_SIZE(irqs); i++) {
res = request_irq(irqs[i].irq, init->handler, SA_INTERRUPT,
irqs[i].str, NULL);
if (res)
break;
}
/* Register interrupts */ if (res) {
irq = IRQ_GPIO_H3600_PCMCIA_CD0; printk(KERN_ERR "%s: request for IRQ%d failed (%d)\n",
res = request_irq( irq, init->handler, SA_INTERRUPT, "PCMCIA_CD0", NULL ); __FUNCTION__, irqs[i].irq, res);
if( res < 0 ) goto irq_err;
irq = IRQ_GPIO_H3600_PCMCIA_CD1;
res = request_irq( irq, init->handler, SA_INTERRUPT, "PCMCIA_CD1", NULL );
if( res < 0 ) goto irq_err;
return 2; while (i--)
free_irq(irqs[i].irq, NULL);
}
irq_err: return res ? res : 2;
printk( KERN_ERR __FUNCTION__ ": Request for IRQ %u failed\n", irq );
return -1;
} }
static int h3600_pcmcia_shutdown(void) static int h3600_pcmcia_shutdown(void)
{ {
/* disable IRQs */ int i;
free_irq( IRQ_GPIO_H3600_PCMCIA_CD0, NULL );
free_irq( IRQ_GPIO_H3600_PCMCIA_CD1, NULL ); /*
* disable IRQs
*/
for (i = 0; i < ARRAY_SIZE(irqs); i++)
free_irq(irqs[i].irq, NULL);
/* Disable CF bus: */ /* Disable CF bus: */
clr_h3600_egpio(EGPIO_H3600_OPT_NVRAM_ON|EGPIO_H3600_OPT_ON); clr_h3600_egpio(IPAQ_EGPIO_OPT_NVRAM_ON);
set_h3600_egpio(EGPIO_H3600_OPT_RESET); clr_h3600_egpio(IPAQ_EGPIO_OPT_ON);
set_h3600_egpio(IPAQ_EGPIO_OPT_RESET);
return 0; return 0;
} }
static int h3600_pcmcia_socket_state(struct pcmcia_state_array static int
*state_array){ h3600_pcmcia_socket_state(struct pcmcia_state_array *state)
{
unsigned long levels; unsigned long levels;
if(state_array->size<2) return -1; if (state->size < 2)
return -1;
memset(state_array->state, 0,
(state_array->size)*sizeof(struct pcmcia_state));
levels=GPLR; levels = GPLR;
state_array->state[0].detect=((levels & GPIO_H3600_PCMCIA_CD0)==0)?1:0; state->state[0].detect = levels & GPIO_H3600_PCMCIA_CD0 ? 0 : 1;
state_array->state[0].ready=(levels & GPIO_H3600_PCMCIA_IRQ0)?1:0; state->state[0].ready = levels & GPIO_H3600_PCMCIA_IRQ0 ? 1 : 0;
state_array->state[0].bvd1= 0; state->state[0].bvd1 = 0;
state_array->state[0].bvd2= 0; state->state[0].bvd2 = 0;
state_array->state[0].wrprot=0; /* Not available on H3600. */ state->state[0].wrprot = 0; /* Not available on H3600. */
state_array->state[0].vs_3v=0; state->state[0].vs_3v = 0;
state_array->state[0].vs_Xv=0; state->state[0].vs_Xv = 0;
state_array->state[1].detect=((levels & GPIO_H3600_PCMCIA_CD1)==0)?1:0; state->state[1].detect = levels & GPIO_H3600_PCMCIA_CD1 ? 0 : 1;
state_array->state[1].ready=(levels & GPIO_H3600_PCMCIA_IRQ1)?1:0; state->state[1].ready = levels & GPIO_H3600_PCMCIA_IRQ1 ? 1 : 0;
state_array->state[1].bvd1=0; state->state[1].bvd1 = 0;
state_array->state[1].bvd2=0; state->state[1].bvd2 = 0;
state_array->state[1].wrprot=0; /* Not available on H3600. */ state->state[1].wrprot = 0; /* Not available on H3600. */
state_array->state[1].vs_3v=0; state->state[1].vs_3v = 0;
state_array->state[1].vs_Xv=0; state->state[1].vs_Xv = 0;
return 1; return 1;
} }
static int h3600_pcmcia_get_irq_info(struct pcmcia_irq_info *info){ static int h3600_pcmcia_get_irq_info(struct pcmcia_irq_info *info)
{
switch (info->sock) { switch (info->sock) {
case 0: case 0:
info->irq=IRQ_GPIO_H3600_PCMCIA_IRQ0; info->irq = IRQ_GPIO_H3600_PCMCIA_IRQ0;
break; break;
case 1: case 1:
info->irq=IRQ_GPIO_H3600_PCMCIA_IRQ1; info->irq = IRQ_GPIO_H3600_PCMCIA_IRQ1;
break; break;
default: default:
return -1; return -1;
...@@ -99,49 +113,85 @@ static int h3600_pcmcia_get_irq_info(struct pcmcia_irq_info *info){ ...@@ -99,49 +113,85 @@ static int h3600_pcmcia_get_irq_info(struct pcmcia_irq_info *info){
return 0; return 0;
} }
static int h3600_pcmcia_configure_socket(const struct pcmcia_configure static int
*configure) h3600_pcmcia_configure_socket(const struct pcmcia_configure *conf)
{ {
unsigned long flags; if (conf->sock > 1)
return -1;
if (conf->vcc != 0 && conf->vcc != 33 && conf->vcc != 50) {
printk(KERN_ERR "h3600_pcmcia: unrecognized Vcc %u.%uV\n",
conf->vcc / 10, conf->vcc % 10);
return -1;
}
if (conf->reset)
set_h3600_egpio(IPAQ_EGPIO_CARD_RESET);
else
clr_h3600_egpio(IPAQ_EGPIO_CARD_RESET);
if(configure->sock>1) return -1; /* Silently ignore Vpp, output enable, speaker enable. */
return 0;
}
static int h3600_pcmcia_socket_init(int sock)
{
/* Enable CF bus: */
set_h3600_egpio(IPAQ_EGPIO_OPT_NVRAM_ON);
set_h3600_egpio(IPAQ_EGPIO_OPT_ON);
clr_h3600_egpio(IPAQ_EGPIO_OPT_RESET);
save_flags_cli(flags); set_current_state(TASK_UNINTERRUPTIBLE);
schedule_timeout(10*HZ / 1000);
switch (configure->vcc) { switch (sock) {
case 0: case 0:
clr_h3600_egpio(EGPIO_H3600_OPT_ON); set_irq_type(IRQ_GPIO_H3600_PCMCIA_CD0, IRQT_BOTHEDGE);
break; break;
case 1:
case 33: set_irq_type(IRQ_GPIO_H3600_PCMCIA_CD1, IRQT_BOTHEDGE);
case 50:
set_h3600_egpio(EGPIO_H3600_OPT_ON);
break; break;
default:
printk(KERN_ERR "%s(): unrecognized Vcc %u\n", __FUNCTION__,
configure->vcc);
restore_flags(flags);
return -1;
} }
if (configure->reset) return 0;
set_h3600_egpio(EGPIO_H3600_CARD_RESET); }
else
clr_h3600_egpio(EGPIO_H3600_CARD_RESET);
/* Silently ignore Vpp, output enable, speaker enable. */ static int h3600_pcmcia_socket_suspend(int sock)
{
switch (sock) {
case 0:
set_irq_type(IRQ_GPIO_H3600_PCMCIA_CD0, IRQT_NOEDGE);
break;
case 1:
set_irq_type(IRQ_GPIO_H3600_PCMCIA_CD1, IRQT_NOEDGE);
break;
}
restore_flags(flags); /*
* FIXME: This doesn't fit well. We don't have the mechanism in
* the generic PCMCIA layer to deal with the idea of two sockets
* on one bus. We rely on the cs.c behaviour shutting down
* socket 0 then socket 1.
*/
if (sock == 1) {
clr_h3600_egpio(IPAQ_EGPIO_OPT_ON);
clr_h3600_egpio(IPAQ_EGPIO_OPT_NVRAM_ON);
/* hmm, does this suck power? */
set_h3600_egpio(IPAQ_EGPIO_OPT_RESET);
}
return 0; return 0;
} }
struct pcmcia_low_level h3600_pcmcia_ops = { struct pcmcia_low_level h3600_pcmcia_ops = {
h3600_pcmcia_init, init: h3600_pcmcia_init,
h3600_pcmcia_shutdown, shutdown: h3600_pcmcia_shutdown,
h3600_pcmcia_socket_state, socket_state: h3600_pcmcia_socket_state,
h3600_pcmcia_get_irq_info, get_irq_info: h3600_pcmcia_get_irq_info,
h3600_pcmcia_configure_socket configure_socket: h3600_pcmcia_configure_socket,
socket_init: h3600_pcmcia_socket_init,
socket_suspend: h3600_pcmcia_socket_suspend,
}; };
...@@ -6,21 +6,24 @@ ...@@ -6,21 +6,24 @@
*/ */
#include <linux/kernel.h> #include <linux/kernel.h>
#include <linux/sched.h> #include <linux/sched.h>
#include <linux/init.h>
#include <asm/delay.h>
#include <asm/hardware.h> #include <asm/hardware.h>
#include <asm/irq.h>
#include <asm/arch/pcmcia.h> #include "sa1100_generic.h"
#include "sa1111_generic.h"
#define SOCKET0_POWER GPIO_GPIO0 #define SOCKET0_POWER GPIO_GPIO0
#define SOCKET0_3V GPIO_GPIO2 #define SOCKET0_3V GPIO_GPIO2
#define SOCKET1_POWER (GPIO_GPIO1 | GPIO_GPIO3) #define SOCKET1_POWER (GPIO_GPIO1 | GPIO_GPIO3)
#warning *** Does SOCKET1_3V actually do anything?
#define SOCKET1_3V GPIO_GPIO3 #define SOCKET1_3V GPIO_GPIO3
static int jornada720_pcmcia_init(struct pcmcia_init *init) static int jornada720_pcmcia_init(struct pcmcia_init *init)
{ {
int return_val=0; /*
* What is all this crap for?
*/
GRER |= 0x00000002; GRER |= 0x00000002;
/* Set GPIO_A<3:1> to be outputs for PCMCIA/CF power controller: */ /* Set GPIO_A<3:1> to be outputs for PCMCIA/CF power controller: */
PA_DDR = 0; PA_DDR = 0;
...@@ -38,178 +41,82 @@ static int jornada720_pcmcia_init(struct pcmcia_init *init) ...@@ -38,178 +41,82 @@ static int jornada720_pcmcia_init(struct pcmcia_init *init)
PC_SDR = 0; PC_SDR = 0;
PC_SSR = 0; PC_SSR = 0;
INTPOL1 |= return sa1111_pcmcia_init(init);
(1 << (S0_READY_NINT - SA1111_IRQ(32))) |
(1 << (S1_READY_NINT - SA1111_IRQ(32))) |
(1 << (S0_CD_VALID - SA1111_IRQ(32))) |
(1 << (S1_CD_VALID - SA1111_IRQ(32))) |
(1 << (S0_BVD1_STSCHG - SA1111_IRQ(32))) |
(1 << (S1_BVD1_STSCHG - SA1111_IRQ(32)));
return_val+=request_irq(S0_CD_VALID, init->handler, SA_INTERRUPT,
"Jornada720 PCMCIA (0) CD", NULL);
return_val+=request_irq(S1_CD_VALID, init->handler, SA_INTERRUPT,
"Jornada720 CF (1) CD", NULL);
return_val+=request_irq(S0_BVD1_STSCHG, init->handler, SA_INTERRUPT,
"Jornada720 PCMCIA (0) BVD1", NULL);
return_val+=request_irq(S1_BVD1_STSCHG, init->handler, SA_INTERRUPT,
"Jornada720 CF (1) BVD1", NULL);
return (return_val<0) ? -1 : 2;
} }
static int jornada720_pcmcia_shutdown(void) static int
jornada720_pcmcia_configure_socket(const struct pcmcia_configure *conf)
{ {
free_irq(S0_CD_VALID, NULL); unsigned int pa_dwr_mask, pa_dwr_set;
free_irq(S1_CD_VALID, NULL); int ret;
free_irq(S0_BVD1_STSCHG, NULL);
free_irq(S1_BVD1_STSCHG, NULL);
INTPOL1 &=
~((1 << (S0_CD_VALID - SA1111_IRQ(32))) |
(1 << (S1_CD_VALID - SA1111_IRQ(32))) |
(1 << (S0_BVD1_STSCHG - SA1111_IRQ(32))) |
(1 << (S1_BVD1_STSCHG - SA1111_IRQ(32))));
return 0;
}
static int jornada720_pcmcia_socket_state(struct pcmcia_state_array printk("%s(): config socket %d vcc %d vpp %d\n", __FUNCTION__,
*state_array) conf->sock, conf->vcc, conf->vpp);
{
unsigned long status;
int return_val=1;
if(state_array->size<2) return -1;
memset(state_array->state, 0,
(state_array->size)*sizeof(struct pcmcia_state));
status=PCSR;
state_array->state[0].detect=((status & PCSR_S0_DETECT)==0)?1:0;
state_array->state[0].ready=((status & PCSR_S0_READY)==0)?0:1;
state_array->state[0].bvd1=((status & PCSR_S0_BVD1)==0)?0:1;
state_array->state[0].bvd2=((status & PCSR_S0_BVD2)==0)?0:1;
state_array->state[0].wrprot=((status & PCSR_S0_WP)==0)?0:1;
state_array->state[0].vs_3v=((status & PCSR_S0_VS1)==0)?1:0;
state_array->state[0].vs_Xv=((status & PCSR_S0_VS2)==0)?1:0;
state_array->state[1].detect=((status & PCSR_S1_DETECT)==0)?1:0;
state_array->state[1].ready=((status & PCSR_S1_READY)==0)?0:1;
state_array->state[1].bvd1=((status & PCSR_S1_BVD1)==0)?0:1;
state_array->state[1].bvd2=((status & PCSR_S1_BVD2)==0)?0:1;
state_array->state[1].wrprot=((status & PCSR_S1_WP)==0)?0:1;
state_array->state[1].vs_3v=((status & PCSR_S1_VS1)==0)?1:0;
state_array->state[1].vs_Xv=((status & PCSR_S1_VS2)==0)?1:0;
return return_val;
}
static int jornada720_pcmcia_get_irq_info(struct pcmcia_irq_info *info) switch (conf->sock) {
{
switch(info->sock){
case 0: case 0:
info->irq=S0_READY_NINT; pa_dwr_mask = SOCKET0_POWER | SOCKET0_3V;
break;
case 1:
info->irq=S1_READY_NINT;
break;
switch (conf->vcc) {
default: default:
return -1; case 0: pa_dwr_set = 0; break;
case 33: pa_dwr_set = SOCKET0_POWER | SOCKET0_3V; break;
case 50: pa_dwr_set = SOCKET0_POWER; break;
} }
return 0;
}
static int jornada720_pcmcia_configure_socket(const struct pcmcia_configure
*configure)
{
unsigned long pccr=PCCR, gpio=PA_DWR;
printk("%s(): config socket %d vcc %d vpp %d\n", __FUNCTION__,
configure->sock, configure->vcc, configure->vpp);
switch(configure->sock){
case 0:
switch(configure->vcc){
case 0:
pccr = (pccr & ~PCCR_S0_FLT);
gpio&=~(SOCKET0_POWER | SOCKET0_3V);
break;
case 33:
pccr = (pccr & ~PCCR_S0_PSE) | PCCR_S0_FLT | PCCR_S0_PWAITEN;
gpio |= SOCKET0_POWER | SOCKET0_3V;
break; break;
case 50: case 1:
pccr = (pccr | PCCR_S0_PSE | PCCR_S0_FLT | PCCR_S0_PWAITEN); pa_dwr_mask = SOCKET1_POWER;
gpio = (gpio & ~SOCKET0_3V) | SOCKET0_POWER;
break;
switch (conf->vcc) {
default: default:
printk(KERN_ERR "%s(): unrecognized Vcc %u\n", __FUNCTION__, case 0: pa_dwr_set = 0; break;
configure->vcc); case 33: pa_dwr_set = SOCKET1_POWER; break;
return -1; case 50: pa_dwr_set = SOCKET1_POWER; break;
} }
switch(configure->vpp){
case 0:
break;
case 50:
printk(KERN_ERR "%s(): 5.0 Vpp %u\n", __FUNCTION__,
configure->vpp);
break;
case 120:
printk(KERN_ERR "%s(): 12 Vpp %u\n", __FUNCTION__,
configure->vpp);
break; break;
default:
printk(KERN_ERR "%s(): unrecognized Vpp %u\n", __FUNCTION__,
configure->vpp);
return -1;
} }
pccr=(configure->reset)?(pccr | PCCR_S0_RST):(pccr & ~PCCR_S0_RST);
break;
case 1:
switch(configure->vcc){
case 0:
pccr = (pccr & ~PCCR_S1_FLT);
gpio &= ~(SOCKET1_POWER);
break;
case 33: if (conf->vpp != conf->vcc && conf->vpp != 0) {
pccr = (pccr & ~PCCR_S1_PSE) | PCCR_S1_FLT | PCCR_S1_PWAITEN; printk(KERN_ERR "%s(): slot cannot support VPP %u\n",
gpio |= SOCKET1_POWER; __FUNCTION__, conf->vpp);
break;
case 50:
pccr = (pccr | PCCR_S1_PSE | PCCR_S1_FLT | PCCR_S1_PWAITEN);
gpio = (gpio & ~(SOCKET1_POWER)) | SOCKET1_POWER;
break;
default:
printk(KERN_ERR "%s(): unrecognized Vcc %u\n", __FUNCTION__,
configure->vcc);
return -1;
}
if(configure->vpp!=configure->vcc && configure->vpp!=0){
printk(KERN_ERR "%s(): CF slot cannot support Vpp %u\n", __FUNCTION__,
configure->vpp);
return -1; return -1;
} }
pccr=(configure->reset)?(pccr | PCCR_S1_RST):(pccr & ~PCCR_S1_RST);
break; ret = sa1111_pcmcia_configure_socket(conf);
default: if (ret == 0) {
return -1; unsigned long flags;
local_irq_save(flags);
PA_DWR = (PA_DWR & ~pa_dwr_mask) | pa_dwr_set;
locla_irq_restore(flags);
} }
PCCR = pccr;
PA_DWR = gpio; return ret;
return 0;
} }
struct pcmcia_low_level jornada720_pcmcia_ops = { static struct pcmcia_low_level jornada720_pcmcia_ops = {
jornada720_pcmcia_init, init: jornada720_pcmcia_init,
jornada720_pcmcia_shutdown, shutdown: sa1111_pcmcia_shutdown,
jornada720_pcmcia_socket_state, socket_state: sa1111_pcmcia_socket_state,
jornada720_pcmcia_get_irq_info, get_irq_info: sa1111_pcmcia_get_irq_info,
jornada720_pcmcia_configure_socket configure_socket: jornada720_pcmcia_configure_socket,
socket_init: sa1111_pcmcia_socket_init,
socket_suspend: sa1111_pcmcia_socket_suspend,
}; };
int __init pcmcia_jornada720_init(void)
{
int ret = -ENODEV;
if (machine_is_jornada720())
ret = sa1100_register_pcmcia(&jornada720_pcmcia_ops);
return ret;
}
void __exit pcmcia_jornada720_exit(void)
{
sa1100_unregister_pcmcia(&jornada720_pcmcia_ops);
}
/* /*
* drivers/pcmcia/sa1100_neponset.c * linux/drivers/pcmcia/sa1100_neponset.c
* *
* Neponset PCMCIA specific routines * Neponset PCMCIA specific routines
*
*/ */
#include <linux/kernel.h> #include <linux/kernel.h>
#include <linux/sched.h> #include <linux/sched.h>
#include <linux/init.h>
#include <asm/delay.h>
#include <asm/hardware.h> #include <asm/hardware.h>
#include <asm/irq.h>
#include <asm/arch/pcmcia.h>
#include <asm/arch/assabet.h> #include <asm/arch/assabet.h>
#include <asm/hardware/sa1111.h>
static int neponset_pcmcia_init(struct pcmcia_init *init){ #include "sa1100_generic.h"
int return_val=0; #include "sa1111_generic.h"
static int neponset_pcmcia_init(struct pcmcia_init *init)
{
/* Set GPIO_A<3:0> to be outputs for PCMCIA/CF power controller: */ /* Set GPIO_A<3:0> to be outputs for PCMCIA/CF power controller: */
PA_DDR &= ~(GPIO_GPIO0 | GPIO_GPIO1 | GPIO_GPIO2 | GPIO_GPIO3); PA_DDR &= ~(GPIO_GPIO0 | GPIO_GPIO1 | GPIO_GPIO2 | GPIO_GPIO3);
...@@ -23,109 +23,18 @@ static int neponset_pcmcia_init(struct pcmcia_init *init){ ...@@ -23,109 +23,18 @@ static int neponset_pcmcia_init(struct pcmcia_init *init){
PA_DWR &= ~(GPIO_GPIO0 | GPIO_GPIO1 | GPIO_GPIO2 | GPIO_GPIO3); PA_DWR &= ~(GPIO_GPIO0 | GPIO_GPIO1 | GPIO_GPIO2 | GPIO_GPIO3);
NCR_0 &= ~(NCR_A0VPP | NCR_A1VPP); NCR_0 &= ~(NCR_A0VPP | NCR_A1VPP);
INTPOL1 |= return sa1111_pcmcia_init(init);
(1 << (S0_READY_NINT - SA1111_IRQ(32))) |
(1 << (S1_READY_NINT - SA1111_IRQ(32))) |
(1 << (S0_CD_VALID - SA1111_IRQ(32))) |
(1 << (S1_CD_VALID - SA1111_IRQ(32))) |
(1 << (S0_BVD1_STSCHG - SA1111_IRQ(32))) |
(1 << (S1_BVD1_STSCHG - SA1111_IRQ(32)));
return_val+=request_irq(S0_CD_VALID, init->handler, SA_INTERRUPT,
"Neponset PCMCIA (0) CD", NULL);
return_val+=request_irq(S1_CD_VALID, init->handler, SA_INTERRUPT,
"Neponset CF (1) CD", NULL);
return_val+=request_irq(S0_BVD1_STSCHG, init->handler, SA_INTERRUPT,
"Neponset PCMCIA (0) BVD1", NULL);
return_val+=request_irq(S1_BVD1_STSCHG, init->handler, SA_INTERRUPT,
"Neponset CF (1) BVD1", NULL);
return (return_val<0) ? -1 : 2;
}
static int neponset_pcmcia_shutdown(void){
free_irq(S0_CD_VALID, NULL);
free_irq(S1_CD_VALID, NULL);
free_irq(S0_BVD1_STSCHG, NULL);
free_irq(S1_BVD1_STSCHG, NULL);
INTPOL1 &=
~((1 << (S0_CD_VALID - SA1111_IRQ(32))) |
(1 << (S1_CD_VALID - SA1111_IRQ(32))) |
(1 << (S0_BVD1_STSCHG - SA1111_IRQ(32))) |
(1 << (S1_BVD1_STSCHG - SA1111_IRQ(32))));
return 0;
}
static int neponset_pcmcia_socket_state(struct pcmcia_state_array
*state_array){
unsigned long status;
int return_val=1;
if(state_array->size<2) return -1;
memset(state_array->state, 0,
(state_array->size)*sizeof(struct pcmcia_state));
status=PCSR;
state_array->state[0].detect=((status & PCSR_S0_DETECT)==0)?1:0;
state_array->state[0].ready=((status & PCSR_S0_READY)==0)?0:1;
state_array->state[0].bvd1=((status & PCSR_S0_BVD1)==0)?0:1;
state_array->state[0].bvd2=((status & PCSR_S0_BVD2)==0)?0:1;
state_array->state[0].wrprot=((status & PCSR_S0_WP)==0)?0:1;
state_array->state[0].vs_3v=((status & PCSR_S0_VS1)==0)?1:0;
state_array->state[0].vs_Xv=((status & PCSR_S0_VS2)==0)?1:0;
state_array->state[1].detect=((status & PCSR_S1_DETECT)==0)?1:0;
state_array->state[1].ready=((status & PCSR_S1_READY)==0)?0:1;
state_array->state[1].bvd1=((status & PCSR_S1_BVD1)==0)?0:1;
state_array->state[1].bvd2=((status & PCSR_S1_BVD2)==0)?0:1;
state_array->state[1].wrprot=((status & PCSR_S1_WP)==0)?0:1;
state_array->state[1].vs_3v=((status & PCSR_S1_VS1)==0)?1:0;
state_array->state[1].vs_Xv=((status & PCSR_S1_VS2)==0)?1:0;
return return_val;
} }
static int neponset_pcmcia_get_irq_info(struct pcmcia_irq_info *info){ static int
neponset_pcmcia_configure_socket(const struct pcmcia_configure *conf)
switch(info->sock){ {
case 0: unsigned int ncr_mask, pa_dwr_mask;
info->irq=S0_READY_NINT; unsigned int ncr_set, pa_dwr_set;
break; int ret;
case 1:
info->irq=S1_READY_NINT;
break;
default:
return -1;
}
return 0;
}
static int neponset_pcmcia_configure_socket(const struct pcmcia_configure
*configure){
unsigned long pccr=PCCR, ncr=NCR_0, gpio=PA_DWR;
/* Neponset uses the Maxim MAX1600, with the following connections: /* Neponset uses the Maxim MAX1600, with the following connections:
*
* MAX1600 Neponset * MAX1600 Neponset
* *
* A0VCC SA-1111 GPIO A<1> * A0VCC SA-1111 GPIO A<1>
...@@ -147,103 +56,90 @@ static int neponset_pcmcia_configure_socket(const struct pcmcia_configure ...@@ -147,103 +56,90 @@ static int neponset_pcmcia_configure_socket(const struct pcmcia_configure
* the corresponding truth table. * the corresponding truth table.
*/ */
switch(configure->sock){ switch (conf->sock) {
case 0: case 0:
pa_dwr_mask = GPIO_GPIO0 | GPIO_GPIO1;
ncr_mask = NCR_A0VPP | NCR_A1VPP;
switch(configure->vcc){ switch (conf->vcc) {
case 0:
pccr=(pccr & ~PCCR_S0_FLT);
gpio&=~(GPIO_GPIO0 | GPIO_GPIO1);
break;
case 33:
pccr=(pccr & ~PCCR_S0_PSE) | PCCR_S0_FLT | PCCR_S0_PWAITEN;
gpio=(gpio & ~(GPIO_GPIO0 | GPIO_GPIO1)) | GPIO_GPIO1;
break;
case 50:
pccr=(pccr | PCCR_S0_PSE | PCCR_S0_FLT | PCCR_S0_PWAITEN);
gpio=(gpio & ~(GPIO_GPIO0 | GPIO_GPIO1)) | GPIO_GPIO0;
break;
default: default:
printk(KERN_ERR "%s(): unrecognized Vcc %u\n", __FUNCTION__, case 0: pa_dwr_set = 0; break;
configure->vcc); case 33: pa_dwr_set = GPIO_GPIO1; break;
return -1; case 50: pa_dwr_set = GPIO_GPIO0; break;
} }
switch(configure->vpp){ switch (conf->vpp) {
case 0: case 0: ncr_set = 0; break;
ncr&=~(NCR_A0VPP | NCR_A1VPP); case 120: ncr_set = NCR_A1VPP; break;
break;
case 120:
ncr=(ncr & ~(NCR_A0VPP | NCR_A1VPP)) | NCR_A1VPP;
break;
default: default:
if(configure->vpp == configure->vcc) if (conf->vpp == conf->vcc)
ncr=(ncr & ~(NCR_A0VPP | NCR_A1VPP)) | NCR_A0VPP; ncr_set = NCR_A0VPP;
else { else {
printk(KERN_ERR "%s(): unrecognized Vpp %u\n", __FUNCTION__, printk(KERN_ERR "%s(): unrecognized VPP %u\n",
configure->vpp); __FUNCTION__, conf->vpp);
return -1; return -1;
} }
} }
pccr=(configure->reset)?(pccr | PCCR_S0_RST):(pccr & ~PCCR_S0_RST);
break; break;
case 1: case 1:
switch(configure->vcc){ pa_dwr_mask = GPIO_GPIO2 | GPIO_GPIO3;
case 0: ncr_mask = 0;
pccr=(pccr & ~PCCR_S1_FLT); ncr_set = 0;
gpio&=~(GPIO_GPIO2 | GPIO_GPIO3);
break;
case 33:
pccr=(pccr & ~PCCR_S1_PSE) | PCCR_S1_FLT | PCCR_S1_PWAITEN;
gpio=(gpio & ~(GPIO_GPIO2 | GPIO_GPIO3)) | GPIO_GPIO2;
break;
case 50:
pccr=(pccr | PCCR_S1_PSE | PCCR_S1_FLT | PCCR_S1_PWAITEN);
gpio=(gpio & ~(GPIO_GPIO2 | GPIO_GPIO3)) | GPIO_GPIO3;
break;
switch (conf->vcc) {
default: default:
printk(KERN_ERR "%s(): unrecognized Vcc %u\n", __FUNCTION__, case 0: pa_dwr_set = 0; break;
configure->vcc); case 33: pa_dwr_set = GPIO_GPIO2; break;
return -1; case 50: pa_dwr_set = GPIO_GPIO3; break;
} }
if(configure->vpp!=configure->vcc && configure->vpp!=0){ if (conf->vpp != conf->vcc && conf->vpp != 0) {
printk(KERN_ERR "%s(): CF slot cannot support Vpp %u\n", __FUNCTION__, printk(KERN_ERR "%s(): CF slot cannot support VPP %u\n",
configure->vpp); __FUNCTION__, conf->vpp);
return -1; return -1;
} }
pccr=(configure->reset)?(pccr | PCCR_S1_RST):(pccr & ~PCCR_S1_RST);
break; break;
default: default:
return -1; return -1;
} }
PCCR = pccr; ret = sa1111_pcmcia_configure_socket(conf);
NCR_0 = ncr; if (ret == 0) {
PA_DWR = gpio; unsigned long flags;
local_irq_save(flags);
NCR_0 = (NCR_0 & ~ncr_mask) | ncr_set;
PA_DWR = (PA_DWR & ~pa_dwr_mask) | pa_dwr_set;
local_irq_restore(flags);
}
return 0; return 0;
} }
struct pcmcia_low_level neponset_pcmcia_ops = { static struct pcmcia_low_level neponset_pcmcia_ops = {
neponset_pcmcia_init, init: neponset_pcmcia_init,
neponset_pcmcia_shutdown, shutdown: sa1111_pcmcia_shutdown,
neponset_pcmcia_socket_state, socket_state: sa1111_pcmcia_socket_state,
neponset_pcmcia_get_irq_info, get_irq_info: sa1111_pcmcia_get_irq_info,
neponset_pcmcia_configure_socket configure_socket: neponset_pcmcia_configure_socket,
socket_init: sa1111_pcmcia_socket_init,
socket_suspend: sa1111_pcmcia_socket_suspend,
}; };
int __init pcmcia_neponset_init(void)
{
int ret = -ENODEV;
if (machine_is_assabet() && machine_has_neponset())
ret = sa1100_register_pcmcia(&neponset_pcmcia_ops);
return ret;
}
void __exit pcmcia_neponset_exit(void)
{
sa1100_unregister_pcmcia(&neponset_pcmcia_ops);
}
...@@ -4,48 +4,45 @@ ...@@ -4,48 +4,45 @@
* PCMCIA implementation routines for Pangolin * PCMCIA implementation routines for Pangolin
* *
*/ */
#include <linux/config.h>
#include <linux/kernel.h> #include <linux/kernel.h>
#include <linux/sched.h> #include <linux/sched.h>
#include <linux/init.h>
#include <asm/hardware.h> #include <asm/hardware.h>
#include <asm/irq.h> #include <asm/irq.h>
#include <asm/arch/pcmcia.h> #include "sa1100_generic.h"
static int pangolin_pcmcia_init(struct pcmcia_init *init){ static int pangolin_pcmcia_init(struct pcmcia_init *init){
int irq, res; int res;
/* set GPIO_PCMCIA_CD & GPIO_PCMCIA_IRQ as inputs */
GPDR &= ~(GPIO_PCMCIA_CD|GPIO_PCMCIA_IRQ);
#ifndef CONFIG_SA1100_PANGOLIN_PCMCIA_IDE #ifndef CONFIG_SA1100_PANGOLIN_PCMCIA_IDE
/* set GPIO pins GPIO_PCMCIA_BUS_ON & GPIO_PCMCIA_RESET as output */
GPDR |= (GPIO_PCMCIA_BUS_ON|GPIO_PCMCIA_RESET);
/* Enable PCMCIA bus: */ /* Enable PCMCIA bus: */
GPCR = GPIO_PCMCIA_BUS_ON; GPCR = GPIO_PCMCIA_BUS_ON;
#else
/* set GPIO pin GPIO_PCMCIA_RESET as output */
GPDR |= GPIO_PCMCIA_RESET;
#endif #endif
/* Set transition detect */ /* Set transition detect */
set_GPIO_IRQ_edge( GPIO_PCMCIA_CD, GPIO_BOTH_EDGES ); set_irq_type(IRQ_PCMCIA_CD, IRQT_NOEDGE);
set_GPIO_IRQ_edge( GPIO_PCMCIA_IRQ, GPIO_FALLING_EDGE ); set_irq_type(IRQ_PCMCIA_IRQ, IRQT_FALLING);
/* Register interrupts */ /* Register interrupts */
irq = IRQ_PCMCIA_CD; res = request_irq(IRQ_PCMCIA_CD, init->handler, SA_INTERRUPT,
res = request_irq( irq, init->handler, SA_INTERRUPT, "PCMCIA_CD", NULL ); "PCMCIA_CD", NULL);
if( res < 0 ) goto irq_err; if (res >= 0)
/* There's only one slot, but it's "Slot 1": */ /* There's only one slot, but it's "Slot 1": */
return 2; return 2;
irq_err: irq_err:
printk( KERN_ERR "%s: Request for IRQ %lu failed\n", __FUNCTION__, irq ); printk(KERN_ERR "%s: request for IRQ%d failed (%d)\n",
return -1; __FUNCTION__, IRQ_PCMCIA_CD, res);
return res;
} }
static int pangolin_pcmcia_shutdown(void) static int pangolin_pcmcia_shutdown(void)
{ {
/* disable IRQs */ /* disable IRQs */
free_irq( IRQ_PCMCIA_CD, NULL ); free_irq(IRQ_PCMCIA_CD, NULL);
#ifndef CONFIG_SA1100_PANGOLIN_PCMCIA_IDE #ifndef CONFIG_SA1100_PANGOLIN_PCMCIA_IDE
/* Disable PCMCIA bus: */ /* Disable PCMCIA bus: */
GPSR = GPIO_PCMCIA_BUS_ON; GPSR = GPIO_PCMCIA_BUS_ON;
...@@ -105,7 +102,7 @@ static int pangolin_pcmcia_configure_socket(const struct pcmcia_configure ...@@ -105,7 +102,7 @@ static int pangolin_pcmcia_configure_socket(const struct pcmcia_configure
#ifndef CONFIG_SA1100_PANGOLIN_PCMCIA_IDE #ifndef CONFIG_SA1100_PANGOLIN_PCMCIA_IDE
if(configure->sock==0) return 0; if(configure->sock==0) return 0;
#endif #endif
save_flags_cli(flags); local_irq_save(flags);
/* Murphy: BUS_ON different from POWER ? */ /* Murphy: BUS_ON different from POWER ? */
...@@ -129,7 +126,7 @@ static int pangolin_pcmcia_configure_socket(const struct pcmcia_configure ...@@ -129,7 +126,7 @@ static int pangolin_pcmcia_configure_socket(const struct pcmcia_configure
default: default:
printk(KERN_ERR "%s(): unrecognized Vcc %u\n", __FUNCTION__, printk(KERN_ERR "%s(): unrecognized Vcc %u\n", __FUNCTION__,
configure->vcc); configure->vcc);
restore_flags(flags); local_irq_restore(flags);
return -1; return -1;
} }
#ifdef CONFIG_SA1100_PANGOLIN_PCMCIA_IDE #ifdef CONFIG_SA1100_PANGOLIN_PCMCIA_IDE
...@@ -143,15 +140,47 @@ static int pangolin_pcmcia_configure_socket(const struct pcmcia_configure ...@@ -143,15 +140,47 @@ static int pangolin_pcmcia_configure_socket(const struct pcmcia_configure
} }
#endif #endif
/* Silently ignore Vpp, output enable, speaker enable. */ /* Silently ignore Vpp, output enable, speaker enable. */
restore_flags(flags); local_irq_restore(flags);
return 0;
}
static int pangolin_pcmcia_socket_init(int sock)
{
if (sock == 1)
set_irq_type(IRQ_PCmCIA_CD, IRQT_BOTHEDGE);
return 0;
}
static int pangolin_pcmcia_socket_suspend(int sock)
{
if (sock == 1)
set_irq_type(IRQ_PCmCIA_CD, IRQT_NOEDGE);
return 0; return 0;
} }
struct pcmcia_low_level pangolin_pcmcia_ops = { static struct pcmcia_low_level pangolin_pcmcia_ops = {
pangolin_pcmcia_init, init: pangolin_pcmcia_init,
pangolin_pcmcia_shutdown, shutdown: pangolin_pcmcia_shutdown,
pangolin_pcmcia_socket_state, socket_state: pangolin_pcmcia_socket_state,
pangolin_pcmcia_get_irq_info, get_irq_info: pangolin_pcmcia_get_irq_info,
pangolin_pcmcia_configure_socket configure_socket: pangolin_pcmcia_configure_socket,
socket_init: pangolin_pcmcia_socket_init,
socket_suspend, pangolin_pcmcia_socket_suspend,
}; };
int __init pcmcia_pangolin_init(void)
{
int ret = -ENODEV;
if (machine_is_pangolin())
ret = sa1100_register_pcmcia(&pangolin_pcmcia_ops);
return ret;
}
void __exit pcmcia_pangolin_exit(void)
{
sa1100_unregister_pcmcia(&pangolin_pcmcia_ops);
}
...@@ -7,121 +7,31 @@ ...@@ -7,121 +7,31 @@
*/ */
#include <linux/kernel.h> #include <linux/kernel.h>
#include <linux/sched.h> #include <linux/sched.h>
#include <linux/init.h>
#include <asm/delay.h> #include <asm/delay.h>
#include <asm/hardware.h> #include <asm/hardware.h>
#include <asm/irq.h> #include <asm/irq.h>
#include <asm/arch/pcmcia.h>
static int pfs168_pcmcia_init(struct pcmcia_init *init){ #include "sa1100_generic.h"
int return_val=0; #include "sa1111_generic.h"
static int pfs168_pcmcia_init(struct pcmcia_init *init)
{
/* TPS2211 to standby mode: */ /* TPS2211 to standby mode: */
PA_DWR &= ~(GPIO_GPIO0 | GPIO_GPIO1 | GPIO_GPIO2 | GPIO_GPIO3); PA_DWR &= ~(GPIO_GPIO0 | GPIO_GPIO1 | GPIO_GPIO2 | GPIO_GPIO3);
/* Set GPIO_A<3:0> to be outputs for PCMCIA (socket 0) power controller: */ /* Set GPIO_A<3:0> to be outputs for PCMCIA (socket 0) power controller: */
PA_DDR &= ~(GPIO_GPIO0 | GPIO_GPIO1 | GPIO_GPIO2 | GPIO_GPIO3); PA_DDR &= ~(GPIO_GPIO0 | GPIO_GPIO1 | GPIO_GPIO2 | GPIO_GPIO3);
INTPOL1 |= return sa1111_pcmcia_init(init);
(1 << (S0_READY_NINT - SA1111_IRQ(32))) |
(1 << (S1_READY_NINT - SA1111_IRQ(32))) |
(1 << (S0_CD_VALID - SA1111_IRQ(32))) |
(1 << (S1_CD_VALID - SA1111_IRQ(32))) |
(1 << (S0_BVD1_STSCHG - SA1111_IRQ(32))) |
(1 << (S1_BVD1_STSCHG - SA1111_IRQ(32)));
return_val+=request_irq(S0_CD_VALID, init->handler, SA_INTERRUPT,
"PFS168 PCMCIA (0) CD", NULL);
return_val+=request_irq(S1_CD_VALID, init->handler, SA_INTERRUPT,
"PFS168 CF (1) CD", NULL);
return_val+=request_irq(S0_BVD1_STSCHG, init->handler, SA_INTERRUPT,
"PFS168 PCMCIA (0) BVD1", NULL);
return_val+=request_irq(S1_BVD1_STSCHG, init->handler, SA_INTERRUPT,
"PFS168 CF (1) BVD1", NULL);
return (return_val<0) ? -1 : 2;
} }
static int pfs168_pcmcia_shutdown(void){ static int
pfs168_pcmcia_configure_socket(const struct pcmcia_configure *conf)
free_irq(S0_CD_VALID, NULL); {
free_irq(S1_CD_VALID, NULL); unsigned int pa_dwr_mask = 0, pa_dwr_set = 0;
free_irq(S0_BVD1_STSCHG, NULL); int ret;
free_irq(S1_BVD1_STSCHG, NULL);
INTPOL1 &=
~((1 << (S0_CD_VALID - SA1111_IRQ(32))) |
(1 << (S1_CD_VALID - SA1111_IRQ(32))) |
(1 << (S0_BVD1_STSCHG - SA1111_IRQ(32))) |
(1 << (S1_BVD1_STSCHG - SA1111_IRQ(32))));
return 0;
}
static int pfs168_pcmcia_socket_state(struct pcmcia_state_array
*state_array){
unsigned long status;
int return_val=1;
if(state_array->size<2) return -1;
memset(state_array->state, 0,
(state_array->size)*sizeof(struct pcmcia_state));
status=PCSR;
state_array->state[0].detect=((status & PCSR_S0_DETECT)==0)?1:0;
state_array->state[0].ready=((status & PCSR_S0_READY)==0)?0:1;
state_array->state[0].bvd1=((status & PCSR_S0_BVD1)==0)?0:1;
state_array->state[0].bvd2=((status & PCSR_S0_BVD2)==0)?0:1;
state_array->state[0].wrprot=((status & PCSR_S0_WP)==0)?0:1;
state_array->state[0].vs_3v=((status & PCSR_S0_VS1)==0)?1:0;
state_array->state[0].vs_Xv=((status & PCSR_S0_VS2)==0)?1:0;
state_array->state[1].detect=((status & PCSR_S1_DETECT)==0)?1:0;
state_array->state[1].ready=((status & PCSR_S1_READY)==0)?0:1;
state_array->state[1].bvd1=((status & PCSR_S1_BVD1)==0)?0:1;
state_array->state[1].bvd2=((status & PCSR_S1_BVD2)==0)?0:1;
state_array->state[1].wrprot=((status & PCSR_S1_WP)==0)?0:1;
state_array->state[1].vs_3v=((status & PCSR_S1_VS1)==0)?1:0;
state_array->state[1].vs_Xv=((status & PCSR_S1_VS2)==0)?1:0;
return return_val;
}
static int pfs168_pcmcia_get_irq_info(struct pcmcia_irq_info *info){
switch(info->sock){
case 0:
info->irq=S0_READY_NINT;
break;
case 1:
info->irq=S1_READY_NINT;
break;
default:
return -1;
}
return 0;
}
static int pfs168_pcmcia_configure_socket(const struct pcmcia_configure
*configure){
unsigned long pccr=PCCR, gpio=PA_DWR;
/* PFS168 uses the Texas Instruments TPS2211 for PCMCIA (socket 0) voltage control only, /* PFS168 uses the Texas Instruments TPS2211 for PCMCIA (socket 0) voltage control only,
* with the following connections: * with the following connections:
...@@ -135,103 +45,100 @@ static int pfs168_pcmcia_configure_socket(const struct pcmcia_configure ...@@ -135,103 +45,100 @@ static int pfs168_pcmcia_configure_socket(const struct pcmcia_configure
* *
*/ */
switch(configure->sock){ switch (conf->sock) {
case 0: case 0:
pa_dwr_mask = GPIO_GPIO0 | GPIO_GPIO1 | GPIO_GPIO2 | GPIO_GPIO3;
switch(configure->vcc){ switch (conf->vcc) {
case 0:
pccr = (pccr & ~PCCR_S0_FLT);
gpio &= ~(GPIO_GPIO0 | GPIO_GPIO1);
break;
case 33:
pccr = (pccr & ~PCCR_S0_PSE) | PCCR_S0_FLT | PCCR_S0_PWAITEN;
gpio = (gpio & ~(GPIO_GPIO0 | GPIO_GPIO1)) | GPIO_GPIO0;
break;
case 50:
pccr = (pccr | PCCR_S0_PSE | PCCR_S0_FLT | PCCR_S0_PWAITEN);
gpio = (gpio & ~(GPIO_GPIO0 | GPIO_GPIO1)) | GPIO_GPIO1;
break;
default: default:
printk(KERN_ERR "%s(): unrecognized Vcc %u\n", __FUNCTION__, case 0: pa_dwr_set = 0; break;
configure->vcc); case 33: pa_dwr_set = GPIO_GPIO0; break;
return -1; case 50: pa_dwr_set = GPIO_GPIO1; break;
} }
switch(configure->vpp){ switch (conf->vpp) {
case 0: case 0:
gpio &= ~(GPIO_GPIO2 | GPIO_GPIO3);
break; break;
case 120: case 120:
printk(KERN_ERR "%s(): PFS-168 does not support Vpp %uV\n", __FUNCTION__, printk(KERN_ERR "%s(): PFS-168 does not support VPP %uV\n",
configure->vpp/10); __FUNCTION__, conf->vpp / 10);
return -1; return -1;
break; break;
default: default:
if(configure->vpp == configure->vcc) if (conf->vpp == conf->vcc)
gpio = (gpio & ~(GPIO_GPIO2 | GPIO_GPIO3)) | GPIO_GPIO3; pa_dwr_set |= GPIO_GPIO3;
else { else {
printk(KERN_ERR "%s(): unrecognized Vpp %u\n", __FUNCTION__, printk(KERN_ERR "%s(): unrecognized VPP %u\n", __FUNCTION__,
configure->vpp); conf->vpp);
return -1; return -1;
} }
} }
pccr = (configure->reset)?(pccr | PCCR_S0_RST):(pccr & ~PCCR_S0_RST);
PA_DWR = gpio;
break; break;
case 1: case 1:
switch(configure->vcc){ pa_dwr_mask = 0;
case 0: pa_dwr_set = 0;
pccr = (pccr & ~PCCR_S1_FLT);
break;
switch (conf->vcc) {
case 0:
case 33: case 33:
pccr = (pccr & ~PCCR_S1_PSE) | PCCR_S1_FLT | PCCR_S1_PWAITEN;
break; break;
case 50: case 50:
printk(KERN_ERR "%s(): PFS-168 CompactFlash socket does not support Vcc %uV\n", __FUNCTION__, printk(KERN_ERR "%s(): PFS-168 CompactFlash socket does not support VCC %uV\n",
configure->vcc/10); __FUNCTION__, conf->vcc / 10);
return -1; return -1;
break;
default: default:
printk(KERN_ERR "%s(): unrecognized Vcc %u\n", __FUNCTION__, printk(KERN_ERR "%s(): unrecognized VCC %u\n", __FUNCTION__,
configure->vcc); conf->vcc);
return -1; return -1;
} }
if(configure->vpp!=configure->vcc && configure->vpp!=0){ if (conf->vpp != conf->vcc && conf->vpp != 0) {
printk(KERN_ERR "%s(): CompactFlash socket does not support Vpp %uV\n", __FUNCTION__, printk(KERN_ERR "%s(): CompactFlash socket does not support VPP %uV\n"
configure->vpp/10); __FUNCTION__, conf->vpp / 10);
return -1; return -1;
} }
pccr = (configure->reset)?(pccr | PCCR_S1_RST):(pccr & ~PCCR_S1_RST);
break; break;
default:
return -1;
} }
PCCR = pccr; ret = sa1111_pcmcia_configure_socket(conf);
if (ret == 0) {
unsigned long flags;
local_irq_save(flags);
PA_DWR = (PA_DWR & ~pa_dwr_mask) | pa_dwr_set;
local_irq_restore(flags);
}
return 0; return 0;
} }
struct pcmcia_low_level pfs168_pcmcia_ops = { static struct pcmcia_low_level pfs168_pcmcia_ops = {
pfs168_pcmcia_init, init: pfs168_pcmcia_init,
pfs168_pcmcia_shutdown, shutdown: sa1111_pcmcia_shutdown,
pfs168_pcmcia_socket_state, socket_state: sa1111_pcmcia_socket_state,
pfs168_pcmcia_get_irq_info, get_irq_info: sa1111_pcmcia_get_irq_info,
pfs168_pcmcia_configure_socket configure_socket: pfs168_pcmcia_configure_socket,
socket_init: sa1111_pcmcia_socket_init,
socket_suspend: sa1111_pcmcia_socket_suspend,
}; };
int __init pcmcia_pfs168_init(void)
{
int ret = -ENODEV;
if (machine_is_pfs168())
ret = sa1100_register_pcmcia(&pfs168_pcmcia_ops);
return ret;
}
void __exit pcmcia_pfs168_exit(void)
{
sa1100_unregister_pcmcia(&pfs168_pcmcia_ops);
}
/*
* drivers/pcmcia/sa1100_shannon.c
*
* PCMCIA implementation routines for Shannon
*
*/
#include <linux/kernel.h>
#include <linux/sched.h>
#include <linux/init.h>
#include <asm/hardware.h>
#include <asm/arch/shannon.h>
#include <asm/irq.h>
#include "sa1100_generic.h"
static struct irqs {
int irq;
const char *str;
} irqs[] = {
{ SHANNON_IRQ_GPIO_EJECT_0, "PCMCIA_CD_0" },
{ SHANNON_IRQ_GPIO_EJECT_1, "PCMCIA_CD_1" },
};
static int shannon_pcmcia_init(struct pcmcia_init *init)
{
int i, res;
/* All those are inputs */
GPDR &= ~(SHANNON_GPIO_EJECT_0 | SHANNON_GPIO_EJECT_1 |
SHANNON_GPIO_RDY_0 | SHANNON_GPIO_RDY_1);
GAFR &= ~(SHANNON_GPIO_EJECT_0 | SHANNON_GPIO_EJECT_1 |
SHANNON_GPIO_RDY_0 | SHANNON_GPIO_RDY_1);
/* Set transition detect */
set_irq_type(SHANNON_IRQ_GPIO_RDY_0, IRQT_FALLING);
set_irq_type(SHANNON_IRQ_GPIO_RDY_1, IRQT_FALLING);
/* Register interrupts */
for (i = 0; i < ARRAY_SIZE(irqs); i++) {
set_irq_type(irqs[i].irq, IRQT_NOEDGE);
res = request_irq(irqs[i].irq, init->handler, SA_INTERRUPT,
irqs[i].str, NULL);
if (res)
goto irq_err;
}
return 2;
irq_err:
printk(KERN_ERR "%s: request for IRQ%d failed (%d)\n",
__FUNCTION__, irqs[i].irq, res);
while (i--)
free_irq(irqs[i].irq, NULL);
return res;
}
static int shannon_pcmcia_shutdown(void)
{
int i;
/* disable IRQs */
for (i = 0; i < ARRAY_SIZE(irqs); i++)
free_irq(irqs[i].irq, NULL);
return 0;
}
static int shannon_pcmcia_socket_state(struct pcmcia_state_array *state_array)
{
unsigned long levels;
memset(state_array->state, 0,
state_array->size * sizeof(struct pcmcia_state));
levels = GPLR;
state_array->state[0].detect = (levels & SHANNON_GPIO_EJECT_0) ? 0 : 1;
state_array->state[0].ready = (levels & SHANNON_GPIO_RDY_0) ? 1 : 0;
state_array->state[0].wrprot = 0; /* Not available on Shannon. */
state_array->state[0].bvd1 = 1;
state_array->state[0].bvd2 = 1;
state_array->state[0].vs_3v = 1; /* FIXME Can only apply 3.3V on Shannon. */
state_array->state[0].vs_Xv = 0;
state_array->state[1].detect = (levels & SHANNON_GPIO_EJECT_1) ? 0 : 1;
state_array->state[1].ready = (levels & SHANNON_GPIO_RDY_1) ? 1 : 0;
state_array->state[1].wrprot = 0; /* Not available on Shannon. */
state_array->state[1].bvd1 = 1;
state_array->state[1].bvd2 = 1;
state_array->state[1].vs_3v = 1; /* FIXME Can only apply 3.3V on Shannon. */
state_array->state[1].vs_Xv = 0;
return 1;
}
static int shannon_pcmcia_get_irq_info(struct pcmcia_irq_info *info)
{
if (info->sock == 0)
info->irq = SHANNON_IRQ_GPIO_RDY_0;
else if (info->sock == 1)
info->irq = SHANNON_IRQ_GPIO_RDY_1;
else return -1;
return 0;
}
static int shannon_pcmcia_configure_socket(const struct pcmcia_configure *configure)
{
switch (configure->vcc) {
case 0: /* power off */
printk(KERN_WARNING __FUNCTION__"(): CS asked for 0V, still applying 3.3V..\n");
break;
case 50:
printk(KERN_WARNING __FUNCTION__"(): CS asked for 5V, applying 3.3V..\n");
case 33:
break;
default:
printk(KERN_ERR __FUNCTION__"(): unrecognized Vcc %u\n",
configure->vcc);
return -1;
}
printk(KERN_WARNING __FUNCTION__"(): Warning, Can't perform reset\n");
/* Silently ignore Vpp, output enable, speaker enable. */
return 0;
}
static int shannon_pcmcia_socket_init(int sock)
{
if (sock == 0)
set_irq_type(SHANNON_IRQ_GPIO_EJECT_0, IRQT_BOTHEDGE);
else if (sock == 1)
set_irq_Type(SHANNON_IRQ_GPIO_EJECT_1, IRQT_BOTHEDGE);
return 0;
}
static int shannon_pcmcia_socket_suspend(int sock)
{
if (sock == 0)
set_irq_type(SHANNON_IRQ_GPIO_EJECT_0, IRQT_NOEDGE);
else if (sock == 1)
set_irq_type(SHANNON_IRQ_GPIO_EJECT_1, IRQT_NOEDGE);
return 0;
}
static struct pcmcia_low_level shannon_pcmcia_ops = {
init: shannon_pcmcia_init,
shutdown: shannon_pcmcia_shutdown,
socket_state: shannon_pcmcia_socket_state,
get_irq_info: shannon_pcmcia_get_irq_info,
configure_socket: shannon_pcmcia_configure_socket,
socket_init: shannon_pcmcia_socket_init,
socket_suspend: shannon_pcmcia_socket_suspend,
};
int __init pcmcia_shannon_init(void)
{
int ret = -ENODEV;
if (machine_is_shannon())
ret = sa1100_register_pcmcia(&shannon_pcmcia_ops);
return ret;
}
void __exit pcmcia_shannon_exit(void)
{
sa1100_unregister_pcmcia(&shannon_pcmcia_ops);
}
...@@ -6,10 +6,11 @@ ...@@ -6,10 +6,11 @@
*/ */
#include <linux/kernel.h> #include <linux/kernel.h>
#include <linux/sched.h> #include <linux/sched.h>
#include <linux/init.h>
#include <asm/hardware.h> #include <asm/hardware.h>
#include <asm/irq.h> #include <asm/irq.h>
#include <asm/arch/pcmcia.h> #include "sa1100_generic.h"
extern long get_cs3_shadow(void); extern long get_cs3_shadow(void);
extern void set_cs3_bit(int value); extern void set_cs3_bit(int value);
...@@ -19,9 +20,6 @@ extern void clear_cs3_bit(int value); ...@@ -19,9 +20,6 @@ extern void clear_cs3_bit(int value);
static int simpad_pcmcia_init(struct pcmcia_init *init){ static int simpad_pcmcia_init(struct pcmcia_init *init){
int irq, res; int irq, res;
/* set GPIO_CF_CD & GPIO_CF_IRQ as inputs */
GPDR &= ~(GPIO_CF_CD|GPIO_CF_IRQ);
set_cs3_bit(PCMCIA_RESET); set_cs3_bit(PCMCIA_RESET);
clear_cs3_bit(PCMCIA_BUFF_DIS); clear_cs3_bit(PCMCIA_BUFF_DIS);
clear_cs3_bit(PCMCIA_RESET); clear_cs3_bit(PCMCIA_RESET);
...@@ -29,8 +27,8 @@ static int simpad_pcmcia_init(struct pcmcia_init *init){ ...@@ -29,8 +27,8 @@ static int simpad_pcmcia_init(struct pcmcia_init *init){
clear_cs3_bit(VCC_3V_EN|VCC_5V_EN|EN0|EN1); clear_cs3_bit(VCC_3V_EN|VCC_5V_EN|EN0|EN1);
/* Set transition detect */ /* Set transition detect */
set_GPIO_IRQ_edge( GPIO_CF_CD, GPIO_BOTH_EDGES ); set_irq_type( IRQ_GPIO_CF_CD, IRQT_NOEDGE );
set_GPIO_IRQ_edge( GPIO_CF_IRQ, GPIO_FALLING_EDGE ); set_irq_type( IRQ_GPIO_CF_IRQ, IRQT_FALLING );
/* Register interrupts */ /* Register interrupts */
irq = IRQ_GPIO_CF_CD; irq = IRQ_GPIO_CF_CD;
...@@ -41,8 +39,9 @@ static int simpad_pcmcia_init(struct pcmcia_init *init){ ...@@ -41,8 +39,9 @@ static int simpad_pcmcia_init(struct pcmcia_init *init){
return 2; return 2;
irq_err: irq_err:
printk( KERN_ERR "%s: Request for IRQ %lu failed\n", __FUNCTION__, irq ); printk( KERN_ERR "%s: request for IRQ%d failed (%d)\n",
return -1; __FUNCTION__, irq, res);
return res;
} }
static int simpad_pcmcia_shutdown(void) static int simpad_pcmcia_shutdown(void)
...@@ -112,7 +111,7 @@ static int simpad_pcmcia_configure_socket(const struct pcmcia_configure ...@@ -112,7 +111,7 @@ static int simpad_pcmcia_configure_socket(const struct pcmcia_configure
if(configure->sock==0) return 0; if(configure->sock==0) return 0;
save_flags_cli(flags); local_irq_save(flags);
/* Murphy: see table of MIC2562a-1 */ /* Murphy: see table of MIC2562a-1 */
...@@ -135,22 +134,51 @@ static int simpad_pcmcia_configure_socket(const struct pcmcia_configure ...@@ -135,22 +134,51 @@ static int simpad_pcmcia_configure_socket(const struct pcmcia_configure
printk(KERN_ERR "%s(): unrecognized Vcc %u\n", __FUNCTION__, printk(KERN_ERR "%s(): unrecognized Vcc %u\n", __FUNCTION__,
configure->vcc); configure->vcc);
clear_cs3_bit(VCC_3V_EN|VCC_5V_EN|EN0|EN1); clear_cs3_bit(VCC_3V_EN|VCC_5V_EN|EN0|EN1);
restore_flags(flags); local_irq_restore(flags);
return -1; return -1;
} }
/* Silently ignore Vpp, output enable, speaker enable. */ /* Silently ignore Vpp, output enable, speaker enable. */
restore_flags(flags); local_irq_restore(flags);
return 0;
}
static int simpad_pcmcia_socket_init(int sock)
{
set_irq_type(IRQ_GPIO_CF_CD, IRQT_BOTHEDGE);
return 0;
}
static int simpad_pcmcia_socket_suspend(int sock)
{
set_irq_type(IRQ_GPIO_CF_CD, IRQT_NOEDGE);
return 0; return 0;
} }
struct pcmcia_low_level simpad_pcmcia_ops = { static struct pcmcia_low_level simpad_pcmcia_ops = {
simpad_pcmcia_init, init: simpad_pcmcia_init,
simpad_pcmcia_shutdown, shutdown: simpad_pcmcia_shutdown,
simpad_pcmcia_socket_state, socket_state: simpad_pcmcia_socket_state,
simpad_pcmcia_get_irq_info, get_irq_info: simpad_pcmcia_get_irq_info,
simpad_pcmcia_configure_socket configure_socket: simpad_pcmcia_configure_socket,
socket_init: simpad_pcmcia_socket_init,
socket_suspend: simpad_pcmcia_socket_suspend,
}; };
int __init pcmcia_simpad_init(void)
{
int ret = -ENODEV;
if (machine_is_simpad())
ret = sa1100_register_pcmcia(&simpad_pcmcia_ops);
return ret;
}
void __exit pcmcia_simpad_exit(void)
{
sa1100_unregister_pcmcia(&simpad_pcmcia_ops);
}
...@@ -18,8 +18,6 @@ ...@@ -18,8 +18,6 @@
* PCMCIA implementation routines for stork * PCMCIA implementation routines for stork
* *
*/ */
#include <linux/config.h>
#include <linux/module.h> #include <linux/module.h>
#include <linux/init.h> #include <linux/init.h>
#include <linux/kernel.h> #include <linux/kernel.h>
...@@ -28,50 +26,58 @@ ...@@ -28,50 +26,58 @@
#include <asm/hardware.h> #include <asm/hardware.h>
#include <asm/irq.h> #include <asm/irq.h>
#include <asm/arch/pcmcia.h> #include "sa1100_generic.h"
static int debug = 0; static int debug = 0;
static struct pcmcia_init sa1100_stork_pcmcia_init; static struct irqs {
int irq;
const char *str;
} irqs[] = {
{ IRQ_GPIO_STORK_PCMCIA_A_CARD_DETECT, "PCMCIA_CD0" },
{ IRQ_GPIO_STORK_PCMCIA_B_CARD_DETECT, "PCMCIA_CD1" },
};
static int stork_pcmcia_init(struct pcmcia_init *init) static int stork_pcmcia_init(struct pcmcia_init *init)
{ {
int irq, res; int irq, res;
printk("in stork_pcmcia_init\n");
sa1100_stork_pcmcia_init = *init;
/* Enable CF bus: */
storkSetLatchA(STORK_PCMCIA_PULL_UPS_POWER_ON);
/* All those are inputs */ printk("in stork_pcmcia_init\n");
GPDR &= ~(GPIO_STORK_PCMCIA_A_CARD_DETECT | GPIO_STORK_PCMCIA_B_CARD_DETECT | GPIO_STORK_PCMCIA_A_RDY| GPIO_STORK_PCMCIA_B_RDY);
/* Set transition detect */ /* Set transition detect */
set_GPIO_IRQ_edge( GPIO_STORK_PCMCIA_A_CARD_DETECT | GPIO_STORK_PCMCIA_B_CARD_DETECT, GPIO_BOTH_EDGES ); set_irq_type(IRQ_GPIO_STORK_PCMCIA_A_RDY, IRQT_FALLING);
set_GPIO_IRQ_edge( GPIO_STORK_PCMCIA_A_RDY| GPIO_STORK_PCMCIA_B_RDY, GPIO_FALLING_EDGE ); set_irq_type(IRQ_GPIO_STORK_PCMCIA_B_RDY, IRQT_FALLING);
/* Register interrupts */ /* Register interrupts */
irq = IRQ_GPIO_STORK_PCMCIA_A_CARD_DETECT; for (i = 0; i < ARRAY_SIZE(irqs); i++) {
res = request_irq( irq, init->handler, SA_INTERRUPT, "PCMCIA_CD0", NULL ); set_irq_type(irqs[i].irq, IRQT_NOEDGE);
if( res < 0 ) goto irq_err; res = request_irq(irqs[i].irq, init->handler, SA_INTERRUPT,
irq = IRQ_GPIO_STORK_PCMCIA_B_CARD_DETECT; irqs[i].str, NULL);
res = request_irq( irq, init->handler, SA_INTERRUPT, "PCMCIA_CD1", NULL ); if (res)
if( res < 0 ) goto irq_err; goto irq_err;
}
return 2; return 2;
irq_err: irq_err:
printk( KERN_ERR __FUNCTION__ ": Request for IRQ %u failed\n", irq ); printk(KERN_ERR "%s: request for IRQ%d failed (%d)\n",
return -1; __FUNCTION__, irq, res);
while (i--)
free_irq(irqs[i].irq, NULL);
return res;
} }
static int stork_pcmcia_shutdown(void) static int stork_pcmcia_shutdown(void)
{ {
int i;
printk(__FUNCTION__ "\n"); printk(__FUNCTION__ "\n");
/* disable IRQs */ /* disable IRQs */
free_irq( IRQ_GPIO_STORK_PCMCIA_A_CARD_DETECT, NULL ); for (i = 0; i < ARRAY_SIZE(irqs); i++)
free_irq( IRQ_GPIO_STORK_PCMCIA_B_CARD_DETECT, NULL ); free_irq(irqs[i].irq, NULL);
/* Disable CF bus: */ /* Disable CF bus: */
storkClearLatchA(STORK_PCMCIA_PULL_UPS_POWER_ON); storkClearLatchA(STORK_PCMCIA_PULL_UPS_POWER_ON);
...@@ -140,7 +146,7 @@ static int stork_pcmcia_configure_socket(const struct pcmcia_configure *configur ...@@ -140,7 +146,7 @@ static int stork_pcmcia_configure_socket(const struct pcmcia_configure *configur
printk(__FUNCTION__ ": socket=%d vcc=%d vpp=%d reset=%d\n", printk(__FUNCTION__ ": socket=%d vcc=%d vpp=%d reset=%d\n",
card, configure->vcc, configure->vpp, configure->reset); card, configure->vcc, configure->vpp, configure->reset);
save_flags_cli(flags); local_irq_save(flags);
if (card == 0) { if (card == 0) {
DETECT = GPIO_STORK_PCMCIA_A_CARD_DETECT; DETECT = GPIO_STORK_PCMCIA_A_CARD_DETECT;
...@@ -174,7 +180,7 @@ static int stork_pcmcia_configure_socket(const struct pcmcia_configure *configur ...@@ -174,7 +180,7 @@ static int stork_pcmcia_configure_socket(const struct pcmcia_configure *configur
default: default:
printk(KERN_ERR "%s(): unrecognized Vcc %u\n", __FUNCTION__, printk(KERN_ERR "%s(): unrecognized Vcc %u\n", __FUNCTION__,
configure->vcc); configure->vcc);
restore_flags(flags); local_irq_restore(flags);
return -1; return -1;
} }
...@@ -183,7 +189,7 @@ static int stork_pcmcia_configure_socket(const struct pcmcia_configure *configur ...@@ -183,7 +189,7 @@ static int stork_pcmcia_configure_socket(const struct pcmcia_configure *configur
else else
storkClearLatchB(RESET); storkClearLatchB(RESET);
restore_flags(flags); local_irq_restore(flags);
/* silently ignore vpp and speaker enables. */ /* silently ignore vpp and speaker enables. */
...@@ -192,11 +198,57 @@ static int stork_pcmcia_configure_socket(const struct pcmcia_configure *configur ...@@ -192,11 +198,57 @@ static int stork_pcmcia_configure_socket(const struct pcmcia_configure *configur
return 0; return 0;
} }
struct pcmcia_low_level stork_pcmcia_ops = { static int stork_pcmcia_socket_init(int sock)
stork_pcmcia_init, {
stork_pcmcia_shutdown, storkSetLatchA(STORK_PCMCIA_PULL_UPS_POWER_ON);
stork_pcmcia_socket_state,
stork_pcmcia_get_irq_info, if (sock == 0)
stork_pcmcia_configure_socket set_irq_type(IRQ_GPIO_STORK_PCMCIA_A_CARD_DETECT, IRQT_BOTHEDGE);
else if (sock == 1)
set_irq_type(IRQ_GPIO_STORK_PCMCIA_B_CARD_DETECT, IRQT_BOTHEDGE);
return 0;
}
static int stork_pcmcia_socket_suspend(int sock)
{
if (sock == 0)
set_irq_type(IRQ_GPIO_STORK_PCMCIA_A_CARD_DETECT, IRQT_NOEDGE);
else if (sock == 1) {
set_irq_type(IRQ_GPIO_STORK_PCMCIA_B_CARD_DETECT, IRQT_NOEDGE);
/*
* Hack!
*/
storkClearLatchA(STORK_PCMCIA_PULL_UPS_POWER_ON);
}
return 0;
}
static struct pcmcia_low_level stork_pcmcia_ops = {
init: stork_pcmcia_init,
shutdown: stork_pcmcia_shutdown,
socket_state: stork_pcmcia_socket_state,
get_irq_info: stork_pcmcia_get_irq_info,
configure_socket: stork_pcmcia_configure_socket,
socket_init: stork_pcmcia_socket_init,
socket_suspend: stork_pcmcia_socket_suspend,
}; };
int __init pcmcia_stork_init(void)
{
int ret = -ENODEV;
if (machine_is_stork())
ret = sa1100_register_pcmcia(&stork_pcmcia_ops);
return ret;
}
void __exit pcmcia_stork_exit(void)
{
sa1100_unregister_pcmcia(&stork_pcmcia_ops);
}
...@@ -7,128 +7,46 @@ ...@@ -7,128 +7,46 @@
#include <linux/kernel.h> #include <linux/kernel.h>
#include <linux/delay.h> #include <linux/delay.h>
#include <linux/sched.h> #include <linux/sched.h>
#include <linux/init.h>
#include <asm/hardware.h> #include <asm/hardware.h>
#include <asm/irq.h> #include <asm/irq.h>
#include <asm/arch/pcmcia.h> #include "sa1100_generic.h"
#define NCR_A0VPP (1<<16) #define NCR_A0VPP (1<<16)
#define NCR_A1VPP (1<<17) #define NCR_A1VPP (1<<17)
static int xp860_pcmcia_init(struct pcmcia_init *init){ static int xp860_pcmcia_init(struct pcmcia_init *init)
int return_val=0; {
/* Set GPIO_A<3:0> to be outputs for PCMCIA/CF power controller: */ /* Set GPIO_A<3:0> to be outputs for PCMCIA/CF power controller: */
PA_DDR &= ~(GPIO_GPIO0 | GPIO_GPIO1 | GPIO_GPIO2 | GPIO_GPIO3); PA_DDR &= ~(GPIO_GPIO0 | GPIO_GPIO1 | GPIO_GPIO2 | GPIO_GPIO3);
/* MAX1600 to standby mode: */ /* MAX1600 to standby mode: */
PA_DWR &= ~(GPIO_GPIO0 | GPIO_GPIO1 | GPIO_GPIO2 | GPIO_GPIO3); PA_DWR &= ~(GPIO_GPIO0 | GPIO_GPIO1 | GPIO_GPIO2 | GPIO_GPIO3);
#error Consider the following comment
/*
* 1- Please move GPDR initialisation where it is interrupt or preemption
* safe (like from xp860_map_io).
* 2- The GPCR line is bogus i.e. it will simply have absolutely no effect.
* Please see its definition in the SA1110 manual.
* 3- Please do not use NCR_* values!
*/
GPDR |= (NCR_A0VPP | NCR_A1VPP); GPDR |= (NCR_A0VPP | NCR_A1VPP);
GPCR &= ~(NCR_A0VPP | NCR_A1VPP); GPCR &= ~(NCR_A0VPP | NCR_A1VPP);
INTPOL1 |= return sa1111_pcmcia_init(init);
(1 << (S0_READY_NINT - SA1111_IRQ(32))) |
(1 << (S1_READY_NINT - SA1111_IRQ(32))) |
(1 << (S0_CD_VALID - SA1111_IRQ(32))) |
(1 << (S1_CD_VALID - SA1111_IRQ(32))) |
(1 << (S0_BVD1_STSCHG - SA1111_IRQ(32))) |
(1 << (S1_BVD1_STSCHG - SA1111_IRQ(32)));
return_val+=request_irq(S0_CD_VALID, init->handler, SA_INTERRUPT,
"XP860 PCMCIA (0) CD", NULL);
return_val+=request_irq(S1_CD_VALID, init->handler, SA_INTERRUPT,
"XP860 CF (1) CD", NULL);
return_val+=request_irq(S0_BVD1_STSCHG, init->handler, SA_INTERRUPT,
"XP860 PCMCIA (0) BVD1", NULL);
return_val+=request_irq(S1_BVD1_STSCHG, init->handler, SA_INTERRUPT,
"XP860 CF (1) BVD1", NULL);
return (return_val<0) ? -1 : 2;
}
static int xp860_pcmcia_shutdown(void){
free_irq(S0_CD_VALID, NULL);
free_irq(S1_CD_VALID, NULL);
free_irq(S0_BVD1_STSCHG, NULL);
free_irq(S1_BVD1_STSCHG, NULL);
INTPOL1 &=
~((1 << (S0_CD_VALID - SA1111_IRQ(32))) |
(1 << (S1_CD_VALID - SA1111_IRQ(32))) |
(1 << (S0_BVD1_STSCHG - SA1111_IRQ(32))) |
(1 << (S1_BVD1_STSCHG - SA1111_IRQ(32))));
return 0;
} }
static int xp860_pcmcia_socket_state(struct pcmcia_state_array static int
*state_array){ xp860_pcmcia_configure_socket(const struct pcmcia_configure *conf)
unsigned long status; {
int return_val=1; unsigned int gpio_mask, pa_dwr_mask;
unsigned int gpio_set, pa_dwr_set;
if(state_array->size<2) return -1; int ret;
memset(state_array->state, 0,
(state_array->size)*sizeof(struct pcmcia_state));
status=PCSR;
state_array->state[0].detect=((status & PCSR_S0_DETECT)==0)?1:0;
state_array->state[0].ready=((status & PCSR_S0_READY)==0)?0:1;
state_array->state[0].bvd1=((status & PCSR_S0_BVD1)==0)?0:1;
state_array->state[0].bvd2=((status & PCSR_S0_BVD2)==0)?0:1;
state_array->state[0].wrprot=((status & PCSR_S0_WP)==0)?0:1;
state_array->state[0].vs_3v=((status & PCSR_S0_VS1)==0)?1:0;
state_array->state[0].vs_Xv=((status & PCSR_S0_VS2)==0)?1:0;
state_array->state[1].detect=((status & PCSR_S1_DETECT)==0)?1:0;
state_array->state[1].ready=((status & PCSR_S1_READY)==0)?0:1;
state_array->state[1].bvd1=((status & PCSR_S1_BVD1)==0)?0:1;
state_array->state[1].bvd2=((status & PCSR_S1_BVD2)==0)?0:1;
state_array->state[1].wrprot=((status & PCSR_S1_WP)==0)?0:1;
state_array->state[1].vs_3v=((status & PCSR_S1_VS1)==0)?1:0;
state_array->state[1].vs_Xv=((status & PCSR_S1_VS2)==0)?1:0;
return return_val;
}
static int xp860_pcmcia_get_irq_info(struct pcmcia_irq_info *info){
switch(info->sock){
case 0:
info->irq=S0_READY_NINT;
break;
case 1:
info->irq=S1_READY_NINT;
break;
default:
return -1;
}
return 0;
}
static int xp860_pcmcia_configure_socket(const struct pcmcia_configure
*configure){
unsigned long pccr=PCCR, ncr=GPLR, gpio=PA_DWR;
/* Neponset uses the Maxim MAX1600, with the following connections: /* Neponset uses the Maxim MAX1600, with the following connections:
#warning ^^^ This isn't a neponset!
* *
* MAX1600 Neponset * MAX1600 Neponset
* *
...@@ -151,105 +69,90 @@ static int xp860_pcmcia_configure_socket(const struct pcmcia_configure ...@@ -151,105 +69,90 @@ static int xp860_pcmcia_configure_socket(const struct pcmcia_configure
* the corresponding truth table. * the corresponding truth table.
*/ */
switch(configure->sock){ switch (conf->sock) {
case 0:
switch(configure->vcc){
case 0: case 0:
gpio&=~(GPIO_GPIO0 | GPIO_GPIO1); pa_dwr_mask = GPIO_GPIO0 | GPIO_GPIO1;
break; gpio_mask = NCR_A0VPP | NCR_A1VPP;
case 33:
pccr=(pccr & ~PCCR_S0_PSE);
gpio=(gpio & ~(GPIO_GPIO0 | GPIO_GPIO1)) | GPIO_GPIO1;
break;
case 50:
pccr=(pccr | PCCR_S0_PSE);
gpio=(gpio & ~(GPIO_GPIO0 | GPIO_GPIO1)) | GPIO_GPIO0;
break;
switch (conf->vcc) {
default: default:
printk(KERN_ERR "%s(): unrecognized Vcc %u\n", __FUNCTION__, case 0: pa_dwr_set = 0; break;
configure->vcc); case 33: pa_dwr_set = GPIO_GPIO1; break;
return -1; case 50: pa_dwr_set = GPIO_GPIO0; break;
} }
switch(configure->vpp){ switch (conf->vpp) {
case 0: case 0: gpio_set = 0; break;
ncr&=~(NCR_A0VPP | NCR_A1VPP); case 120: gpio_set = NCR_A1VPP; break;
break;
case 120:
ncr=(ncr & ~(NCR_A0VPP | NCR_A1VPP)) | NCR_A1VPP;
break;
default: default:
if(configure->vpp == configure->vcc) if (conf->vpp == conf->vcc)
ncr=(ncr & ~(NCR_A0VPP | NCR_A1VPP)) | NCR_A0VPP; gpio_set = NCR_A0VPP;
else { else {
printk(KERN_ERR "%s(): unrecognized Vpp %u\n", __FUNCTION__, printk(KERN_ERR "%s(): unrecognized Vpp %u\n",
configure->vpp); __FUNCTION__, conf->vpp);
return -1; return -1;
} }
} }
pccr=(configure->reset)?(pccr | PCCR_S0_RST):(pccr & ~PCCR_S0_RST);
pccr=(configure->output)?(pccr | PCCR_S0_FLT):(pccr & ~PCCR_S0_FLT);
break; break;
case 1: case 1:
switch(configure->vcc){ pa_dwr_mask = GPIO_GPIO2 | GPIO_GPIO3;
case 0: gpio_mask = 0;
gpio&=~(GPIO_GPIO2 | GPIO_GPIO3); gpio_set = 0;
break;
case 33:
pccr=(pccr & ~PCCR_S1_PSE);
gpio=(gpio & ~(GPIO_GPIO2 | GPIO_GPIO3)) | GPIO_GPIO2;
break;
case 50:
pccr=(pccr | PCCR_S1_PSE);
gpio=(gpio & ~(GPIO_GPIO2 | GPIO_GPIO3)) | GPIO_GPIO3;
break;
switch (conf->vcc) {
default: default:
printk(KERN_ERR "%s(): unrecognized Vcc %u\n", __FUNCTION__, case 0: pa_dwr_set = 0; break;
configure->vcc); case 33: pa_dwr_set = GPIO_GPIO2; break;
return -1; case 50: pa_dwr_set = GPIO_GPIO3; break;
} }
if(configure->vpp!=configure->vcc && configure->vpp!=0){ if (conf->vpp != conf->vcc && conf->vpp != 0) {
printk(KERN_ERR "%s(): CF slot cannot support Vpp %u\n", __FUNCTION__, printk(KERN_ERR "%s(): CF slot cannot support Vpp %u\n",
configure->vpp); __FUNCTION__, conf->vpp);
return -1; return -1;
} }
pccr=(configure->reset)?(pccr | PCCR_S1_RST):(pccr & ~PCCR_S1_RST);
pccr=(configure->output)?(pccr | PCCR_S1_FLT):(pccr & ~PCCR_S1_FLT);
break; break;
default:
return -1;
} }
PCCR = pccr; ret = sa1111_pcmcia_configure_socket(conf);
ncr &= NCR_A0VPP|NCR_A1VPP; if (ret == 0) {
GPSR = ncr; unsigned long flags;
GPCR = (~ncr)&(NCR_A0VPP|NCR_A1VPP);
PA_DWR = gpio;
return 0; local_irq_save(flags);
PA_DWR = (PA_DWR & ~pa_dwr_mask) | pa_dwr_set;
GPSR = gpio_set;
GPCR = gpio_set ^ gpio_mask;
local_irq_restore(flags);
}
return ret;
} }
struct pcmcia_low_level xp860_pcmcia_ops = { static struct pcmcia_low_level xp860_pcmcia_ops = {
xp860_pcmcia_init, init: xp860_pcmcia_init,
xp860_pcmcia_shutdown, shutdown: sa1111_pcmcia_shutdown,
xp860_pcmcia_socket_state, socket_state: sa1111_pcmcia_socket_state,
xp860_pcmcia_get_irq_info, get_irq_info: sa1111_pcmcia_get_irq_info,
xp860_pcmcia_configure_socket configure_socket: xp860_pcmcia_configure_socket,
socket_init: sa1111_pcmcia_socket_init,
socket_suspend: sa1111_pcmcia_socket_suspend,
}; };
int __init pcmcia_xp860_init(void)
{
int ret = -ENODEV;
if (machine_is_xp860())
ret = sa1100_register_pcmcia(&xp860_pcmcia_ops);
return ret;
}
void __exit pcmcia_xp860_exit(void)
{
sa1100_unregister_pcmcia(&xp860_pcmcia_ops);
}
...@@ -6,10 +6,11 @@ ...@@ -6,10 +6,11 @@
*/ */
#include <linux/kernel.h> #include <linux/kernel.h>
#include <linux/sched.h> #include <linux/sched.h>
#include <linux/init.h>
#include <asm/hardware.h> #include <asm/hardware.h>
#include <asm/irq.h> #include <asm/irq.h>
#include <asm/arch/pcmcia.h> #include "sa1100_generic.h"
static inline void pcmcia_power(int on) { static inline void pcmcia_power(int on) {
...@@ -23,45 +24,53 @@ static inline void pcmcia_reset(int reset) ...@@ -23,45 +24,53 @@ static inline void pcmcia_reset(int reset)
yopy_gpio_set(GPIO_CF_RESET, reset); yopy_gpio_set(GPIO_CF_RESET, reset);
} }
static struct irqs {
int irq;
const char *str;
} irqs[] = {
{ IRQ_CF_CD, "CF_CD" },
{ IRQ_CF_BVD2, "CF_BVD2" },
{ IRQ_CF_BVD1, "CF_BVD1" },
};
static int yopy_pcmcia_init(struct pcmcia_init *init) static int yopy_pcmcia_init(struct pcmcia_init *init)
{ {
int irq, res; int i, res;
pcmcia_power(0); pcmcia_power(0);
pcmcia_reset(1); pcmcia_reset(1);
/* All those are inputs */
GPDR &= ~(GPIO_CF_CD | GPIO_CF_BVD2 | GPIO_CF_BVD1 | GPIO_CF_IREQ);
GAFR &= ~(GPIO_CF_CD | GPIO_CF_BVD2 | GPIO_CF_BVD1 | GPIO_CF_IREQ);
/* Set transition detect */ /* Set transition detect */
set_GPIO_IRQ_edge( GPIO_CF_CD|GPIO_CF_BVD2|GPIO_CF_BVD1, set_irq_type(IRQ_CF_IREQ, IRQT_FALLING);
GPIO_BOTH_EDGES );
set_GPIO_IRQ_edge( GPIO_CF_IREQ, GPIO_FALLING_EDGE );
/* Register interrupts */ /* Register interrupts */
irq = IRQ_CF_CD; for (i = 0; i < ARRAY_SIZE(irqs); i++) {
res = request_irq(irq, init->handler, SA_INTERRUPT, "CF_CD", NULL); set_irq_type(irqs[i].irq, IRQT_NOEDGE);
if (res < 0) goto irq_err; res = request_irq(irqs[i].irq, init->handler, SA_INTERRUPT,
irq = IRQ_CF_BVD2; irqs[i].str, NULL);
res = request_irq(irq, init->handler, SA_INTERRUPT, "CF_BVD2", NULL); if (res)
if (res < 0) goto irq_err; goto irq_err;
irq = IRQ_CF_BVD1; }
res = request_irq(irq, init->handler, SA_INTERRUPT, "CF_BVD1", NULL);
if (res < 0) goto irq_err;
return 1; return 1;
irq_err:
printk(KERN_ERR "%s: Request for IRQ %d failed\n", __FUNCTION__, irq); irq_err:
return -1; printk(KERN_ERR "%s: request for IRQ%d failed (%d)\n",
__FUNCTION__, irqs[i].irq, res);
while (i--)
free_irq(irqs[i].irq, NULL);
return res;
} }
static int yopy_pcmcia_shutdown(void) static int yopy_pcmcia_shutdown(void)
{ {
int i;
/* disable IRQs */ /* disable IRQs */
free_irq( IRQ_CF_CD, NULL ); for (i = 0; i < ARRAY_SIZE(irqs); i++)
free_irq( IRQ_CF_BVD2, NULL ); free_irq(irqs[i].irq, NULL);
free_irq( IRQ_CF_BVD1, NULL );
/* Disable CF */ /* Disable CF */
pcmcia_reset(1); pcmcia_reset(1);
...@@ -109,7 +118,7 @@ static int yopy_pcmcia_configure_socket(const struct pcmcia_configure *configure ...@@ -109,7 +118,7 @@ static int yopy_pcmcia_configure_socket(const struct pcmcia_configure *configure
return -1; return -1;
switch (configure->vcc) { switch (configure->vcc) {
case 0: /* power off */; case 0: /* power off */
pcmcia_power(0); pcmcia_power(0);
break; break;
case 50: case 50:
...@@ -130,10 +139,49 @@ static int yopy_pcmcia_configure_socket(const struct pcmcia_configure *configure ...@@ -130,10 +139,49 @@ static int yopy_pcmcia_configure_socket(const struct pcmcia_configure *configure
return 0; return 0;
} }
struct pcmcia_low_level yopy_pcmcia_ops = { static int yopy_pcmcia_socket_init(int sock)
yopy_pcmcia_init, {
yopy_pcmcia_shutdown, int i;
yopy_pcmcia_socket_state,
yopy_pcmcia_get_irq_info, for (i = 0; i < ARRAY_SIZE(irqs); i++)
yopy_pcmcia_configure_socket set_irq_type(irqs[i].irq, IRQT_BOTHEDGE);
return 0;
}
static int yopy_pcmcia_socket_suspend(int sock)
{
int i;
for (i = 0; i < ARRAY_SIZE(irqs); i++)
set_irq_type(irqs[i].irq, IRQT_NOEDGE);
return 0;
}
static struct pcmcia_low_level yopy_pcmcia_ops = {
init: yopy_pcmcia_init,
shutdown: yopy_pcmcia_shutdown,
socket_state: yopy_pcmcia_socket_state,
get_irq_info: yopy_pcmcia_get_irq_info,
configure_socket: yopy_pcmcia_configure_socket,
socket_init: yopy_pcmcia_socket_init,
socket_suspend: yopy_pcmcia_socket_suspend,
}; };
int __init pcmcia_yopy_init(void)
{
int ret = -ENODEV;
if (machine_is_yopy())
ret = sa1100_register_pcmcia(&yopy_pcmcia_ops);
return ret;
}
void __exit pcmcia_yopy_exit(void)
{
sa1100_unregister_pcmcia(&yopy_pcmcia_ops);
}
/*
* linux/drivers/pcmcia/sa1100_sa1111.c
*
* We implement the generic parts of a SA1111 PCMCIA driver. This
* basically means we handle everything except controlling the
* power. Power is machine specific...
*/
#include <linux/kernel.h>
#include <linux/sched.h>
#include <linux/ioport.h>
#include <asm/hardware.h>
#include <asm/hardware/sa1111.h>
#include <asm/irq.h>
#include "sa1100_generic.h"
#include "sa1111_generic.h"
static struct irqs {
int irq;
const char *str;
} irqs[] = {
{ S0_CD_VALID, "SA1111 PCMCIA card detect" },
{ S0_BVD1_STSCHG, "SA1111 PCMCIA BVD1" },
{ S1_CD_VALID, "SA1111 CF card detect" },
{ S1_BVD1_STSCHG, "SA1111 CF BVD1" },
};
int sa1111_pcmcia_init(struct pcmcia_init *init)
{
int i, ret;
if (!request_mem_region(_PCCR, 512, "PCMCIA"))
return -1;
for (i = ret = 0; i < ARRAY_SIZE(irqs); i++) {
set_irq_type(irqs[i].irq, IRQT_FALLING);
ret = request_irq(irqs[i].irq, init->handler, SA_INTERRUPT,
irqs[i].str, NULL);
if (ret)
break;
}
if (i < ARRAY_SIZE(irqs)) {
printk(KERN_ERR "sa1111_pcmcia: unable to grab IRQ%d (%d)\n",
irqs[i].irq, ret);
while (i--)
free_irq(irqs[i].irq, NULL);
release_mem_region(_PCCR, 16);
}
return ret ? -1 : 2;
}
int sa1111_pcmcia_shutdown(void)
{
int i;
for (i = 0; i < ARRAY_SIZE(irqs); i++)
free_irq(irqs[i].irq, NULL);
release_mem_region(_PCCR, 512);
return 0;
}
int sa1111_pcmcia_socket_state(struct pcmcia_state_array *state)
{
unsigned long status;
if (state->size < 2)
return -1;
status = PCSR;
state->state[0].detect = status & PCSR_S0_DETECT ? 0 : 1;
state->state[0].ready = status & PCSR_S0_READY ? 1 : 0;
state->state[0].bvd1 = status & PCSR_S0_BVD1 ? 1 : 0;
state->state[0].bvd2 = status & PCSR_S0_BVD2 ? 1 : 0;
state->state[0].wrprot = status & PCSR_S0_WP ? 1 : 0;
state->state[0].vs_3v = status & PCSR_S0_VS1 ? 0 : 1;
state->state[0].vs_Xv = status & PCSR_S0_VS2 ? 0 : 1;
state->state[1].detect = status & PCSR_S1_DETECT ? 0 : 1;
state->state[1].ready = status & PCSR_S1_READY ? 1 : 0;
state->state[1].bvd1 = status & PCSR_S1_BVD1 ? 1 : 0;
state->state[1].bvd2 = status & PCSR_S1_BVD2 ? 1 : 0;
state->state[1].wrprot = status & PCSR_S1_WP ? 1 : 0;
state->state[1].vs_3v = status & PCSR_S1_VS1 ? 0 : 1;
state->state[1].vs_Xv = status & PCSR_S1_VS2 ? 0 : 1;
return 1;
}
int sa1111_pcmcia_get_irq_info(struct pcmcia_irq_info *info)
{
int ret = 0;
switch (info->sock) {
case 0: info->irq = S0_READY_NINT; break;
case 1: info->irq = S1_READY_NINT; break;
default: ret = 1;
}
return ret;
}
int sa1111_pcmcia_configure_socket(const struct pcmcia_configure *conf)
{
unsigned int rst, flt, wait, pse, irq, pccr_mask;
unsigned long flags;
switch (conf->sock) {
case 0:
rst = PCCR_S0_RST;
flt = PCCR_S0_FLT;
wait = PCCR_S0_PWAITEN;
pse = PCCR_S0_PSE;
irq = S0_READY_NINT;
break;
case 1:
rst = PCCR_S1_RST;
flt = PCCR_S1_FLT;
wait = PCCR_S1_PWAITEN;
pse = PCCR_S1_PSE;
irq = S1_READY_NINT;
break;
default:
return -1;
}
switch (conf->vcc) {
case 0:
pccr_mask = 0;
break;
case 33:
pccr_mask = wait;
break;
case 50:
pccr_mask = pse | wait;
break;
default:
printk(KERN_ERR "sa1111_pcmcia: unrecognised VCC %u\n",
conf->vcc);
return -1;
}
if (conf->reset)
pccr_mask |= rst;
if (conf->output)
pccr_mask |= flt;
local_irq_save(flags);
PCCR = (PCCR & ~(pse | flt | wait | rst)) | pccr_mask;
local_irq_restore(flags);
if (conf->irq)
enable_irq(irq);
else
disable_irq(irq);
return 0;
}
int sa1111_pcmcia_socket_init(int sock)
{
return 0;
}
int sa1111_pcmcia_socket_suspend(int sock)
{
return 0;
}
extern int sa1111_pcmcia_init(struct pcmcia_init *);
extern int sa1111_pcmcia_shutdown(void);
extern int sa1111_pcmcia_socket_state(struct pcmcia_state_array *);
extern int sa1111_pcmcia_get_irq_info(struct pcmcia_irq_info *);
extern int sa1111_pcmcia_configure_socket(const struct pcmcia_configure *);
extern int sa1111_pcmcia_socket_init(int);
extern int sa1111_pcmcia_socket_suspend(int);
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment