Commit 3a630cb6 authored by Russell King's avatar Russell King

Merge flint.arm.linux.org.uk:/usr/src/linux-bk-2.5/linux-2.5-rmk

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