Commit 5e4301f1 authored by Russell King's avatar Russell King

[PCMCIA] Reorganise SA11xx PCMCIA support.

The SA1100 PCMCIA structure didn't lend itself well to the device
model.  With this reorganisation, we end up with a reasonable
structure which fits better with the driver model.  It is now
obvious that SA11x0-based socket drivers are separate from
SA1111-based socket drivers, and are treated as two separate drivers
by the driver model.
parent 18e4786e
......@@ -82,10 +82,22 @@ config HD64465_PCMCIA
config PCMCIA_SA1100
tristate "SA1100 support"
depends on ARM && ARCH_SA1100 && PCMCIA
help
Say Y here to include support for SA11x0-based PCMCIA or CF
sockets, found on HP iPAQs, Yopy, and other StrongARM(R)/
Xscale(R) embedded machines.
This driver is also available as a module called sa1100_cs.
config PCMCIA_SA1111
tristate "SA1111 support"
depends on PCMCIA_SA1100 && SA1111
depends on ARM && ARCH_SA1100 && SA1111 && PCMCIA
help
Say Y here to include support for SA1111-based PCMCIA or CF
sockets, found on the Jornada 720, Graphicsmaster and other
StrongARM(R)/Xscale(R) embedded machines.
This driver is also available as a module called sa1111_cs.
config PCMCIA_PROBE
bool
......
......@@ -11,8 +11,8 @@ obj-$(CONFIG_I82365) += i82365.o
obj-$(CONFIG_I82092) += i82092.o
obj-$(CONFIG_TCIC) += tcic.o
obj-$(CONFIG_HD64465_PCMCIA) += hd64465_ss.o
obj-$(CONFIG_PCMCIA_SA1100) += sa1100_cs.o
obj-$(CONFIG_PCMCIA_SA1111) += sa1111_cs.o
obj-$(CONFIG_PCMCIA_SA1100) += sa11xx_core.o sa1100_cs.o
obj-$(CONFIG_PCMCIA_SA1111) += sa11xx_core.o sa1111_cs.o
yenta_socket-y += pci_socket.o yenta.o
......
......@@ -152,36 +152,6 @@ static inline unsigned int sa1100_pcmcia_cmd_time(unsigned int cpu_clock_khz,
struct pcmcia_low_level;
/* This structure encapsulates per-socket state which we might need to
* use when responding to a Card Services query of some kind.
*/
struct sa1100_pcmcia_socket {
/*
* Core PCMCIA state
*/
int nr;
struct resource res;
socket_state_t cs_state;
pccard_io_map io_map[MAX_IO_WIN];
pccard_mem_map pc_mem_map[MAX_WIN];
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;
/*
* Info from low level handler
*/
unsigned int irq;
unsigned int irq_state;
struct pcmcia_low_level *ops;
};
/* I/O pins replacing memory pins
* (PCMCIA System Architecture, 2nd ed., by Don Anderson, p.75)
*
......@@ -191,59 +161,4 @@ struct sa1100_pcmcia_socket {
#define iostschg bvd1
#define iospkr bvd2
/*
* Declaration for all machine specific init/exit functions.
*/
extern int pcmcia_adsbitsy_init(struct device *);
extern void pcmcia_adsbitsy_exit(struct device *);
extern int pcmcia_assabet_init(struct device *);
extern void pcmcia_assabet_exit(struct device *);
extern int pcmcia_badge4_init(struct device *);
extern void pcmcia_badge4_exit(struct device *);
extern int pcmcia_cerf_init(struct device *);
extern void pcmcia_cerf_exit(struct device *);
extern int pcmcia_flexanet_init(struct device *);
extern void pcmcia_flexanet_exit(struct device *);
extern int pcmcia_freebird_init(struct device *);
extern void pcmcia_freebird_exit(struct device *);
extern int pcmcia_gcplus_init(struct device *);
extern void pcmcia_gcplus_exit(struct device *);
extern int pcmcia_graphicsmaster_init(struct device *);
extern void pcmcia_graphicsmaster_exit(struct device *);
extern int pcmcia_pangolin_init(struct device *);
extern void pcmcia_pangolin_exit(struct device *);
extern int pcmcia_pfs168_init(struct device *);
extern void pcmcia_pfs168_exit(struct device *);
extern int pcmcia_shannon_init(struct device *);
extern void pcmcia_shannon_exit(struct device *);
extern int pcmcia_simpad_init(struct device *);
extern void pcmcia_simpad_exit(struct device *);
extern int pcmcia_stork_init(struct device *);
extern void pcmcia_stork_exit(struct device *);
extern int pcmcia_system3_init(struct device *);
extern void pcmcia_system3_exit(struct device *);
extern int pcmcia_trizeps_init(struct device *);
extern void pcmcia_trizeps_exit(struct device *);
extern int pcmcia_xp860_init(struct device *);
extern void pcmcia_xp860_exit(struct device *);
extern int pcmcia_yopy_init(struct device *);
extern void pcmcia_yopy_exit(struct device *);
#endif /* !defined(_PCMCIA_SA1100_H) */
......@@ -18,10 +18,9 @@
#include <asm/hardware.h>
#include <asm/mach-types.h>
#include "sa1100_generic.h"
#include "sa1111_generic.h"
static int adsbitsy_pcmcia_init(struct pcmcia_init *init)
static int adsbitsy_pcmcia_hw_init(struct sa1100_pcmcia_socket *skt)
{
/* Set GPIO_A<3:0> to be outputs for PCMCIA/CF power controller: */
PA_DDR &= ~(GPIO_GPIO0 | GPIO_GPIO1 | GPIO_GPIO2 | GPIO_GPIO3);
......@@ -32,20 +31,20 @@ static int adsbitsy_pcmcia_init(struct pcmcia_init *init)
/* Why? */
MECR = 0x09430943;
return sa1111_pcmcia_init(init);
return sa1111_pcmcia_init(skt);
}
static int
adsbitsy_pcmcia_configure_socket(int sock, const struct pcmcia_configure *conf)
adsbitsy_pcmcia_configure_socket(struct sa1100_pcmcia_socket *skt, const socket_state_t *state)
{
unsigned int pa_dwr_mask, pa_dwr_set;
int ret;
switch (sock) {
switch (skt->nr) {
case 0:
pa_dwr_mask = GPIO_GPIO0 | GPIO_GPIO1;
switch (conf->vcc) {
switch (state->Vcc) {
default:
case 0: pa_dwr_set = GPIO_GPIO0 | GPIO_GPIO1; break;
case 33: pa_dwr_set = GPIO_GPIO1; break;
......@@ -56,7 +55,7 @@ adsbitsy_pcmcia_configure_socket(int sock, const struct pcmcia_configure *conf)
case 1:
pa_dwr_mask = GPIO_GPIO2 | GPIO_GPIO3;
switch (conf->vcc) {
switch (state->Vcc) {
default:
case 0: pa_dwr_set = 0; break;
case 33: pa_dwr_set = GPIO_GPIO2; break;
......@@ -67,13 +66,13 @@ adsbitsy_pcmcia_configure_socket(int sock, const struct pcmcia_configure *conf)
return -1;
}
if (conf->vpp != conf->vcc && conf->vpp != 0) {
if (state->Vpp != state->Vcc && state->Vpp != 0) {
printk(KERN_ERR "%s(): CF slot cannot support VPP %u\n",
__FUNCTION__, conf->vpp);
__FUNCTION__, state->Vpp);
return -1;
}
ret = sa1111_pcmcia_configure_socket(sock, conf);
ret = sa1111_pcmcia_configure_socket(skt, state);
if (ret == 0) {
unsigned long flags;
......@@ -87,11 +86,10 @@ adsbitsy_pcmcia_configure_socket(int sock, const struct pcmcia_configure *conf)
static struct pcmcia_low_level adsbitsy_pcmcia_ops = {
.owner = THIS_MODULE,
.init = adsbitsy_pcmcia_init,
.shutdown = sa1111_pcmcia_shutdown,
.hw_init = adsbitsy_pcmcia_hw_init,
.hw_shutdown = sa1111_pcmcia_hw_shutdown,
.socket_state = sa1111_pcmcia_socket_state,
.configure_socket = adsbitsy_pcmcia_configure_socket,
.socket_init = sa1111_pcmcia_socket_init,
.socket_suspend = sa1111_pcmcia_socket_suspend,
};
......@@ -100,11 +98,6 @@ int __init pcmcia_adsbitsy_init(struct device *dev)
{
int ret = -ENODEV;
if (machine_is_adsbitsy())
ret = sa1100_register_pcmcia(&adsbitsy_pcmcia_ops, dev);
ret = sa11xx_drv_pcmcia_probe(dev, &adsbitsy_pcmcia_ops, 0, 2);
return ret;
}
void __exit pcmcia_adsbitsy_exit(struct device *dev)
{
sa1100_unregister_pcmcia(&adsbitsy_pcmcia_ops, dev);
}
......@@ -20,63 +20,32 @@
#include "sa1100_generic.h"
static struct irqs {
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" },
static struct pcmcia_irqs irqs[] = {
{ 1, ASSABET_IRQ_GPIO_CF_CD, "CF CD" },
{ 1, ASSABET_IRQ_GPIO_CF_BVD2, "CF BVD2" },
{ 1, ASSABET_IRQ_GPIO_CF_BVD1, "CF BVD1" },
};
static int assabet_pcmcia_init(struct pcmcia_init *init)
static int assabet_pcmcia_hw_init(struct sa1100_pcmcia_socket *skt)
{
int i, res;
/* Register interrupts */
for (i = 0; i < ARRAY_SIZE(irqs); i++) {
res = request_irq(irqs[i].irq, sa1100_pcmcia_interrupt,
SA_INTERRUPT, irqs[i].str, NULL);
if (res)
goto irq_err;
set_irq_type(irqs[i].irq, IRQT_NOEDGE);
}
init->socket_irq[0] = NO_IRQ;
init->socket_irq[1] = ASSABET_IRQ_GPIO_CF_IRQ;
/* There's only one slot, but it's "Slot 1": */
return 2;
irq_err:
printk(KERN_ERR "%s: request for IRQ%d failed (%d)\n",
__FUNCTION__, irqs[i].irq, res);
skt->irq = ASSABET_IRQ_GPIO_CF_IRQ;
while (i--)
free_irq(irqs[i].irq, NULL);
return res;
return sa11xx_request_irqs(skt, irqs, ARRAY_SIZE(irqs));
}
/*
* Release all resources.
*/
static int assabet_pcmcia_shutdown(void)
static void assabet_pcmcia_hw_shutdown(struct sa1100_pcmcia_socket *skt)
{
int i;
for (i = 0; i < ARRAY_SIZE(irqs); i++)
free_irq(irqs[i].irq, NULL);
return 0;
sa11xx_free_irqs(skt, irqs, ARRAY_SIZE(irqs));
}
static void
assabet_pcmcia_socket_state(int sock, struct pcmcia_state *state)
assabet_pcmcia_socket_state(struct sa1100_pcmcia_socket *skt, struct pcmcia_state *state)
{
unsigned long levels = GPLR;
if (sock == 1) {
state->detect = (levels & ASSABET_GPIO_CF_CD) ? 0 : 1;
state->ready = (levels & ASSABET_GPIO_CF_IRQ) ? 1 : 0;
state->bvd1 = (levels & ASSABET_GPIO_CF_BVD1) ? 1 : 0;
......@@ -84,21 +53,14 @@ assabet_pcmcia_socket_state(int sock, struct pcmcia_state *state)
state->wrprot = 0; /* Not available on Assabet. */
state->vs_3v = 1; /* Can only apply 3.3V on Assabet. */
state->vs_Xv = 0;
}
}
static int
assabet_pcmcia_configure_socket(int sock, const struct pcmcia_configure *configure)
assabet_pcmcia_configure_socket(struct sa1100_pcmcia_socket *skt, const socket_state_t *state)
{
unsigned int mask;
if (sock > 1)
return -1;
if (sock == 0)
return 0;
switch (configure->vcc) {
switch (state->Vcc) {
case 0:
mask = 0;
break;
......@@ -113,13 +75,13 @@ assabet_pcmcia_configure_socket(int sock, const struct pcmcia_configure *configu
default:
printk(KERN_ERR "%s(): unrecognized Vcc %u\n", __FUNCTION__,
configure->vcc);
state->Vcc);
return -1;
}
/* Silently ignore Vpp, output enable, speaker enable. */
if (configure->reset)
if (state->flags & SS_RESET)
mask |= ASSABET_BCR_CF_RST;
ASSABET_BCR_frob(ASSABET_BCR_CF_RST | ASSABET_BCR_CF_PWR, mask);
......@@ -132,48 +94,36 @@ assabet_pcmcia_configure_socket(int sock, const struct pcmcia_configure *configu
* be called at initialisation, power management event, or
* pcmcia event.
*/
static int assabet_pcmcia_socket_init(int sock)
static void assabet_pcmcia_socket_init(struct sa1100_pcmcia_socket *skt)
{
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;
sa11xx_enable_irqs(skt, irqs, ARRAY_SIZE(irqs));
}
/*
* Disable card status IRQs on suspend.
*/
static int assabet_pcmcia_socket_suspend(int sock)
static void assabet_pcmcia_socket_suspend(struct sa1100_pcmcia_socket *skt)
{
int i;
if (sock == 1) {
for (i = 0; i < ARRAY_SIZE(irqs); i++)
set_irq_type(irqs[i].irq, IRQT_NOEDGE);
sa11xx_disable_irqs(skt, irqs, ARRAY_SIZE(irqs));
/*
* 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 = {
.owner = THIS_MODULE,
.init = assabet_pcmcia_init,
.shutdown = assabet_pcmcia_shutdown,
.hw_init = assabet_pcmcia_hw_init,
.hw_shutdown = assabet_pcmcia_hw_shutdown,
.socket_state = assabet_pcmcia_socket_state,
.configure_socket = assabet_pcmcia_configure_socket,
......@@ -185,20 +135,8 @@ int __init pcmcia_assabet_init(struct device *dev)
{
int ret = -ENODEV;
if (machine_is_assabet()) {
if (!machine_has_neponset())
ret = sa1100_register_pcmcia(&assabet_pcmcia_ops, dev);
#ifndef CONFIG_ASSABET_NEPONSET
else
printk(KERN_ERR "Card Services disabled: missing "
"Neponset support\n");
#endif
}
return ret;
}
if (machine_is_assabet() && !machine_has_neponset())
ret = sa11xx_drv_pcmcia_probe(dev, &assabet_pcmcia_ops, 1, 1);
void __exit pcmcia_assabet_exit(struct device *dev)
{
sa1100_unregister_pcmcia(&assabet_pcmcia_ops, dev);
return ret;
}
......@@ -24,7 +24,6 @@
#include <asm/arch/badge4.h>
#include <asm/hardware/sa1111.h>
#include "sa1100_generic.h"
#include "sa1111_generic.h"
/*
......@@ -62,27 +61,6 @@ static int badge4_pcmvcc = 50; /* pins 3 and 5 jumpered on JP6 */
static int badge4_pcmvpp = 50; /* pins 2 and 4 jumpered on JP6 */
static int badge4_cfvcc = 33; /* pins 1 and 2 jumpered on JP10 */
static int badge4_pcmcia_init(struct pcmcia_init *init)
{
printk(KERN_INFO
"%s: badge4_pcmvcc=%d, badge4_pcmvpp=%d, badge4_cfvcc=%d\n",
__FUNCTION__,
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 5v0 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)
......@@ -97,32 +75,32 @@ static void complain_about_jumpering(const char *whom,
}
static int
badge4_pcmcia_configure_socket(int sock, const struct pcmcia_configure *conf)
badge4_pcmcia_configure_socket(struct sa1100_pcmcia_socket *skt, const socket_state_t *state)
{
int ret;
switch (sock) {
switch (skt->nr) {
case 0:
if ((conf->vcc != 0) &&
(conf->vcc != badge4_pcmvcc)) {
if ((state->Vcc != 0) &&
(state->Vcc != badge4_pcmvcc)) {
complain_about_jumpering(__FUNCTION__, "pcmvcc",
badge4_pcmvcc, conf->vcc);
badge4_pcmvcc, state->Vcc);
// Apply power regardless of the jumpering.
// return -1;
}
if ((conf->vpp != 0) &&
(conf->vpp != badge4_pcmvpp)) {
if ((state->Vpp != 0) &&
(state->Vpp != badge4_pcmvpp)) {
complain_about_jumpering(__FUNCTION__, "pcmvpp",
badge4_pcmvpp, conf->vpp);
badge4_pcmvpp, state->Vpp);
return -1;
}
break;
case 1:
if ((conf->vcc != 0) &&
(conf->vcc != badge4_cfvcc)) {
if ((state->Vcc != 0) &&
(state->Vcc != badge4_cfvcc)) {
complain_about_jumpering(__FUNCTION__, "cfvcc",
badge4_cfvcc, conf->vcc);
badge4_cfvcc, state->Vcc);
return -1;
}
break;
......@@ -131,16 +109,16 @@ badge4_pcmcia_configure_socket(int sock, const struct pcmcia_configure *conf)
return -1;
}
ret = sa1111_pcmcia_configure_socket(sock, conf);
ret = sa1111_pcmcia_configure_socket(skt, state);
if (ret == 0) {
unsigned long flags;
int need5V;
local_irq_save(flags);
need5V = ((conf->vcc == 50) || (conf->vpp == 50));
need5V = ((state->Vcc == 50) || (state->Vpp == 50));
badge4_set_5V(BADGE4_5V_PCMCIA_SOCK(conf->sock), need5V);
badge4_set_5V(BADGE4_5V_PCMCIA_SOCK(skt->nr), need5V);
local_irq_restore(flags);
}
......@@ -150,8 +128,8 @@ badge4_pcmcia_configure_socket(int sock, const struct pcmcia_configure *conf)
static struct pcmcia_low_level badge4_pcmcia_ops = {
.owner = THIS_MODULE,
.init = badge4_pcmcia_init,
.shutdown = badge4_pcmcia_shutdown,
.init = sa1111_pcmcia_hw_init,
.shutdown = sa1111_pcmcia_hw_shutdown,
.socket_state = sa1111_pcmcia_socket_state,
.configure_socket = badge4_pcmcia_configure_socket,
......@@ -163,15 +141,16 @@ int pcmcia_badge4_init(struct device *dev)
{
int ret = -ENODEV;
if (machine_is_badge4())
ret = sa1100_register_pcmcia(&badge4_pcmcia_ops, dev);
if (machine_is_badge4()) {
printk(KERN_INFO
"%s: badge4_pcmvcc=%d, badge4_pcmvpp=%d, badge4_cfvcc=%d\n",
__FUNCTION__,
badge4_pcmvcc, badge4_pcmvpp, badge4_cfvcc);
return ret;
}
ret = sa11xx_drv_pcmcia_probe(dev, &badge4_pcmcia_ops, 0, 2);
}
void __devexit pcmcia_badge4_exit(struct device *dev)
{
sa1100_unregister_pcmcia(&badge4_pcmcia_ops, dev);
return ret;
}
static int __init pcmv_setup(char *s)
......
......@@ -23,56 +23,29 @@
#define CERF_SOCKET 1
#endif
static struct irqs {
int irq;
const char *str;
} irqs[] = {
{ IRQ_GPIO_CF_CD, "CF_CD" },
{ IRQ_GPIO_CF_BVD2, "CF_BVD2" },
{ IRQ_GPIO_CF_BVD1, "CF_BVD1" }
static struct pcmcia_irqs irqs[] = {
{ CERF_SOCKET, IRQ_GPIO_CF_CD, "CF_CD" },
{ CERF_SOCKET, IRQ_GPIO_CF_BVD2, "CF_BVD2" },
{ CERF_SOCKET, IRQ_GPIO_CF_BVD1, "CF_BVD1" }
};
static int cerf_pcmcia_init(struct pcmcia_init *init)
static int cerf_pcmcia_hw_init(struct sa1100_pcmcia_socket *skt)
{
int i, res;
for (i = 0; i < ARRAY_SIZE(irqs); i++) {
res = request_irq(irqs[i].irq, sa1100_pcmcia_interrupt, SA_INTERRUPT,
irqs[i].str, NULL);
if (res)
goto irq_err;
set_irq_type(irqs[i].irq, IRQT_NOEDGE);
}
init->socket_irq[CERF_SOCKET] = IRQ_GPIO_CF_IRQ;
return 2;
skt->irq = IRQ_GPIO_CF_IRQ;
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;
return sa11xx_request_irqs(skt, irqs, ARRAY_SIZE(irqs));
}
static int cerf_pcmcia_shutdown(void)
static void cerf_pcmcia_hw_shutdown(struct sa1100_pcmcia_socket *skt)
{
int i;
for (i = 0; i < ARRAY_SIZE(irqs); i++)
free_irq(irqs[i].irq, NULL);
return 0;
sa11xx_free_irqs(skt, irqs, ARRAY_SIZE(irqs));
}
static void cerf_pcmcia_socket_state(int sock, struct pcmcia_state *state)
static void
cerf_pcmcia_socket_state(struct sa1100_pcmcia_socket *skt, struct pcmcia_state *state)
{
unsigned long levels=GPLR;
unsigned long levels = GPLR;
if (sock == CERF_SOCKET) {
state->detect=((levels & GPIO_CF_CD)==0)?1:0;
state->ready=(levels & GPIO_CF_IRQ)?1:0;
state->bvd1=(levels & GPIO_CF_BVD1)?1:0;
......@@ -80,19 +53,13 @@ static void cerf_pcmcia_socket_state(int sock, struct pcmcia_state *state)
state->wrprot=0;
state->vs_3v=1;
state->vs_Xv=0;
}
}
static int cerf_pcmcia_configure_socket(int sock, const struct pcmcia_configure
*configure)
static int
cerf_pcmcia_configure_socket(struct sa1100_pcmcia_socket *skt,
const socket_state_t *state)
{
if (sock>1)
return -1;
if (sock != CERF_SOCKET)
return 0;
switch(configure->vcc){
switch (state->Vcc) {
case 0:
break;
......@@ -104,19 +71,16 @@ static int cerf_pcmcia_configure_socket(int sock, const struct pcmcia_configure
break;
default:
printk(KERN_ERR "%s(): unrecognized Vcc %u\n", __FUNCTION__,
configure->vcc);
printk(KERN_ERR "%s(): unrecognized Vcc %u\n",
__FUNCTION__, state->Vcc);
return -1;
}
if(configure->reset)
{
if (state->flags & SS_RESET) {
#ifdef CONFIG_SA1100_CERF_CPLD
GPSR = GPIO_CF_RESET;
#endif
}
else
{
} else {
#ifdef CONFIG_SA1100_CERF_CPLD
GPCR = GPIO_CF_RESET;
#endif
......@@ -125,32 +89,20 @@ static int cerf_pcmcia_configure_socket(int sock, const struct pcmcia_configure
return 0;
}
static int cerf_pcmcia_socket_init(int sock)
static void cerf_pcmcia_socket_init(struct sa1100_pcmcia_socket *skt)
{
int i;
if (sock == CERF_SOCKET)
for (i = 0; i < ARRAY_SIZE(irqs); i++)
set_irq_type(irqs[i].irq, IRQT_BOTHEDGE);
return 0;
sa11xx_enable_irqs(skt, irqs, ARRAY_SIZE(irqs));
}
static int cerf_pcmcia_socket_suspend(int sock)
static void cerf_pcmcia_socket_suspend(struct sa1100_pcmcia_socket *skt)
{
int i;
if (sock == CERF_SOCKET)
for (i = 0; i < ARRAY_SIZE(irqs); i++)
set_irq_type(irqs[i].irq, IRQT_NOEDGE);
return 0;
sa11xx_disable_irqs(skt, irqs, ARRAY_SIZE(irqs));
}
static struct pcmcia_low_level cerf_pcmcia_ops = {
.owner = THIS_MODULE,
.init = cerf_pcmcia_init,
.shutdown = cerf_pcmcia_shutdown,
.init = cerf_pcmcia_hw_init,
.shutdown = cerf_pcmcia_hw_shutdown,
.socket_state = cerf_pcmcia_socket_state,
.configure_socket = cerf_pcmcia_configure_socket,
......@@ -163,12 +115,7 @@ int __init pcmcia_cerf_init(struct device *dev)
int ret = -ENODEV;
if (machine_is_cerf())
ret = sa1100_register_pcmcia(&cerf_pcmcia_ops, dev);
ret = sa11xx_drv_pcmcia_probe(dev, &cerf_pcmcia_ops, CERF_SOCKET, 1);
return ret;
}
void __exit pcmcia_cerf_exit(struct device *dev)
{
sa1100_unregister_pcmcia(&cerf_pcmcia_ops, dev);
}
......@@ -16,69 +16,32 @@
#include <asm/irq.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" }
static struct pcmcia_irqs irqs[] = {
{ 0, IRQ_GPIO_CF1_CD, "CF1_CD" },
{ 0, IRQ_GPIO_CF1_BVD1, "CF1_BVD1" },
{ 1, IRQ_GPIO_CF2_CD, "CF2_CD" },
{ 1, IRQ_GPIO_CF2_BVD1, "CF2_BVD1" }
};
/*
* Socket initialization.
*
* Called by sa1100_pcmcia_driver_init on startup.
* Must return the number of slots.
*
*/
static int flexanet_pcmcia_init(struct pcmcia_init *init)
static int flexanet_pcmcia_hw_init(struct sa1100_pcmcia_socket *skt)
{
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 );
/* Register the socket interrupts (not the card interrupts) */
for (i = 0; i < ARRAY_SIZE(irqs); i++) {
res = request_irq(irqs[i].irq, sa1100_pcmcia_interrupt, SA_INTERRUPT,
irqs[i].name, NULL);
if (res < 0)
break;
set_irq_type(irqs[i].irq, IRQT_NOEDGE);
}
init->socket_irq[0] = IRQ_GPIO_CF1_IRQ;
init->socket_irq[1] = IRQ_GPIO_CF2_IRQ;
/* 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;
}
skt->irq = skt->nr ? IRQ_GPIO_CF2_IRQ : IRQ_GPIO_CF1_IRQ;
return 2;
return sa11xx_request_irqs(skt, irqs, ARRAY_SIZE(irqs));
}
/*
* Socket shutdown
*
*/
static int flexanet_pcmcia_shutdown(void)
static void flexanet_pcmcia_hw_shutdown(struct sa1100_pcmcia_socket *skt)
{
int i;
/* disable IRQs */
for (i = 0; i < ARRAY_SIZE(irqs); i++)
free_irq(irqs[i].irq, NULL);
return 0;
sa11xx_free_irqs(skt, irqs, ARRAY_SIZE(irqs));
}
......@@ -88,12 +51,14 @@ static int flexanet_pcmcia_shutdown(void)
* Sockets in Flexanet are 3.3V only, without BVD2.
*
*/
static void flexanet_pcmcia_socket_state(int sock, struct pcmcia_state *state)
static void
flexanet_pcmcia_socket_state(struct sa1100_pcmcia_socket *skt,
struct pcmcia_state *state)
{
unsigned long levels = GPLR; /* Sense the GPIOs, asynchronously */
switch (sock) {
case 0: /* Socket 0 */
switch (skt->nr) {
ase 0: /* Socket 0 */
state->detect = ((levels & GPIO_CF1_NCD)==0)?1:0;
state->ready = (levels & GPIO_CF1_IRQ)?1:0;
state->bvd1 = (levels & GPIO_CF1_BVD1)?1:0;
......@@ -119,19 +84,17 @@ static void flexanet_pcmcia_socket_state(int sock, struct pcmcia_state *state)
/*
*
*/
static int flexanet_pcmcia_configure_socket(int sock, const struct pcmcia_configure
*configure)
static int
flexanet_pcmcia_configure_socket(struct sa1100_pcmcia_socket *skt,
const socket_state_t *state)
{
unsigned long value, flags, mask;
if (sock > 1)
return -1;
/* Ignore the VCC level since it is 3.3V and always on */
switch (configure->vcc)
{
switch (state->Vcc) {
case 0:
printk(KERN_WARNING "%s(): CS asked to power off.\n", __FUNCTION__);
printk(KERN_WARNING "%s(): CS asked to power off.\n",
__FUNCTION__);
break;
case 50:
......@@ -143,23 +106,26 @@ static int flexanet_pcmcia_configure_socket(int sock, const struct pcmcia_config
default:
printk(KERN_ERR "%s(): unrecognized Vcc %u\n", __FUNCTION__,
configure->vcc);
state->Vcc);
return -1;
}
/* Reset the slot(s) using the controls in the BCR */
mask = 0;
switch (sock)
{
case 0 : mask = FHH_BCR_CF1_RST; break;
case 1 : mask = FHH_BCR_CF2_RST; break;
switch (skt->nr) {
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);
value = (state->flags & SS_RESET) ? (value | mask) : (value & ~mask);
FHH_BCR = flexanet_BCR = value;
local_irq_restore(flags);
......@@ -167,30 +133,14 @@ static int flexanet_pcmcia_configure_socket(int sock, const struct pcmcia_config
return 0;
}
static int flexanet_pcmcia_socket_init(int sock)
static void flexanet_pcmcia_socket_init(struct sa1100_pcmcia_socket *skt)
{
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;
sa11xx_enable_irqs(skt, irqs, ARRAY_SIZE(irqs));
}
static int flexanet_pcmcia_socket_suspend(int sock)
static void flexanet_pcmcia_socket_suspend(struct sa1100_pcmcia_socket *skt)
{
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;
sa11xx_disable_irqs(skt, irqs, ARRAY_SIZE(irqs));
}
/*
......@@ -199,11 +149,10 @@ static int flexanet_pcmcia_socket_suspend(int sock)
*/
static struct pcmcia_low_level flexanet_pcmcia_ops = {
.owner = THIS_MODULE,
.init = flexanet_pcmcia_init,
.shutdown = flexanet_pcmcia_shutdown,
.hw_init = flexanet_pcmcia_hw_init,
.hw_shutdown = flexanet_pcmcia_hw_shutdown,
.socket_state = flexanet_pcmcia_socket_state,
.configure_socket = flexanet_pcmcia_configure_socket,
.socket_init = flexanet_pcmcia_socket_init,
.socket_suspend = flexanet_pcmcia_socket_suspend,
};
......@@ -213,13 +162,7 @@ int __init pcmcia_flexanet_init(struct device *dev)
int ret = -ENODEV;
if (machine_is_flexanet())
ret = sa1100_register_pcmcia(&flexanet_pcmcia_ops, dev);
ret = sa11xx_drv_pcmcia_probe(dev, &flexanet_pcmcia_ops, 0, 2);
return ret;
}
void __exit pcmcia_flexanet_exit(struct device *dev)
{
sa1100_unregister_pcmcia(&flexanet_pcmcia_ops, dev);
}
......@@ -15,17 +15,13 @@
#include <asm/irq.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 struct pcmcia_irqs irqs[] = {
{ 0, IRQ_GPIO_FREEBIRD_CF_CD, "CF_CD" },
{ 0, IRQ_GPIO_FREEBIRD_CF_BVD, "CF_BVD1" },
};
static int freebird_pcmcia_init(struct pcmcia_init *init){
int i, res;
static int freebird_pcmcia_init(struct sa1100_pcmcia_socket *skt)
{
/* Enable Linkup CF card */
LINKUP_PRC = 0xc0;
mdelay(100);
......@@ -37,51 +33,26 @@ static int freebird_pcmcia_init(struct pcmcia_init *init){
mdelay(100);
LINKUP_PRC = 0xc0;
/* Register interrupts */
for (i = 0; i < ARRAY_SIZE(irqs); i++) {
res = request_irq(irqs[i].irq, sa1100_pcmcia_interrupt, SA_INTERRUPT,
irqs[i].str, NULL);
if (res)
goto irq_err;
set_irq_type(irqs[i].irq, IRQT_NOEDGE);
}
init->socket_irq[0] = IRQ_GPIO_FREEBIRD_CF_IRQ;
/* There's only one slot, but it's "Slot 1": */
return 2;
irq_err:
printk(KERN_ERR "%s: request for IRQ%d failed (%d)\n",
__FUNCTION__, irqs[i].irq, res);
skt->irq = IRQ_GPIO_FREEBIRD_CF_IRQ;
while (i--)
free_irq(irqs[i].irq, NULL);
return res;
return sa11xx_request_irqs(skt, irqs, ARRAY_SIZE(irqs));
}
static int freebird_pcmcia_shutdown(void)
static void freebird_pcmcia_shutdown(struct sa1100_pcmcia_socket *skt)
{
int i;
/* disable IRQs */
for (i = 0; i < ARRAY_SIZE(irqs); i++)
free_irq(irqs[i].irq, NULL);
sa11xx_free_irqs(skt, irqs, ARRAY_SIZE(irqs);
/* Disable CF card */
LINKUP_PRC = 0x40; /* SSP=1 SOE=0 */
mdelay(100);
return 0;
}
static void freebird_pcmcia_socket_state(int sock, struct pcmcia_state *state)
static void
freebird_pcmcia_socket_state(struct sa1100_pcmcia_socket *skt, struct pcmcia_state *state)
{
unsigned long levels = LINKUP_PRS;
//printk("LINKUP_PRS=%x\n",levels);
// printk("LINKUP_PRS=%x\n",levels);
if (sock == 0) {
state->detect = ((levels & (LINKUP_CD1 | LINKUP_CD2))==0)?1:0;
state->ready = (levels & LINKUP_RDY)?1:0;
state->bvd1 = (levels & LINKUP_BVD1)?1:0;
......@@ -89,25 +60,20 @@ static void freebird_pcmcia_socket_state(int sock, struct pcmcia_state *state)
state->wrprot = 0; /* Not available on Assabet. */
state->vs_3v = 1; /* Can only apply 3.3V on Assabet. */
state->vs_Xv = 0;
}
}
static int freebird_pcmcia_configure_socket(int sock, const struct pcmcia_configure
*configure)
static int
freebird_pcmcia_configure_socket(struct sa1100_pcmcia_socket *skt,
socket_state_t *state)
{
unsigned long value, flags;
if(sock>1) return -1;
if(sock==1) return 0;
local_irq_save(flags);
value = 0xc0; /* SSP=1 SOE=1 CFE=1 */
switch(configure->vcc){
switch (state->Vcc) {
case 0:
break;
case 50:
......@@ -119,46 +85,38 @@ static int freebird_pcmcia_configure_socket(int sock, const struct pcmcia_config
break;
default:
printk(KERN_ERR "%s(): unrecognized Vcc %u\n", __FUNCTION__,
configure->vcc);
printk(KERN_ERR "%s(): unrecognized Vcc %u\n",
__FUNCTION__, state->Vcc);
local_irq_restore(flags);
return -1;
}
if (configure->reset)
value = (configure->reset) ? (value | LINKUP_RESET) : (value & ~LINKUP_RESET);
if (state->flags & SS_RESET)
value |= LINKUP_RESET;
/* Silently ignore Vpp, output enable, speaker enable. */
LINKUP_PRC = value;
//printk("LINKUP_PRC=%x\n",value);
// printk("LINKUP_PRC=%x\n",value);
local_irq_restore(flags);
return 0;
}
static int freebird_pcmcia_socket_init(int sock)
static void freebird_pcmcia_socket_init(struct sa1100_pcmcia_socket *skt)
{
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;
sa11xx_disable_irqs(skt, irqs, ARRAY_SIZE(irqs));
}
static int freebird_pcmcia_socket_suspend(int sock)
static void freebird_pcmcia_socket_suspend(struct sa1100_pcmcia_socket *skt)
{
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;
sa11xx_enable_irqs(skt, irqs, ARRAY_SIZE(irqs));
}
static struct pcmcia_low_level freebird_pcmcia_ops = {
.owner = THIS_MODULE,
.init = freebird_pcmcia_init,
.shutdown = freebird_pcmcia_shutdown,
.hw_init = freebird_pcmcia_hw_init,
.hw_shutdown = freebird_pcmcia_hw_shutdown,
.socket_state = freebird_pcmcia_socket_state,
.configure_socket = freebird_pcmcia_configure_socket,
......@@ -171,12 +129,7 @@ int __init pcmcia_freebird_init(struct device *dev)
int ret = -ENODEV;
if (machine_is_freebird())
ret = sa1100_register_pcmcia(&freebird_pcmcia_ops, dev);
ret = sa11xx_drv_pcmcia_probe(dev, &freebird_pcmcia_ops, 0, 1);
return ret;
}
void __exit pcmcia_freebird_exit(struct device *dev)
{
sa1100_unregister_pcmcia(&freebird_pcmcia_ops, dev);
}
......@@ -37,1120 +37,124 @@
#include <linux/module.h>
#include <linux/init.h>
#include <linux/config.h>
#include <linux/cpufreq.h>
#include <linux/delay.h>
#include <linux/ioport.h>
#include <linux/kernel.h>
#include <linux/workqueue.h>
#include <linux/timer.h>
#include <linux/mm.h>
#include <linux/notifier.h>
#include <linux/proc_fs.h>
#include <linux/version.h>
#include <pcmcia/version.h>
#include <pcmcia/cs_types.h>
#include <pcmcia/cs.h>
#include <pcmcia/ss.h>
#include <asm/hardware.h>
#include <asm/io.h>
#include <asm/irq.h>
#include <asm/system.h>
#include <asm/arch/assabet.h>
#include "sa1100.h"
#ifdef PCMCIA_DEBUG
static int pc_debug;
#endif
/* This structure maintains housekeeping state for each socket, such
* as the last known values of the card detect pins, or the Card Services
* callback value associated with the socket:
*/
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: */
static struct pcmcia_low_level *pcmcia_low_level;
static struct timer_list poll_timer;
static struct work_struct sa1100_pcmcia_task;
/*
* sa1100_pcmcia_default_mecr_timing
* ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
*
* Calculate MECR clock wait states for given CPU clock
* speed and command wait state. This function can be over-
* written by a board specific version.
*
* The default is to simply calculate the BS values as specified in
* the INTEL SA1100 development manual
* "Expansion Memory (PCMCIA) Configuration Register (MECR)"
* that's section 10.2.5 in _my_ version of the manuial ;)
*/
static unsigned int
sa1100_pcmcia_default_mecr_timing(unsigned int sock, unsigned int cpu_speed,
unsigned int cmd_time)
{
return sa1100_pcmcia_mecr_bs(cmd_time, cpu_speed);
}
/* sa1100_pcmcia_set_mecr()
* ^^^^^^^^^^^^^^^^^^^^^^^^^^^
*
* set MECR value for socket <sock> based on this sockets
* io, mem and attribute space access speed.
* Call board specific BS value calculation to allow boards
* to tweak the BS values.
*/
static int
sa1100_pcmcia_set_mecr(struct sa1100_pcmcia_socket *skt, unsigned int cpu_clock)
{
u32 mecr;
unsigned long flags;
unsigned int bs;
local_irq_save(flags);
bs = skt->ops->socket_get_timing(skt->nr, cpu_clock, skt->speed_io);
mecr = MECR;
MECR_FAST_SET(mecr, skt->nr, 0);
MECR_BSIO_SET(mecr, skt->nr, bs );
MECR_BSA_SET(mecr, skt->nr, bs );
MECR_BSM_SET(mecr, skt->nr, bs );
MECR = mecr;
local_irq_restore(flags);
DEBUG(4, "%s(): sock %u FAST %X BSM %X BSA %X BSIO %X\n",
__FUNCTION__, skt->nr, MECR_FAST_GET(mecr, skt->nr),
MECR_BSM_GET(mecr, skt->nr), MECR_BSA_GET(mecr, skt->nr),
MECR_BSIO_GET(mecr, skt->nr));
return 0;
}
/*
* sa1100_pcmcia_config_skt
* ^^^^^^^^^^^^^^^^^^^^^^^^
*
* Convert PCMCIA socket state to our socket configure structure.
*/
static int
sa1100_pcmcia_config_skt(struct sa1100_pcmcia_socket *skt, socket_state_t *state)
{
struct pcmcia_configure conf;
int ret;
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;
ret = skt->ops->configure_socket(skt->nr, &conf);
if (ret == 0) {
/*
* This really needs a better solution. The IRQ
* may or may not be claimed by the driver.
*/
if (skt->irq_state != 1 && state->io_irq) {
skt->irq_state = 1;
set_irq_type(skt->irq, IRQT_FALLING);
} else if (skt->irq_state == 1 && state->io_irq == 0) {
skt->irq_state = 0;
set_irq_type(skt->irq, IRQT_NOEDGE);
}
skt->cs_state = *state;
}
if (ret < 0)
printk(KERN_ERR "sa1100_pcmcia: unable to configure "
"socket %d\n", skt->nr);
return ret;
}
/* sa1100_pcmcia_sock_init()
* ^^^^^^^^^^^^^^^^^^^^^^^^^
*
* (Re-)Initialise the socket, turning on status interrupts
* and PCMCIA bus. This must wait for power to stabilise
* so that the card status signals report correctly.
*
* Returns: 0
*/
static int sa1100_pcmcia_sock_init(unsigned int sock)
{
struct sa1100_pcmcia_socket *skt = PCMCIA_SOCKET(sock);
DEBUG(2, "%s(): initializing socket %u\n", __FUNCTION__, sock);
sa1100_pcmcia_config_skt(skt, &dead_socket);
return skt->ops->socket_init(skt->nr);
}
/*
* sa1100_pcmcia_suspend()
* ^^^^^^^^^^^^^^^^^^^^^^^
*
* Remove power on the socket, disable IRQs from the card.
* Turn off status interrupts, and disable the PCMCIA bus.
*
* Returns: 0
*/
static int sa1100_pcmcia_suspend(unsigned int sock)
{
struct sa1100_pcmcia_socket *skt = PCMCIA_SOCKET(sock);
int ret;
DEBUG(2, "%s(): suspending socket %u\n", __FUNCTION__, skt->nr);
ret = sa1100_pcmcia_config_skt(skt, &dead_socket);
if (ret == 0)
ret = skt->ops->socket_suspend(skt->nr);
return ret;
}
/* sa1100_pcmcia_events()
* ^^^^^^^^^^^^^^^^^^^^^^
* Helper routine to generate a Card Services event mask based on
* state information obtained from the kernel low-level PCMCIA layer
* in a recent (and previous) sampling. Updates `prev_state'.
*
* Returns: an event mask for the given socket state.
*/
static inline unsigned int
sa1100_pcmcia_events(struct pcmcia_state *state,
struct pcmcia_state *prev_state,
unsigned int mask, unsigned int flags)
{
unsigned int events = 0;
if (state->detect != prev_state->detect) {
DEBUG(3, "%s(): card detect value %u\n", __FUNCTION__, state->detect);
events |= SS_DETECT;
}
if (state->ready != prev_state->ready) {
DEBUG(3, "%s(): card ready value %u\n", __FUNCTION__, state->ready);
events |= flags & SS_IOCARD ? 0 : SS_READY;
}
if (state->bvd1 != prev_state->bvd1) {
DEBUG(3, "%s(): card BVD1 value %u\n", __FUNCTION__, state->bvd1);
events |= flags & SS_IOCARD ? SS_STSCHG : SS_BATDEAD;
}
if (state->bvd2 != prev_state->bvd2) {
DEBUG(3, "%s(): card BVD2 value %u\n", __FUNCTION__, state->bvd2);
events |= flags & SS_IOCARD ? 0 : SS_BATWARN;
}
*prev_state = *state;
events &= mask;
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_task_handler()
* ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
* Processes serviceable socket events using the "eventd" thread context.
*
* Event processing (specifically, the invocation of the Card Services event
* callback) occurs in this thread rather than in the actual interrupt
* handler due to the use of scheduling operations in the PCMCIA core.
*/
static void sa1100_pcmcia_task_handler(void *data)
{
struct pcmcia_state state;
unsigned int all_events;
DEBUG(4, "%s(): entering PCMCIA monitoring thread\n", __FUNCTION__);
do {
unsigned int events;
int i;
DEBUG(4, "%s(): interrogating low-level PCMCIA service\n", __FUNCTION__);
all_events = 0;
for (i = 0; i < sa1100_pcmcia_socket_count; i++) {
struct sa1100_pcmcia_socket *skt = PCMCIA_SOCKET(i);
memset(&state, 0, sizeof(state));
skt->ops->socket_state(skt->nr, &state);
events = sa1100_pcmcia_events(&state, &skt->k_state,
skt->cs_state.csc_mask,
skt->cs_state.flags);
if (events && skt->handler != NULL)
skt->handler(skt->handler_info, events);
all_events |= events;
}
} while(all_events);
} /* sa1100_pcmcia_task_handler() */
static DECLARE_WORK(sa1100_pcmcia_task, sa1100_pcmcia_task_handler, NULL);
/* sa1100_pcmcia_poll_event()
* ^^^^^^^^^^^^^^^^^^^^^^^^^^
* Let's poll for events in addition to IRQs since IRQ only is unreliable...
*/
static void sa1100_pcmcia_poll_event(unsigned long dummy)
{
DEBUG(4, "%s(): polling for events\n", __FUNCTION__);
init_timer(&poll_timer);
poll_timer.function = sa1100_pcmcia_poll_event;
poll_timer.expires = jiffies + SA1100_PCMCIA_POLL_PERIOD;
add_timer(&poll_timer);
schedule_work(&sa1100_pcmcia_task);
}
/* sa1100_pcmcia_interrupt()
* ^^^^^^^^^^^^^^^^^^^^^^^^^
* Service routine for socket driver interrupts (requested by the
* low-level PCMCIA init() operation via sa1100_pcmcia_thread()).
* The actual interrupt-servicing work is performed by
* sa1100_pcmcia_thread(), largely because the Card Services event-
* handling code performs scheduling operations which cannot be
* executed from within an interrupt context.
*/
void sa1100_pcmcia_interrupt(int irq, void *dev, struct pt_regs *regs)
{
DEBUG(3, "%s(): servicing IRQ %d\n", __FUNCTION__, irq);
schedule_work(&sa1100_pcmcia_task);
}
/* sa1100_pcmcia_register_callback()
* ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
* Implements the register_callback() operation for the in-kernel
* PCMCIA service (formerly SS_RegisterCallback in Card Services). If
* the function pointer `handler' is not NULL, remember the callback
* location in the state for `sock', and increment the usage counter
* for the driver module. (The callback is invoked from the interrupt
* service routine, sa1100_pcmcia_interrupt(), to notify Card Services
* of interesting events.) Otherwise, clear the callback pointer in the
* socket state and decrement the module usage count.
*
* Returns: 0
*/
static int
sa1100_pcmcia_register_callback(unsigned int sock,
void (*handler)(void *, unsigned int),
void *info)
{
struct sa1100_pcmcia_socket *skt = PCMCIA_SOCKET(sock);
if (handler && !try_module_get(skt->ops->owner))
return -ENODEV;
if (handler == NULL) {
skt->handler = NULL;
} else {
skt->handler_info = info;
skt->handler = handler;
}
if (!handler)
module_put(skt->ops->owner);
return 0;
}
/* sa1100_pcmcia_inquire_socket()
* ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
* Implements the inquire_socket() operation for the in-kernel PCMCIA
* service (formerly SS_InquireSocket in Card Services). Of note is
* the setting of the SS_CAP_PAGE_REGS bit in the `features' field of
* `cap' to "trick" Card Services into tolerating large "I/O memory"
* addresses. Also set is SS_CAP_STATIC_MAP, which disables the memory
* resource database check. (Mapped memory is set up within the socket
* driver itself.)
*
* In conjunction with the STATIC_MAP capability is a new field,
* `io_offset', recommended by David Hinds. Rather than go through
* the SetIOMap interface (which is not quite suited for communicating
* window locations up from the socket driver), we just pass up
* an offset which is applied to client-requested base I/O addresses
* in alloc_io_space().
*
* 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
* general, the mapped * addresses of the PCMCIA memory regions
* will not be within 0xffff, setting force_low would be
* undesirable.
*
* SS_CAP_STATIC_MAP: don't bother with the (user-configured) memory
* resource database; we instead pass up physical address ranges
* and allow other parts of Card Services to deal with remapping.
*
* SS_CAP_PCCARD: we can deal with 16-bit PCMCIA & CF cards, but
* not 32-bit CardBus devices.
*
* Return value is irrelevant; the pcmcia subsystem ignores it.
*/
static int
sa1100_pcmcia_inquire_socket(unsigned int sock, socket_cap_t *cap)
{
struct sa1100_pcmcia_socket *skt = PCMCIA_SOCKET(sock);
int ret = -1;
DEBUG(2, "%s() for sock %u\n", __FUNCTION__, skt->nr);
if (sock < sa1100_pcmcia_socket_count) {
cap->features = SS_CAP_PAGE_REGS | SS_CAP_STATIC_MAP | SS_CAP_PCCARD;
cap->irq_mask = 0;
cap->map_size = PAGE_SIZE;
cap->pci_irq = skt->irq;
cap->io_offset = (unsigned long)skt->virt_io;
ret = 0;
}
return ret;
}
/* sa1100_pcmcia_get_status()
* ^^^^^^^^^^^^^^^^^^^^^^^^^^
* Implements the get_status() operation for the in-kernel PCMCIA
* service (formerly SS_GetStatus in Card Services). Essentially just
* fills in bits in `status' according to internal driver state or
* the value of the voltage detect chipselect register.
*
* As a debugging note, during card startup, the PCMCIA core issues
* three set_socket() commands in a row the first with RESET deasserted,
* the second with RESET asserted, and the last with RESET deasserted
* again. Following the third set_socket(), a get_status() command will
* be issued. The kernel is looking for the SS_READY flag (see
* setup_socket(), reset_socket(), and unreset_socket() in cs.c).
*
* Returns: 0
*/
static int
sa1100_pcmcia_get_status(unsigned int sock, unsigned int *status)
{
struct sa1100_pcmcia_socket *skt = PCMCIA_SOCKET(sock);
struct pcmcia_state state;
unsigned int stat;
DEBUG(2, "%s() for sock %u\n", __FUNCTION__, skt->nr);
memset(&state, 0, sizeof(state));
skt->ops->socket_state(skt->nr, &state);
skt->k_state = state;
stat = state.detect ? SS_DETECT : 0;
stat |= state.ready ? SS_READY : 0;
stat |= state.vs_3v ? SS_3VCARD : 0;
stat |= state.vs_Xv ? SS_XVCARD : 0;
/* The power status of individual sockets is not available
* explicitly from the hardware, so we just remember the state
* and regurgitate it upon request:
*/
stat |= skt->cs_state.Vcc ? SS_POWERON : 0;
if (skt->cs_state.flags & SS_IOCARD)
stat |= state.bvd1 ? SS_STSCHG : 0;
else {
if (state.bvd1 == 0)
stat |= SS_BATDEAD;
else if (state.bvd2 == 0)
stat |= SS_BATWARN;
}
DEBUG(3, "\tstatus: %s%s%s%s%s%s%s%s\n",
stat & SS_DETECT ? "DETECT " : "",
stat & SS_READY ? "READY " : "",
stat & SS_BATDEAD ? "BATDEAD " : "",
stat & SS_BATWARN ? "BATWARN " : "",
stat & SS_POWERON ? "POWERON " : "",
stat & SS_STSCHG ? "STSCHG " : "",
stat & SS_3VCARD ? "3VCARD " : "",
stat & SS_XVCARD ? "XVCARD " : "");
*status = stat;
return 0;
} /* sa1100_pcmcia_get_status() */
/* sa1100_pcmcia_get_socket()
* ^^^^^^^^^^^^^^^^^^^^^^^^^^
* Implements the get_socket() operation for the in-kernel PCMCIA
* service (formerly SS_GetSocket in Card Services). Not a very
* exciting routine.
*
* Returns: 0
*/
static int
sa1100_pcmcia_get_socket(unsigned int sock, socket_state_t *state)
{
struct sa1100_pcmcia_socket *skt = PCMCIA_SOCKET(sock);
DEBUG(2, "%s() for sock %u\n", __FUNCTION__, skt->nr);
*state = skt->cs_state;
return 0;
}
/* sa1100_pcmcia_set_socket()
* ^^^^^^^^^^^^^^^^^^^^^^^^^^
* Implements the set_socket() operation for the in-kernel PCMCIA
* service (formerly SS_SetSocket in Card Services). We more or
* less punt all of this work and let the kernel handle the details
* of power configuration, reset, &c. We also record the value of
* `state' in order to regurgitate it to the PCMCIA core later.
*
* Returns: 0
*/
static int
sa1100_pcmcia_set_socket(unsigned int sock, socket_state_t *state)
{
struct sa1100_pcmcia_socket *skt = PCMCIA_SOCKET(sock);
DEBUG(2, "%s() for sock %u\n", __FUNCTION__, skt->nr);
DEBUG(3, "\tmask: %s%s%s%s%s%s\n\tflags: %s%s%s%s%s%s\n",
(state->csc_mask==0)?"<NONE>":"",
(state->csc_mask&SS_DETECT)?"DETECT ":"",
(state->csc_mask&SS_READY)?"READY ":"",
(state->csc_mask&SS_BATDEAD)?"BATDEAD ":"",
(state->csc_mask&SS_BATWARN)?"BATWARN ":"",
(state->csc_mask&SS_STSCHG)?"STSCHG ":"",
(state->flags==0)?"<NONE>":"",
(state->flags&SS_PWR_AUTO)?"PWR_AUTO ":"",
(state->flags&SS_IOCARD)?"IOCARD ":"",
(state->flags&SS_RESET)?"RESET ":"",
(state->flags&SS_SPKR_ENA)?"SPKR_ENA ":"",
(state->flags&SS_OUTPUT_ENA)?"OUTPUT_ENA ":"");
DEBUG(3, "\tVcc %d Vpp %d irq %d\n",
state->Vcc, state->Vpp, state->io_irq);
return sa1100_pcmcia_config_skt(skt, state);
} /* sa1100_pcmcia_set_socket() */
/* sa1100_pcmcia_set_io_map()
* ^^^^^^^^^^^^^^^^^^^^^^^^^^
* Implements the set_io_map() operation for the in-kernel PCMCIA
* service (formerly SS_SetIOMap in Card Services). We configure
* the map speed as requested, but override the address ranges
* supplied by Card Services.
*
* Returns: 0 on success, -1 on error
*/
static int
sa1100_pcmcia_set_io_map(unsigned int sock, struct pccard_io_map *map)
{
struct sa1100_pcmcia_socket *skt = PCMCIA_SOCKET(sock);
DEBUG(2, "%s() for sock %u\n", __FUNCTION__, skt->nr);
DEBUG(3, "\tmap %u speed %u\n\tstart 0x%08x stop 0x%08x\n",
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&MAP_ACTIVE)?"ACTIVE ":"",
(map->flags&MAP_16BIT)?"16BIT ":"",
(map->flags&MAP_AUTOSZ)?"AUTOSZ ":"",
(map->flags&MAP_0WS)?"0WS ":"",
(map->flags&MAP_WRPROT)?"WRPROT ":"",
(map->flags&MAP_USE_WAIT)?"USE_WAIT ":"",
(map->flags&MAP_PREFETCH)?"PREFETCH ":"");
if (map->map >= MAX_IO_WIN) {
printk(KERN_ERR "%s(): map (%d) out of range\n", __FUNCTION__,
map->map);
return -1;
}
if (map->flags & MAP_ACTIVE) {
if ( map->speed == 0)
map->speed = SA1100_PCMCIA_IO_ACCESS;
sa1100_pcmcia_set_mecr(skt, cpufreq_get(0));
}
if (map->stop == 1)
map->stop = PAGE_SIZE-1;
map->stop -= map->start;
map->stop += (unsigned long)skt->virt_io;
map->start = (unsigned long)skt->virt_io;
skt->io_map[map->map] = *map;
return 0;
} /* sa1100_pcmcia_set_io_map() */
/* sa1100_pcmcia_set_mem_map()
* ^^^^^^^^^^^^^^^^^^^^^^^^^^^
* Implements the set_mem_map() operation for the in-kernel PCMCIA
* service (formerly SS_SetMemMap in Card Services). We configure
* the map speed as requested, but override the address ranges
* supplied by Card Services.
*
* Returns: 0 on success, -1 on error
*/
static int
sa1100_pcmcia_set_mem_map(unsigned int sock, struct pccard_mem_map *map)
{
struct sa1100_pcmcia_socket *skt = PCMCIA_SOCKET(sock);
unsigned long start;
DEBUG(2, "%s() for sock %u\n", __FUNCTION__, skt->nr);
DEBUG(3, "\tmap %u speed %u sys_start %08lx sys_stop %08lx card_start %08x\n",
map->map, map->speed, map->sys_start, map->sys_stop, map->card_start);
DEBUG(3, "\tflags: %s%s%s%s%s%s%s%s\n",
(map->flags==0)?"<NONE>":"",
(map->flags&MAP_ACTIVE)?"ACTIVE ":"",
(map->flags&MAP_16BIT)?"16BIT ":"",
(map->flags&MAP_AUTOSZ)?"AUTOSZ ":"",
(map->flags&MAP_0WS)?"0WS ":"",
(map->flags&MAP_WRPROT)?"WRPROT ":"",
(map->flags&MAP_ATTRIB)?"ATTRIB ":"",
(map->flags&MAP_USE_WAIT)?"USE_WAIT ":"");
if (map->map >= MAX_WIN) {
printk(KERN_ERR "%s(): map (%d) out of range\n", __FUNCTION__,
map->map);
return -1;
}
if (map->flags & MAP_ACTIVE) {
/*
* When clients issue RequestMap, the access speed is not always
* properly configured. Choose some sensible defaults.
*/
if (map->speed == 0) {
if (skt->cs_state.Vcc == 33)
map->speed = SA1100_PCMCIA_3V_MEM_ACCESS;
else
map->speed = SA1100_PCMCIA_5V_MEM_ACCESS;
}
sa1100_pcmcia_set_mecr(skt, cpufreq_get(0));
}
if (map->sys_stop == 0)
map->sys_stop = PAGE_SIZE-1;
start = (map->flags & MAP_ATTRIB) ? skt->phys_attr : skt->phys_mem;
map->sys_stop -= map->sys_start;
map->sys_stop += start + map->card_start;
map->sys_start = start + map->card_start;
skt->pc_mem_map[map->map] = *map;
return 0;
} /* sa1100_pcmcia_set_mem_map() */
#if defined(CONFIG_PROC_FS)
/* sa1100_pcmcia_proc_status()
* ^^^^^^^^^^^^^^^^^^^^^^^^^^^
* Implements the /proc/bus/pccard/??/status file.
*
* Returns: the number of characters added to the buffer
*/
static int
sa1100_pcmcia_proc_status(char *buf, char **start, off_t pos,
int count, int *eof, void *data)
{
struct sa1100_pcmcia_socket *skt = data;
unsigned int clock = cpufreq_get(0);
unsigned long mecr = MECR;
char *p = buf;
p+=sprintf(p, "k_state : %s%s%s%s%s%s%s\n",
skt->k_state.detect ? "detect " : "",
skt->k_state.ready ? "ready " : "",
skt->k_state.bvd1 ? "bvd1 " : "",
skt->k_state.bvd2 ? "bvd2 " : "",
skt->k_state.wrprot ? "wrprot " : "",
skt->k_state.vs_3v ? "vs_3v " : "",
skt->k_state.vs_Xv ? "vs_Xv " : "");
p+=sprintf(p, "status : %s%s%s%s%s%s%s%s%s\n",
skt->k_state.detect ? "SS_DETECT " : "",
skt->k_state.ready ? "SS_READY " : "",
skt->cs_state.Vcc ? "SS_POWERON " : "",
skt->cs_state.flags & SS_IOCARD ? "SS_IOCARD " : "",
(skt->cs_state.flags & SS_IOCARD &&
skt->k_state.bvd1) ? "SS_STSCHG " : "",
((skt->cs_state.flags & SS_IOCARD)==0 &&
(skt->k_state.bvd1==0)) ? "SS_BATDEAD " : "",
((skt->cs_state.flags & SS_IOCARD)==0 &&
(skt->k_state.bvd2==0)) ? "SS_BATWARN " : "",
skt->k_state.vs_3v ? "SS_3VCARD " : "",
skt->k_state.vs_Xv ? "SS_XVCARD " : "");
p+=sprintf(p, "mask : %s%s%s%s%s\n",
skt->cs_state.csc_mask & SS_DETECT ? "SS_DETECT " : "",
skt->cs_state.csc_mask & SS_READY ? "SS_READY " : "",
skt->cs_state.csc_mask & SS_BATDEAD ? "SS_BATDEAD " : "",
skt->cs_state.csc_mask & SS_BATWARN ? "SS_BATWARN " : "",
skt->cs_state.csc_mask & SS_STSCHG ? "SS_STSCHG " : "");
p+=sprintf(p, "cs_flags : %s%s%s%s%s\n",
skt->cs_state.flags & SS_PWR_AUTO ? "SS_PWR_AUTO " : "",
skt->cs_state.flags & SS_IOCARD ? "SS_IOCARD " : "",
skt->cs_state.flags & SS_RESET ? "SS_RESET " : "",
skt->cs_state.flags & SS_SPKR_ENA ? "SS_SPKR_ENA " : "",
skt->cs_state.flags & SS_OUTPUT_ENA ? "SS_OUTPUT_ENA " : "");
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, "I/O : %u (%u)\n", skt->speed_io,
sa1100_pcmcia_cmd_time(clock, MECR_BSIO_GET(mecr, skt->nr)));
p+=sprintf(p, "attribute: %u (%u)\n", skt->speed_attr,
sa1100_pcmcia_cmd_time(clock, MECR_BSA_GET(mecr, skt->nr)));
p+=sprintf(p, "common : %u (%u)\n", skt->speed_mem,
sa1100_pcmcia_cmd_time(clock, MECR_BSM_GET(mecr, skt->nr)));
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 = PCMCIA_SOCKET(sock);
}
#endif /* defined(CONFIG_PROC_FS) */
static struct pccard_operations sa1100_pcmcia_operations = {
.owner = THIS_MODULE,
.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,
.set_io_map = sa1100_pcmcia_set_io_map,
.set_mem_map = sa1100_pcmcia_set_mem_map,
#ifdef CONFIG_PROC_FS
.proc_setup = sa1100_pcmcia_proc_setup
#endif
};
#ifdef CONFIG_CPU_FREQ
/* sa1100_pcmcia_update_mecr()
* ^^^^^^^^^^^^^^^^^^^^^^^^^^^
* When sa1100_pcmcia_notifier() decides that a MECR adjustment (due
* to a core clock frequency change) is needed, this routine establishes
* new BS_xx values consistent with the clock speed `clock'.
*/
static void sa1100_pcmcia_update_mecr(unsigned int clock)
{
unsigned int sock;
for (sock = 0; sock < SA1100_PCMCIA_MAX_SOCK; ++sock) {
struct sa1100_pcmcia_socket *skt = PCMCIA_SOCKET(sock);
sa1100_pcmcia_set_mecr(skt, clock);
}
}
/* sa1100_pcmcia_notifier()
* ^^^^^^^^^^^^^^^^^^^^^^^^
* When changing the processor core clock frequency, it is necessary
* to adjust the MECR timings accordingly. We've recorded the timings
* requested by Card Services, so this is just a matter of finding
* out what our current speed is, and then recomputing the new MECR
* values.
*
* Returns: 0 on success, -1 on error
*/
static int
sa1100_pcmcia_notifier(struct notifier_block *nb, unsigned long val,
void *data)
{
struct cpufreq_freqs *freqs = data;
switch (val) {
case CPUFREQ_PRECHANGE:
if (freqs->new > freqs->old) {
DEBUG(2, "%s(): new frequency %u.%uMHz > %u.%uMHz, "
"pre-updating\n", __FUNCTION__,
freqs->new / 1000, (freqs->new / 100) % 10,
freqs->old / 1000, (freqs->old / 100) % 10);
sa1100_pcmcia_update_mecr(freqs->new);
}
break;
case CPUFREQ_POSTCHANGE:
if (freqs->new < freqs->old) {
DEBUG(2, "%s(): new frequency %u.%uMHz < %u.%uMHz, "
"post-updating\n", __FUNCTION__,
freqs->new / 1000, (freqs->new / 100) % 10,
freqs->old / 1000, (freqs->old / 100) % 10);
sa1100_pcmcia_update_mecr(freqs->new);
}
break;
}
return 0;
}
static struct notifier_block sa1100_pcmcia_notifier_block = {
.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 device *dev)
{
struct pcmcia_init pcmcia_init;
struct pcmcia_socket_class_data *cls;
unsigned int i, cpu_clock;
int ret;
/*
* Refuse to replace an existing driver.
*/
if (pcmcia_low_level)
return -EBUSY;
pcmcia_low_level = ops;
/*
* set default MECR calculation if the board specific
* code did not specify one...
*/
if (!ops->socket_get_timing)
ops->socket_get_timing = sa1100_pcmcia_default_mecr_timing;
pcmcia_init.socket_irq[0] = NO_IRQ;
pcmcia_init.socket_irq[1] = NO_IRQ;
ret = ops->init(&pcmcia_init);
if (ret < 0) {
printk(KERN_ERR "Unable to initialize kernel PCMCIA service (%d).\n", ret);
goto out;
}
sa1100_pcmcia_socket_count = ret;
cpu_clock = cpufreq_get(0);
for (i = 0; i < sa1100_pcmcia_socket_count; i++) {
struct sa1100_pcmcia_socket *skt = PCMCIA_SOCKET(i);
memset(skt, 0, sizeof(*skt));
}
/*
* We initialize the MECR to default values here, because we are
* not guaranteed to see a SetIOMap operation at runtime.
*/
for (i = 0; i < sa1100_pcmcia_socket_count; i++) {
struct sa1100_pcmcia_socket *skt = PCMCIA_SOCKET(i);
skt->res.start = _PCMCIA(i);
skt->res.end = _PCMCIA(i) + PCMCIASp - 1;
skt->res.name = "PCMCIA";
skt->res.flags = IORESOURCE_MEM;
ret = request_resource(&iomem_resource, &skt->res);
if (ret)
goto out_err;
skt->nr = i;
skt->ops = ops;
skt->irq = pcmcia_init.socket_irq[i];
skt->irq_state = 0;
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;
}
ops->socket_state(skt->nr, &skt->k_state);
sa1100_pcmcia_set_mecr(skt, cpu_clock);
}
cls = kmalloc(sizeof(struct pcmcia_socket_class_data), GFP_KERNEL);
if (!cls) {
ret = -ENOMEM;
goto out_err;
}
memset(cls, 0, sizeof(struct pcmcia_socket_class_data));
cls->ops = &sa1100_pcmcia_operations;
cls->nsock = sa1100_pcmcia_socket_count;
dev->class_data = cls;
/*
* 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;
if (skt->res.start)
release_resource(&skt->res);
}
ops->shutdown();
out:
pcmcia_low_level = NULL;
return ret;
}
EXPORT_SYMBOL(sa1100_register_pcmcia);
/* sa1100_unregister_pcmcia()
* ^^^^^^^^^^^^^^^^^^^^^^^^^^
*
* Unregister a previously registered pcmcia driver
*/
void sa1100_unregister_pcmcia(struct pcmcia_low_level *ops, struct device *dev)
{
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);
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_resource(&skt->res);
}
ops->shutdown();
flush_scheduled_work();
kfree(dev->class_data);
dev->class_data = NULL;
pcmcia_low_level = NULL;
}
EXPORT_SYMBOL(sa1100_unregister_pcmcia);
static struct device_driver sa1100_pcmcia_driver = {
.name = "sa11x0-pcmcia",
.bus = &platform_bus_type,
.devclass = &pcmcia_socket_class,
.suspend = pcmcia_socket_dev_suspend,
.resume = pcmcia_socket_dev_resume,
};
static struct platform_device sa1100_pcmcia_device = {
.name = "sa11x0-pcmcia",
.id = 0,
.dev = {
.name = "Intel Corporation SA11x0 [PCMCIA]",
},
};
struct ll_fns {
int (*init)(struct device *dev);
void (*exit)(struct device *dev);
};
static struct ll_fns sa1100_ll_fns[] = {
static int (*sa11x0_pcmcia_hw_init[])(struct device *dev) = {
#ifdef CONFIG_SA1100_ASSABET
{ .init = pcmcia_assabet_init, .exit = pcmcia_assabet_exit, },
pcmcia_assabet_init,
#endif
#ifdef CONFIG_SA1100_CERF
{ .init = pcmcia_cerf_init, .exit = pcmcia_cerf_exit, },
pcmcia_cerf_init,
#endif
#ifdef CONFIG_SA1100_FLEXANET
{ .init = pcmcia_flexanet_init, .exit = pcmcia_flexanet_exit, },
pcmcia_flexanet_init,
#endif
#ifdef CONFIG_SA1100_FREEBIRD
{ .init = pcmcia_freebird_init, .exit = pcmcia_freebird_exit, },
pcmcia_freebird_init,
#endif
#ifdef CONFIG_SA1100_GRAPHICSCLIENT
{ .init = pcmcia_gcplus_init, .exit = pcmcia_gcplus_exit, },
pcmcia_gcplus_init,
#endif
#ifdef CONFIG_SA1100_H3600
{ .init = pcmcia_h3600_init, .exit = pcmcia_h3600_exit, },
pcmcia_h3600_init,
#endif
#ifdef CONFIG_SA1100_PANGOLIN
{ .init = pcmcia_pangolin_init, .exit = pcmcia_pangolin_exit, },
pcmcia_pangolin_init,
#endif
#ifdef CONFIG_SA1100_SHANNON
{ .init = pcmcia_shannon_init, .exit = pcmcia_shannon_exit, },
pcmcia_shannon_init,
#endif
#ifdef CONFIG_SA1100_SIMPAD
{ .init = pcmcia_simpad_init, .exit = pcmcia_simpad_exit, },
pcmcia_simpad_init,
#endif
#ifdef CONFIG_SA1100_STORK
{ .init = pcmcia_stork_init, .exit = pcmcia_stork_exit, },
pcmcia_stork_init,
#endif
#ifdef CONFIG_SA1100_TRIZEPS
{ .init = pcmcia_trizeps_init, .exit = pcmcia_trizeps_exit, },
pcmcia_trizeps_init,
#endif
#ifdef CONFIG_SA1100_YOPY
{ .init = pcmcia_yopy_init, .exit = pcmcia_yopy_exit, },
pcmcia_yopy_init,
#endif
};
/* 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)
static int sa11x0_drv_pcmcia_probe(struct device *dev)
{
servinfo_t info;
int ret, i;
printk(KERN_INFO "SA11x0 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;
}
#ifdef CONFIG_CPU_FREQ
ret = cpufreq_register_notifier(&sa1100_pcmcia_notifier_block,
CPUFREQ_TRANSITION_NOTIFIER);
if (ret < 0) {
printk(KERN_ERR "Unable to register CPU frequency change "
"notifier (%d)\n", ret);
driver_unregister(&sa1100_pcmcia_driver);
return ret;
}
#endif
driver_register(&sa1100_pcmcia_driver);
int i, ret = -ENODEV;
/*
* Initialise any "on-board" PCMCIA sockets.
*/
for (i = 0; i < ARRAY_SIZE(sa1100_ll_fns); i++) {
ret = sa1100_ll_fns[i].init(&sa1100_pcmcia_device.dev);
for (i = 0; i < ARRAY_SIZE(sa11x0_pcmcia_hw_init); i++) {
ret = sa11x0_pcmcia_hw_init[i](dev);
if (ret == 0)
break;
}
if (ret == 0)
platform_device_register(&sa1100_pcmcia_device);
return ret;
}
/*
* Don't fail if we don't find any on-board sockets.
static struct device_driver sa11x0_pcmcia_driver = {
.probe = sa11x0_drv_pcmcia_probe,
.remove = sa11xx_drv_pcmcia_remove,
.name = "sa11x0-pcmcia",
.bus = &platform_bus_type,
.devclass = &pcmcia_socket_class,
.suspend = pcmcia_socket_dev_suspend,
.resume = pcmcia_socket_dev_resume,
};
static struct platform_device sa11x0_pcmcia_device = {
.name = "sa11x0-pcmcia",
.id = 0,
.dev = {
.name = "Intel Corporation SA11x0 [PCMCIA]",
},
};
/* sa11x0_pcmcia_init()
* ^^^^^^^^^^^^^^^^^^^^
*
* This routine performs low-level PCMCIA initialization and then
* registers this socket driver with Card Services.
*
* Returns: 0 on success, -ve error code on failure
*/
return 0;
static int __init sa11x0_pcmcia_init(void)
{
int ret;
ret = driver_register(&sa11x0_pcmcia_driver);
if (ret == 0) {
ret = platform_device_register(&sa11x0_pcmcia_device);
if (ret)
driver_unregister(&sa11x0_pcmcia_driver);
}
return ret;
}
/* sa1100_pcmcia_exit()
/* sa11x0_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)
static void __exit sa11x0_pcmcia_exit(void)
{
platform_device_unregister(&sa1100_pcmcia_device);
#ifdef CONFIG_CPU_FREQ
cpufreq_unregister_notifier(&sa1100_pcmcia_notifier_block, CPUFREQ_TRANSITION_NOTIFIER);
#endif
driver_unregister(&sa1100_pcmcia_driver);
platform_device_unregister(&sa11x0_pcmcia_device);
driver_unregister(&sa11x0_pcmcia_driver);
}
MODULE_AUTHOR("John Dorsey <john+@cs.cmu.edu>");
MODULE_DESCRIPTION("Linux PCMCIA Card Services: SA-1100 Socket Controller");
MODULE_DESCRIPTION("Linux PCMCIA Card Services: SA-11x0 Socket Controller");
MODULE_LICENSE("Dual MPL/GPL");
module_init(sa1100_pcmcia_init);
module_exit(sa1100_pcmcia_exit);
module_init(sa11x0_pcmcia_init);
module_exit(sa11x0_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 {
int socket_irq[SA1100_PCMCIA_MAX_SOCK];
};
struct pcmcia_state {
unsigned detect: 1,
ready: 1,
bvd1: 1,
bvd2: 1,
wrprot: 1,
vs_3v: 1,
vs_Xv: 1;
};
struct pcmcia_configure {
unsigned vcc: 8,
vpp: 8,
output: 1,
speaker: 1,
reset: 1,
irq: 1;
};
struct pcmcia_low_level {
struct module *owner;
#include "sa11xx_core.h"
int (*init)(struct pcmcia_init *);
int (*shutdown)(void);
void (*socket_state)(int sock, struct pcmcia_state *);
int (*configure_socket)(int sock, 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);
/*
* Calculate MECR timing clock wait states
/*
* Declaration for all machine specific init/exit functions.
*/
unsigned int (*socket_get_timing)(unsigned int sock,
unsigned int cpu_speed, unsigned int cmd_time);
};
extern int sa1100_register_pcmcia(struct pcmcia_low_level *, struct device *);
extern void sa1100_unregister_pcmcia(struct pcmcia_low_level *, struct device *);
extern void sa1100_pcmcia_interrupt(int, void *, struct pt_regs *);
#endif
extern int pcmcia_adsbitsy_init(struct device *);
extern int pcmcia_assabet_init(struct device *);
extern int pcmcia_badge4_init(struct device *);
extern int pcmcia_cerf_init(struct device *);
extern int pcmcia_flexanet_init(struct device *);
extern int pcmcia_freebird_init(struct device *);
extern int pcmcia_gcplus_init(struct device *);
extern int pcmcia_graphicsmaster_init(struct device *);
extern int pcmcia_h3600_init(struct device *);
extern int pcmcia_pangolin_init(struct device *);
extern int pcmcia_pfs168_init(struct device *);
extern int pcmcia_shannon_init(struct device *);
extern int pcmcia_simpad_init(struct device *);
extern int pcmcia_stork_init(struct device *);
extern int pcmcia_system3_init(struct device *);
extern int pcmcia_trizeps_init(struct device *);
extern int pcmcia_xp860_init(struct device *);
extern int pcmcia_yopy_init(struct device *);
......@@ -34,10 +34,12 @@ static volatile unsigned long *PCMCIA_Status =
static volatile unsigned long *PCMCIA_Power =
((volatile unsigned long *) ADS_p2v(_ADS_CS_PR));
static int gcplus_pcmcia_init(struct pcmcia_init *init)
{
int irq, res;
static struct pcmcia_irqs irqs[] = {
{ 0, S0_CD_IRQ, "PCMCIA 0 CD" },
};
static int gcplus_pcmcia_init(struct sa1100_pcmcia_socket *skt)
{
// Reset PCMCIA
// Reset Timing for CPLD(U2) version 8001E or later
*PCMCIA_Power &= ~ ADS_CS_PR_A_RESET;
......@@ -49,37 +51,27 @@ static int gcplus_pcmcia_init(struct pcmcia_init *init)
// Turn off 5V
*PCMCIA_Power &= ~0x03;
/* Register interrupts */
irq = S0_CD_IRQ;
res = request_irq(irq, sa1100_pcmcia_interrupt, SA_INTERRUPT, "PCMCIA 0 CD", NULL);
if (res < 0) {
printk(KERN_ERR "%s: request for IRQ%d failed (%d)\n",
__FUNCTION__, irq, res);
return res;
}
skt->irq = S0_STS_IRQ;
init->socket_irq[0] = S0_STS_IRQ;
return 1; // 1 PCMCIA Slot
/* Register interrupts */
return sa11xx_request_irqs(skt, irqs, ARRAY_SIZE(irqs));
}
static int gcplus_pcmcia_shutdown(void)
static void gcplus_pcmcia_hw_shutdown(struct sa1100_pcmcia_socket *skt)
{
/* disable IRQs */
free_irq( S0_CD_IRQ, NULL);
free_irq(S0_CD_IRQ, skt);
/* Shutdown PCMCIA power */
mdelay(2); // 2msec
*PCMCIA_Power &= ~0x03;
return 0;
}
static void gcplus_pcmcia_socket_state(int sock, struct pcmcia_state *state_array)
static void
gcplus_pcmcia_socket_state(struct sa1100_pcmcia_socket *skt, struct pcmcia_state *state)
{
unsigned long levels = *PCMCIA_Status;
if (sock == 0) {
state->detect=(levels & ADS_CS_ST_A_CD)?1:0;
state->ready=(levels & ADS_CS_ST_A_READY)?1:0;
state->bvd1= 0;
......@@ -87,19 +79,17 @@ static void gcplus_pcmcia_socket_state(int sock, struct pcmcia_state *state_arra
state->wrprot=0;
state->vs_3v=0;
state->vs_Xv=0;
}
}
static int gcplus_pcmcia_configure_socket(int sock, const struct pcmcia_configure
*configure)
static int
gcplus_pcmcia_configure_socket(struct sa1100_pcmcia_socket *skt,
const socket_state_t *state)
{
unsigned long flags;
if(sock>1) return -1;
local_irq_save(flags);
switch (configure->vcc) {
switch (state->Vcc) {
case 0:
*PCMCIA_Power &= ~(ADS_CS_PR_A_3V_POWER | ADS_CS_PR_A_5V_POWER);
break;
......@@ -115,8 +105,8 @@ static int gcplus_pcmcia_configure_socket(int sock, const struct pcmcia_configur
break;
default:
printk(KERN_ERR "%s(): unrecognized Vcc %u\n", __FUNCTION__,
configure->vcc);
printk(KERN_ERR "%s(): unrecognized Vcc %u\n",
__FUNCTION__, state->Vcc);
local_irq_restore(flags);
return -1;
}
......@@ -135,23 +125,20 @@ static int gcplus_pcmcia_configure_socket(int sock, const struct pcmcia_configur
return 0;
}
static int gcplus_pcmcia_socket_init(int sock)
static void gcplus_pcmcia_socket_init(struct sa1100_pcmcia_socket *skt)
{
return 0;
}
static int gcplus_pcmcia_socket_suspend(int sock)
static void gcplus_pcmcia_socket_suspend(struct sa1100_pcmcia_socket *skt)
{
return 0;
}
static struct pcmcia_low_level gcplus_pcmcia_ops = {
.owner = THIS_MODULE,
.init = gcplus_pcmcia_init,
.shutdown = gcplus_pcmcia_shutdown,
.hw_init = gcplus_pcmcia_hw_init,
.hw_shutdown = gcplus_pcmcia_hw_shutdown,
.socket_state = gcplus_pcmcia_socket_state,
.configure_socket = gcplus_pcmcia_configure_socket,
.socket_init = gcplus_pcmcia_socket_init,
.socket_suspend = gcplus_pcmcia_socket_suspend,
};
......@@ -161,13 +148,7 @@ int __init pcmcia_gcplus_init(struct device *dev)
int ret = -ENODEV;
if (machine_is_gcplus())
ret = sa1100_register_pcmcia(&gcplus_pcmcia_ops, dev);
ret = sa11xx_drv_pcmcia_probe(dev, &gcplus_pcmcia_ops, 0, 1);
return ret;
}
void __exit pcmcia_gcplus_exit(struct device *dev)
{
sa1100_unregister_pcmcia(&gcplus_pcmcia_ops, dev);
}
......@@ -17,10 +17,9 @@
#include <asm/hardware.h>
#include <asm/mach-types.h>
#include "sa1100_generic.h"
#include "sa1111_generic.h"
static int graphicsmaster_pcmcia_init(struct pcmcia_init *init)
static int graphicsmaster_pcmcia_hw_init(struct sa1100_pcmcia_socket *skt)
{
int return_val=0;
......@@ -33,20 +32,21 @@ static int graphicsmaster_pcmcia_init(struct pcmcia_init *init)
/* why? */
MECR = 0x09430943;
return sa1111_pcmcia_init(init);
return sa1111_pcmcia_hwinit(skt);
}
static int
graphicsmaster_pcmcia_configure_socket(int sock, const struct pcmcia_configure *conf)
graphicsmaster_pcmcia_configure_socket(struct sa1100_pcmcia_socket *skt,
const socket_state_t *state)
{
unsigned int pa_dwr_mask, pa_dwr_set;
int ret;
switch (sock) {
switch (skt->nr) {
case 0:
pa_dwr_mask = GPIO_GPIO0 | GPIO_GPIO1;
switch (conf->vcc) {
switch (state->Vcc) {
default:
case 0: pa_dwr_set = GPIO_GPIO0 | GPIO_GPIO1; break;
case 33: pa_dwr_set = GPIO_GPIO1; break;
......@@ -57,21 +57,22 @@ graphicsmaster_pcmcia_configure_socket(int sock, const struct pcmcia_configure *
case 1:
pa_dwr_mask = GPIO_GPIO2 | GPIO_GPIO3;
switch (conf->vcc) {
switch (state->Vcc) {
default:
case 0: pa_dwr_set = GPIO_GPIO2 | GPIO_GPIO3; break;
case 33: pa_dwr_set = GPIO_GPIO3; break;
case 50: pa_dwr_set = GPIO_GPIO2; break;
}
break;
}
if (conf->vpp != conf->vcc && conf->vpp != 0) {
printk(KERN_ERR "%s(): CF slot cannot support Vpp %u\n", __FUNCTION__,
conf->vpp);
if (state->Vpp != state->Vcc && state->Vpp != 0) {
printk(KERN_ERR "%s(): CF slot cannot support Vpp %u\n",
__FUNCTION__, state->Vpp);
return -1;
}
ret = sa1111_pcmcia_configure_socket(sock, conf);
ret = sa1111_pcmcia_configure_socket(skt, state);
if (ret == 0) {
unsigned long flags;
......@@ -85,8 +86,8 @@ graphicsmaster_pcmcia_configure_socket(int sock, const struct pcmcia_configure *
static struct pcmcia_low_level graphicsmaster_pcmcia_ops = {
.owner = THIS_MODULE,
.init = graphicsmaster_pcmcia_init,
.shutdown = sa1111_pcmcia_shutdown,
.hw_init = graphicsmaster_pcmcia_init,
.hw_shutdown = sa1111_pcmcia_hw_shutdown,
.socket_state = sa1111_pcmcia_socket_state,
.configure_socket = graphicsmaster_pcmcia_configure_socket,
......@@ -99,13 +100,7 @@ int __init pcmcia_graphicsmaster_init(struct device *dev)
int ret = -ENODEV;
if (machine_is_graphicsmaster())
ret = sa1100_register_pcmcia(&graphicsmaster_pcmcia_ops, dev);
ret = sa11xx_drv_pcmcia_probe(dev, &graphicsmaster_pcmcia_ops, 0, 2);
return ret;
}
void __exit pcmcia_graphicsmaster_exit(struct device *dev)
{
sa1100_unregister_pcmcia(&graphicsmaster_pcmcia_ops, dev);
}
......@@ -8,6 +8,7 @@
#include <linux/kernel.h>
#include <linux/sched.h>
#include <linux/device.h>
#include <linux/interrupt.h>
#include <linux/init.h>
#include <asm/hardware.h>
......@@ -17,65 +18,36 @@
#include "sa1100_generic.h"
static struct irqs {
int irq;
const char *str;
} irqs[] = {
{ IRQ_GPIO_H3600_PCMCIA_CD0, "PCMCIA CD0" },
{ IRQ_GPIO_H3600_PCMCIA_CD1, "PCMCIA CD1" }
static struct pcmcia_irqs irqs[] = {
{ 0, IRQ_GPIO_H3600_PCMCIA_CD0, "PCMCIA CD0" },
{ 1, IRQ_GPIO_H3600_PCMCIA_CD1, "PCMCIA CD1" }
};
static int h3600_pcmcia_init(struct pcmcia_init *init)
static int h3600_pcmcia_hw_init(struct sa1100_pcmcia_socket *skt)
{
int i, res;
skt->irq = skt->nr ? IRQ_GPIO_H3600_PCMCIA_IRQ1
: IRQ_GPIO_H3600_PCMCIA_IRQ0;
/*
* Register interrupts
*/
for (i = res = 0; i < ARRAY_SIZE(irqs); i++) {
res = request_irq(irqs[i].irq, sa1100_pcmcia_interrupt,
SA_INTERRUPT, irqs[i].str, NULL);
if (res)
break;
}
if (res) {
printk(KERN_ERR "%s: request for IRQ%d failed (%d)\n",
__FUNCTION__, irqs[i].irq, res);
while (i--)
free_irq(irqs[i].irq, NULL);
}
init->socket_irq[0] = IRQ_GPIO_H3600_PCMCIA_IRQ0;
init->socket_irq[1] = IRQ_GPIO_H3600_PCMCIA_IRQ1;
return res ? res : 2;
return sa11xx_request_irqs(skt, irqs, ARRAY_SIZE(irqs));
}
static int h3600_pcmcia_shutdown(void)
static void h3600_pcmcia_hw_shutdown(struct sa1100_pcmcia_socket *skt)
{
int i;
/*
* disable IRQs
*/
for (i = 0; i < ARRAY_SIZE(irqs); i++)
free_irq(irqs[i].irq, NULL);
sa11xx_free_irqs(skt, irqs, ARRAY_SIZE(irqs));
/* Disable CF bus: */
clr_h3600_egpio(IPAQ_EGPIO_OPT_NVRAM_ON);
clr_h3600_egpio(IPAQ_EGPIO_OPT_ON);
set_h3600_egpio(IPAQ_EGPIO_OPT_RESET);
return 0;
}
static void h3600_pcmcia_socket_state(int sock, struct pcmcia_state *state)
static void
h3600_pcmcia_socket_state(struct sa1100_pcmcia_socket *skt, struct pcmcia_state *state)
{
unsigned long levels = GPLR;
switch (sock) {
switch (skt->nr) {
case 0:
state->detect = levels & GPIO_H3600_PCMCIA_CD0 ? 0 : 1;
state->ready = levels & GPIO_H3600_PCMCIA_IRQ0 ? 1 : 0;
......@@ -99,18 +71,15 @@ static void h3600_pcmcia_socket_state(int sock, struct pcmcia_state *state)
}
static int
h3600_pcmcia_configure_socket(int sock, const struct pcmcia_configure *conf)
h3600_pcmcia_configure_socket(struct sa1100_pcmcia_socket *skt, const socket_state_t *state)
{
if (sock > 1)
return -1;
if (conf->vcc != 0 && conf->vcc != 33 && conf->vcc != 50) {
if (state->Vcc != 0 && state->Vcc != 33 && state->Vcc != 50) {
printk(KERN_ERR "h3600_pcmcia: unrecognized Vcc %u.%uV\n",
conf->vcc / 10, conf->vcc % 10);
state->Vcc / 10, state->Vcc % 10);
return -1;
}
if (conf->reset)
if (state->flags & SS_RESET)
set_h3600_egpio(IPAQ_EGPIO_CARD_RESET);
else
clr_h3600_egpio(IPAQ_EGPIO_CARD_RESET);
......@@ -120,7 +89,7 @@ h3600_pcmcia_configure_socket(int sock, const struct pcmcia_configure *conf)
return 0;
}
static int h3600_pcmcia_socket_init(int sock)
static void h3600_pcmcia_socket_init(struct sa1100_pcmcia_socket *skt)
{
/* Enable CF bus: */
set_h3600_egpio(IPAQ_EGPIO_OPT_NVRAM_ON);
......@@ -130,28 +99,12 @@ static int h3600_pcmcia_socket_init(int sock)
set_current_state(TASK_UNINTERRUPTIBLE);
schedule_timeout(10*HZ / 1000);
switch (sock) {
case 0:
set_irq_type(IRQ_GPIO_H3600_PCMCIA_CD0, IRQT_BOTHEDGE);
break;
case 1:
set_irq_type(IRQ_GPIO_H3600_PCMCIA_CD1, IRQT_BOTHEDGE);
break;
}
return 0;
sa11xx_enable_irqs(skt, irqs, ARRAY_SIZE(irqs));
}
static int h3600_pcmcia_socket_suspend(int sock)
static void h3600_pcmcia_socket_suspend(struct sa1100_pcmcia_socket *skt)
{
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;
}
sa11xx_disable_irqs(skt, irqs, ARRAY_SIZE(irqs));
/*
* FIXME: This doesn't fit well. We don't have the mechanism in
......@@ -159,20 +112,18 @@ static int h3600_pcmcia_socket_suspend(int sock)
* on one bus. We rely on the cs.c behaviour shutting down
* socket 0 then socket 1.
*/
if (sock == 1) {
if (skt->nr == 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;
}
struct pcmcia_low_level h3600_pcmcia_ops = {
.owner = THIS_MODULE,
.init = h3600_pcmcia_init,
.shutdown = h3600_pcmcia_shutdown,
.hw_init = h3600_pcmcia_hw_init,
.hw_shutdown = h3600_pcmcia_hw_shutdown,
.socket_state = h3600_pcmcia_socket_state,
.configure_socket = h3600_pcmcia_configure_socket,
......@@ -185,12 +136,7 @@ int __init pcmcia_h3600_init(struct device *dev)
int ret = -ENODEV;
if (machine_is_h3600())
ret = sa1100_register_pcmcia(&h3600_pcmcia_ops, dev);
ret = sa11xx_drv_pcmcia_probe(dev, &h3600_pcmcia_ops, 0, 2);
return ret;
}
void __exit pcmcia_h3600_exit(struct device *dev)
{
sa1100_unregister_pcmcia(&h3600_pcmcia_ops, dev);
}
......@@ -15,7 +15,6 @@
#include <asm/hardware/sa1111.h>
#include <asm/mach-types.h>
#include "sa1100_generic.h"
#include "sa1111_generic.h"
#define SOCKET0_POWER GPIO_GPIO0
......@@ -24,7 +23,7 @@
#warning *** Does SOCKET1_3V actually do anything?
#define SOCKET1_3V GPIO_GPIO3
static int jornada720_pcmcia_init(struct pcmcia_init *init)
static int jornada720_pcmcia_hw_init(struct sa1100_pcmcia_socket *skt)
{
/*
* What is all this crap for?
......@@ -46,23 +45,23 @@ static int jornada720_pcmcia_init(struct pcmcia_init *init)
PC_SDR = 0;
PC_SSR = 0;
return sa1111_pcmcia_init(init);
return sa1111_pcmcia_hw_init(skt);
}
static int
jornada720_pcmcia_configure_socket(int sock, const struct pcmcia_configure *conf)
jornada720_pcmcia_configure_socket(struct sa1100_pcmcia_socket *skt, const socket_state_t *state)
{
unsigned int pa_dwr_mask, pa_dwr_set;
int ret;
printk("%s(): config socket %d vcc %d vpp %d\n", __FUNCTION__,
sock, conf->vcc, conf->vpp);
skt->nr, state->Vcc, state->Vpp);
switch (sock) {
switch (skt->nr) {
case 0:
pa_dwr_mask = SOCKET0_POWER | SOCKET0_3V;
switch (conf->vcc) {
switch (state->Vcc) {
default:
case 0: pa_dwr_set = 0; break;
case 33: pa_dwr_set = SOCKET0_POWER | SOCKET0_3V; break;
......@@ -73,7 +72,7 @@ printk("%s(): config socket %d vcc %d vpp %d\n", __FUNCTION__,
case 1:
pa_dwr_mask = SOCKET1_POWER;
switch (conf->vcc) {
switch (state->Vcc) {
default:
case 0: pa_dwr_set = 0; break;
case 33: pa_dwr_set = SOCKET1_POWER; break;
......@@ -85,13 +84,13 @@ printk("%s(): config socket %d vcc %d vpp %d\n", __FUNCTION__,
return -1;
}
if (conf->vpp != conf->vcc && conf->vpp != 0) {
if (state->Vpp != state->Vcc && state->Vpp != 0) {
printk(KERN_ERR "%s(): slot cannot support VPP %u\n",
__FUNCTION__, conf->vpp);
__FUNCTION__, state->Vpp);
return -1;
}
ret = sa1111_pcmcia_configure_socket(sock, conf);
ret = sa1111_pcmcia_configure_socket(skt, state);
if (ret == 0) {
unsigned long flags;
......@@ -105,8 +104,8 @@ printk("%s(): config socket %d vcc %d vpp %d\n", __FUNCTION__,
static struct pcmcia_low_level jornada720_pcmcia_ops = {
.owner = THIS_MODULE,
.init = jornada720_pcmcia_init,
.shutdown = sa1111_pcmcia_shutdown,
.hw_init = jornada720_pcmcia_hw_init,
.hw_shutdown = sa1111_pcmcia_hw_shutdown,
.socket_state = sa1111_pcmcia_socket_state,
.configure_socket = jornada720_pcmcia_configure_socket,
......@@ -119,12 +118,7 @@ int __init pcmcia_jornada720_init(struct device *dev)
int ret = -ENODEV;
if (machine_is_jornada720())
ret = sa1100_register_pcmcia(&jornada720_pcmcia_ops, dev);
ret = sa11xx_drv_pcmcia_probe(dev, &jornada720_pcmcia_ops, 0, 2);
return ret;
}
void __devexit pcmcia_jornada720_exit(struct device *dev)
{
sa1100_unregister_pcmcia(&jornada720_pcmcia_ops, dev);
}
......@@ -15,7 +15,6 @@
#include <asm/arch/neponset.h>
#include <asm/hardware/sa1111.h>
#include "sa1100_generic.h"
#include "sa1111_generic.h"
/*
......@@ -42,53 +41,28 @@
* the corresponding truth table.
*/
static int neponset_pcmcia_init(struct pcmcia_init *init)
{
NCR_0 &= ~(NCR_A0VPP | NCR_A1VPP);
/*
* Set GPIO_A<3:0> to be outputs for the MAX1600,
* and switch to standby mode.
*/
PA_DDR = 0;
PA_SDR = 0;
PA_DWR = 0;
PA_SSR = 0;
return sa1111_pcmcia_init(init);
}
static int
neponset_pcmcia_configure_socket(int sock, const struct pcmcia_configure *conf)
neponset_pcmcia_configure_socket(struct sa1100_pcmcia_socket *skt, const socket_state_t *state)
{
unsigned int ncr_mask, pa_dwr_mask;
unsigned int ncr_set, pa_dwr_set;
unsigned int ncr_mask, ncr_set, pa_dwr_mask, pa_dwr_set;
int ret;
switch (sock) {
switch (skt->nr) {
case 0:
pa_dwr_mask = GPIO_GPIO0 | GPIO_GPIO1;
ncr_mask = NCR_A0VPP | NCR_A1VPP;
switch (conf->vcc) {
default:
case 0: pa_dwr_set = 0; break;
case 33: pa_dwr_set = GPIO_GPIO1; break;
case 50: pa_dwr_set = GPIO_GPIO0; break;
}
switch (conf->vpp) {
case 0: ncr_set = 0; break;
case 120: ncr_set = NCR_A1VPP; break;
default:
if (conf->vpp == conf->vcc)
if (state->Vpp == 0)
ncr_set = 0;
else if (state->Vpp == 120)
ncr_set = NCR_A1VPP;
else if (state->Vpp == state->Vcc)
ncr_set = NCR_A0VPP;
else {
printk(KERN_ERR "%s(): unrecognized VPP %u\n",
__FUNCTION__, conf->vpp);
__FUNCTION__, state->Vpp);
return -1;
}
}
break;
case 1:
......@@ -96,16 +70,9 @@ neponset_pcmcia_configure_socket(int sock, const struct pcmcia_configure *conf)
ncr_mask = 0;
ncr_set = 0;
switch (conf->vcc) {
default:
case 0: pa_dwr_set = 0; break;
case 33: pa_dwr_set = GPIO_GPIO2; break;
case 50: pa_dwr_set = GPIO_GPIO3; break;
}
if (conf->vpp != conf->vcc && conf->vpp != 0) {
if (state->Vpp != state->Vcc && state->Vpp != 0) {
printk(KERN_ERR "%s(): CF slot cannot support VPP %u\n",
__FUNCTION__, conf->vpp);
__FUNCTION__, state->Vpp);
return -1;
}
break;
......@@ -114,27 +81,46 @@ neponset_pcmcia_configure_socket(int sock, const struct pcmcia_configure *conf)
return -1;
}
ret = sa1111_pcmcia_configure_socket(sock, conf);
/*
* pa_dwr_set is the mask for selecting Vcc on both sockets.
* pa_dwr_mask selects which bits (and therefore socket) we change.
*/
switch (state->Vcc) {
default:
case 0: pa_dwr_set = 0; break;
case 33: pa_dwr_set = GPIO_GPIO1|GPIO_GPIO2; break;
case 50: pa_dwr_set = GPIO_GPIO0|GPIO_GPIO3; break;
}
ret = sa1111_pcmcia_configure_socket(skt, state);
if (ret == 0) {
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;
PA_DWR = (PA_DWR & ~pa_dwr_mask) | (pa_dwr_set & pa_dwr_mask);
local_irq_restore(flags);
}
return 0;
}
static void neponset_pcmcia_socket_init(struct sa1100_pcmcia_socket *skt)
{
if (skt->nr == 0)
NCR_0 &= ~(NCR_A0VPP | NCR_A1VPP);
sa1111_pcmcia_socket_init(skt);
}
static struct pcmcia_low_level neponset_pcmcia_ops = {
.owner = THIS_MODULE,
.init = neponset_pcmcia_init,
.shutdown = sa1111_pcmcia_shutdown,
.hw_init = sa1111_pcmcia_hw_init,
.hw_shutdown = sa1111_pcmcia_hw_shutdown,
.socket_state = sa1111_pcmcia_socket_state,
.configure_socket = neponset_pcmcia_configure_socket,
.socket_init = sa1111_pcmcia_socket_init,
.socket_init = neponset_pcmcia_socket_init,
.socket_suspend = sa1111_pcmcia_socket_suspend,
};
......@@ -142,13 +128,17 @@ int __init pcmcia_neponset_init(struct device *dev)
{
int ret = -ENODEV;
if (machine_is_assabet())
ret = sa1100_register_pcmcia(&neponset_pcmcia_ops, dev);
if (machine_is_assabet()) {
/*
* Set GPIO_A<3:0> to be outputs for the MAX1600,
* and switch to standby mode.
*/
PA_DDR = 0;
PA_DWR = 0;
PA_SDR = 0;
PA_SSR = 0;
ret = sa11xx_drv_pcmcia_probe(dev, &neponset_pcmcia_ops, 0, 2);
}
return ret;
}
void __devexit pcmcia_neponset_exit(struct device *dev)
{
sa1100_unregister_pcmcia(&neponset_pcmcia_ops, dev);
}
......@@ -22,7 +22,12 @@
#define PANGOLIN_SOCK 0
#endif
static int pangolin_pcmcia_init(struct pcmcia_init *init){
static struct pcmcia_irqs irqs[] = {
{ PANGOLIN_SOCK, IRQ_PCMCIA_CD, "PCMCIA CD" },
};
static int pangolin_pcmcia_hw_init(struct sa1100_pcmcia_socket *skt)
{
int res;
#ifndef CONFIG_SA1100_PANGOLIN_PCMCIA_IDE
......@@ -30,42 +35,27 @@ static int pangolin_pcmcia_init(struct pcmcia_init *init){
GPCR = GPIO_PCMCIA_BUS_ON;
#endif
init->socket_irq[PANGOLIN_SOCK] = IRQ_PCMCIA_IRQ;
/* Set transition detect */
set_irq_type(IRQ_PCMCIA_CD, IRQT_NOEDGE);
set_irq_type(IRQ_PCMCIA_IRQ, IRQT_FALLING);
/* Register interrupts */
res = request_irq(IRQ_PCMCIA_CD, sa1100_pcmcia_interrupt, SA_INTERRUPT,
"PCMCIA_CD", NULL);
if (res >= 0)
/* There's only one slot, but it's "Slot 1": */
return 2;
skt->irq = IRQ_PCMCIA_IRQ;
irq_err:
printk(KERN_ERR "%s: request for IRQ%d failed (%d)\n",
__FUNCTION__, IRQ_PCMCIA_CD, res);
return res;
return sa11xx_request_irqs(skt, irqs, ARRAY_SIZE(irqs));
}
static int pangolin_pcmcia_shutdown(void)
static void pangolin_pcmcia_hw_shutdown(struct sa1100_pcmcia_socket *skt)
{
/* disable IRQs */
free_irq(IRQ_PCMCIA_CD, NULL);
sa11xx_free_irqs(skt, irqs, ARRAY_SIZE(irqs));
#ifndef CONFIG_SA1100_PANGOLIN_PCMCIA_IDE
/* Disable PCMCIA bus: */
GPSR = GPIO_PCMCIA_BUS_ON;
#endif
return 0;
}
static void pangolin_pcmcia_socket_state(int sock, struct pcmcia_state *state)
static void
pangolin_pcmcia_socket_state(struct sa1100_pcmcia_socket *skt,
struct pcmcia_state *state)
{
unsigned long levels = GPLR;;
if (sock == PANGOLIN_SOCK) {
state->detect=((levels & GPIO_PCMCIA_CD)==0)?1:0;
state->ready=(levels & GPIO_PCMCIA_IRQ)?1:0;
state->bvd1=1; /* Not available on Pangolin. */
......@@ -73,23 +63,19 @@ static void pangolin_pcmcia_socket_state(int sock, struct pcmcia_state *state)
state->wrprot=0; /* Not available on Pangolin. */
state->vs_3v=1; /* Can only apply 3.3V on Pangolin. */
state->vs_Xv=0;
}
}
static int pangolin_pcmcia_configure_socket(int sock, const struct pcmcia_configure
*configure)
static int
pangolin_pcmcia_configure_socket(struct sa1100_pcmcia_socket *skt,
const socket_state_t *state)
{
unsigned long value, flags;
if(sock>1) return -1;
#ifndef CONFIG_SA1100_PANGOLIN_PCMCIA_IDE
if(sock==0) return 0;
#endif
local_irq_save(flags);
/* Murphy: BUS_ON different from POWER ? */
switch(configure->vcc){
switch (state->Vcc) {
case 0:
break;
#ifndef CONFIG_SA1100_PANGOLIN_PCMCIA_IDE
......@@ -100,25 +86,27 @@ static int pangolin_pcmcia_configure_socket(int sock, const struct pcmcia_config
break;
#else
case 50:
printk(KERN_WARNING "%s(): CS asked for 5V, determinded by jumper setting...\n", __FUNCTION__);
printk(KERN_WARNING "%s(): CS asked for 5V, determinded by "
"jumper setting...\n", __FUNCTION__);
break;
case 33:
printk(KERN_WARNING "%s(): CS asked for 3.3V, determined by jumper setting...\n", __FUNCTION__);
printk(KERN_WARNING "%s(): CS asked for 3.3V, determined by "
"jumper setting...\n", __FUNCTION__);
break;
#endif
default:
printk(KERN_ERR "%s(): unrecognized Vcc %u\n", __FUNCTION__,
configure->vcc);
printk(KERN_ERR "%s(): unrecognized Vcc %u\n",
__FUNCTION__, state->Vcc);
local_irq_restore(flags);
return -1;
}
#ifdef CONFIG_SA1100_PANGOLIN_PCMCIA_IDE
/* reset & unreset request */
if(sock==0) {
if(configure->reset) {
GPSR |= GPIO_PCMCIA_RESET;
if (skt->nr == 0) {
if (state->flags & SS_RESET) {
GPSR = GPIO_PCMCIA_RESET;
} else {
GPCR |= GPIO_PCMCIA_RESET;
GPCR = GPIO_PCMCIA_RESET;
}
}
#endif
......@@ -127,24 +115,20 @@ static int pangolin_pcmcia_configure_socket(int sock, const struct pcmcia_config
return 0;
}
static int pangolin_pcmcia_socket_init(int sock)
static void pangolin_pcmcia_socket_init(struct sa1100_pcmcia_socket *skt)
{
if (sock == 1)
set_irq_type(IRQ_PCMCIA_CD, IRQT_BOTHEDGE);
return 0;
sa11xx_enable_irqs(skt, irqs, ARRAY_SIZE(irqs));
}
static int pangolin_pcmcia_socket_suspend(int sock)
static void pangolin_pcmcia_socket_suspend(struct sa1100_pcmcia_socket *skt)
{
if (sock == 1)
set_irq_type(IRQ_PCMCIA_CD, IRQT_NOEDGE);
return 0;
sa11xx_disable_irqs(skt, irqs, ARRAY_SIZE(irqs));
}
static struct pcmcia_low_level pangolin_pcmcia_ops = {
.owner = THIS_MODULE,
.init = pangolin_pcmcia_init,
.shutdown = pangolin_pcmcia_shutdown,
.hw_init = pangolin_pcmcia_hw_init,
.hw_shutdown = pangolin_pcmcia_hw_shutdown,
.socket_state = pangolin_pcmcia_socket_state,
.configure_socket = pangolin_pcmcia_configure_socket,
......@@ -157,13 +141,7 @@ int __init pcmcia_pangolin_init(struct device *dev)
int ret = -ENODEV;
if (machine_is_pangolin())
ret = sa1100_register_pcmcia(&pangolin_pcmcia_ops, dev);
ret = sa11xx_drv_pcmcia_probe(dev, &pangolin_pcmcia_ops, PANGOLIN_SOCK, 1);
return ret;
}
void __exit pcmcia_pangolin_exit(struct device *dev)
{
sa1100_unregister_pcmcia(&pangolin_pcmcia_ops, dev);
}
......@@ -16,10 +16,9 @@
#include <asm/mach-types.h>
#include <asm/irq.h>
#include "sa1100_generic.h"
#include "sa1111_generic.h"
static int pfs168_pcmcia_init(struct pcmcia_init *init)
static int pfs168_pcmcia_init(struct sa1100_pcmcia_socket *skt)
{
/* TPS2211 to standby mode: */
PA_DWR &= ~(GPIO_GPIO0 | GPIO_GPIO1 | GPIO_GPIO2 | GPIO_GPIO3);
......@@ -27,11 +26,12 @@ static int pfs168_pcmcia_init(struct pcmcia_init *init)
/* Set GPIO_A<3:0> to be outputs for PCMCIA (socket 0) power controller: */
PA_DDR &= ~(GPIO_GPIO0 | GPIO_GPIO1 | GPIO_GPIO2 | GPIO_GPIO3);
return sa1111_pcmcia_init(init);
return sa1111_pcmcia_init(skt);
}
static int
pfs168_pcmcia_configure_socket(int sock, const struct pcmcia_configure *conf)
pfs168_pcmcia_configure_socket(struct sa1100_pcmcia_socket *skt,
const socket_state_t *state)
{
unsigned int pa_dwr_mask = 0, pa_dwr_set = 0;
int ret;
......@@ -48,33 +48,33 @@ pfs168_pcmcia_configure_socket(int sock, const struct pcmcia_configure *conf)
*
*/
switch (sock) {
switch (skt->nr) {
case 0:
pa_dwr_mask = GPIO_GPIO0 | GPIO_GPIO1 | GPIO_GPIO2 | GPIO_GPIO3;
switch (conf->vcc) {
switch (state->Vcc) {
default:
case 0: pa_dwr_set = 0; break;
case 33: pa_dwr_set = GPIO_GPIO0; break;
case 50: pa_dwr_set = GPIO_GPIO1; break;
}
switch (conf->vpp) {
switch (state->Vpp) {
case 0:
break;
case 120:
printk(KERN_ERR "%s(): PFS-168 does not support VPP %uV\n",
__FUNCTION__, conf->vpp / 10);
__FUNCTION__, state->Vpp / 10);
return -1;
break;
default:
if (conf->vpp == conf->vcc)
if (state->Vpp == state->Vcc)
pa_dwr_set |= GPIO_GPIO3;
else {
printk(KERN_ERR "%s(): unrecognized VPP %u\n", __FUNCTION__,
conf->vpp);
state->Vpp);
return -1;
}
}
......@@ -91,24 +91,24 @@ pfs168_pcmcia_configure_socket(int sock, const struct pcmcia_configure *conf)
case 50:
printk(KERN_ERR "%s(): PFS-168 CompactFlash socket does not support VCC %uV\n",
__FUNCTION__, conf->vcc / 10);
__FUNCTION__, state->Vcc / 10);
return -1;
default:
printk(KERN_ERR "%s(): unrecognized VCC %u\n", __FUNCTION__,
conf->vcc);
state->Vcc);
return -1;
}
if (conf->vpp != conf->vcc && conf->vpp != 0) {
if (state->Vpp != state->Vcc && state->Vpp != 0) {
printk(KERN_ERR "%s(): CompactFlash socket does not support VPP %uV\n"
__FUNCTION__, conf->vpp / 10);
__FUNCTION__, state->Vpp / 10);
return -1;
}
break;
}
ret = sa1111_pcmcia_configure_socket(sock, conf);
ret = sa1111_pcmcia_configure_socket(skt, state);
if (ret == 0) {
unsigned long flags;
......@@ -122,11 +122,10 @@ pfs168_pcmcia_configure_socket(int sock, const struct pcmcia_configure *conf)
static struct pcmcia_low_level pfs168_pcmcia_ops = {
.owner = THIS_MODULE,
.init = pfs168_pcmcia_init,
.shutdown = sa1111_pcmcia_shutdown,
.hw_init = pfs168_pcmcia_hw_init,
.hw_shutdown = sa1111_pcmcia_hw_shutdown,
.socket_state = sa1111_pcmcia_socket_state,
.configure_socket = pfs168_pcmcia_configure_socket,
.socket_init = sa1111_pcmcia_socket_init,
.socket_suspend = sa1111_pcmcia_socket_suspend,
};
......@@ -136,12 +135,7 @@ int __init pcmcia_pfs168_init(struct device *dev)
int ret = -ENODEV;
if (machine_is_pfs168())
ret = sa1100_register_pcmcia(&pfs168_pcmcia_ops, dev);
ret = sa11xx_drv_pcmcia_probe(dev, &pfs168_pcmcia_ops, 0, 2);
return ret;
}
void __exit pcmcia_pfs168_exit(struct device *dev)
{
sa1100_unregister_pcmcia(&pfs168_pcmcia_ops, dev);
}
......@@ -16,64 +16,36 @@
#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 struct pcmcia_irqs irqs[] = {
{ 0, SHANNON_IRQ_GPIO_EJECT_0, "PCMCIA_CD_0" },
{ 1, SHANNON_IRQ_GPIO_EJECT_1, "PCMCIA_CD_1" },
};
static int shannon_pcmcia_init(struct pcmcia_init *init)
static int shannon_pcmcia_hw_init(struct sa1100_pcmcia_socket *skt)
{
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);
init->socket_irq[0] = SHANNON_IRQ_GPIO_RDY_0;
init->socket_irq[1] = SHANNON_IRQ_GPIO_RDY_1;
/* Register interrupts */
for (i = 0; i < ARRAY_SIZE(irqs); i++) {
res = request_irq(irqs[i].irq, sa1100_pcmcia_interrupt,
SA_INTERRUPT, irqs[i].str, NULL);
if (res)
goto irq_err;
set_irq_type(irqs[i].irq, IRQT_NOEDGE);
}
return 2;
skt->irq = skt->nr ? SHANNON_IRQ_GPIO_RDY_1 : SHANNON_IRQ_GPIO_RDY_0;
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;
return sa11xx_request_irqs(skt, irqs, ARRAY_SIZE(irqs));
}
static int shannon_pcmcia_shutdown(void)
static void shannon_pcmcia_hw_shutdown(struct sa1100_pcmcia_socket *skt)
{
int i;
/* disable IRQs */
for (i = 0; i < ARRAY_SIZE(irqs); i++)
free_irq(irqs[i].irq, NULL);
return 0;
sa11xx_free_irqs(skt, irqs, ARRAY_SIZE(irqs));
}
static void shannon_pcmcia_socket_state(int sock, struct pcmcia_state *state)
static void
shannon_pcmcia_socket_state(struct sa1100_pcmcia_socket *skt,
struct pcmcia_state *state)
{
unsigned long levels = GPLR;
switch (sock) {
switch (skt->nr) {
case 0:
state->detect = (levels & SHANNON_GPIO_EJECT_0) ? 0 : 1;
state->ready = (levels & SHANNON_GPIO_RDY_0) ? 1 : 0;
......@@ -96,9 +68,11 @@ static void shannon_pcmcia_socket_state(int sock, struct pcmcia_state *state)
}
}
static int shannon_pcmcia_configure_socket(int sock, const struct pcmcia_configure *configure)
static int
shannon_pcmcia_configure_socket(struct sa1100_pcmcia_socket *skt,
const socket_state_t *state)
{
switch (configure->vcc) {
switch (state->Vcc) {
case 0: /* power off */
printk(KERN_WARNING __FUNCTION__"(): CS asked for 0V, still applying 3.3V..\n");
break;
......@@ -108,7 +82,7 @@ static int shannon_pcmcia_configure_socket(int sock, const struct pcmcia_configu
break;
default:
printk(KERN_ERR __FUNCTION__"(): unrecognized Vcc %u\n",
configure->vcc);
state->Vcc);
return -1;
}
......@@ -119,30 +93,20 @@ static int shannon_pcmcia_configure_socket(int sock, const struct pcmcia_configu
return 0;
}
static int shannon_pcmcia_socket_init(int sock)
static void shannon_pcmcia_socket_init(struct sa1100_pcmcia_socket *skt)
{
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;
sa11xx_enable_irqs(skt, irqs, ARRAY_SIZE(irqs));
}
static int shannon_pcmcia_socket_suspend(int sock)
static void shannon_pcmcia_socket_suspend(struct sa1100_pcmcia_socket *skt)
{
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;
sa11xx_disable_irqs(skt, irqs, ARRAY_SIZE(irqs));
}
static struct pcmcia_low_level shannon_pcmcia_ops = {
.owner = THIS_MODULE,
.init = shannon_pcmcia_init,
.shutdown = shannon_pcmcia_shutdown,
.hw_init = shannon_pcmcia_hw_init,
.hw_shutdown = shannon_pcmcia_hw_shutdown,
.socket_state = shannon_pcmcia_socket_state,
.configure_socket = shannon_pcmcia_configure_socket,
......@@ -155,12 +119,7 @@ int __init pcmcia_shannon_init(struct device *dev)
int ret = -ENODEV;
if (machine_is_shannon())
ret = sa1100_register_pcmcia(&shannon_pcmcia_ops, dev);
ret = sa11xx_drv_pcmcia_probe(dev, &shannon_pcmcia_ops, 0, 2);
return ret;
}
void __exit pcmcia_shannon_exit(struct device *dev)
{
sa1100_unregister_pcmcia(&shannon_pcmcia_ops, dev);
}
......@@ -19,51 +19,36 @@ extern long get_cs3_shadow(void);
extern void set_cs3_bit(int value);
extern void clear_cs3_bit(int value);
static struct pcmcia_irqs irqs[] = {
{ 1, IRQ_GPIO_CF_CD, "CF_CD" },
};
static int simpad_pcmcia_init(struct pcmcia_init *init){
int irq, res;
static int simpad_pcmcia_hw_init(struct sa1100_pcmcia_socket *skt)
{
set_cs3_bit(PCMCIA_RESET);
clear_cs3_bit(PCMCIA_BUFF_DIS);
clear_cs3_bit(PCMCIA_RESET);
clear_cs3_bit(VCC_3V_EN|VCC_5V_EN|EN0|EN1);
init->socket_irq[1] = IRQ_GPIO_CF_IRQ;
/* Register interrupts */
irq = IRQ_GPIO_CF_CD;
res = request_irq(irq, sa1100_pcmcia_interrupt, SA_INTERRUPT,
"CF_CD", NULL );
if( res < 0 ) goto irq_err;
set_irq_type( IRQ_GPIO_CF_CD, IRQT_NOEDGE );
skt->irq = IRQ_GPIO_CF_IRQ;
/* There's only one slot, but it's "Slot 1": */
return 2;
irq_err:
printk( KERN_ERR "%s: request for IRQ%d failed (%d)\n",
__FUNCTION__, irq, res);
return res;
return sa11xx_request_irqs(skt, irqs, ARRAY_SIZE(irqs));
}
static int simpad_pcmcia_shutdown(void)
static void simpad_pcmcia_hw_shutdown(struct sa1100_pcmcia_socket *skt)
{
/* disable IRQs */
free_irq( IRQ_GPIO_CF_CD, NULL );
sa11xx_free_irqs(skt, irqs, ARRAY_SIZE(irqs));
/* Disable CF bus: */
//set_cs3_bit(PCMCIA_BUFF_DIS);
clear_cs3_bit(PCMCIA_RESET);
return 0;
}
static void simpad_pcmcia_socket_state(int sock, struct pcmcia_state *state)
static void
simpad_pcmcia_socket_state(struct sa1100_pcmcia_socket *skt,
struct pcmcia_state *state)
{
if (sock == 1) {
unsigned long levels = GPLR;
unsigned long *cs3reg = CS3_BASE;
......@@ -80,23 +65,18 @@ static void simpad_pcmcia_socket_state(int sock, struct pcmcia_state *state)
state->vs_3v=1;
state->vs_Xv=0;
}
}
}
static int simpad_pcmcia_configure_socket(int sock, const struct pcmcia_configure
*configure)
static int
simpad_pcmcia_configure_socket(struct sa1100_pcmcia_socket *skt,
const socket_state_t *state)
{
unsigned long value, flags;
if(sock>1) return -1;
if(sock==0) return 0;
local_irq_save(flags);
/* Murphy: see table of MIC2562a-1 */
switch(configure->vcc){
switch (state->Vcc) {
case 0:
clear_cs3_bit(VCC_3V_EN|VCC_5V_EN|EN0|EN1);
break;
......@@ -112,39 +92,35 @@ static int simpad_pcmcia_configure_socket(int sock, const struct pcmcia_configur
break;
default:
printk(KERN_ERR "%s(): unrecognized Vcc %u\n", __FUNCTION__,
configure->vcc);
printk(KERN_ERR "%s(): unrecognized Vcc %u\n",
__FUNCTION__, state->Vcc);
clear_cs3_bit(VCC_3V_EN|VCC_5V_EN|EN0|EN1);
local_irq_restore(flags);
return -1;
}
/* Silently ignore Vpp, output enable, speaker enable. */
local_irq_restore(flags);
return 0;
}
static int simpad_pcmcia_socket_init(int sock)
static void simpad_pcmcia_socket_init(struct sa1100_pcmcia_socket *skt)
{
set_irq_type(IRQ_GPIO_CF_CD, IRQT_BOTHEDGE);
return 0;
sa11xx_enable_irqs(skt, irqs, ARRAY_SIZE(irqs));
}
static int simpad_pcmcia_socket_suspend(int sock)
static void simpad_pcmcia_socket_suspend(struct sa1100_pcmcia_socket *skt)
{
set_irq_type(IRQ_GPIO_CF_CD, IRQT_NOEDGE);
return 0;
sa11xx_disable_irqs(skt, irqs, ARRAY_SIZE(irqs));
}
static struct pcmcia_low_level simpad_pcmcia_ops = {
.owner = THIS_MODULE,
.init = simpad_pcmcia_init,
.shutdown = simpad_pcmcia_shutdown,
.hw_init = simpad_pcmcia_hw_init,
.hw_shutdown = simpad_pcmcia_hw_shutdown,
.socket_state = simpad_pcmcia_socket_state,
.configure_socket = simpad_pcmcia_configure_socket,
.socket_init = simpad_pcmcia_socket_init,
.socket_suspend = simpad_pcmcia_socket_suspend,
};
......@@ -154,12 +130,7 @@ int __init pcmcia_simpad_init(struct device *dev)
int ret = -ENODEV;
if (machine_is_simpad())
ret = sa1100_register_pcmcia(&simpad_pcmcia_ops, dev);
ret = sa11xx_drv_pcmcia_probe(dev, &simpad_pcmcia_ops, 1, 1);
return ret;
}
void __exit pcmcia_simpad_exit(struct device *dev)
{
sa1100_unregister_pcmcia(&simpad_pcmcia_ops, dev);
}
......@@ -32,62 +32,39 @@
static int debug = 0;
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 struct pcmcia_irqs irqs[] = {
{ 0, IRQ_GPIO_STORK_PCMCIA_A_CARD_DETECT, "PCMCIA_CD0" },
{ 1, IRQ_GPIO_STORK_PCMCIA_B_CARD_DETECT, "PCMCIA_CD1" },
};
static int stork_pcmcia_init(struct pcmcia_init *init)
static int stork_pcmcia_hw_init(struct sa1100_pcmcia_socket *skt)
{
int irq, res;
printk("in stork_pcmcia_init\n");
init->socket_irq[0] = IRQ_GPIO_STORK_PCMCIA_A_RDY;
init->socket_irq[1] = IRQ_GPIO_STORK_PCMCIA_B_RDY;
/* Register interrupts */
for (i = 0; i < ARRAY_SIZE(irqs); i++) {
res = request_irq(irqs[i].irq, sa1100_pcmcia_interrupt,
SA_INTERRUPT, irqs[i].str, NULL);
if (res)
goto irq_err;
set_irq_type(irqs[i].irq, IRQT_NOEDGE);
}
return 2;
skt->irq = skt->nr ? IRQ_GPIO_STORK_PCMCIA_B_RDY
: IRQ_GPIO_STORK_PCMCIA_A_RDY;
irq_err:
printk(KERN_ERR "%s: request for IRQ%d failed (%d)\n",
__FUNCTION__, irq, res);
while (i--)
free_irq(irqs[i].irq, NULL);
return res;
return sa11xx_request_irqs(skt, irqs, ARRAY_SIZE(irqs));
}
static int stork_pcmcia_shutdown(void)
static void stork_pcmcia_hw_shutdown(struct sa1100_pcmcia_socket *skt)
{
int i;
printk(__FUNCTION__ "\n");
/* disable IRQs */
for (i = 0; i < ARRAY_SIZE(irqs); i++)
free_irq(irqs[i].irq, NULL);
sa11xx_free_irqs(skt, irqs, ARRAY_SIZE(irqs));
/* Disable CF bus: */
storkClearLatchA(STORK_PCMCIA_PULL_UPS_POWER_ON);
storkClearLatchA(STORK_PCMCIA_A_POWER_ON);
storkClearLatchA(STORK_PCMCIA_B_POWER_ON);
return 0;
}
static void stork_pcmcia_socket_state(int sock, struct pcmcia_state *state)
static void
stork_pcmcia_socket_state(struct sa1100_pcmcia_socket *skt,
struct pcmcia_state *state)
{
unsigned long levels = GPLR;
......@@ -95,7 +72,7 @@ static void stork_pcmcia_socket_state(int sock, struct pcmcia_state *state)
printk(__FUNCTION__ " GPLR=%x IRQ[1:0]=%x\n", levels,
(levels & (GPIO_STORK_PCMCIA_A_RDY|GPIO_STORK_PCMCIA_B_RDY)));
switch (sock) {
switch (skt->nr) {
case 0:
state->detect=((levels & GPIO_STORK_PCMCIA_A_CARD_DETECT)==0)?1:0;
state->ready=(levels & GPIO_STORK_PCMCIA_A_RDY)?1:0;
......@@ -118,20 +95,19 @@ static void stork_pcmcia_socket_state(int sock, struct pcmcia_state *state)
}
}
static int stork_pcmcia_configure_socket(int sock, const struct pcmcia_configure *configure)
static int
stork_pcmcia_configure_socket(struct sa1100_pcmcia_socket *skt,
const socket_state_t *state)
{
unsigned long flags;
int DETECT, RDY, POWER, RESET;
if (sock > 1) return -1;
printk(__FUNCTION__ ": socket=%d vcc=%d vpp=%d reset=%d\n",
sock, configure->vcc, configure->vpp, configure->reset);
printk("%s: socket=%d vcc=%d vpp=%d reset=%d\n", __FUNCTION__,
skt->nr, state->Vcc, state->Vpp, state->flags & SS_RESET ? 1 : 0);
local_irq_save(flags);
if (sock == 0) {
if (skt->nr == 0) {
DETECT = GPIO_STORK_PCMCIA_A_CARD_DETECT;
RDY = GPIO_STORK_PCMCIA_A_RDY;
POWER = STORK_PCMCIA_A_POWER_ON;
......@@ -148,7 +124,7 @@ static int stork_pcmcia_configure_socket(int sock, const struct pcmcia_configure
printk("no card detected - but resetting anyway\r\n");
}
*/
switch (configure->vcc) {
switch (state->Vcc) {
case 0:
/* storkClearLatchA(STORK_PCMCIA_PULL_UPS_POWER_ON); */
storkClearLatchA(POWER);
......@@ -162,12 +138,12 @@ static int stork_pcmcia_configure_socket(int sock, const struct pcmcia_configure
default:
printk(KERN_ERR "%s(): unrecognized Vcc %u\n", __FUNCTION__,
configure->vcc);
state->Vcc);
local_irq_restore(flags);
return -1;
}
if (configure->reset)
if (state->flags & SS_RESET)
storkSetLatchB(RESET);
else
storkClearLatchB(RESET);
......@@ -176,43 +152,35 @@ static int stork_pcmcia_configure_socket(int sock, const struct pcmcia_configure
/* silently ignore vpp and speaker enables. */
printk(__FUNCTION__ ": finished\n");
printk("%s: finished\n", __FUNCTION__);
return 0;
}
static int stork_pcmcia_socket_init(int sock)
static void stork_pcmcia_socket_init(struct sa1100_pcmcia_socket *skt)
{
storkSetLatchA(STORK_PCMCIA_PULL_UPS_POWER_ON);
if (sock == 0)
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;
sa11xx_enable_irqs(skt, irqs, ARRAY_SIZE(irqs));
}
static int stork_pcmcia_socket_suspend(int sock)
static void stork_pcmcia_socket_suspend(struct sa1100_pcmcia_socket *skt)
{
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);
sa11xx_disable_irqs(skt, irqs, ARRAY_SIZE(irqs));
/*
* Hack!
*/
if (skt->nr == 1)
storkClearLatchA(STORK_PCMCIA_PULL_UPS_POWER_ON);
}
return 0;
}
static struct pcmcia_low_level stork_pcmcia_ops = {
.owner = THIS_MODULE,
.init = stork_pcmcia_init,
.shutdown = stork_pcmcia_shutdown,
.hw_init = stork_pcmcia_hw_init,
.hw_shutdown = stork_pcmcia_hw_shutdown,
.socket_state = stork_pcmcia_socket_state,
.configure_socket = stork_pcmcia_configure_socket,
......@@ -225,13 +193,7 @@ int __init pcmcia_stork_init(struct device *dev)
int ret = -ENODEV;
if (machine_is_stork())
ret = sa1100_register_pcmcia(&stork_pcmcia_ops, dev);
ret = sa11xx_drv_pcmcia_probe(dev, &stork_pcmcia_ops, 0, 2);
return ret;
}
void __exit pcmcia_stork_exit(struct device *dev)
{
sa1100_unregister_pcmcia(&stork_pcmcia_ops, dev);
}
......@@ -37,7 +37,6 @@
#include <asm/irq.h>
#include <asm/hardware/sa1111.h>
#include "sa1100_generic.h"
#include "sa1111_generic.h"
#define DEBUG 0
......@@ -48,34 +47,24 @@
# define DPRINTK( x, args... ) /* nix */
#endif
int system3_pcmcia_init(struct pcmcia_init *init)
static int system3_pcmcia_hw_init(struct sa1100_pcmcia_socket *skt)
{
init->socket_irq[0] = IRQ_S0_READY_NINT;
init->socket_irq[1] = IRQ_S1_READY_NINT;
skt->irq = skt->nr ? IRQ_S1_READY_NINT : IRQ_S0_READY_NINT;
/* Don't need no CD and BVD* interrupts */
return 2;
}
int system3_pcmcia_shutdown(void)
{
return 0;
}
int system3_pcmcia_configure_socket(int sock, const struct pcmcia_configure *conf)
void system3_pcmcia_hw_shutdown(struct sa1100_pcmcia_socket *skt)
{
/* only CF ATM */
if (sock == 0)
return -1;
return sa1111_pcmcia_configure_socket(sock, conf);
}
static void system3_pcmcia_socket_state(int sock, struct pcmcia_state *state)
static void
system3_pcmcia_socket_state(struct sa1100_pcmcia_socket *skt, struct pcmcia_state *state)
{
unsigned long status = PCSR;
switch (sock) {
switch (skt->nr) {
#if 0 /* PCMCIA socket not yet connected */
case 0:
state->detect = status & PCSR_S0_DETECT ? 0 : 1;
......@@ -100,15 +89,15 @@ static void system3_pcmcia_socket_state(int sock, struct pcmcia_state *state)
}
DPRINTK("Sock %d PCSR=0x%08lx, Sx_RDY_nIREQ=%d\n",
sock, status, state->ready);
skt->nr, status, state->ready);
}
struct pcmcia_low_level system3_pcmcia_ops = {
.owner = THIS_MODULE,
.init = system3_pcmcia_init,
.shutdown = system3_pcmcia_shutdown,
.init = system3_pcmcia_hw_init,
.shutdown = system3_pcmcia_hw_shutdown,
.socket_state = system3_pcmcia_socket_state,
.configure_socket = system3_pcmcia_configure_socket,
.configure_socket = sa1111_pcmcia_configure_socket,
.socket_init = sa1111_pcmcia_socket_init,
.socket_suspend = sa1111_pcmcia_socket_suspend,
......@@ -119,12 +108,8 @@ int __init pcmcia_system3_init(struct device *dev)
int ret = -ENODEV;
if (machine_is_pt_system3())
ret = sa1100_register_pcmcia(&system3_pcmcia_ops, dev);
/* only CF ATM */
ret = sa11xx_drv_pcmcia_probe(dev, &system3_pcmcia_ops, 1, 1);
return ret;
}
void __exit pcmcia_system3_exit(struct device *dev)
{
sa1100_unregister_pcmcia(&system3_pcmcia_ops, dev);
}
......@@ -23,15 +23,18 @@
#include "sa1100_generic.h"
#define NUMBER_OF_TRIZEPS_PCMCIA_SLOTS 1
static struct pcmcia_irqs irqs[] = {
{ 0, TRIZEPS_IRQ_PCMCIA_CD0, "PCMCIA_CD0" },
};
/**
*
*
******************************************************/
static int trizeps_pcmcia_init(struct pcmcia_init *init)
static int trizeps_pcmcia_init(struct sa1100_pcmcia_socket *skt)
{
int res;
init->socket_irq[0] = TRIZEPS_IRQ_PCMCIA_IRQ0;
skt->irq = TRIZEPS_IRQ_PCMCIA_IRQ0;
/* Enable CF bus: */
TRIZEPS_BCR_clear(TRIZEPS_BCR1, TRIZEPS_nPCM_ENA_REG);
......@@ -40,51 +43,32 @@ static int trizeps_pcmcia_init(struct pcmcia_init *init)
GPDR &= ~((GPIO_GPIO(TRIZEPS_GPIO_PCMCIA_CD0))
| (GPIO_GPIO(TRIZEPS_GPIO_PCMCIA_IRQ0)));
/* Register SOCKET interrupts */
/* WHY? */
res = request_irq(TRIZEPS_IRQ_PCMCIA_CD0, sa1100_pcmcia_interrupt,
SA_INTERRUPT, "PCMCIA_CD0", NULL );
if( res < 0 ) goto irq_err;
set_irq_type(TRIZEPS_IRQ_PCMCIA_CD0, IRQT_NOEDGE);
//MECR = 0x00060006; // Initialised on trizeps init
// return=sa1100_pcmcia_socket_count (sa1100_generic.c)
// -> number of PCMCIA Slots
// Slot 0 -> Trizeps PCMCIA
// Slot 1 -> Trizeps ISA-Bus
return NUMBER_OF_TRIZEPS_PCMCIA_SLOTS;
irq_err:
printk( KERN_ERR "%s(): PCMCIA Request for IRQ %u failed\n", __FUNCTION__, TRIZEPS_IRQ_PCMCIA_CD0 );
return -1;
return sa11xx_request_irqs(skt, irqs, ARRAY_SIZE(irqs));
}
/**
*
*
******************************************************/
static int trizeps_pcmcia_shutdown(void)
static void trizeps_pcmcia_shutdown(struct sa1100_pcmcia_socket *skt)
{
printk(">>>>>PCMCIA TRIZEPS shutdown\n");
/* disable IRQs */
free_irq(TRIZEPS_IRQ_PCMCIA_CD0, NULL );
sa11xx_free_irqs(skt, irqs, ARRAY_SIZE(irqs));
/* Disable CF bus: */
TRIZEPS_BCR_set(TRIZEPS_BCR1, TRIZEPS_nPCM_ENA_REG);
return 0;
}
/**
*
******************************************************/
static void trizeps_pcmcia_socket_state(int sock, struct pcmcia_state *state_array)
static void
trizeps_pcmcia_socket_state(struct sa1100_pcmcia_socket *skt,
struct pcmcia_state *state_array)
{
unsigned long levels = GPLR;
if (sock == 0) {
state->detect = ((levels & GPIO_GPIO(TRIZEPS_GPIO_PCMCIA_CD0)) == 0) ? 1 : 0;
state->ready = ((levels & GPIO_GPIO(TRIZEPS_GPIO_PCMCIA_IRQ0)) != 0) ? 1 : 0;
state->bvd1 = ((TRIZEPS_BCR1 & TRIZEPS_PCM_BVD1) !=0 ) ? 1 : 0;
......@@ -92,22 +76,21 @@ static void trizeps_pcmcia_socket_state(int sock, struct pcmcia_state *state_arr
state->wrprot = 0; // not write protected
state->vs_3v = ((TRIZEPS_BCR1 & TRIZEPS_nPCM_VS1) == 0) ? 1 : 0; //VS1=0 -> vs_3v=1
state->vs_Xv = ((TRIZEPS_BCR1 & TRIZEPS_nPCM_VS2) == 0) ? 1 : 0; //VS2=0 -> vs_Xv=1
}
}
/**
*
*
******************************************************/
static int trizeps_pcmcia_configure_socket(int sock, const struct pcmcia_configure *configure)
static int
trizeps_pcmcia_configure_socket(struct sa1100_pcmcia_socket *skt,
const socket_state_t *state)
{
unsigned long flags;
if(sock>1) return -1;
local_irq_save(flags);
switch (configure->vcc) {
switch (state->Vcc) {
case 0:
printk(">>> PCMCIA Power off\n");
TRIZEPS_BCR_clear(TRIZEPS_BCR1, TRIZEPS_PCM_V3_EN_REG);
......@@ -126,19 +109,19 @@ static int trizeps_pcmcia_configure_socket(int sock, const struct pcmcia_configu
break;
default:
printk(KERN_ERR "%s(): unrecognized Vcc %u\n", __FUNCTION__,
configure->vcc);
state->Vcc);
local_irq_restore(flags);
return -1;
}
if (configure->reset)
if (state->flags & SS_RESET)
TRIZEPS_BCR_set(TRIZEPS_BCR1, TRIZEPS_nPCM_RESET_DISABLE); // Reset
else
TRIZEPS_BCR_clear(TRIZEPS_BCR1, TRIZEPS_nPCM_RESET_DISABLE); // no Reset
/*
printk(" vcc=%u vpp=%u -->reset=%i\n",
configure->vcc,
configure->vpp,
state->Vcc,
state->Vpp,
((BCR_read(1) & nPCM_RESET_DISABLE)? 1:0));
*/
local_irq_restore(flags);
......@@ -146,16 +129,14 @@ static int trizeps_pcmcia_configure_socket(int sock, const struct pcmcia_configu
return 0;
}
static int trizeps_pcmcia_socket_init(int sock)
static void trizeps_pcmcia_socket_init(struct sa1100_pcmcia_socket *skt)
{
set_irq_type(TRIZEPS_IRQ_PCMCIA_CD0, IRQT_BOTHEDGE);
return 0;
sa11xx_enable_irqs(skt, irqs, ARRAY_SIZE(irqs));
}
static int trizeps_pcmcia_socket_suspend(int sock)
static void trizeps_pcmcia_socket_suspend(struct sa1100_pcmcia_socket *skt)
{
set_irq_type(TRIZEPS_IRQ_PCMCIA_CD0, IRQT_NOEDGE);
return 0;
sa11xx_disable_irqs(skt, irqs, ARRAY_SIZE(irqs));
}
/**
......@@ -164,8 +145,8 @@ static int trizeps_pcmcia_socket_suspend(int sock)
******************************************************/
struct pcmcia_low_level trizeps_pcmcia_ops = {
.owner = THIS_MODULE,
.init = trizeps_pcmcia_init,
.shutdown = trizeps_pcmcia_shutdown,
.hw_init = trizeps_pcmcia_hw_init,
.hw_shutdown = trizeps_pcmcia_hw_shutdown,
.socket_state = trizeps_pcmcia_socket_state,
.configure_socket = trizeps_pcmcia_configure_socket,
.socket_init = trizeps_pcmcia_socket_init,
......@@ -174,13 +155,11 @@ struct pcmcia_low_level trizeps_pcmcia_ops = {
int __init pcmcia_trizeps_init(struct device *dev)
{
if (machine_is_trizeps()) {
return sa1100_register_pcmcia(&trizeps_pcmcia_ops, dev);
}
return -ENODEV;
}
int ret = -ENODEV;
void __exit pcmcia_trizeps_exit(struct device *dev)
{
sa1100_unregister_pcmcia(&trizeps_pcmcia_ops, dev);
if (machine_is_trizeps())
ret = sa11xx_drv_pcmcia_probe(dev, &trizeps_pcmcia_ops, 0,
NUMBER_OF_TRIZEPS_PCMCIA_SLOTS);
return ret;
}
......@@ -19,7 +19,7 @@
#define NCR_A0VPP (1<<16)
#define NCR_A1VPP (1<<17)
static int xp860_pcmcia_init(struct pcmcia_init *init)
static int xp860_pcmcia_hw_init(struct sa1100_pcmcia_socket *skt)
{
/* Set GPIO_A<3:0> to be outputs for PCMCIA/CF power controller: */
PA_DDR &= ~(GPIO_GPIO0 | GPIO_GPIO1 | GPIO_GPIO2 | GPIO_GPIO3);
......@@ -38,11 +38,11 @@ static int xp860_pcmcia_init(struct pcmcia_init *init)
GPDR |= (NCR_A0VPP | NCR_A1VPP);
GPCR &= ~(NCR_A0VPP | NCR_A1VPP);
return sa1111_pcmcia_init(init);
return sa1111_pcmcia_hw_init(skt);
}
static int
xp860_pcmcia_configure_socket(int sock, const struct pcmcia_configure *conf)
xp860_pcmcia_configure_socket(struct sa1100_pcmcia_socket *skt, const socket_state_t *state)
{
unsigned int gpio_mask, pa_dwr_mask;
unsigned int gpio_set, pa_dwr_set;
......@@ -72,28 +72,28 @@ xp860_pcmcia_configure_socket(int sock, const struct pcmcia_configure *conf)
* the corresponding truth table.
*/
switch (sock) {
switch (skt->nr) {
case 0:
pa_dwr_mask = GPIO_GPIO0 | GPIO_GPIO1;
gpio_mask = NCR_A0VPP | NCR_A1VPP;
switch (conf->vcc) {
switch (state->Vcc) {
default:
case 0: pa_dwr_set = 0; break;
case 33: pa_dwr_set = GPIO_GPIO1; break;
case 50: pa_dwr_set = GPIO_GPIO0; break;
}
switch (conf->vpp) {
switch (state->Vpp) {
case 0: gpio_set = 0; break;
case 120: gpio_set = NCR_A1VPP; break;
default:
if (conf->vpp == conf->vcc)
if (state->Vpp == state->Vcc)
gpio_set = NCR_A0VPP;
else {
printk(KERN_ERR "%s(): unrecognized Vpp %u\n",
__FUNCTION__, conf->vpp);
__FUNCTION__, state->Vpp);
return -1;
}
}
......@@ -104,22 +104,22 @@ xp860_pcmcia_configure_socket(int sock, const struct pcmcia_configure *conf)
gpio_mask = 0;
gpio_set = 0;
switch (conf->vcc) {
switch (state->Vcc) {
default:
case 0: pa_dwr_set = 0; break;
case 33: pa_dwr_set = GPIO_GPIO2; break;
case 50: pa_dwr_set = GPIO_GPIO3; break;
}
if (conf->vpp != conf->vcc && conf->vpp != 0) {
if (state->Vpp != state->Vcc && state->Vpp != 0) {
printk(KERN_ERR "%s(): CF slot cannot support Vpp %u\n",
__FUNCTION__, conf->vpp);
__FUNCTION__, state->Vpp);
return -1;
}
break;
}
ret = sa1111_pcmcia_configure_socket(sock, conf);
ret = sa1111_pcmcia_configure_socket(skt, state);
if (ret == 0) {
unsigned long flags;
......@@ -135,11 +135,10 @@ xp860_pcmcia_configure_socket(int sock, const struct pcmcia_configure *conf)
static struct pcmcia_low_level xp860_pcmcia_ops = {
.owner = THIS_MODULE,
.init = xp860_pcmcia_init,
.shutdown = sa1111_pcmcia_shutdown,
.hw_init = xp860_pcmcia_hw_init,
.hw_shutdown = sa1111_pcmcia_hw_shutdown,
.socket_state = sa1111_pcmcia_socket_state,
.configure_socket = xp860_pcmcia_configure_socket,
.socket_init = sa1111_pcmcia_socket_init,
.socket_suspend = sa1111_pcmcia_socket_suspend,
};
......@@ -149,13 +148,7 @@ int __init pcmcia_xp860_init(struct device *dev)
int ret = -ENODEV;
if (machine_is_xp860())
ret = sa1100_register_pcmcia(&xp860_pcmcia_ops, dev);
ret = sa11xx_drv_pcmcia_probe(dev, &xp860_pcmcia_ops, 0, 2);
return ret;
}
void __exit pcmcia_xp860_exit(struct device *dev)
{
sa1100_unregister_pcmcia(&xp860_pcmcia_ops, dev);
}
......@@ -27,65 +27,37 @@ static inline void pcmcia_reset(int 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 struct pcmcia_irqs irqs[] = {
{ 0, IRQ_CF_CD, "CF_CD" },
{ 0, IRQ_CF_BVD2, "CF_BVD2" },
{ 0, IRQ_CF_BVD1, "CF_BVD1" },
};
static int yopy_pcmcia_init(struct pcmcia_init *init)
static int yopy_pcmcia_hw_init(struct sa1100_pcmcia_socket *skt)
{
int i, res;
init->socket_irq[0] = IRQ_CF_IREQ;
skt->irq = IRQ_CF_IREQ;
pcmcia_power(0);
pcmcia_reset(1);
/* Register interrupts */
for (i = 0; i < ARRAY_SIZE(irqs); i++) {
res = request_irq(irqs[i].irq, sa1100_pcmcia_interrupt,
SA_INTERRUPT, irqs[i].str, NULL);
if (res)
goto irq_err;
set_irq_type(irqs[i].irq, IRQT_NOEDGE);
}
return 1;
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;
return sa11xx_request_irqs(skt, irqs, ARRAY_SIZE(irqs));
}
static int yopy_pcmcia_shutdown(void)
static void yopy_pcmcia_hw_shutdown(struct sa1100_pcmcia_socket *skt)
{
int i;
/* disable IRQs */
for (i = 0; i < ARRAY_SIZE(irqs); i++)
free_irq(irqs[i].irq, NULL);
sa11xx_free_irqs(skt, irqs, ARRAY_SIZE(irqs));
/* Disable CF */
pcmcia_reset(1);
pcmcia_power(0);
return 0;
}
static void yopy_pcmcia_socket_state(int sock, struct pcmcia_state_array *state)
static void
yopy_pcmcia_socket_state(struct sa1100_pcmcia_socket *skt,
struct pcmcia_state_array *state)
{
unsigned long levels = GPLR;
if (sock == 0) {
state->detect = (levels & GPIO_CF_CD) ? 0 : 1;
state->ready = (levels & GPIO_CF_READY) ? 1 : 0;
state->bvd1 = (levels & GPIO_CF_BVD1) ? 1 : 0;
......@@ -93,15 +65,13 @@ static void yopy_pcmcia_socket_state(int sock, struct pcmcia_state_array *state)
state->wrprot = 0; /* Not available on Yopy. */
state->vs_3v = 0; /* FIXME Can only apply 3.3V on Yopy. */
state->vs_Xv = 0;
}
}
static int yopy_pcmcia_configure_socket(int sock, const struct pcmcia_configure *configure)
static int
yopy_pcmcia_configure_socket(struct sa1100_pcmcia_socket *skt,
const socket_state_t *state)
{
if (sock != 0)
return -1;
switch (configure->vcc) {
switch (state->Vcc) {
case 0: /* power off */
pcmcia_power(0);
break;
......@@ -112,35 +82,25 @@ static int yopy_pcmcia_configure_socket(int sock, const struct pcmcia_configure
break;
default:
printk(KERN_ERR __FUNCTION__"(): unrecognized Vcc %u\n",
configure->vcc);
state->Vcc);
return -1;
}
pcmcia_reset(configure->reset);
pcmcia_reset(state->flags & SS_RESET ? 1 : 0);
/* Silently ignore Vpp, output enable, speaker enable. */
return 0;
}
static int yopy_pcmcia_socket_init(int sock)
static void yopy_pcmcia_socket_init(struct sa1100_pcmcia_socket *skt)
{
int i;
for (i = 0; i < ARRAY_SIZE(irqs); i++)
set_irq_type(irqs[i].irq, IRQT_BOTHEDGE);
return 0;
sa11xx_enable_irqs(skt, irqs, ARRAY_SIZE(irqs));
}
static int yopy_pcmcia_socket_suspend(int sock)
static void yopy_pcmcia_socket_suspend(struct sa1100_pcmcia_socket *skt)
{
int i;
for (i = 0; i < ARRAY_SIZE(irqs); i++)
set_irq_type(irqs[i].irq, IRQT_NOEDGE);
return 0;
sa11xx_disable_irqs(skt, irqs, ARRAY_SIZE(irqs));
}
static struct pcmcia_low_level yopy_pcmcia_ops = {
......@@ -159,13 +119,7 @@ int __init pcmcia_yopy_init(struct device *dev)
int ret = -ENODEV;
if (machine_is_yopy())
ret = sa1100_register_pcmcia(&yopy_pcmcia_ops, dev);
ret = sa11xx_drv_pcmcia_probe(dev, &yopy_pcmcia_ops, 0, 1);
return ret;
}
void __exit pcmcia_yopy_exit(struct device *dev)
{
sa1100_unregister_pcmcia(&yopy_pcmcia_ops, dev);
}
/*
* linux/drivers/pcmcia/sa1100_sa1111.c
* linux/drivers/pcmcia/sa1111_generic.c
*
* We implement the generic parts of a SA1111 PCMCIA driver. This
* basically means we handle everything except controlling the
......@@ -19,63 +19,34 @@
#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[] = {
{ IRQ_S0_CD_VALID, "SA1111 PCMCIA card detect" },
{ IRQ_S0_BVD1_STSCHG, "SA1111 PCMCIA BVD1" },
{ IRQ_S1_CD_VALID, "SA1111 CF card detect" },
{ IRQ_S1_BVD1_STSCHG, "SA1111 CF BVD1" },
static struct pcmcia_irqs irqs[] = {
{ 0, IRQ_S0_CD_VALID, "SA1111 PCMCIA card detect" },
{ 0, IRQ_S0_BVD1_STSCHG, "SA1111 PCMCIA BVD1" },
{ 1, IRQ_S1_CD_VALID, "SA1111 CF card detect" },
{ 1, IRQ_S1_BVD1_STSCHG, "SA1111 CF BVD1" },
};
static struct sa1111_dev *pcmcia;
int sa1111_pcmcia_init(struct pcmcia_init *init)
int sa1111_pcmcia_hw_init(struct sa1100_pcmcia_socket *skt)
{
int i, ret;
if (init->socket_irq[0] == NO_IRQ)
init->socket_irq[0] = IRQ_S0_READY_NINT;
if (init->socket_irq[1] == NO_IRQ)
init->socket_irq[1] = IRQ_S1_READY_NINT;
for (i = ret = 0; i < ARRAY_SIZE(irqs); i++) {
ret = request_irq(irqs[i].irq, sa1100_pcmcia_interrupt,
SA_INTERRUPT, irqs[i].str, NULL);
if (ret)
break;
set_irq_type(irqs[i].irq, IRQT_FALLING);
}
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);
}
if (skt->irq == NO_IRQ)
skt->irq = skt->nr ? IRQ_S1_READY_NINT : IRQ_S0_READY_NINT;
return ret ? -1 : 2;
return sa11xx_request_irqs(skt, irqs, ARRAY_SIZE(irqs));
}
int sa1111_pcmcia_shutdown(void)
void sa1111_pcmcia_hw_shutdown(struct sa1100_pcmcia_socket *skt)
{
int i;
for (i = 0; i < ARRAY_SIZE(irqs); i++)
free_irq(irqs[i].irq, NULL);
return 0;
sa11xx_free_irqs(skt, irqs, ARRAY_SIZE(irqs));
}
void sa1111_pcmcia_socket_state(int sock, struct pcmcia_state *state)
void sa1111_pcmcia_socket_state(struct sa1100_pcmcia_socket *skt, struct pcmcia_state *state)
{
unsigned long status = sa1111_readl(pcmcia->mapbase + SA1111_PCSR);
struct sa1111_dev *sadev = SA1111_DEV(skt->dev);
unsigned long status = sa1111_readl(sadev->mapbase + SA1111_PCSR);
switch (sock) {
switch (skt->nr) {
case 0:
state->detect = status & PCSR_S0_DETECT ? 0 : 1;
state->ready = status & PCSR_S0_READY ? 1 : 0;
......@@ -98,91 +69,61 @@ void sa1111_pcmcia_socket_state(int sock, struct pcmcia_state *state)
}
}
int sa1111_pcmcia_configure_socket(int sock, const struct pcmcia_configure *conf)
int sa1111_pcmcia_configure_socket(struct sa1100_pcmcia_socket *skt, const socket_state_t *state)
{
unsigned int rst, flt, wait, pse, irq, pccr_mask, val;
struct sa1111_dev *sadev = SA1111_DEV(skt->dev);
unsigned int pccr_skt_mask, pccr_set_mask, val;
unsigned long flags;
switch (sock) {
switch (skt->nr) {
case 0:
rst = PCCR_S0_RST;
flt = PCCR_S0_FLT;
wait = PCCR_S0_PWAITEN;
pse = PCCR_S0_PSE;
irq = IRQ_S0_READY_NINT;
pccr_skt_mask = PCCR_S0_RST|PCCR_S0_FLT|PCCR_S0_PWAITEN|PCCR_S0_PSE;
break;
case 1:
rst = PCCR_S1_RST;
flt = PCCR_S1_FLT;
wait = PCCR_S1_PWAITEN;
pse = PCCR_S1_PSE;
irq = IRQ_S1_READY_NINT;
pccr_skt_mask = PCCR_S1_RST|PCCR_S1_FLT|PCCR_S1_PWAITEN|PCCR_S1_PSE;
break;
default:
return -1;
}
switch (conf->vcc) {
case 0:
pccr_mask = 0;
break;
pccr_set_mask = 0;
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;
if (state->Vcc != 0)
pccr_set_mask |= PCCR_S0_PWAITEN|PCCR_S1_PWAITEN;
if (state->Vcc == 50)
pccr_set_mask |= PCCR_S0_PSE|PCCR_S1_PSE;
if (state->flags & SS_RESET)
pccr_set_mask |= PCCR_S0_RST|PCCR_S1_RST;
if (state->flags & SS_OUTPUT_ENA)
pccr_set_mask |= PCCR_S0_FLT|PCCR_S1_FLT;
local_irq_save(flags);
val = sa1111_readl(pcmcia->mapbase + SA1111_PCCR);
val = (val & ~(pse | flt | wait | rst)) | pccr_mask;
sa1111_writel(val, pcmcia->mapbase + SA1111_PCCR);
val = sa1111_readl(sadev->mapbase + SA1111_PCCR);
val &= ~pccr_skt_mask;
val |= pccr_set_mask & pccr_skt_mask;
sa1111_writel(val, sadev->mapbase + SA1111_PCCR);
local_irq_restore(flags);
return 0;
}
int sa1111_pcmcia_socket_init(int sock)
void sa1111_pcmcia_socket_init(struct sa1100_pcmcia_socket *skt)
{
return 0;
sa11xx_enable_irqs(skt, irqs, ARRAY_SIZE(irqs));
}
int sa1111_pcmcia_socket_suspend(int sock)
void sa1111_pcmcia_socket_suspend(struct sa1100_pcmcia_socket *skt)
{
return 0;
sa11xx_disable_irqs(skt, irqs, ARRAY_SIZE(irqs));
}
static int pcmcia_probe(struct device *dev)
{
struct sa1111_dev *sadev = SA1111_DEV(dev);
unsigned long flags;
char *base;
local_irq_save(flags);
if (pcmcia) {
local_irq_restore(flags);
return -EBUSY;
}
pcmcia = sadev;
local_irq_restore(flags);
if (!request_mem_region(sadev->res.start, 512,
SA1111_DRIVER_NAME(sadev)))
return -EBUSY;
......@@ -226,34 +167,8 @@ static int __devexit pcmcia_remove(struct device *dev)
{
struct sa1111_dev *sadev = SA1111_DEV(dev);
#ifdef CONFIG_SA1100_ADSBITSY
pcmcia_adsbitsy_exit(dev);
#endif
#ifdef CONFIG_SA1100_BADGE4
pcmcia_badge4_exit(dev);
#endif
#ifdef CONFIG_SA1100_GRAPHICSMASTER
pcmcia_graphicsmaster_exit(dev);
#endif
#ifdef CONFIG_SA1100_JORNADA720
pcmcia_jornada720_exit(dev);
#endif
#ifdef CONFIG_ASSABET_NEPONSET
pcmcia_neponset_exit(dev);
#endif
#ifdef CONFIG_SA1100_PFS168
pcmcia_pfs_exit(dev);
#endif
#ifdef CONFIG_SA1100_PT_SYSTEM3
pcmcia_system3_exit(dev);
#endif
#ifdef CONFIG_SA1100_XP860
pcmcia_xp860_exit(dev);
#endif
sa11xx_drv_pcmcia_remove(dev);
release_mem_region(sadev->res.start, 512);
pcmcia = NULL;
return 0;
}
......
extern int sa1111_pcmcia_init(struct pcmcia_init *);
extern int sa1111_pcmcia_shutdown(void);
extern void sa1111_pcmcia_socket_state(int sock, struct pcmcia_state *);
extern int sa1111_pcmcia_configure_socket(int sock, const struct pcmcia_configure *);
extern int sa1111_pcmcia_socket_init(int);
extern int sa1111_pcmcia_socket_suspend(int);
#include "sa11xx_core.h"
extern int sa1111_pcmcia_hw_init(struct sa1100_pcmcia_socket *);
extern void sa1111_pcmcia_hw_shutdown(struct sa1100_pcmcia_socket *);
extern void sa1111_pcmcia_socket_state(struct sa1100_pcmcia_socket *, struct pcmcia_state *);
extern int sa1111_pcmcia_configure_socket(struct sa1100_pcmcia_socket *, const socket_state_t *);
extern void sa1111_pcmcia_socket_init(struct sa1100_pcmcia_socket *);
extern void sa1111_pcmcia_socket_suspend(struct sa1100_pcmcia_socket *);
extern int pcmcia_jornada720_init(struct device *);
extern void pcmcia_jornada720_exit(struct device *);
extern int pcmcia_neponset_init(struct device *);
extern void pcmcia_neponset_exit(struct device *);
/*======================================================================
Device driver for the PCMCIA control functionality of StrongARM
SA-1100 microprocessors.
The contents of this file are subject to the Mozilla Public
License Version 1.1 (the "License"); you may not use this file
except in compliance with the License. You may obtain a copy of
the License at http://www.mozilla.org/MPL/
Software distributed under the License is distributed on an "AS
IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
implied. See the License for the specific language governing
rights and limitations under the License.
The initial developer of the original code is John G. Dorsey
<john+@cs.cmu.edu>. Portions created by John G. Dorsey are
Copyright (C) 1999 John G. Dorsey. All Rights Reserved.
Alternatively, the contents of this file may be used under the
terms of the GNU Public License version 2 (the "GPL"), in which
case the provisions of the GPL are applicable instead of the
above. If you wish to allow the use of your version of this file
only under the terms of the GPL and not to allow others to use
your version of this file under the MPL, indicate your decision
by deleting the provisions above and replace them with the notice
and other provisions required by the GPL. If you do not delete
the provisions above, a recipient may use your version of this
file under either the MPL or the GPL.
======================================================================*/
/*
* Please see linux/Documentation/arm/SA1100/PCMCIA for more information
* on the low-level kernel interface.
*/
#include <linux/module.h>
#include <linux/init.h>
#include <linux/config.h>
#include <linux/cpufreq.h>
#include <linux/delay.h>
#include <linux/ioport.h>
#include <linux/kernel.h>
#include <linux/workqueue.h>
#include <linux/timer.h>
#include <linux/mm.h>
#include <linux/notifier.h>
#include <linux/proc_fs.h>
#include <linux/version.h>
#include <linux/interrupt.h>
#include <asm/hardware.h>
#include <asm/io.h>
#include <asm/irq.h>
#include <asm/system.h>
#include "sa11xx_core.h"
#include "sa1100.h"
#ifdef PCMCIA_DEBUG
static int pc_debug;
#endif
/* This structure maintains housekeeping state for each socket, such
* as the last known values of the card detect pins, or the Card Services
* callback value associated with the socket:
*/
static struct sa1100_pcmcia_socket sa1100_pcmcia_socket[SA1100_PCMCIA_MAX_SOCK];
#define PCMCIA_SOCKET(x) (sa1100_pcmcia_socket + (x))
/*
* sa1100_pcmcia_default_mecr_timing
* ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
*
* Calculate MECR clock wait states for given CPU clock
* speed and command wait state. This function can be over-
* written by a board specific version.
*
* The default is to simply calculate the BS values as specified in
* the INTEL SA1100 development manual
* "Expansion Memory (PCMCIA) Configuration Register (MECR)"
* that's section 10.2.5 in _my_ version of the manual ;)
*/
static unsigned int
sa1100_pcmcia_default_mecr_timing(struct sa1100_pcmcia_socket *skt,
unsigned int cpu_speed,
unsigned int cmd_time)
{
return sa1100_pcmcia_mecr_bs(cmd_time, cpu_speed);
}
static unsigned short
calc_speed(unsigned short *spds, int num, unsigned short dflt)
{
unsigned short speed = 0;
int i;
for (i = 0; i < num; i++)
if (speed < spds[i])
speed = spds[i];
if (speed == 0)
speed = dflt;
return speed;
}
/* sa1100_pcmcia_set_mecr()
* ^^^^^^^^^^^^^^^^^^^^^^^^^^^
*
* set MECR value for socket <sock> based on this sockets
* io, mem and attribute space access speed.
* Call board specific BS value calculation to allow boards
* to tweak the BS values.
*/
static int
sa1100_pcmcia_set_mecr(struct sa1100_pcmcia_socket *skt, unsigned int cpu_clock)
{
u32 mecr, old_mecr;
unsigned long flags;
unsigned short speed;
unsigned int bs_io, bs_mem, bs_attr;
int i;
speed = calc_speed(skt->spd_io, MAX_IO_WIN, SA1100_PCMCIA_IO_ACCESS);
bs_io = skt->ops->socket_get_timing(skt, cpu_clock, speed);
speed = calc_speed(skt->spd_mem, MAX_WIN, SA1100_PCMCIA_3V_MEM_ACCESS);
bs_mem = skt->ops->socket_get_timing(skt, cpu_clock, speed);
speed = calc_speed(skt->spd_attr, MAX_WIN, SA1100_PCMCIA_3V_MEM_ACCESS);
bs_attr = skt->ops->socket_get_timing(skt, cpu_clock, speed);
local_irq_save(flags);
old_mecr = mecr = MECR;
MECR_FAST_SET(mecr, skt->nr, 0);
MECR_BSIO_SET(mecr, skt->nr, bs_io);
MECR_BSA_SET(mecr, skt->nr, bs_attr);
MECR_BSM_SET(mecr, skt->nr, bs_mem);
if (old_mecr != mecr)
MECR = mecr;
local_irq_restore(flags);
DEBUG(4, "%s(): sock %u FAST %X BSM %X BSA %X BSIO %X\n",
__FUNCTION__, skt->nr, MECR_FAST_GET(mecr, skt->nr),
MECR_BSM_GET(mecr, skt->nr), MECR_BSA_GET(mecr, skt->nr),
MECR_BSIO_GET(mecr, skt->nr));
return 0;
}
static unsigned int sa1100_pcmcia_skt_state(struct sa1100_pcmcia_socket *skt)
{
struct pcmcia_state state;
unsigned int stat;
memset(&state, 0, sizeof(struct pcmcia_state));
skt->ops->socket_state(skt, &state);
stat = state.detect ? SS_DETECT : 0;
stat |= state.ready ? SS_READY : 0;
stat |= state.wrprot ? SS_WRPROT : 0;
stat |= state.vs_3v ? SS_3VCARD : 0;
stat |= state.vs_Xv ? SS_XVCARD : 0;
/* The power status of individual sockets is not available
* explicitly from the hardware, so we just remember the state
* and regurgitate it upon request:
*/
stat |= skt->cs_state.Vcc ? SS_POWERON : 0;
if (skt->cs_state.flags & SS_IOCARD)
stat |= state.bvd1 ? SS_STSCHG : 0;
else {
if (state.bvd1 == 0)
stat |= SS_BATDEAD;
else if (state.bvd2 == 0)
stat |= SS_BATWARN;
}
return stat;
}
/*
* sa1100_pcmcia_config_skt
* ^^^^^^^^^^^^^^^^^^^^^^^^
*
* Convert PCMCIA socket state to our socket configure structure.
*/
static int
sa1100_pcmcia_config_skt(struct sa1100_pcmcia_socket *skt, socket_state_t *state)
{
int ret;
ret = skt->ops->configure_socket(skt, state);
if (ret == 0) {
/*
* This really needs a better solution. The IRQ
* may or may not be claimed by the driver.
*/
if (skt->irq_state != 1 && state->io_irq) {
skt->irq_state = 1;
set_irq_type(skt->irq, IRQT_FALLING);
} else if (skt->irq_state == 1 && state->io_irq == 0) {
skt->irq_state = 0;
set_irq_type(skt->irq, IRQT_NOEDGE);
}
skt->cs_state = *state;
}
if (ret < 0)
printk(KERN_ERR "sa1100_pcmcia: unable to configure "
"socket %d\n", skt->nr);
return ret;
}
/* sa1100_pcmcia_sock_init()
* ^^^^^^^^^^^^^^^^^^^^^^^^^
*
* (Re-)Initialise the socket, turning on status interrupts
* and PCMCIA bus. This must wait for power to stabilise
* so that the card status signals report correctly.
*
* Returns: 0
*/
static int sa1100_pcmcia_sock_init(unsigned int sock)
{
struct sa1100_pcmcia_socket *skt = PCMCIA_SOCKET(sock);
DEBUG(2, "%s(): initializing socket %u\n", __FUNCTION__, skt->nr);
skt->ops->socket_init(skt);
sa1100_pcmcia_config_skt(skt, &dead_socket);
return 0;
}
/*
* sa1100_pcmcia_suspend()
* ^^^^^^^^^^^^^^^^^^^^^^^
*
* Remove power on the socket, disable IRQs from the card.
* Turn off status interrupts, and disable the PCMCIA bus.
*
* Returns: 0
*/
static int sa1100_pcmcia_suspend(unsigned int sock)
{
struct sa1100_pcmcia_socket *skt = PCMCIA_SOCKET(sock);
int ret;
DEBUG(2, "%s(): suspending socket %u\n", __FUNCTION__, skt->nr);
ret = sa1100_pcmcia_config_skt(skt, &dead_socket);
if (ret == 0)
skt->ops->socket_suspend(skt);
return ret;
}
/* sa1100_pcmcia_task_handler()
* ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
* Processes serviceable socket events using the "eventd" thread context.
*
* Event processing (specifically, the invocation of the Card Services event
* callback) occurs in this thread rather than in the actual interrupt
* handler due to the use of scheduling operations in the PCMCIA core.
*/
static void sa1100_pcmcia_task_handler(void *data)
{
struct sa1100_pcmcia_socket *skt = data;
unsigned int events;
DEBUG(4, "%s(): entering PCMCIA monitoring thread\n", __FUNCTION__);
do {
unsigned int status;
status = sa1100_pcmcia_skt_state(skt);
events = (status ^ skt->status) & skt->cs_state.csc_mask;
skt->status = status;
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 " : "");
if (events && skt->handler != NULL)
skt->handler(skt->handler_info, events);
} while (events);
}
/* sa1100_pcmcia_poll_event()
* ^^^^^^^^^^^^^^^^^^^^^^^^^^
* Let's poll for events in addition to IRQs since IRQ only is unreliable...
*/
static void sa1100_pcmcia_poll_event(unsigned long dummy)
{
struct sa1100_pcmcia_socket *skt = (struct sa1100_pcmcia_socket *)dummy;
DEBUG(4, "%s(): polling for events\n", __FUNCTION__);
mod_timer(&skt->poll_timer, jiffies + SA1100_PCMCIA_POLL_PERIOD);
schedule_work(&skt->work);
}
/* sa1100_pcmcia_interrupt()
* ^^^^^^^^^^^^^^^^^^^^^^^^^
* Service routine for socket driver interrupts (requested by the
* low-level PCMCIA init() operation via sa1100_pcmcia_thread()).
* The actual interrupt-servicing work is performed by
* sa1100_pcmcia_thread(), largely because the Card Services event-
* handling code performs scheduling operations which cannot be
* executed from within an interrupt context.
*/
static void sa1100_pcmcia_interrupt(int irq, void *dev, struct pt_regs *regs)
{
struct sa1100_pcmcia_socket *skt = dev;
DEBUG(3, "%s(): servicing IRQ %d\n", __FUNCTION__, irq);
schedule_work(&skt->work);
}
/* sa1100_pcmcia_register_callback()
* ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
* Implements the register_callback() operation for the in-kernel
* PCMCIA service (formerly SS_RegisterCallback in Card Services). If
* the function pointer `handler' is not NULL, remember the callback
* location in the state for `sock', and increment the usage counter
* for the driver module. (The callback is invoked from the interrupt
* service routine, sa1100_pcmcia_interrupt(), to notify Card Services
* of interesting events.) Otherwise, clear the callback pointer in the
* socket state and decrement the module usage count.
*
* Returns: 0
*/
static int
sa1100_pcmcia_register_callback(unsigned int sock,
void (*handler)(void *, unsigned int),
void *info)
{
struct sa1100_pcmcia_socket *skt = PCMCIA_SOCKET(sock);
if (handler) {
if (!try_module_get(skt->ops->owner))
return -ENODEV;
skt->handler_info = info;
skt->handler = handler;
} else {
skt->handler = NULL;
module_put(skt->ops->owner);
}
return 0;
}
/* sa1100_pcmcia_inquire_socket()
* ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
* Implements the inquire_socket() operation for the in-kernel PCMCIA
* service (formerly SS_InquireSocket in Card Services). We set
* SS_CAP_STATIC_MAP, which disables the memory resource database
* check. (Mapped memory is set up within the socket driver itself.)
*
* In conjunction with the STATIC_MAP capability is a new field,
* `io_offset', recommended by David Hinds. Rather than go through
* the SetIOMap interface (which is not quite suited for communicating
* window locations up from the socket driver), we just pass up
* an offset which is applied to client-requested base I/O addresses
* in alloc_io_space().
*
* SS_CAP_STATIC_MAP: don't bother with the (user-configured) memory
* resource database; we instead pass up physical address ranges
* and allow other parts of Card Services to deal with remapping.
*
* SS_CAP_PCCARD: we can deal with 16-bit PCMCIA & CF cards, but
* not 32-bit CardBus devices.
*
* Return value is irrelevant; the pcmcia subsystem ignores it.
*/
static int
sa1100_pcmcia_inquire_socket(unsigned int sock, socket_cap_t *cap)
{
struct sa1100_pcmcia_socket *skt = PCMCIA_SOCKET(sock);
int ret = -1;
if (skt) {
DEBUG(2, "%s() for sock %u\n", __FUNCTION__, skt->nr);
cap->features = SS_CAP_STATIC_MAP|SS_CAP_PCCARD;
cap->irq_mask = 0;
cap->map_size = PAGE_SIZE;
cap->pci_irq = skt->irq;
cap->io_offset = (unsigned long)skt->virt_io;
ret = 0;
}
return ret;
}
/* sa1100_pcmcia_get_status()
* ^^^^^^^^^^^^^^^^^^^^^^^^^^
* Implements the get_status() operation for the in-kernel PCMCIA
* service (formerly SS_GetStatus in Card Services). Essentially just
* fills in bits in `status' according to internal driver state or
* the value of the voltage detect chipselect register.
*
* As a debugging note, during card startup, the PCMCIA core issues
* three set_socket() commands in a row the first with RESET deasserted,
* the second with RESET asserted, and the last with RESET deasserted
* again. Following the third set_socket(), a get_status() command will
* be issued. The kernel is looking for the SS_READY flag (see
* setup_socket(), reset_socket(), and unreset_socket() in cs.c).
*
* Returns: 0
*/
static int
sa1100_pcmcia_get_status(unsigned int sock, unsigned int *status)
{
struct sa1100_pcmcia_socket *skt = PCMCIA_SOCKET(sock);
skt->status = sa1100_pcmcia_skt_state(skt);
*status = skt->status;
return 0;
}
/* sa1100_pcmcia_get_socket()
* ^^^^^^^^^^^^^^^^^^^^^^^^^^
* Implements the get_socket() operation for the in-kernel PCMCIA
* service (formerly SS_GetSocket in Card Services). Not a very
* exciting routine.
*
* Returns: 0
*/
static int
sa1100_pcmcia_get_socket(unsigned int sock, socket_state_t *state)
{
struct sa1100_pcmcia_socket *skt = PCMCIA_SOCKET(sock);
DEBUG(2, "%s() for sock %u\n", __FUNCTION__, skt->nr);
*state = skt->cs_state;
return 0;
}
/* sa1100_pcmcia_set_socket()
* ^^^^^^^^^^^^^^^^^^^^^^^^^^
* Implements the set_socket() operation for the in-kernel PCMCIA
* service (formerly SS_SetSocket in Card Services). We more or
* less punt all of this work and let the kernel handle the details
* of power configuration, reset, &c. We also record the value of
* `state' in order to regurgitate it to the PCMCIA core later.
*
* Returns: 0
*/
static int
sa1100_pcmcia_set_socket(unsigned int sock, socket_state_t *state)
{
struct sa1100_pcmcia_socket *skt = PCMCIA_SOCKET(sock);
DEBUG(2, "%s() for sock %u\n", __FUNCTION__, skt->nr);
DEBUG(3, "\tmask: %s%s%s%s%s%s\n\tflags: %s%s%s%s%s%s\n",
(state->csc_mask==0)?"<NONE>":"",
(state->csc_mask&SS_DETECT)?"DETECT ":"",
(state->csc_mask&SS_READY)?"READY ":"",
(state->csc_mask&SS_BATDEAD)?"BATDEAD ":"",
(state->csc_mask&SS_BATWARN)?"BATWARN ":"",
(state->csc_mask&SS_STSCHG)?"STSCHG ":"",
(state->flags==0)?"<NONE>":"",
(state->flags&SS_PWR_AUTO)?"PWR_AUTO ":"",
(state->flags&SS_IOCARD)?"IOCARD ":"",
(state->flags&SS_RESET)?"RESET ":"",
(state->flags&SS_SPKR_ENA)?"SPKR_ENA ":"",
(state->flags&SS_OUTPUT_ENA)?"OUTPUT_ENA ":"");
DEBUG(3, "\tVcc %d Vpp %d irq %d\n",
state->Vcc, state->Vpp, state->io_irq);
return sa1100_pcmcia_config_skt(skt, state);
} /* sa1100_pcmcia_set_socket() */
/* sa1100_pcmcia_set_io_map()
* ^^^^^^^^^^^^^^^^^^^^^^^^^^
* Implements the set_io_map() operation for the in-kernel PCMCIA
* service (formerly SS_SetIOMap in Card Services). We configure
* the map speed as requested, but override the address ranges
* supplied by Card Services.
*
* Returns: 0 on success, -1 on error
*/
static int
sa1100_pcmcia_set_io_map(unsigned int sock, struct pccard_io_map *map)
{
struct sa1100_pcmcia_socket *skt = PCMCIA_SOCKET(sock);
unsigned short speed = map->speed;
DEBUG(2, "%s() for sock %u\n", __FUNCTION__, skt->nr);
DEBUG(3, "\tmap %u speed %u\n\tstart 0x%08x stop 0x%08x\n",
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&MAP_ACTIVE)?"ACTIVE ":"",
(map->flags&MAP_16BIT)?"16BIT ":"",
(map->flags&MAP_AUTOSZ)?"AUTOSZ ":"",
(map->flags&MAP_0WS)?"0WS ":"",
(map->flags&MAP_WRPROT)?"WRPROT ":"",
(map->flags&MAP_USE_WAIT)?"USE_WAIT ":"",
(map->flags&MAP_PREFETCH)?"PREFETCH ":"");
if (map->map >= MAX_IO_WIN) {
printk(KERN_ERR "%s(): map (%d) out of range\n", __FUNCTION__,
map->map);
return -1;
}
if (map->flags & MAP_ACTIVE) {
if (speed == 0)
speed = SA1100_PCMCIA_IO_ACCESS;
} else {
speed = 0;
}
skt->spd_io[map->map] = speed;
sa1100_pcmcia_set_mecr(skt, cpufreq_get(0));
if (map->stop == 1)
map->stop = PAGE_SIZE-1;
map->stop -= map->start;
map->stop += (unsigned long)skt->virt_io;
map->start = (unsigned long)skt->virt_io;
return 0;
} /* sa1100_pcmcia_set_io_map() */
/* sa1100_pcmcia_set_mem_map()
* ^^^^^^^^^^^^^^^^^^^^^^^^^^^
* Implements the set_mem_map() operation for the in-kernel PCMCIA
* service (formerly SS_SetMemMap in Card Services). We configure
* the map speed as requested, but override the address ranges
* supplied by Card Services.
*
* Returns: 0 on success, -1 on error
*/
static int
sa1100_pcmcia_set_mem_map(unsigned int sock, struct pccard_mem_map *map)
{
struct sa1100_pcmcia_socket *skt = PCMCIA_SOCKET(sock);
struct resource *res;
unsigned short speed = map->speed;
DEBUG(2, "%s() for sock %u\n", __FUNCTION__, skt->nr);
DEBUG(3, "\tmap %u speed %u card_start %08x\n",
map->map, map->speed, map->card_start);
DEBUG(3, "\tflags: %s%s%s%s%s%s%s%s\n",
(map->flags==0)?"<NONE>":"",
(map->flags&MAP_ACTIVE)?"ACTIVE ":"",
(map->flags&MAP_16BIT)?"16BIT ":"",
(map->flags&MAP_AUTOSZ)?"AUTOSZ ":"",
(map->flags&MAP_0WS)?"0WS ":"",
(map->flags&MAP_WRPROT)?"WRPROT ":"",
(map->flags&MAP_ATTRIB)?"ATTRIB ":"",
(map->flags&MAP_USE_WAIT)?"USE_WAIT ":"");
if (map->map >= MAX_WIN)
return -EINVAL;
if (map->flags & MAP_ACTIVE) {
if (speed == 0)
speed = 300;
} else {
speed = 0;
}
if (map->flags & MAP_ATTRIB) {
res = &skt->res_attr;
skt->spd_attr[map->map] = speed;
skt->spd_mem[map->map] = 0;
} else {
res = &skt->res_mem;
skt->spd_attr[map->map] = 0;
skt->spd_mem[map->map] = speed;
}
sa1100_pcmcia_set_mecr(skt, cpufreq_get(0));
map->sys_stop -= map->sys_start;
map->sys_stop += res->start + map->card_start;
map->sys_start = res->start + map->card_start;
return 0;
}
#if defined(CONFIG_PROC_FS)
struct bittbl {
unsigned int mask;
const char *name;
};
static struct bittbl status_bits[] = {
{ SS_WRPROT, "SS_WRPROT" },
{ SS_BATDEAD, "SS_BATDEAD" },
{ SS_BATWARN, "SS_BATWARN" },
{ SS_READY, "SS_READY" },
{ SS_DETECT, "SS_DETECT" },
{ SS_POWERON, "SS_POWERON" },
{ SS_STSCHG, "SS_STSCHG" },
{ SS_3VCARD, "SS_3VCARD" },
{ SS_XVCARD, "SS_XVCARD" },
};
static struct bittbl conf_bits[] = {
{ SS_PWR_AUTO, "SS_PWR_AUTO" },
{ SS_IOCARD, "SS_IOCARD" },
{ SS_RESET, "SS_RESET" },
{ SS_DMA_MODE, "SS_DMA_MODE" },
{ SS_SPKR_ENA, "SS_SPKR_ENA" },
{ SS_OUTPUT_ENA, "SS_OUTPUT_ENA" },
{ SS_DEBOUNCED, "SS_DEBOUNCED" },
};
static void
dump_bits(char **p, const char *prefix, unsigned int val, struct bittbl *bits, int sz)
{
char *b = *p;
int i;
b += sprintf(b, "%-9s:", prefix);
for (i = 0; i < sz; i++)
if (val & bits[i].mask)
b += sprintf(b, " %s", bits[i].name);
*b++ = '\n';
*p = b;
}
/* sa1100_pcmcia_proc_status()
* ^^^^^^^^^^^^^^^^^^^^^^^^^^^
* Implements the /proc/bus/pccard/??/status file.
*
* Returns: the number of characters added to the buffer
*/
static int
sa1100_pcmcia_proc_status(char *buf, char **start, off_t pos,
int count, int *eof, void *data)
{
struct sa1100_pcmcia_socket *skt = data;
unsigned int clock = cpufreq_get(0);
unsigned long mecr = MECR;
char *p = buf;
p+=sprintf(p, "slot : %d\n", skt->nr);
dump_bits(&p, "status", skt->status,
status_bits, ARRAY_SIZE(status_bits));
dump_bits(&p, "csc_mask", skt->cs_state.csc_mask,
status_bits, ARRAY_SIZE(status_bits));
dump_bits(&p, "cs_flags", skt->cs_state.flags,
conf_bits, ARRAY_SIZE(conf_bits));
p+=sprintf(p, "Vcc : %d\n", skt->cs_state.Vcc);
p+=sprintf(p, "Vpp : %d\n", skt->cs_state.Vpp);
p+=sprintf(p, "IRQ : %d (%d)\n", skt->cs_state.io_irq, skt->irq);
p+=sprintf(p, "I/O : %u (%u)\n",
calc_speed(skt->spd_io, MAX_IO_WIN, SA1100_PCMCIA_IO_ACCESS),
sa1100_pcmcia_cmd_time(clock, MECR_BSIO_GET(mecr, skt->nr)));
p+=sprintf(p, "attribute: %u (%u)\n",
calc_speed(skt->spd_attr, MAX_WIN, SA1100_PCMCIA_3V_MEM_ACCESS),
sa1100_pcmcia_cmd_time(clock, MECR_BSA_GET(mecr, skt->nr)));
p+=sprintf(p, "common : %u (%u)\n",
calc_speed(skt->spd_mem, MAX_WIN, SA1100_PCMCIA_3V_MEM_ACCESS),
sa1100_pcmcia_cmd_time(clock, MECR_BSM_GET(mecr, skt->nr)));
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;
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 = PCMCIA_SOCKET(sock);
}
#else
#define sa1100_pcmcia_proc_setup NULL
#endif /* defined(CONFIG_PROC_FS) */
static struct pccard_operations sa11xx_pcmcia_operations = {
.owner = THIS_MODULE,
.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,
.set_io_map = sa1100_pcmcia_set_io_map,
.set_mem_map = sa1100_pcmcia_set_mem_map,
.proc_setup = sa1100_pcmcia_proc_setup
};
int sa11xx_request_irqs(struct sa1100_pcmcia_socket *skt, struct pcmcia_irqs *irqs, int nr)
{
int i, res = 0;
for (i = 0; i < nr; i++) {
if (irqs[i].sock != skt->nr)
continue;
res = request_irq(irqs[i].irq, sa1100_pcmcia_interrupt,
SA_INTERRUPT, irqs[i].str, skt);
if (res)
break;
set_irq_type(irqs[i].irq, IRQT_NOEDGE);
}
if (res) {
printk(KERN_ERR "PCMCIA: request for IRQ%d failed (%d)\n",
irqs[i].irq, res);
while (i--)
if (irqs[i].sock == skt->nr)
free_irq(irqs[i].irq, skt);
}
return res;
}
EXPORT_SYMBOL(sa11xx_request_irqs);
void sa11xx_free_irqs(struct sa1100_pcmcia_socket *skt, struct pcmcia_irqs *irqs, int nr)
{
int i;
for (i = 0; i < nr; i++)
if (irqs[i].sock == skt->nr)
free_irq(irqs[i].irq, skt);
}
EXPORT_SYMBOL(sa11xx_free_irqs);
void sa11xx_disable_irqs(struct sa1100_pcmcia_socket *skt, struct pcmcia_irqs *irqs, int nr)
{
int i;
for (i = 0; i < nr; i++)
if (irqs[i].sock == skt->nr)
set_irq_type(irqs[i].irq, IRQT_NOEDGE);
}
EXPORT_SYMBOL(sa11xx_disable_irqs);
void sa11xx_enable_irqs(struct sa1100_pcmcia_socket *skt, struct pcmcia_irqs *irqs, int nr)
{
int i;
for (i = 0; i < nr; i++)
if (irqs[i].sock == skt->nr) {
set_irq_type(irqs[i].irq, IRQT_RISING);
set_irq_type(irqs[i].irq, IRQT_BOTHEDGE);
}
}
EXPORT_SYMBOL(sa11xx_enable_irqs);
static const char *skt_names[] = {
"PCMCIA socket 0",
"PCMCIA socket 1",
};
int sa11xx_drv_pcmcia_probe(struct device *dev, struct pcmcia_low_level *ops, int first, int nr)
{
struct pcmcia_socket_class_data *cls;
unsigned int cpu_clock;
int ret, i;
cls = kmalloc(sizeof(struct pcmcia_socket_class_data), GFP_KERNEL);
if (!cls) {
ret = -ENOMEM;
goto out;
}
memset(cls, 0, sizeof(struct pcmcia_socket_class_data));
cls->ops = &sa11xx_pcmcia_operations;
cls->nsock = nr;
/*
* set default MECR calculation if the board specific
* code did not specify one...
*/
if (!ops->socket_get_timing)
ops->socket_get_timing = sa1100_pcmcia_default_mecr_timing;
cpu_clock = cpufreq_get(0);
/*
* Initialise the per-socket structure.
*/
for (i = 0; i < nr; i++) {
struct sa1100_pcmcia_socket *skt = PCMCIA_SOCKET(i);
memset(skt, 0, sizeof(*skt));
INIT_WORK(&skt->work, sa1100_pcmcia_task_handler, skt);
init_timer(&skt->poll_timer);
skt->poll_timer.function = sa1100_pcmcia_poll_event;
skt->poll_timer.data = (unsigned long)skt;
skt->poll_timer.expires = jiffies + SA1100_PCMCIA_POLL_PERIOD;
skt->nr = first + i;
skt->irq = NO_IRQ;
skt->dev = dev;
skt->ops = ops;
skt->res_skt.start = _PCMCIA(skt->nr);
skt->res_skt.end = _PCMCIA(skt->nr) + PCMCIASp - 1;
skt->res_skt.name = skt_names[skt->nr];
skt->res_skt.flags = IORESOURCE_MEM;
ret = request_resource(&iomem_resource, &skt->res_skt);
if (ret)
goto out_err_1;
skt->res_io.start = _PCMCIAIO(skt->nr);
skt->res_io.end = _PCMCIAIO(skt->nr) + PCMCIAIOSp - 1;
skt->res_io.name = "io";
skt->res_io.flags = IORESOURCE_MEM | IORESOURCE_BUSY;
ret = request_resource(&skt->res_skt, &skt->res_io);
if (ret)
goto out_err_2;
skt->res_mem.start = _PCMCIAMem(skt->nr);
skt->res_mem.end = _PCMCIAMem(skt->nr) + PCMCIAMemSp - 1;
skt->res_mem.name = "memory";
skt->res_mem.flags = IORESOURCE_MEM;
ret = request_resource(&skt->res_skt, &skt->res_mem);
if (ret)
goto out_err_3;
skt->res_attr.start = _PCMCIAAttr(skt->nr);
skt->res_attr.end = _PCMCIAAttr(skt->nr) + PCMCIAAttrSp - 1;
skt->res_attr.name = "attribute";
skt->res_attr.flags = IORESOURCE_MEM;
ret = request_resource(&skt->res_skt, &skt->res_attr);
if (ret)
goto out_err_4;
skt->virt_io = ioremap(skt->res_io.start, 0x10000);
if (skt->virt_io == NULL) {
ret = -ENOMEM;
goto out_err_5;
}
/*
* We initialize the MECR to default values here, because
* we are not guaranteed to see a SetIOMap operation at
* runtime.
*/
sa1100_pcmcia_set_mecr(skt, cpu_clock);
ret = ops->hw_init(skt);
if (ret)
goto out_err_6;
skt->status = sa1100_pcmcia_skt_state(skt);
add_timer(&skt->poll_timer);
}
dev->class_data = cls;
return 0;
do {
struct sa1100_pcmcia_socket *skt = PCMCIA_SOCKET(i);
del_timer_sync(&skt->poll_timer);
flush_scheduled_work();
ops->hw_shutdown(skt);
out_err_6:
iounmap(skt->virt_io);
out_err_5:
release_resource(&skt->res_attr);
out_err_4:
release_resource(&skt->res_mem);
out_err_3:
release_resource(&skt->res_io);
out_err_2:
release_resource(&skt->res_skt);
out_err_1:
i--;
} while (i > 0);
kfree(cls);
out:
return ret;
}
EXPORT_SYMBOL(sa11xx_drv_pcmcia_probe);
int sa11xx_drv_pcmcia_remove(struct device *dev)
{
struct pcmcia_socket_class_data *cls = dev->class_data;
int i;
dev->class_data = NULL;
for (i = 0; i < cls->nsock; i++) {
struct sa1100_pcmcia_socket *skt = PCMCIA_SOCKET(cls->sock_offset + i);
skt->ops->hw_shutdown(skt);
del_timer_sync(&skt->poll_timer);
flush_scheduled_work();
sa1100_pcmcia_config_skt(skt, &dead_socket);
iounmap(skt->virt_io);
skt->virt_io = NULL;
release_resource(&skt->res_attr);
release_resource(&skt->res_mem);
release_resource(&skt->res_io);
release_resource(&skt->res_skt);
}
kfree(cls);
return 0;
}
EXPORT_SYMBOL(sa11xx_drv_pcmcia_remove);
#ifdef CONFIG_CPU_FREQ
/* sa1100_pcmcia_update_mecr()
* ^^^^^^^^^^^^^^^^^^^^^^^^^^^
* When sa1100_pcmcia_notifier() decides that a MECR adjustment (due
* to a core clock frequency change) is needed, this routine establishes
* new BS_xx values consistent with the clock speed `clock'.
*/
static void sa1100_pcmcia_update_mecr(unsigned int clock)
{
unsigned int sock;
for (sock = 0; sock < SA1100_PCMCIA_MAX_SOCK; ++sock) {
struct sa1100_pcmcia_socket *skt = PCMCIA_SOCKET(sock);
sa1100_pcmcia_set_mecr(skt, clock);
}
}
/* sa1100_pcmcia_notifier()
* ^^^^^^^^^^^^^^^^^^^^^^^^
* When changing the processor core clock frequency, it is necessary
* to adjust the MECR timings accordingly. We've recorded the timings
* requested by Card Services, so this is just a matter of finding
* out what our current speed is, and then recomputing the new MECR
* values.
*
* Returns: 0 on success, -1 on error
*/
static int
sa1100_pcmcia_notifier(struct notifier_block *nb, unsigned long val,
void *data)
{
struct cpufreq_freqs *freqs = data;
switch (val) {
case CPUFREQ_PRECHANGE:
if (freqs->new > freqs->old) {
DEBUG(2, "%s(): new frequency %u.%uMHz > %u.%uMHz, "
"pre-updating\n", __FUNCTION__,
freqs->new / 1000, (freqs->new / 100) % 10,
freqs->old / 1000, (freqs->old / 100) % 10);
sa1100_pcmcia_update_mecr(freqs->new);
}
break;
case CPUFREQ_POSTCHANGE:
if (freqs->new < freqs->old) {
DEBUG(2, "%s(): new frequency %u.%uMHz < %u.%uMHz, "
"post-updating\n", __FUNCTION__,
freqs->new / 1000, (freqs->new / 100) % 10,
freqs->old / 1000, (freqs->old / 100) % 10);
sa1100_pcmcia_update_mecr(freqs->new);
}
break;
}
return 0;
}
static struct notifier_block sa1100_pcmcia_notifier_block = {
.notifier_call = sa1100_pcmcia_notifier
};
static int __init sa11xx_pcmcia_init(void)
{
int ret;
printk(KERN_INFO "SA11xx PCMCIA (CS release %s)\n", CS_RELEASE);
ret = cpufreq_register_notifier(&sa1100_pcmcia_notifier_block,
CPUFREQ_TRANSITION_NOTIFIER);
if (ret < 0)
printk(KERN_ERR "Unable to register CPU frequency change "
"notifier (%d)\n", ret);
return ret;
}
module_init(sa11xx_pcmcia_init);
static void __exit sa11xx_pcmcia_exit(void)
{
cpufreq_unregister_notifier(&sa1100_pcmcia_notifier_block, CPUFREQ_TRANSITION_NOTIFIER);
}
module_exit(sa11xx_pcmcia_exit);
#endif
MODULE_AUTHOR("John Dorsey <john+@cs.cmu.edu>");
MODULE_DESCRIPTION("Linux PCMCIA Card Services: SA-11xx core socket driver");
MODULE_LICENSE("Dual MPL/GPL");
/*
* 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
#include <linux/proc_fs.h>
/* include the world */
#include <pcmcia/version.h>
#include <pcmcia/cs_types.h>
#include <pcmcia/cs.h>
#include <pcmcia/ss.h>
#include <pcmcia/bulkmem.h>
#include <pcmcia/cistpl.h>
#include "cs_internal.h"
struct device;
/* 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_state {
unsigned detect: 1,
ready: 1,
bvd1: 1,
bvd2: 1,
wrprot: 1,
vs_3v: 1,
vs_Xv: 1;
};
/*
* This structure encapsulates per-socket state which we might need to
* use when responding to a Card Services query of some kind.
*/
struct sa1100_pcmcia_socket {
/*
* Info from low level handler
*/
struct device *dev;
unsigned int nr;
unsigned int irq;
/*
* Core PCMCIA state
*/
struct pcmcia_low_level *ops;
unsigned int status;
socket_state_t cs_state;
void (*handler)(void *, unsigned int);
void *handler_info;
unsigned short spd_io[MAX_IO_WIN];
unsigned short spd_mem[MAX_WIN];
unsigned short spd_attr[MAX_WIN];
struct resource res_skt;
struct resource res_io;
struct resource res_mem;
struct resource res_attr;
void *virt_io;
unsigned int irq_state;
struct timer_list poll_timer;
struct work_struct work;
};
struct pcmcia_low_level {
struct module *owner;
int (*hw_init)(struct sa1100_pcmcia_socket *);
void (*hw_shutdown)(struct sa1100_pcmcia_socket *);
void (*socket_state)(struct sa1100_pcmcia_socket *, struct pcmcia_state *);
int (*configure_socket)(struct sa1100_pcmcia_socket *, const socket_state_t *);
/*
* Enable card status IRQs on (re-)initialisation. This can
* be called at initialisation, power management event, or
* pcmcia event.
*/
void (*socket_init)(struct sa1100_pcmcia_socket *);
/*
* Disable card status IRQs and PCMCIA bus on suspend.
*/
void (*socket_suspend)(struct sa1100_pcmcia_socket *);
/*
* Calculate MECR timing clock wait states
*/
unsigned int (*socket_get_timing)(struct sa1100_pcmcia_socket *,
unsigned int cpu_speed, unsigned int cmd_time);
};
struct pcmcia_irqs {
int sock;
int irq;
const char *str;
};
int sa11xx_request_irqs(struct sa1100_pcmcia_socket *skt, struct pcmcia_irqs *irqs, int nr);
void sa11xx_free_irqs(struct sa1100_pcmcia_socket *skt, struct pcmcia_irqs *irqs, int nr);
void sa11xx_disable_irqs(struct sa1100_pcmcia_socket *skt, struct pcmcia_irqs *irqs, int nr);
void sa11xx_enable_irqs(struct sa1100_pcmcia_socket *skt, struct pcmcia_irqs *irqs, int nr);
extern int sa11xx_drv_pcmcia_probe(struct device *dev, struct pcmcia_low_level *ops, int first, int nr);
extern int sa11xx_drv_pcmcia_remove(struct device *dev);
#endif
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