|
From: James S. <jsi...@us...> - 2001-09-05 18:30:06
|
Update of /cvsroot/linux-mips/linux/drivers/pcmcia
In directory usw-pr-cvs1:/tmp/cvs-serv7925
Added Files:
Config.in Makefile au1000_generic.c au1000_pb1000.c
Log Message:
Added PCMICA support for au1000 boards
--- NEW FILE: Config.in ---
#
# PCMCIA bus subsystem configuration
#
# Right now the non-CardBus choices are not supported
# by the integrated kernel driver.
#
mainmenu_option next_comment
comment 'PCMCIA/CardBus support'
#dep_tristate 'CardBus support' CONFIG_PCMCIA $CONFIG_PCI
#if [ "$CONFIG_PCMCIA" != "n" ]; then
# define_bool CONFIG_CARDBUS y
#fi
tristate 'PCMCIA/CardBus support' CONFIG_PCMCIA
if [ "$CONFIG_PCMCIA" != "n" ]; then
if [ "$CONFIG_PCI" != "n" ]; then
bool ' CardBus support' CONFIG_CARDBUS
fi
bool ' i82365 compatible bridge support' CONFIG_I82365
bool ' Databook TCIC host bridge support' CONFIG_TCIC
if [ "$CONFIG_HD64465" = "y" ]; then
dep_tristate ' HD64465 host bridge support' CONFIG_HD64465_PCMCIA $CONFIG_PCMCIA
fi
if [ "$CONFIG_MIPS_AU1000" = "y" ]; then
dep_tristate ' AU1000 support' CONFIG_PCMCIA_AU1000 $CONFIG_PCMCIA
fi
fi
endmenu
--- NEW FILE: Makefile ---
#
# Makefile for the kernel pcmcia subsystem (c/o David Hinds)
#
# Note! Dependencies are done automagically by 'make dep', which also
# removes any old dependencies. DON'T put your own dependencies here
# unless it's something special (ie not a .c file).
#
# Note 2! The CFLAGS definitions are now inherited from the
# parent makes..
O_TARGET := pcmcia.o
export-objs := ds.o cs.o cb_enabler.o yenta.o pci_socket.o
list-multi := pcmcia_core.o yenta_socket.o
yenta_socket-objs := pci_socket.o yenta.o
pcmcia_core-objs := cistpl.o rsrc_mgr.o bulkmem.o cs.o
ifeq ($(CONFIG_CARDBUS),y)
pcmcia_core-objs += cardbus.o
endif
ifeq ($(CONFIG_PCMCIA),y)
obj-y := cistpl.o rsrc_mgr.o bulkmem.o ds.o cs.o
ifeq ($(CONFIG_CARDBUS),y)
obj-y += cardbus.o cb_enabler.o yenta.o pci_socket.o
endif
ifeq ($(CONFIG_I82365),y)
obj-y += i82365.o
endif
ifeq ($(CONFIG_TCIC),y)
obj-y += tcic.o
endif
ifeq ($(CONFIG_HD64465_PCMCIA),y)
obj-y += hd64465_ss.o
endif
else
ifeq ($(CONFIG_PCMCIA),m)
obj-m := pcmcia_core.o ds.o
ifeq ($(CONFIG_I82365),y)
obj-m += i82365.o
endif
ifeq ($(CONFIG_TCIC),y)
obj-m += tcic.o
endif
ifeq ($(CONFIG_HD64465_PCMCIA),m)
obj-m += hd64465_ss.o
endif
ifeq ($(CONFIG_CARDBUS),y)
obj-m += yenta_socket.o cb_enabler.o
endif
endif
endif
obj-$(CONFIG_PCMCIA_AU1000) += au1000_ss.o
au1000_ss-objs-y := au1000_generic.o
au1000_ss-objs-$(CONFIG_MIPS_PB1000) += au1000_pb1000.o
include $(TOPDIR)/Rules.make
pcmcia_core.o: $(pcmcia_core-objs)
$(LD) $(LD_RFLAG) -r -o $@ $(pcmcia_core-objs)
au1000_ss.o: $(au1000_ss-objs-y)
$(LD) -r -o $@ $(au1000_ss-objs-y)
yenta_socket.o: $(yenta_socket-objs)
$(LD) $(LD_RFLAG) -r -o $@ $(yenta_socket-objs)
--- NEW FILE: au1000_generic.c ---
/*
* Alchemy Semi Au1000 pcmcia driver
*
* Copyright 2001 MontaVista Software Inc.
* Author: MontaVista Software, Inc.
* pp...@mv... or so...@mv...
*
* ########################################################################
*
* This program is free software; you can distribute it and/or modify it
* under the terms of the GNU General Public License (Version 2) as
* published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 59 Temple Place - Suite 330, Boston MA 02111-1307, USA.
*
* ########################################################################
*
*
*/
#include <linux/module.h>
#include <linux/init.h>
#include <linux/config.h>
#include <linux/delay.h>
#include <linux/ioport.h>
#include <linux/kernel.h>
#include <linux/tqueue.h>
#include <linux/timer.h>
#include <linux/mm.h>
#include <linux/proc_fs.h>
#include <linux/version.h>
#include <linux/types.h>
#include <linux/vmalloc.h>
#include <pcmcia/version.h>
#include <pcmcia/cs_types.h>
#include <pcmcia/cs.h>
#include <pcmcia/ss.h>
#include <pcmcia/bulkmem.h>
#include <pcmcia/cistpl.h>
#include <pcmcia/bus_ops.h>
#include "cs_internal.h"
#include <asm/io.h>
#include <asm/irq.h>
#include <asm/system.h>
#include <asm/au1000.h>
#include <asm/au1000_pcmcia.h>
#ifdef PCMCIA_DEBUG
static int pc_debug;
#endif
MODULE_AUTHOR("Pete Popov, MontaVista Software <pp...@mv...>");
MODULE_DESCRIPTION("Linux PCMCIA Card Services: Au1000 Socket Controller");
#define MAP_SIZE 0x1000000
/* 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 au1000_pcmcia_socket *pcmcia_socket;
static int socket_count;
/* 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 au1000_pcmcia_driver_init(void);
static void au1000_pcmcia_driver_shutdown(void);
static void au1000_pcmcia_task_handler(void *data);
static void au1000_pcmcia_poll_event(unsigned long data);
static void au1000_pcmcia_interrupt(int irq, void *dev,
struct pt_regs *regs);
static struct tq_struct au1000_pcmcia_task;
#ifdef CONFIG_PROC_FS
static int au1000_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:
*/
static int au1000_pcmcia_init(unsigned int sock);
static int au1000_pcmcia_suspend(unsigned int sock);
static int au1000_pcmcia_register_callback(unsigned int sock,
void (*handler) (void *,
unsigned int),
void *info);
static int au1000_pcmcia_inquire_socket(unsigned int sock,
socket_cap_t * cap);
static int au1000_pcmcia_get_status(unsigned int sock, u_int * value);
static int au1000_pcmcia_get_socket(unsigned int sock,
socket_state_t * state);
static int au1000_pcmcia_set_socket(unsigned int sock,
socket_state_t * state);
static int au1000_pcmcia_get_io_map(unsigned int sock,
struct pccard_io_map *io);
static int au1000_pcmcia_set_io_map(unsigned int sock,
struct pccard_io_map *io);
static int au1000_pcmcia_get_mem_map(unsigned int sock,
struct pccard_mem_map *mem);
static int au1000_pcmcia_set_mem_map(unsigned int sock,
struct pccard_mem_map *mem);
#ifdef CONFIG_PROC_FS
static void au1000_pcmcia_proc_setup(unsigned int sock,
struct proc_dir_entry *base);
#endif
static struct pccard_operations au1000_pcmcia_operations = {
au1000_pcmcia_init,
au1000_pcmcia_suspend,
au1000_pcmcia_register_callback,
au1000_pcmcia_inquire_socket,
au1000_pcmcia_get_status,
au1000_pcmcia_get_socket,
au1000_pcmcia_set_socket,
au1000_pcmcia_get_io_map,
au1000_pcmcia_set_io_map,
au1000_pcmcia_get_mem_map,
au1000_pcmcia_set_mem_map,
#ifdef CONFIG_PROC_FS
au1000_pcmcia_proc_setup
#endif
};
static int __init au1000_pcmcia_driver_init(void)
{
servinfo_t info;
struct pcmcia_init pcmcia_init;
struct pcmcia_state state;
unsigned int i;
unsigned long timing3;
printk("\nAU1000 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;
}
#ifdef CONFIG_MIPS_PB1000
pcmcia_low_level = &pb1000_pcmcia_ops;
#else
#error Unsupported AU1000 board.
#endif
pcmcia_init.handler = au1000_pcmcia_interrupt;
if ((socket_count = pcmcia_low_level->init(&pcmcia_init)) < 0) {
printk(KERN_ERR
"Unable to initialize kernel PCMCIA service.\n");
return -EIO;
}
/* setup the static bus controller */
timing3 = 0x100e3a07;
writel(0x00000002, STATIC_CONFIG_3); /* type = PCMCIA */
writel(timing3, STATIC_TIMING_3);
writel(0x10000000, STATIC_ADDRESS_3); /* any PCMCIA select */
pcmcia_socket =
kmalloc(sizeof(struct au1000_pcmcia_socket) * socket_count,
GFP_KERNEL);
memset(pcmcia_socket, 0,
sizeof(struct au1000_pcmcia_socket) * socket_count);
if (!pcmcia_socket) {
printk(KERN_ERR "Card Services can't get memory \n");
return -1;
}
for (i = 0; i < socket_count; i++) {
if (pcmcia_low_level->socket_state(i, &state) < 0) {
printk(KERN_ERR
"Unable to get PCMCIA status from kernel.\n");
return -EIO;
}
pcmcia_socket[i].k_state = state;
pcmcia_socket[i].cs_state.csc_mask = SS_DETECT;
if (i == 0) {
pcmcia_socket[i].virt_io =
(u32) ioremap(0xC0000000, 0x1000);
pcmcia_socket[i].phys_attr = 0xC4000000;
pcmcia_socket[i].phys_mem = 0xC8000000;
} else {
printk(KERN_ERR
"au1000: pcmcia socket 1 not supported\n");
return 1;
}
}
/* Only advertise as many sockets as we can detect: */
if (register_ss_entry(socket_count, &au1000_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. */
au1000_pcmcia_poll_event(0);
DEBUG(1, "au1000: initialization complete\n");
return 0;
} /* au1000_pcmcia_driver_init() */
module_init(au1000_pcmcia_driver_init);
static void __exit au1000_pcmcia_driver_shutdown(void)
{
int i;
del_timer_sync(&poll_timer);
unregister_ss_entry(&au1000_pcmcia_operations);
pcmcia_low_level->shutdown();
flush_scheduled_tasks();
for (i = 0; i < socket_count; i++) {
if (pcmcia_socket[i].virt_io)
iounmap((void *) pcmcia_socket[i].virt_io);
}
DEBUG(1, "au1000: shutdown complete\n");
}
module_exit(au1000_pcmcia_driver_shutdown);
static int au1000_pcmcia_init(unsigned int sock)
{
return 0;
}
static int au1000_pcmcia_suspend(unsigned int sock)
{
return 0;
}
static inline unsigned au1000_pcmcia_events(struct pcmcia_state *state, struct pcmcia_state
*prev_state, unsigned int mask,
unsigned int flags)
{
unsigned int events = 0;
if (state->detect != prev_state->detect) {
DEBUG(2, "%s(): card detect value %u\n", __FUNCTION__,
state->detect);
events |= mask & SS_DETECT;
}
if (state->ready != prev_state->ready) {
DEBUG(2, "%s(): card ready value %u\n", __FUNCTION__,
state->ready);
events |= mask & ((flags & SS_IOCARD) ? 0 : SS_READY);
}
if (state->bvd1 != prev_state->bvd1) {
DEBUG(2, "%s(): card BVD1 value %u\n", __FUNCTION__,
state->bvd1);
events |=
mask & (flags & SS_IOCARD) ? SS_STSCHG : SS_BATDEAD;
}
if (state->bvd2 != prev_state->bvd2) {
DEBUG(2, "%s(): card BVD2 value %u\n", __FUNCTION__,
state->bvd2);
events |= mask & (flags & SS_IOCARD) ? 0 : SS_BATWARN;
}
*prev_state = *state;
return events;
} /* au1000_pcmcia_events() */
/*
* Au1000_pcmcia_task_handler()
* Processes socket events.
*/
static void au1000_pcmcia_task_handler(void *data)
{
struct pcmcia_state state;
int i, events, irq_status;
for (i = 0; i < socket_count; i++) {
if (
(irq_status =
pcmcia_low_level->socket_state(i, &state)) < 0)
printk(KERN_ERR
"Error in kernel low-level PCMCIA service.\n");
events = au1000_pcmcia_events(&state,
&pcmcia_socket[i].k_state,
pcmcia_socket[i].
cs_state.csc_mask,
pcmcia_socket[i].
cs_state.flags);
if (pcmcia_socket[i].handler != NULL) {
pcmcia_socket[i].
handler(pcmcia_socket[i].handler_info, events);
}
}
} /* au1000_pcmcia_task_handler() */
static struct tq_struct au1000_pcmcia_task = {
routine:au1000_pcmcia_task_handler
};
static void au1000_pcmcia_poll_event(unsigned long dummy)
{
poll_timer.function = au1000_pcmcia_poll_event;
poll_timer.expires = jiffies + AU1000_PCMCIA_POLL_PERIOD;
add_timer(&poll_timer);
schedule_task(&au1000_pcmcia_task);
//au1000_pcmcia_task_handler(0);
}
/*
* au1000_pcmcia_interrupt()
* The actual interrupt work is performed by au1000_pcmcia_task(),
* because the Card Services event handling code performs scheduling
* operations which cannot be executed from within an interrupt context.
*/
static void au1000_pcmcia_interrupt(int irq, void *dev,
struct pt_regs *regs)
{
schedule_task(&au1000_pcmcia_task);
}
static int
au1000_pcmcia_register_callback(unsigned int sock,
void (*handler) (void *, unsigned int),
void *info)
{
if (handler == NULL) {
pcmcia_socket[sock].handler = NULL;
MOD_DEC_USE_COUNT;
} else {
MOD_INC_USE_COUNT;
pcmcia_socket[sock].handler = handler;
pcmcia_socket[sock].handler_info = info;
}
return 0;
}
/* au1000_pcmcia_inquire_socket()
*
* From the sa1100 socket driver :
*
* Implements the inquire_socket() operation for the in-kernel PCMCIA
* service (formerly SS_InquireSocket in Card Services). We set
* SS_CAP_STATIC_MAP, which disables the memory resource database check.
* (Mapped memory is set up within the socket driver itself.)
*
* In conjunction with the STATIC_MAP capability is a new field,
* `io_offset', recommended by David Hinds. Rather than go through
* the SetIOMap interface (which is not quite suited for communicating
* window locations up from the socket driver), we just pass up
* an offset which is applied to client-requested base I/O addresses
* in alloc_io_space().
*
* Returns: 0 on success, -1 if no pin has been configured for `sock'
*/
static int au1000_pcmcia_inquire_socket(unsigned int sock,
socket_cap_t * cap)
{
struct pcmcia_irq_info irq_info;
if (sock > socket_count) {
printk(KERN_ERR "au1000: socket %u not configured\n",
sock);
return -1;
}
/* from the sa1100_generic driver: */
/* 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);
irq_info.sock = sock;
irq_info.irq = -1;
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;
}
cap->irq_mask = 0;
cap->map_size = MAP_SIZE;
cap->pci_irq = irq_info.irq;
cap->io_offset = pcmcia_socket[sock].virt_io;
return 0;
} /* au1000_pcmcia_inquire_socket() */
static int
au1000_pcmcia_get_status(unsigned int sock, unsigned int *status)
{
struct pcmcia_state state;
if ((pcmcia_low_level->socket_state(sock, &state)) < 0) {
printk(KERN_ERR
"Unable to get PCMCIA status from kernel.\n");
return -1;
}
pcmcia_socket[sock].k_state = state;
*status = state.detect ? SS_DETECT : 0;
*status |= state.ready ? SS_READY : 0;
*status |= pcmcia_socket[sock].cs_state.Vcc ? SS_POWERON : 0;
if (pcmcia_socket[sock].cs_state.flags & SS_IOCARD)
*status |= state.bvd1 ? SS_STSCHG : 0;
else {
if (state.bvd1 == 0)
*status |= SS_BATDEAD;
else if (state.bvd2 == 0)
*status |= SS_BATWARN;
}
*status |= state.vs_3v ? SS_3VCARD : 0;
*status |= state.vs_Xv ? SS_XVCARD : 0;
DEBUG(2, "\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 " : "");
return 0;
} /* au1000_pcmcia_get_status() */
static int
au1000_pcmcia_get_socket(unsigned int sock, socket_state_t * state)
{
*state = pcmcia_socket[sock].cs_state;
return 0;
}
static int
au1000_pcmcia_set_socket(unsigned int sock, socket_state_t * state)
{
struct pcmcia_configure configure;
DEBUG(2, "\tmask: %s%s%s%s%s%s\n\tflags: %s%s%s%s%s%s\n"
"\tVcc %d Vpp %d irq %d\n",
(state->csc_mask == 0) ? "<NONE>" : "",
(state->csc_mask & SS_DETECT) ? "DETECT " : "",
(state->csc_mask & SS_READY) ? "READY " : "",
(state->csc_mask & SS_BATDEAD) ? "BATDEAD " : "",
(state->csc_mask & SS_BATWARN) ? "BATWARN " : "",
(state->csc_mask & SS_STSCHG) ? "STSCHG " : "",
(state->flags == 0) ? "<NONE>" : "",
(state->flags & SS_PWR_AUTO) ? "PWR_AUTO " : "",
(state->flags & SS_IOCARD) ? "IOCARD " : "",
(state->flags & SS_RESET) ? "RESET " : "",
(state->flags & SS_SPKR_ENA) ? "SPKR_ENA " : "",
(state->flags & SS_OUTPUT_ENA) ? "OUTPUT_ENA " : "",
state->Vcc, state->Vpp, state->io_irq);
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;
if (pcmcia_low_level->configure_socket(&configure) < 0) {
printk(KERN_ERR "Unable to configure socket %u\n", sock);
return -1;
}
pcmcia_socket[sock].cs_state = *state;
return 0;
} /* au1000_pcmcia_set_socket() */
static int
au1000_pcmcia_get_io_map(unsigned int sock, struct pccard_io_map *map)
{
DEBUG(1, "au1000_pcmcia_get_io_map: sock %d\n", sock);
if (map->map >= MAX_IO_WIN) {
printk(KERN_ERR "%s(): map (%d) out of range\n",
__FUNCTION__, map->map);
return -1;
}
*map = pcmcia_socket[sock].io_map[map->map];
return 0;
}
int au1000_pcmcia_set_io_map(unsigned int sock, struct pccard_io_map *map)
{
unsigned int speed;
unsigned long start;
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) {
speed =
(map->speed > 0) ? map->speed : AU1000_PCMCIA_IO_SPEED;
pcmcia_socket[sock].speed_io = speed;
}
start = map->start;
if (map->stop == 1) {
map->stop = PAGE_SIZE - 1;
}
map->start = pcmcia_socket[sock].virt_io;
map->stop = map->start + (map->stop - start);
pcmcia_socket[sock].io_map[map->map] = *map;
DEBUG(3, "set_io_map %d start %x stop %x\n", map->map, map->start,
map->stop);
return 0;
}
static int
au1000_pcmcia_get_mem_map(unsigned int sock, struct pccard_mem_map *map)
{
if (map->map >= MAX_WIN) {
printk(KERN_ERR "%s(): map (%d) out of range\n",
__FUNCTION__, map->map);
return -1;
}
*map = pcmcia_socket[sock].mem_map[map->map];
return 0;
}
static int
au1000_pcmcia_set_mem_map(unsigned int sock, struct pccard_mem_map *map)
{
unsigned int speed;
unsigned long start;
u_long flags;
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) {
speed =
(map->speed >
0) ? map->speed : AU1000_PCMCIA_MEM_SPEED;
/* TBD */
if (map->flags & MAP_ATTRIB) {
pcmcia_socket[sock].speed_attr = speed;
} else {
pcmcia_socket[sock].speed_mem = speed;
}
}
save_flags(flags);
cli();
start = map->sys_start;
if (map->sys_stop == 0)
map->sys_stop = MAP_SIZE - 1;
if (map->flags & MAP_ATTRIB) {
map->sys_start =
pcmcia_socket[sock].phys_attr + map->card_start;
} else {
map->sys_start =
pcmcia_socket[sock].phys_mem + map->card_start;
}
map->sys_stop = map->sys_start + (map->sys_stop - start);
pcmcia_socket[sock].mem_map[map->map] = *map;
restore_flags(flags);
DEBUG(3, "set_mem_map %d start %x stop %x card_start %x\n",
map->map, map->sys_start, map->sys_stop, map->card_start);
return 0;
}
#if defined(CONFIG_PROC_FS)
static void
au1000_pcmcia_proc_setup(unsigned int sock, struct proc_dir_entry *base)
{
struct proc_dir_entry *entry;
if ((entry = create_proc_entry("status", 0, base)) == NULL) {
printk(KERN_ERR
"Unable to install \"status\" procfs entry\n");
return;
}
entry->read_proc = au1000_pcmcia_proc_status;
entry->data = (void *) sock;
}
/* au1000_pcmcia_proc_status()
* Implements the /proc/bus/pccard/??/status file.
*
* Returns: the number of characters added to the buffer
*/
static int
au1000_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;
p += sprintf(p, "k_flags : %s%s%s%s%s%s%s\n",
pcmcia_socket[sock].k_state.detect ? "detect " : "",
pcmcia_socket[sock].k_state.ready ? "ready " : "",
pcmcia_socket[sock].k_state.bvd1 ? "bvd1 " : "",
pcmcia_socket[sock].k_state.bvd2 ? "bvd2 " : "",
pcmcia_socket[sock].k_state.wrprot ? "wrprot " : "",
pcmcia_socket[sock].k_state.vs_3v ? "vs_3v " : "",
pcmcia_socket[sock].k_state.vs_Xv ? "vs_Xv " : "");
p += sprintf(p, "status : %s%s%s%s%s%s%s%s%s\n",
pcmcia_socket[sock].
k_state.detect ? "SS_DETECT " : "",
pcmcia_socket[sock].k_state.ready ? "SS_READY " : "",
pcmcia_socket[sock].cs_state.Vcc ? "SS_POWERON " : "",
pcmcia_socket[sock].
cs_state.flags & SS_IOCARD ? \"SS_IOCARD " : "",
(pcmcia_socket[sock].cs_state.flags & SS_IOCARD
&& pcmcia_socket[sock].
k_state.bvd1) ? "SS_STSCHG " : "",
((pcmcia_socket[sock].cs_state.flags & SS_IOCARD) == 0
&& (pcmcia_socket[sock].k_state.bvd1 ==
0)) ? "SS_BATDEAD " : "",
((pcmcia_socket[sock].cs_state.flags & SS_IOCARD) == 0
&& (pcmcia_socket[sock].k_state.bvd2 ==
0)) ? "SS_BATWARN " : "",
pcmcia_socket[sock].k_state.vs_3v ? "SS_3VCARD " : "",
pcmcia_socket[sock].
k_state.vs_Xv ? "SS_XVCARD " : "");
p += sprintf(p, "mask : %s%s%s%s%s\n",
pcmcia_socket[sock].
cs_state.csc_mask & SS_DETECT ? \"SS_DETECT " : "",
pcmcia_socket[sock].
cs_state.csc_mask & SS_READY ? \"SS_READY " : "",
pcmcia_socket[sock].
cs_state.csc_mask & SS_BATDEAD ? \"SS_BATDEAD " : "",
pcmcia_socket[sock].
cs_state.csc_mask & SS_BATWARN ? \"SS_BATWARN " : "",
pcmcia_socket[sock].
cs_state.csc_mask & SS_STSCHG ? \"SS_STSCHG " : "");
p += sprintf(p, "cs_flags : %s%s%s%s%s\n",
pcmcia_socket[sock].
cs_state.flags & SS_PWR_AUTO ? \"SS_PWR_AUTO " : "",
pcmcia_socket[sock].
cs_state.flags & SS_IOCARD ? \"SS_IOCARD " : "",
pcmcia_socket[sock].
cs_state.flags & SS_RESET ? \"SS_RESET " : "",
pcmcia_socket[sock].
cs_state.flags & SS_SPKR_ENA ? \"SS_SPKR_ENA " : "",
pcmcia_socket[sock].
cs_state.flags & SS_OUTPUT_ENA ? \"SS_OUTPUT_ENA " :
"");
p +=
sprintf(p, "Vcc : %d\n",
pcmcia_socket[sock].cs_state.Vcc);
p +=
sprintf(p, "Vpp : %d\n",
pcmcia_socket[sock].cs_state.Vpp);
p +=
sprintf(p, "irq : %d\n",
pcmcia_socket[sock].cs_state.io_irq);
p += sprintf(p, "I/O : %u\n", pcmcia_socket[sock].speed_io);
p += sprintf(p, "attribute: %u\n", pcmcia_socket[sock].speed_attr);
p += sprintf(p, "common : %u\n", pcmcia_socket[sock].speed_mem);
return p - buf;
}
#endif /* defined(CONFIG_PROC_FS) */
--- NEW FILE: au1000_pb1000.c ---
/*
* Alchemy Semi PB1000 board specific pcmcia routines.
*
* Copyright 2001 MontaVista Software Inc.
* Author: MontaVista Software, Inc.
* pp...@mv... or so...@mv...
*
* ########################################################################
*
* This program is free software; you can distribute it and/or modify it
* under the terms of the GNU General Public License (Version 2) as
* published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 59 Temple Place - Suite 330, Boston MA 02111-1307, USA.
*
* ########################################################################
*
*
*/
#include <linux/module.h>
#include <linux/init.h>
#include <linux/config.h>
#include <linux/delay.h>
#include <linux/ioport.h>
#include <linux/kernel.h>
#include <linux/tqueue.h>
#include <linux/timer.h>
#include <linux/mm.h>
#include <linux/proc_fs.h>
#include <linux/version.h>
#include <linux/types.h>
#include <pcmcia/version.h>
#include <pcmcia/cs_types.h>
#include <pcmcia/cs.h>
#include <pcmcia/ss.h>
#include <pcmcia/bulkmem.h>
#include <pcmcia/cistpl.h>
#include <pcmcia/bus_ops.h>
#include "cs_internal.h"
#include <asm/io.h>
#include <asm/irq.h>
#include <asm/system.h>
#include <asm/au1000.h>
#include <asm/au1000_pcmcia.h>
#include <asm/pb1000.h>
extern struct pcmcia_x_table x_table;
static int pb1000_pcmcia_init(struct pcmcia_init *init)
{
u32 pcr;
pcr = PCR_SLOT_0_RST | PCR_SLOT_1_RST;
//writel(readl(PIN_FUNCTION) & ~(1<<8), PIN_FUNCTION); /* pin15 is gpio */
writel(0, PIN_FUNCTION); /* pin15 is gpio */
writel(readl(TSTATE_STATE_SET) | (1 << 15), TSTATE_STATE_SET); /* tristate gpio15 */
au_sync();
writel(0x8000, AU1000_MDR); /* clear pcmcia interrupt */
writel(0x4000, AU1000_MDR); /* enable pcmcia interrupt */
au_sync();
/* There's two sockets, but only the first one, 0, is used and tested */
return 1;
}
static int pb1000_pcmcia_shutdown(void)
{
u16 pcr;
pcr = 0;
writew(pcr, AU1000_PCR);
mdelay(20);
return 0;
}
static int
pb1000_pcmcia_socket_state(unsigned sock, struct pcmcia_state *state)
{
u16 levels, pcr;
unsigned char vs;
levels = readw(AU1000_ACR1);
pcr = readw(AU1000_PCR);
state->ready = 0;
state->vs_Xv = 0;
state->vs_3v = 0;
state->detect = 0;
/*
* CD1/2 are active low; so are the VSS pins; Ready is active high
*/
if (sock == 0) {
if ((levels & ACR1_SLOT_0_READY))
state->ready = 1;
if (!(levels & (ACR1_SLOT_0_CD1 | ACR1_SLOT_0_CD2))) {
state->detect = 1;
vs = (levels >> 4) & 0x3;
switch (vs) {
case 0:
case 1:
DEBUG(1, "%d: vs_3v\n", sock);
state->vs_3v = 1;
break;
case 2:
state->vs_Xv = 1;
DEBUG(1, "%d: vs_Xv\n", sock);
break;
case 3:
default:
break;
}
}
} else if (sock == 1) {
if ((levels & ACR1_SLOT_1_READY))
state->ready = 1;
if (!(levels & (ACR1_SLOT_1_CD1 | ACR1_SLOT_1_CD2))) {
state->detect = 1;
vs = (levels >> 12) & 0x3;
switch (vs) {
case 0:
case 1:
state->vs_3v = 1;
DEBUG(1, "%d: vs_3v\n", sock);
break;
case 2:
state->vs_Xv = 1;
DEBUG(1, "%d: vs_Xv\n", sock);
break;
case 3:
default:
break;
}
}
} else {
printk(KERN_ERR "pb1000 socket_state bad sock %d\n", sock);
}
state->bvd1 = 1;
state->bvd2 = 1;
state->wrprot = 0;
return 1;
}
static int pb1000_pcmcia_get_irq_info(struct pcmcia_irq_info *info)
{
if (info->sock > PCMCIA_MAX_SOCK)
return -1;
if (info->sock == 0)
info->irq = AU1000_GPIO_15;
else
info->irq = -1;
return 0;
}
static int
pb1000_pcmcia_configure_socket(const struct pcmcia_configure *configure)
{
u16 pcr;
if (configure->sock > PCMCIA_MAX_SOCK)
return -1;
pcr = readw(AU1000_PCR);
if (configure->sock == 0)
pcr &= ~(PCR_SLOT_0_VCC0 | PCR_SLOT_0_VCC1);
else
pcr &= ~(PCR_SLOT_1_VCC0 | PCR_SLOT_1_VCC1);
pcr &= ~PCR_SLOT_0_RST;
writew(pcr, AU1000_PCR);
mdelay(20);
switch (configure->vcc) {
case 0: /* Vcc 0 */
switch (configure->vpp) {
case 0:
pcr |=
SET_VCC_VPP(VCC_HIZ, VPP_GND, configure->sock);
DEBUG(3, "Vcc 0V Vpp 0V, pcr %x\n", pcr);
break;
case 12:
pcr |=
SET_VCC_VPP(VCC_HIZ, VPP_12V, configure->sock);
DEBUG(3, "Vcc 0V Vpp 12V, pcr %x\n", pcr);
break;
case 50:
pcr |=
SET_VCC_VPP(VCC_HIZ, VPP_5V, configure->sock);
DEBUG(3, "Vcc 0V Vpp 5V, pcr %x\n", pcr);
break;
case 33:
default:
pcr |=
SET_VCC_VPP(VCC_HIZ, VPP_HIZ, configure->sock);
printk(KERN_ERR "%s: bad Vcc/Vpp combo (%d:%d)\n",
__FUNCTION__, configure->vcc,
configure->vpp);
break;
}
break;
case 50: /* Vcc 5V */
switch (configure->vpp) {
case 0:
pcr |=
SET_VCC_VPP(VCC_5V, VPP_GND, configure->sock);
DEBUG(3, "Vcc 5V Vpp 0V, pcr %x\n", pcr);
break;
case 50:
pcr |=
SET_VCC_VPP(VCC_5V, VPP_5V, configure->sock);
DEBUG(3, "Vcc 5V Vpp 5V, pcr %x\n", pcr);
break;
case 12:
pcr |=
SET_VCC_VPP(VCC_5V, VPP_12V, configure->sock);
DEBUG(3, "Vcc 5V Vpp 12V, pcr %x\n", pcr);
break;
case 33:
default:
pcr |=
SET_VCC_VPP(VCC_HIZ, VPP_HIZ, configure->sock);
printk(KERN_ERR "%s: bad Vcc/Vpp combo (%d:%d)\n",
__FUNCTION__, configure->vcc,
configure->vpp);
break;
}
break;
case 33: /* Vcc 3.3V */
switch (configure->vpp) {
case 0:
pcr |=
SET_VCC_VPP(VCC_3V, VPP_GND, configure->sock);
DEBUG(3, "Vcc 3V Vpp 0V, pcr %x\n", pcr);
break;
case 50:
pcr |=
SET_VCC_VPP(VCC_3V, VPP_5V, configure->sock);
DEBUG(3, "Vcc 3V Vpp 5V, pcr %x\n", pcr);
break;
case 12:
pcr |=
SET_VCC_VPP(VCC_3V, VPP_12V, configure->sock);
DEBUG(3, "Vcc 3V Vpp 12V, pcr %x\n", pcr);
break;
case 33:
default:
pcr |=
SET_VCC_VPP(VCC_HIZ, VPP_HIZ, configure->sock);
printk(KERN_ERR "%s: bad Vcc/Vpp combo (%d:%d)\n",
__FUNCTION__, configure->vcc,
configure->vpp);
break;
}
break;
default: /* what's this ? */
pcr |= SET_VCC_VPP(VCC_HIZ, VPP_HIZ, configure->sock);
printk(KERN_ERR "%s: bad Vcc %d\n", __FUNCTION__,
configure->vcc);
break;
}
writew(pcr, AU1000_PCR);
mdelay(400);
pcr &= ~PCR_SLOT_0_RST;
if (configure->reset) {
pcr |= PCR_SLOT_0_RST;
}
writew(pcr, AU1000_PCR);
mdelay(200);
return 0;
}
struct pcmcia_low_level pb1000_pcmcia_ops = {
pb1000_pcmcia_init,
pb1000_pcmcia_shutdown,
pb1000_pcmcia_socket_state,
pb1000_pcmcia_get_irq_info,
pb1000_pcmcia_configure_socket
};
|