Dear all
I made a Cirrus PD6729 PCI-to-PCMCIA Bridge for 2.6.x kernel.
Please see the following patch. ( sorry for big patch)
Best Regards
diff -ruN linux/drivers/pcmcia.orig/Kconfig linux/drivers/pcmcia/Kconfig
--- linux/drivers/pcmcia.orig/Kconfig 2003-12-18 11:59:16.000000000
+0900
+++ linux/drivers/pcmcia/Kconfig 2003-12-30 21:38:44.000000000 +0900
@@ -50,6 +50,13 @@
depends on YENTA
default y if YENTA
+config PD6729
+ tristate "Cirrus PD6729 compatible bridge support"
+ depends on PCMCIA && PCI
+ help
+ This provides support for the Cirrus PD6729 PCI-to-PCMCIA bridge
device,
+ found in some older laptops and PCMCIA card readers.
+
config I82092
tristate "i82092 compatible bridge support"
depends on PCMCIA && PCI
diff -ruN linux/drivers/pcmcia.orig/Makefile
linux/drivers/pcmcia/Makefile
--- linux/drivers/pcmcia.orig/Makefile 2003-12-18 11:59:18.000000000
+0900
+++ linux/drivers/pcmcia/Makefile 2003-12-30 21:38:44.000000000 +0900
@@ -5,6 +5,7 @@
obj-$(CONFIG_PCMCIA) += pcmcia_core.o ds.o
obj-$(CONFIG_YENTA) += yenta_socket.o
+obj-$(CONFIG_PD6729) += pd6729.o
obj-$(CONFIG_I82365) += i82365.o
obj-$(CONFIG_I82092) += i82092.o
obj-$(CONFIG_TCIC) += tcic.o
diff -ruN linux/drivers/pcmcia.orig/pd6729.c
linux/drivers/pcmcia/pd6729.c
--- linux/drivers/pcmcia.orig/pd6729.c 1970-01-01 09:00:00.000000000
+0900
+++ linux/drivers/pcmcia/pd6729.c 2003-12-31 09:15:52.243963080 +0900
@@ -0,0 +1,756 @@
+/*
+ * Driver for the Cirrus PD6729 PCI-PCMCIA bridge.
+ *
+ * Based on the i82092.c driver.
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/pci.h>
+#include <linux/init.h>
+#include <linux/workqueue.h>
+#include <linux/interrupt.h>
+#include <linux/device.h>
+
+#include <pcmcia/cs_types.h>
+#include <pcmcia/ss.h>
+#include <pcmcia/cs.h>
+
+#include <asm/system.h>
+#include <asm/io.h>
+
+#include "pd6729.h"
+#include "i82365.h"
+#include "cirrus.h"
+
+MODULE_LICENSE("GPL");
+
+/* PCI core routines */
+static struct pci_device_id pd6729_pci_ids[] = {
+ {
+ .vendor = PCI_VENDOR_ID_CIRRUS,
+ .device = PCI_DEVICE_ID_CIRRUS_6729,
+ .subvendor = PCI_ANY_ID,
+ .subdevice = PCI_ANY_ID,
+ },
+ {}
+};
+MODULE_DEVICE_TABLE(pci, pd6729_pci_ids);
+
+static int pd6729_socket_suspend (struct pci_dev *dev, u32 state)
+{
+ return pcmcia_socket_dev_suspend(&dev->dev, state);
+}
+
+static int pd6729_socket_resume (struct pci_dev *dev)
+{
+ return pcmcia_socket_dev_resume(&dev->dev);
+}
+
+static struct pci_driver pd6729_pci_drv = {
+ .name = "pd6729",
+ .id_table = pd6729_pci_ids,
+ .probe = pd6729_pci_probe,
+ .remove = __devexit_p(pd6729_pci_remove),
+ .suspend = pd6729_socket_suspend,
+ .resume = pd6729_socket_resume,
+};
+
+
+/* the pccard structure and its functions */
+static struct pccard_operations pd6729_operations = {
+ .init = pd6729_init,
+ .suspend = pd6729_suspend,
+ .get_status = pd6729_get_status,
+ .get_socket = pd6729_get_socket,
+ .set_socket = pd6729_set_socket,
+ .set_io_map = pd6729_set_io_map,
+ .set_mem_map = pd6729_set_mem_map,
+};
+
+/* The card can do upto 4 sockets, allocate a structure for each of
them */
+
+struct socket_info {
+ int number;
+ int card_state; /* 0 = no socket,
+ 1 = empty socket,
+ 2 = card but not initialized,
+ 3 = operational card */
+ int io_base; /* base io address of the socket */
+
+ struct pcmcia_socket socket;
+ struct pci_dev *dev; /* The PCI device for the socket */
+};
+
+#define MAX_SOCKETS 4
+static struct socket_info sockets[MAX_SOCKETS];
+static int socket_count; /* shortcut
*/
+
+static int __devinit pd6729_pci_probe(struct pci_dev *dev, const struct
pci_device_id *id)
+{
+ int i, ret;
+ char configbyte;
+
+ if ((ret = pci_enable_device(dev)))
+ return ret;
+
+ socket_count = 2;
+ printk(KERN_INFO "pd6729: Cirrus PD6729 PCI to PCMCIA Bridge.\n");
+ printk(KERN_INFO "pd6729: configured as a %d socket device.\n",
socket_count);
+ /* Since we have no memory BARs some firmware we may not
+ have had PCI_COMMAND_MEM enabled, yet the device needs
+ it. */
+ pci_read_config_byte(dev, PCI_COMMAND, &configbyte);
+ if (!(configbyte & PCI_COMMAND_MEMORY)) {
+ printk(KERN_DEBUG "pd6729: Enabling PCI_COMMAND_MEMORY.\n");
+ configbyte |= PCI_COMMAND_MEMORY;
+ pci_write_config_byte(dev, PCI_COMMAND, configbyte);
+ }
+
+ if (pci_request_regions(dev, "pd6729")) {
+ ret = -EBUSY;
+ goto err_out_disable;
+ }
+
+ for (i = 0;i<socket_count;i++) {
+ sockets[i].card_state = 1; /* 1 = present but empty */
+ sockets[i].io_base = pci_resource_start(dev, 0);
+ sockets[i].socket.features |= SS_CAP_PAGE_REGS | SS_CAP_PCCARD;
+ sockets[i].socket.map_size = 0x1000;
+ sockets[i].socket.irq_mask = 0;
+ sockets[i].socket.pci_irq = dev->irq;
+ sockets[i].socket.owner = THIS_MODULE;
+
+ sockets[i].number = i;
+
+ if (card_present(i)) {
+ sockets[i].card_state = 3;
+ dprintk(KERN_DEBUG "pd6729: slot %i is occupied\n",i);
+ } else {
+ dprintk(KERN_DEBUG "pd6729: slot %i is vacant\n",i);
+ }
+ }
+
+ /* Register the interrupt handler */
+ dprintk(KERN_DEBUG "Requesting interrupt %i \n",dev->irq);
+ if ((ret = request_irq(dev->irq, pd6729_interrupt, SA_SHIRQ, "pd6729",
pd6729_interrupt))) {
+ printk(KERN_ERR "pd6729: Failed to register IRQ %d, aborting\n",
dev->irq);
+ goto err_out_free_res;
+ }
+
+ pci_set_drvdata(dev, &sockets[i].socket);
+
+ for (i = 0; i<socket_count; i++) {
+ sockets[i].socket.dev.dev = &dev->dev;
+ sockets[i].socket.ops = &pd6729_operations;
+ ret = pcmcia_register_socket(&sockets[i].socket);
+ if (ret) {
+ goto err_out_free_sockets;
+ }
+ }
+
+ return 0;
+
+err_out_free_sockets:
+ if (i) {
+ for (i--;i>=0;i--) {
+ pcmcia_unregister_socket(&sockets[i].socket);
+ }
+ }
+ free_irq(dev->irq, pd6729_interrupt);
+err_out_free_res:
+ pci_release_regions(dev);
+err_out_disable:
+ pci_disable_device(dev);
+ return ret;
+}
+
+static void __devexit pd6729_pci_remove(struct pci_dev *dev)
+{
+ struct pcmcia_socket *socket = pci_get_drvdata(dev);
+
+ free_irq(dev->irq, pd6729_interrupt);
+
+ if (socket)
+ pcmcia_unregister_socket(socket);
+}
+
+static spinlock_t port_lock = SPIN_LOCK_UNLOCKED;
+
+/* basic value read/write functions */
+
+static unsigned char indirect_read(int socket, unsigned short reg)
+{
+ unsigned short int port;
+ unsigned char val;
+ unsigned long flags;
+ spin_lock_irqsave(&port_lock,flags);
+ reg += socket * 0x40;
+ port = sockets[socket].io_base;
+ outb(reg,port);
+ val = inb(port+1);
+ spin_unlock_irqrestore(&port_lock,flags);
+ return val;
+}
+
+static unsigned short indirect_read16(int socket, unsigned short reg)
+{
+ unsigned short int port;
+ unsigned short tmp;
+ unsigned long flags;
+ spin_lock_irqsave(&port_lock,flags);
+ reg = reg + socket * 0x40;
+ port = sockets[socket].io_base;
+ outb(reg,port);
+ tmp = inb(port+1);
+ reg++;
+ outb(reg,port);
+ tmp = tmp | (inb(port+1)<<8);
+ spin_unlock_irqrestore(&port_lock,flags);
+ return tmp;
+}
+
+static void indirect_write(int socket, unsigned short reg, unsigned
char value)
+{
+ unsigned short int port;
+ unsigned long flags;
+ spin_lock_irqsave(&port_lock,flags);
+ reg = reg + socket * 0x40;
+ port = sockets[socket].io_base;
+ outb(reg,port);
+ outb(value,port+1);
+ spin_unlock_irqrestore(&port_lock,flags);
+}
+
+static void indirect_setbit(int socket, unsigned short reg, unsigned
char mask)
+{
+ unsigned short int port;
+ unsigned char val;
+ unsigned long flags;
+ spin_lock_irqsave(&port_lock,flags);
+ reg = reg + socket * 0x40;
+ port = sockets[socket].io_base;
+ outb(reg,port);
+ val = inb(port+1);
+ val |= mask;
+ outb(reg,port);
+ outb(val,port+1);
+ spin_unlock_irqrestore(&port_lock,flags);
+}
+
+static void indirect_resetbit(int socket, unsigned short reg, unsigned
char mask)
+{
+ unsigned short int port;
+ unsigned char val;
+ unsigned long flags;
+ spin_lock_irqsave(&port_lock,flags);
+ reg = reg + socket * 0x40;
+ port = sockets[socket].io_base;
+ outb(reg,port);
+ val = inb(port+1);
+ val &= ~mask;
+ outb(reg,port);
+ outb(val,port+1);
+ spin_unlock_irqrestore(&port_lock,flags);
+}
+
+static void indirect_write16(int socket, unsigned short reg, unsigned
short value)
+{
+ unsigned short int port;
+ unsigned char val;
+ unsigned long flags;
+ spin_lock_irqsave(&port_lock,flags);
+ reg = reg + socket * 0x40;
+ port = sockets[socket].io_base;
+
+ outb(reg,port);
+ val = value & 255;
+ outb(val,port+1);
+
+ reg++;
+
+ outb(reg,port);
+ val = value>>8;
+ outb(val,port+1);
+ spin_unlock_irqrestore(&port_lock,flags);
+}
+
+/* simple helper functions */
+/* External clock time, in nanoseconds. 120 ns = 8.33 MHz */
+static int cycle_time = 120;
+
+static int to_cycles(int ns)
+{
+ if (cycle_time!=0)
+ return ns/cycle_time;
+ else
+ return 0;
+}
+
+/* Interrupt handler functionality */
+
+static irqreturn_t pd6729_interrupt(int irq, void *dev, struct pt_regs
*regs)
+{
+ int i;
+ int loopcount = 0;
+ int handled = 0;
+
+ unsigned int events, active=0;
+
+ while (1) {
+ loopcount++;
+ if (loopcount>20) {
+ printk(KERN_ERR "pd6729: infinite eventloop in interrupt \n");
+ break;
+ }
+
+ active = 0;
+
+ for (i=0;i<socket_count;i++) {
+ int csc;
+ if (sockets[i].card_state==0) /* Inactive socket, should not happen
*/
+ continue;
+
+ csc = indirect_read(i,I365_CSC); /* card status change register */
+
+ if (csc==0) /* no events on this socket */
+ continue;
+ handled = 1;
+ events = 0;
+
+ if (csc & I365_CSC_DETECT) {
+ events |= SS_DETECT;
+ dprintk("Card detected in socket %i!\n",i);
+ }
+
+ if (indirect_read(i,I365_INTCTL) & I365_PC_IOCARD) {
+ /* For IO/CARDS, bit 0 means "read the card" */
+ events |= (csc & I365_CSC_STSCHG) ? SS_STSCHG : 0;
+ } else {
+ /* Check for battery/ready events */
+ events |= (csc & I365_CSC_BVD1) ? SS_BATDEAD : 0;
+ events |= (csc & I365_CSC_BVD2) ? SS_BATWARN : 0;
+ events |= (csc & I365_CSC_READY) ? SS_READY : 0;
+ }
+
+ if (events) {
+ pcmcia_parse_events(&sockets[i].socket, events);
+ }
+ active |= events;
+ }
+
+ if (active==0) /* no more events to handle */
+ break;
+
+ }
+ return IRQ_RETVAL(handled);
+}
+
+/* socket functions */
+
+static int card_present(int socketno)
+{
+ unsigned int val;
+
+ if ((socketno<0) || (socketno >= MAX_SOCKETS))
+ return 0;
+ if (sockets[socketno].io_base == 0)
+ return 0;
+
+ val = indirect_read(socketno, 1); /* Interface status register */
+ if ((val&12)==12) {
+ dprintk("card_present 1");
+ return 1;
+ }
+
+ dprintk("card_present 0");
+ return 0;
+}
+
+static void set_bridge_state(int sock)
+{
+ indirect_write(sock, I365_GBLCTL,0x00);
+ indirect_write(sock, I365_GENCTL,0x00);
+
+ indirect_setbit(sock, I365_INTCTL,0x08);
+}
+
+static int pd6729_init(struct pcmcia_socket *sock)
+{
+ int i;
+ pccard_io_map io = { 0, 0, 0, 0, 1 };
+ pccard_mem_map mem = { 0, 0, 0, 0, 0, 0 };
+
+ mem.sys_stop = 0x0fff;
+ pd6729_set_socket(sock, &dead_socket);
+ for (i = 0; i < 2; i++) {
+ io.map = i;
+ pd6729_set_io_map(sock, &io);
+ }
+ for (i = 0; i < 5; i++) {
+ mem.map = i;
+ pd6729_set_mem_map(sock, &mem);
+ }
+
+ return 0;
+}
+
+static int pd6729_suspend(struct pcmcia_socket *sock)
+{
+ int retval;
+ retval = pd6729_set_socket(sock, &dead_socket);
+ return retval;
+}
+
+static int pd6729_get_status(struct pcmcia_socket *socket, u_int
*value)
+{
+ unsigned int sock = container_of(socket, struct socket_info,
socket)->number;
+ unsigned int status;
+ unsigned int data, t;
+
+ status = indirect_read(sock,I365_STATUS); /* Interface Status Register
*/
+ *value = 0;
+
+ if ((status & I365_CS_DETECT) == I365_CS_DETECT) {
+ *value |= SS_DETECT;
+ }
+
+ /* IO cards have a different meaning of bits 0,1 */
+ /* Also notice the inverse-logic on the bits */
+ if (indirect_read(sock, I365_INTCTL) & I365_PC_IOCARD) {
+ /* IO card */
+ if (!(status & I365_CS_STSCHG))
+ *value |= SS_STSCHG;
+ } else { /* non I/O card */
+ if (!(status & I365_CS_BVD1))
+ *value |= SS_BATDEAD;
+ if (!(status & I365_CS_BVD2))
+ *value |= SS_BATWARN;
+
+ }
+
+ if (status & I365_CS_WRPROT)
+ (*value) |= SS_WRPROT; /* card is write protected */
+
+ if (status & I365_CS_READY)
+ (*value) |= SS_READY; /* card is not busy */
+
+ if (status & I365_CS_POWERON)
+ (*value) |= SS_POWERON; /* power is applied to the card */
+
+ t = (sock) ? sock : sock+1;
+ indirect_write(t, PD67_EXT_INDEX, PD67_EXTERN_DATA);
+ data = indirect_read16(t, PD67_EXT_DATA);
+ *value |= (data & PD67_EXD_VS1(sock)) ? 0 : SS_3VCARD; /* 3.3V card */
+
+ return 0;
+}
+
+
+static int pd6729_get_socket(struct pcmcia_socket *socket,
socket_state_t *state)
+{
+ unsigned int sock = container_of(socket, struct socket_info,
socket)->number;
+ unsigned char reg,vcc,vpp;
+
+ state->flags = 0;
+ state->Vcc = 0;
+ state->Vpp = 0;
+ state->io_irq = 0;
+ state->csc_mask = 0;
+
+ /* First the power status of the socket */
+ reg = indirect_read(sock,I365_POWER); /* PCTRL - Power Control
Register */
+
+ if (reg & I365_PWR_AUTO)
+ state->flags |= SS_PWR_AUTO; /* Automatic Power Switch */
+
+ if (reg & I365_PWR_OUT)
+ state->flags |= SS_OUTPUT_ENA; /* Output signals are enabled */
+
+ vcc = reg & I365_VCC_MASK; vpp = reg & I365_VPP1_MASK;
+
+ if (reg & I365_VCC_5V) {
+ state->Vcc = (indirect_read(sock, PD67_MISC_CTL_1) &
+ PD67_MC1_VCC_3V) ? 33 : 50;
+
+ if (vpp == I365_VPP1_5V) {
+ if (state->Vcc == 50) state->Vpp = 50;
+ else state->Vpp = 33;
+ }
+ if (vpp == I365_VPP1_12V)
+ state->Vpp = 120;
+
+ }
+
+ /* Now the IO card, RESET flags and IO interrupt */
+
+ reg = indirect_read(sock, I365_INTCTL); /* IGENC, Interrupt and
General Control */
+
+ if ((reg & I365_PC_RESET)==0)
+ state->flags |= SS_RESET;
+ if (reg & I365_PC_IOCARD)
+ state->flags |= SS_IOCARD; /* This is an IO card */
+
+ /* Set the IRQ number */
+ if (sockets[sock].dev!=NULL)
+ state->io_irq = sockets[sock].dev->irq;
+
+ /* Card status change */
+ reg = indirect_read(sock, I365_CSCINT); /* CSCICR, Card Status Change
Interrupt Configuration */
+
+ if (reg & I365_CSC_DETECT)
+ state->csc_mask |= SS_DETECT; /* Card detect is enabled */
+
+ if (state->flags & SS_IOCARD) {/* IO Cards behave different */
+ if (reg & I365_CSC_STSCHG)
+ state->csc_mask |= SS_STSCHG;
+ } else {
+ if (reg & I365_CSC_BVD1)
+ state->csc_mask |= SS_BATDEAD;
+ if (reg & I365_CSC_BVD2)
+ state->csc_mask |= SS_BATWARN;
+ if (reg & I365_CSC_READY)
+ state->csc_mask |= SS_READY;
+ }
+
+ return 0;
+}
+
+static int pd6729_set_socket(struct pcmcia_socket *socket,
socket_state_t *state)
+{
+ unsigned int sock = container_of(socket, struct socket_info,
socket)->number;
+ unsigned char reg;
+
+ /* First, set the global controller options */
+
+ set_bridge_state(sock);
+
+ /* Values for the IGENC register */
+
+ reg = 0;
+ if (!(state->flags & SS_RESET)) /* The reset bit has "inverse" logic
*/
+ reg = reg | I365_PC_RESET;
+ if (state->flags & SS_IOCARD)
+ reg = reg | I365_PC_IOCARD;
+
+ indirect_write(sock,I365_INTCTL,reg); /* IGENC, Interrupt and General
Control Register */
+
+ /* Power registers */
+
+ reg = I365_PWR_NORESET; /* default: disable resetdrv on resume */
+
+ if (state->flags & SS_PWR_AUTO) {
+ dprintk("Auto power\n");
+ reg |= I365_PWR_AUTO; /* automatic power mngmnt */
+ }
+ if (state->flags & SS_OUTPUT_ENA) {
+ dprintk("Power Enabled \n");
+ reg |= I365_PWR_OUT; /* enable power */
+ }
+
+ switch (state->Vcc) {
+ case 0:
+ break;
+ case 33:
+ dprintk("setting voltage to Vcc to 3.3V on socket %i\n",sock);
+ reg |= I365_VCC_5V;
+ indirect_setbit(sock,PD67_MISC_CTL_1,PD67_MC1_VCC_3V);
+ break;
+ case 50:
+ dprintk("setting voltage to Vcc to 5V on socket %i\n",sock);
+ reg |= I365_VCC_5V;
+ indirect_resetbit(sock,PD67_MISC_CTL_1,PD67_MC1_VCC_3V);
+ break;
+ default:
+ dprintk("pd6729: pd6729_set_socket called with invalid VCC power
value: %i ", state->Vcc);
+ return -EINVAL;
+ }
+
+ switch (state->Vpp) {
+ case 0:
+ dprintk("not setting Vpp on socket %i\n",sock);
+ break;
+ case 33:
+ case 50:
+ dprintk("setting Vpp to Vcc for socket %i\n",sock);
+ reg |= I365_VPP1_5V;
+ break;
+ case 120:
+ dprintk("setting Vpp to 12.0\n");
+ reg |= I365_VPP1_12V;
+ break;
+ default:
+ dprintk("pd6729: pd6729_set_socket called with invalid VPP power
value: %i ", state->Vpp);
+ return -EINVAL;
+ }
+
+ if (reg != indirect_read(sock,I365_POWER)) /* only write if changed */
+ indirect_write(sock,I365_POWER,reg);
+
+ /* Now, specifiy that all interrupts are to be done as PCI interrupts
*/
+ indirect_write(sock, PD67_EXT_INDEX, PD67_EXT_CTL_1);
+ indirect_write(sock, PD67_EXT_DATA, PD67_EC1_INV_MGMT_IRQ |
PD67_EC1_INV_CARD_IRQ);
+
+ /* Enable specific interrupt events */
+
+ reg = 0x00;
+ if (state->csc_mask & SS_DETECT) {
+ reg |= I365_CSC_DETECT;
+ }
+ if (state->flags & SS_IOCARD) {
+ if (state->csc_mask & SS_STSCHG)
+ reg |= I365_CSC_STSCHG;
+ } else {
+ if (state->csc_mask & SS_BATDEAD)
+ reg |= I365_CSC_BVD1;
+ if (state->csc_mask & SS_BATWARN)
+ reg |= I365_CSC_BVD2;
+ if (state->csc_mask & SS_READY)
+ reg |= I365_CSC_READY;
+ }
+ reg |= 0x30; /* management IRQ: PCI INTA# = "irq 3" */
+ indirect_write(sock,I365_CSCINT,reg);
+
+ reg = indirect_read(sock,I365_INTCTL);
+ reg |= 0x03; /* card IRQ: PCI INTA# = "irq 3" */
+ indirect_write(sock,I365_INTCTL,reg);
+
+ /* now clear the (probably bogus) pending stuff by doing a dummy
read*/
+ (void)indirect_read(sock,I365_CSC);
+
+ return 0;
+}
+
+static int pd6729_set_io_map(struct pcmcia_socket *socket, struct
pccard_io_map *io)
+{
+ unsigned int sock = container_of(socket, struct socket_info,
socket)->number;
+ unsigned char map, ioctl;
+
+ map = io->map;
+
+ /* Check error conditions */
+ if (map > 1) {
+ dprintk("pd6729_set_io_map with invalid map");
+ return -EINVAL;
+ }
+ if ((io->start > 0xffff) || (io->stop > 0xffff) || (io->stop <
io->start)){
+ dprintk("pd6729_set_io_map with invalid io");
+ return -EINVAL;
+ }
+
+ /* Turn off the window before changing anything */
+ if (indirect_read(sock, I365_ADDRWIN) & I365_ENA_IO(map))
+ indirect_resetbit(sock, I365_ADDRWIN, I365_ENA_IO(map));
+
+/* printk("set_io_map: Setting range to %x - %x
\n",io->start,io->stop); */
+
+ /* write the new values */
+ indirect_write16(sock,I365_IO(map)+I365_W_START,io->start);
+ indirect_write16(sock,I365_IO(map)+I365_W_STOP,io->stop);
+
+ ioctl = indirect_read(sock,I365_IOCTL) & ~I365_IOCTL_MASK(map);
+
+ if (io->flags & MAP_0WS) ioctl |= I365_IOCTL_0WS(map);
+ if (io->flags & MAP_16BIT) ioctl |= I365_IOCTL_16BIT(map);
+ if (io->flags & MAP_AUTOSZ) ioctl |= I365_IOCTL_IOCS16(map);
+
+ indirect_write(sock,I365_IOCTL,ioctl);
+
+ /* Turn the window back on if needed */
+ if (io->flags & MAP_ACTIVE)
+ indirect_setbit(sock,I365_ADDRWIN,I365_ENA_IO(map));
+
+ return 0;
+}
+
+static int pd6729_set_mem_map(struct pcmcia_socket *socket, struct
pccard_mem_map *mem)
+{
+ unsigned int sock = container_of(socket, struct socket_info,
socket)->number;
+ unsigned short base, i;
+ unsigned char map;
+
+ map = mem->map;
+ if (map > 4) {
+ printk("pd6729_set_mem_map: invalid map");
+ return -EINVAL;
+ }
+
+ if ( (mem->sys_start > mem->sys_stop) || (mem->speed > 1000) ) {
+ printk("pd6729_set_mem_map: invalid address / speed");
+ printk("invalid mem map for socket %i : %lx to %lx with a start of %x
\n",sock,mem->sys_start, mem->sys_stop, mem->card_start);
+ return -EINVAL;
+ }
+
+ /* Turn off the window before changing anything */
+ if (indirect_read(sock, I365_ADDRWIN) & I365_ENA_MEM(map))
+ indirect_resetbit(sock, I365_ADDRWIN,
I365_ENA_MEM(map));
+
+/* printk("set_mem_map: Setting map %i range to %x - %x on socket %i,
speed is %i, active = %i \n",map,
mem->sys_start,mem->sys_stop,sock,mem->speed,mem->flags & MAP_ACTIVE);
*/
+
+ /* write the start address */
+ base = I365_MEM(map);
+ i = (mem->sys_start >> 12) & 0x0fff;
+ if (mem->flags & MAP_16BIT)
+ i |= I365_MEM_16BIT;
+ if (mem->flags & MAP_0WS)
+ i |= I365_MEM_0WS;
+ indirect_write16(sock,base+I365_W_START,i);
+
+ /* write the stop address */
+
+ i= (mem->sys_stop >> 12) & 0x0fff;
+ switch (to_cycles(mem->speed)) {
+ case 0:
+ break;
+ case 1:
+ i |= I365_MEM_WS0;
+ break;
+ case 2:
+ i |= I365_MEM_WS1;
+ break;
+ default:
+ i |= I365_MEM_WS1 | I365_MEM_WS0;
+ break;
+ }
+
+ indirect_write16(sock,base+I365_W_STOP,i);
+
+ /* Take care of high byte */
+ indirect_write(sock, PD67_EXT_INDEX, PD67_MEM_PAGE(map));
+ indirect_write(sock, PD67_EXT_DATA, mem->sys_start >> 24);
+
+ /* card start */
+
+ i = ((mem->card_start - mem->sys_start) >> 12) & 0x3fff;
+ if (mem->flags & MAP_WRPROT)
+ i |= I365_MEM_WRPROT;
+ if (mem->flags & MAP_ATTRIB) {
+/* printk("requesting attribute memory for socket %i\n",sock);*/
+ i |= I365_MEM_REG;
+ } else {
+/* printk("requesting normal memory for socket %i\n",sock);*/
+ }
+ indirect_write16(sock,base+I365_W_OFF,i);
+
+ /* Enable the window if necessary */
+ if (mem->flags & MAP_ACTIVE)
+ indirect_setbit(sock, I365_ADDRWIN, I365_ENA_MEM(map));
+
+ return 0;
+}
+
+static int pd6729_module_init(void)
+{
+ return pci_module_init (&pd6729_pci_drv);
+}
+
+static void pd6729_module_exit(void)
+{
+ pci_unregister_driver(&pd6729_pci_drv);
+}
+
+module_init(pd6729_module_init);
+module_exit(pd6729_module_exit);
+
diff -ruN linux/drivers/pcmcia.orig/pd6729.h
linux/drivers/pcmcia/pd6729.h
--- linux/drivers/pcmcia.orig/pd6729.h 1970-01-01 09:00:00.000000000
+0900
+++ linux/drivers/pcmcia/pd6729.h 2003-12-30 21:38:44.000000000 +0900
@@ -0,0 +1,42 @@
+#ifndef _INCLUDE_GUARD_PD6729_H_
+#define _INCLUDE_GUARD_PD6729_H_
+
+#include <linux/interrupt.h>
+
+
+/* Debuging defines */
+#ifdef NOTRACE
+#define enter(x) printk("Enter: %s, %s line
%i\n",x,__FILE__,__LINE__)
+#define leave(x) printk("Leave: %s, %s line
%i\n",x,__FILE__,__LINE__)
+#define dprintk(fmt, args...) printk(fmt , ## args)
+#else
+#define enter(x) do {} while (0)
+#define leave(x) do {} while (0)
+#define dprintk(fmt, args...) do {} while (0)
+#endif
+
+
+/* Flags for I365_GENCTL */
+#define I365_DF_VS1 0x40 /* DF-step Voltage Sense */
+#define I365_DF_VS2 0x80
+
+/* Fields in PD67_EXTERN_DATA */
+#define PD67_EXD_VS1(s) (0x01 << ((s)<<1))
+#define PD67_EXD_VS2(s) (0x02 << ((s)<<1))
+
+/* prototypes */
+
+static int pd6729_pci_probe(struct pci_dev *dev, const struct
pci_device_id *id);
+static void pd6729_pci_remove(struct pci_dev *dev);
+static int card_present(int socketno);
+static irqreturn_t pd6729_interrupt(int irq, void *dev, struct pt_regs
*regs);
+static int pd6729_get_status(struct pcmcia_socket *socket, u_int
*value);
+static int pd6729_get_socket(struct pcmcia_socket *socket,
socket_state_t *state);
+static int pd6729_set_socket(struct pcmcia_socket *socket,
socket_state_t *state);
+static int pd6729_set_io_map(struct pcmcia_socket *socket, struct
pccard_io_map *io);
+static int pd6729_set_mem_map(struct pcmcia_socket *socket, struct
pccard_mem_map *mem);
+static int pd6729_init(struct pcmcia_socket *socket);
+static int pd6729_suspend(struct pcmcia_socket *socket);
+
+#endif
+
On Sun, 2004-01-11 at 12:16, Komuro wrote:
> Dear all
>
> I made a Cirrus PD6729 PCI-to-PCMCIA Bridge for 2.6.x kernel.
> Please see the following patch. ( sorry for big patch)
looks good on first sight; worth adding to -mm for sure (and then into
mainline)
On Sun, 2004-01-11 at 12:16, Komuro wrote:
> Dear all
>
> I made a Cirrus PD6729 PCI-to-PCMCIA Bridge for 2.6.x kernel.
> Please see the following patch. ( sorry for big patch)
one question though; is it worth trying to share some code with the
i82092 driver ? (I don't have the specs with me but it looks like the
Cirrus is not THAT different in most respects so maybe some code can be
shared)