/*
* drivers/pcmcia/m32r_cfc.c
*
* Device driver for the CFC functionality of M32R.
*
* Copyright (c) 2001, 2002, 2003, 2004
* Hiroyuki Kondo, Naoto Sugai, Hayato Fujiwara
*/
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/init.h>
#include <linux/config.h>
#include <linux/types.h>
#include <linux/fcntl.h>
#include <linux/string.h>
#include <linux/kernel.h>
#include <linux/errno.h>
#include <linux/timer.h>
#include <linux/sched.h>
#include <linux/slab.h>
#include <linux/ioport.h>
#include <linux/delay.h>
#include <linux/workqueue.h>
#include <linux/interrupt.h>
#include <linux/device.h>
#include <asm/irq.h>
#include <asm/io.h>
#include <asm/bitops.h>
#include <asm/system.h>
#include <pcmcia/version.h>
#include <pcmcia/cs_types.h>
#include <pcmcia/ss.h>
#include <pcmcia/cs.h>
#undef MAX_IO_WIN /* FIXME */
#define MAX_IO_WIN 1
#undef MAX_WIN /* FIXME */
#define MAX_WIN 1
#include "m32r_cfc.h"
#ifdef DEBUG
static int m32r_cfc_debug;
module_param(m32r_cfc_debug, int, 0644);
#define debug(lvl, fmt, arg...) do { \
if (m32r_cfc_debug > (lvl)) \
printk(KERN_DEBUG "m32r_cfc: " fmt , ## arg); \
} while (0)
#else
#define debug(n, args...) do { } while (0)
#endif
/* Poll status interval -- 0 means default to interrupt */
static int poll_interval = 0;
typedef enum pcc_space { as_none = 0, as_comm, as_attr, as_io } pcc_as_t;
typedef struct pcc_socket {
u_short type, flags;
struct pcmcia_socket socket;
unsigned int number;
kio_addr_t ioaddr;
u_long mapaddr;
u_long base; /* PCC register base */
u_char cs_irq1, cs_irq2, intr;
pccard_io_map io_map[MAX_IO_WIN];
pccard_mem_map mem_map[MAX_WIN];
u_char io_win;
u_char mem_win;
pcc_as_t current_space;
u_char last_iodbex;
#ifdef CONFIG_PROC_FS
struct proc_dir_entry *proc;
#endif
} pcc_socket_t;
static int pcc_sockets = 0;
static pcc_socket_t socket[M32R_MAX_PCC] = {
{ 0, }, /* ... */
};
/*====================================================================*/
static unsigned int pcc_get(u_short, unsigned int);
static void pcc_set(u_short, unsigned int , unsigned int );
static DEFINE_SPINLOCK(pcc_lock);
#if !defined(CONFIG_PLAT_USRV)
static inline u_long pcc_port2addr(unsigned long port, int size) {
u_long addr = 0;
u_long odd;
if (size == 1) { /* byte access */
odd = (port&1) << 11;
port -= port & 1;
addr = CFC_IO_MAPBASE_BYTE - CFC_IOPORT_BASE + odd + port;
} else if (size == 2)
addr = CFC_IO_MAPBASE_WORD - CFC_IOPORT_BASE + port;
return addr;
}
#else /* CONFIG_PLAT_USRV */
static inline u_long pcc_port2addr(unsigned long port, int size) {
u_long odd;
u_long addr = ((port - CFC_IOPORT_BASE) & 0xf000) << 8;
if (size == 1) { /* byte access */
odd = port & 1;
port -= odd;
odd <<= 11;
addr = (addr | CFC_IO_MAPBASE_BYTE) + odd + (port & 0xfff);
} else if (size == 2) /* word access */
addr = (addr | CFC_IO_MAPBASE_WORD) + (port & 0xfff);
return addr;
}
#endif /* CONFIG_PLAT_USRV */
void pcc_ioread_byte(int sock, unsigned long port, void *buf, size_t size,
size_t nmemb, int flag)
{
u_long addr;
unsigned char *bp = (unsigned char *)buf;
unsigned long flags;
debug(3, "m32r_cfc: pcc_ioread_byte: sock=%d, port=%#lx, buf=%p, "
"size=%u, nmemb=%d, flag=%d\n",
sock, port, buf, size, nmemb, flag);
addr = pcc_port2addr(port, 1);
if (!addr) {
printk("m32r_cfc:ioread_byte null port :%#lx\n",port);
return;
}
debug(3, "m32r_cfc: pcc_ioread_byte: addr=%#lx\n", addr);
spin_lock_irqsave(&pcc_lock, flags);
/* read Byte */
while (nmemb--)
*bp++ = readb(addr);
spin_unlock_irqrestore(&pcc_lock, flags);
}
void pcc_ioread_word(int sock, unsigned long port, void *buf, size_t size,
size_t nmemb, int flag)
{
u_long addr;
unsigned short *bp = (unsigned short *)buf;
unsigned long flags;
debug(3, "m32r_cfc: pcc_ioread_word: sock=%d, port=%#lx, "
"buf=%p, size=%u, nmemb=%d, flag=%d\n",
sock, port, buf, size, nmemb, flag);
if (size != 2)
printk("m32r_cfc: ioread_word :illigal size %u : %#lx\n", size,
port);
if (size == 9)
printk("m32r_cfc: ioread_word :insw \n");
addr = pcc_port2addr(port, 2);
if (!addr) {
printk("m32r_cfc:ioread_word null port :%#lx\n",port);
return;
}
debug(3,