Hi Dominik,

Could you please give me some feedback on this driver (since I lost the last 
letter).

diff --git a/drivers/pcmcia/hd64461_ss.c b/drivers/pcmcia/hd64461_ss.c
new file mode 100644
index 0000000..ead178a
--- /dev/null
+++ b/drivers/pcmcia/hd64461_ss.c
@@ -0,0 +1,555 @@
+/*
+ * drivers/pcmcia/hd64461_ss.c
+ *
+ * PCMCIA platform driver for Hitachi HD64461 companion chip
+ * Copyright (C) 2006-2008 Kristoffer Ericson <[EMAIL PROTECTED]>
+ *
+ * This driver is based on hd64461_ss.c that was maintained in LinuxSH cvs 
+ * before merger with mainline by
+ * COPYRIGHT (C) 2002-2005 Andriy Skulysh <[EMAIL PROTECTED]>
+ * COPYRIGHT (C) ?  Greg Banks <[EMAIL PROTECTED]>
+ * COPYRIGHT (C) 2000 YAEGASHI Takeshi
+ *
+ * Please note that although the hd64461 chipset supports two sockets (0 & 1) 
this driver only
+ * supports the PCMCIA one. The CF slot cannot handle anything other than 
memory cards, so its
+ * better to leave that to other drivers such as pata.
+ *
+ */
+#include <linux/autoconf.h>
+#include <linux/types.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/string.h>
+#include <linux/kernel.h>
+#include <linux/ioport.h>
+#include <linux/mm.h>
+#include <linux/vmalloc.h>
+#include <linux/irq.h>
+#include <linux/interrupt.h>
+#include <linux/platform_device.h>
+
+#include <pcmcia/cs_types.h>
+#include <pcmcia/cs.h>
+#include <pcmcia/ss.h>
+#include <pcmcia/bulkmem.h>
+#include <pcmcia/cistpl.h>
+
+#include "cs_internal.h"
+
+#include <asm/io.h>
+#include <asm/hd64461.h>
+#include <asm/hp6xx.h>
+
+#define MODNAME "HD64461_ss"
+
+typedef struct hd64461_socket_t {
+       u8                      cscier;
+       unsigned int            irq;
+       unsigned long           mem_base;
+       socket_state_t          state;
+       pccard_mem_map          mem_maps[MAX_WIN];
+       unsigned char           IC_memory;
+       struct pcmcia_socket    socket;
+} hd64461_socket_t;
+
+static hd64461_socket_t hd64461_sockets[1];
+
+static int hd64461_set_voltage(int Vcc, int Vpp)
+{
+       u8 gcr, scr;
+       u16 stbcr;
+       u32 gcr_reg = HD64461_PCC0GCR;
+       u32 scr_reg = HD64461_PCC0SCR;
+
+       gcr = inb(gcr_reg);
+       scr = inb(scr_reg);
+
+       switch (Vcc) {
+       case 0:
+                       gcr |= HD64461_PCCGCR_VCC0;
+                       scr |= HD64461_PCCSCR_VCC1;
+                       break;
+       case 33:
+                       gcr |= HD64461_PCCGCR_VCC0;
+                       scr &= ~HD64461_PCCSCR_VCC1;
+                       break;
+       case 50:
+                       gcr &= ~HD64461_PCCGCR_VCC0;
+                       scr &= ~HD64461_PCCSCR_VCC1;
+                       break;
+       }
+
+       outb(gcr, gcr_reg);
+       outb(scr, scr_reg);
+
+       stbcr = ctrl_inw(HD64461_STBCR);
+
+       if (Vcc > 0) {
+               stbcr &= ~HD64461_STBCR_SPC0ST;
+       } else {
+               stbcr |= HD64461_STBCR_SPC0ST;
+       }
+
+       outw(stbcr, HD64461_STBCR);
+
+       return 1;
+}
+
+static int hd64461_init(struct pcmcia_socket *s)
+{
+       u16 gpadr;
+       hd64461_socket_t *sp = container_of(s, struct hd64461_socket_t, socket);
+
+       sp->state.Vcc = 0;
+       sp->state.Vpp = 0;
+       hd64461_set_voltage(0, 0);
+
+       gpadr = ctrl_inw(HD64461_GPADR);
+       gpadr &= ~HD64461_GPADR_PCMCIA0;
+       ctrl_outw(gpadr, HD64461_GPADR);
+
+       return 0;
+}
+
+static int hd64461_suspend(struct pcmcia_socket *s)
+{
+       u16 gpadr;
+       u8 gcr;
+       u32 gcr_reg = HD64461_PCC0GCR;
+
+       gcr = inb(gcr_reg);
+       gcr &= ~HD64461_PCCGCR_DRVE;
+       ctrl_outb(gcr, gcr_reg);
+       hd64461_set_voltage(0, 0);
+
+       gpadr = ctrl_inw(HD64461_GPADR);
+       gpadr |= HD64461_GPADR_PCMCIA0;
+       ctrl_outw(gpadr, HD64461_GPADR);
+
+       return 0;
+}
+
+static int hd64461_get_status(struct pcmcia_socket *s, u32 *value)
+{
+       u8 isr;
+       u32 status = 0;
+       hd64461_socket_t *sp = container_of(s, struct hd64461_socket_t, socket);
+
+       /* get status of pcmcia socket */
+       isr = inb(HD64461_PCC0ISR);
+
+       /* is card inserted and powerd? */
+       if (!(isr & HD64461_PCCISR_PCD_MASK)) {
+               status |= SS_DETECT;
+
+               /* If its an memory card, lets find out the voltage */
+               if (sp->IC_memory) {
+                       switch (isr & HD64461_PCCISR_BVD_MASK) {
+                       case HD64461_PCCISR_BVD_BATGOOD:
+                               break;
+                       case HD64461_PCCISR_BVD_BATWARN:
+                               status |= SS_BATWARN;
+                               break;
+                       default:
+                               status |= SS_BATDEAD;
+                               break;
+                       }
+
+                       if (isr & HD64461_PCCISR_READY) {
+                               status |= SS_READY;
+                       }
+
+                       if (isr & HD64461_PCCISR_MWP) {
+                               status |= SS_WRPROT;
+                       }
+               } else {
+                       status |= SS_STSCHG;
+               }
+
+               switch (~isr & (HD64461_PCCISR_VS2 | HD64461_PCCISR_VS1)) {
+               case HD64461_PCCISR_VS1:
+                       /* 5V card, Implies that the card shouldn't work, but 
sometimes it actually does */
+                       /* so we set the 3V just to give it a try */
+                       status |= SS_3VCARD;
+                       break;
+               case 0:
+               case HD64461_PCCISR_VS2:
+                       /* This card is an ordinary 3V card */
+                       status |= SS_3VCARD;
+                       break;
+               case HD64461_PCCISR_VS2 | HD64461_PCCISR_VS1:
+                       break;
+               }
+
+               if ((sp->state.Vcc != 0) || (sp->state.Vpp != 0)) {
+                       status |= SS_POWERON;
+       }
+       }
+       *value = status;
+       return 0;
+}
+
+static int hd64461_set_socket(struct pcmcia_socket *s, socket_state_t * state)
+{
+       u32 flags;
+       u32 changed;
+       u8 gcr, cscier;
+       hd64461_socket_t *sp = container_of(s, struct hd64461_socket_t, socket);
+       u32 gcr_reg = HD64461_PCC0GCR;
+       u32 cscier_reg = HD64461_PCC0CSCIER;
+
+       local_irq_save(flags);
+
+       /* compair old power status with new */
+       if (state->Vpp != sp->state.Vpp || state->Vcc != sp->state.Vcc) {
+               if (!hd64461_set_voltage(state->Vcc, state->Vpp)) {
+                       local_irq_restore(flags);
+                       return -EINVAL;
+               }
+       }
+
+       /* lets only push the changes */
+       changed = sp->state.csc_mask ^ state->csc_mask;
+       cscier = ctrl_inb(cscier_reg);
+
+       /* set it so interrupt occurs when values of CD1 and CD2 are changed */
+       if (changed & SS_DETECT) {
+               if (state->csc_mask & SS_DETECT)
+                       cscier |= HD64461_PCCCSCIER_CDE;
+               else
+                       cscier &= ~HD64461_PCCCSCIER_CDE;
+       }
+
+       /* set so interrupt occurs when pin changes from low -> high */
+       if (changed & SS_READY) {
+               if (state->csc_mask & SS_READY)
+                       cscier |= HD64461_PCCCSCIER_RE;
+               else
+                       cscier &= ~HD64461_PCCCSCIER_RE;
+       }
+
+       /* set so interrupt occurs when BVD1 & BVD2 are set to bat_dead */
+       if (changed & SS_BATDEAD) {
+               if (state->csc_mask & SS_BATDEAD)
+                       cscier |= HD64461_PCCCSCIER_BDE;
+               else
+                       cscier &= ~HD64461_PCCCSCIER_BDE;
+       }
+
+       /* set so interrupt occurs when BVD1 & BVD2 are set to bat_warn */
+       if (changed & SS_BATWARN) {
+               if (state->csc_mask & SS_BATWARN)
+                       cscier |= HD64461_PCCCSCIER_BWE;
+               else
+                       cscier &= ~HD64461_PCCCSCIER_BWE;
+       }
+
+       /* set so "pccard connection" interrupt initializes PCC0SCR and PCC0GCR 
*/
+       if (changed & SS_STSCHG) {
+               if (state->csc_mask & SS_STSCHG)
+                       cscier |= HD64461_PCCCSCIER_SCE;
+               else
+                       cscier &= ~HD64461_PCCCSCIER_SCE;
+       }
+
+       ctrl_outb(cscier, cscier_reg);
+
+       changed = sp->state.flags ^ state->flags;
+
+       gcr = ctrl_inb(gcr_reg);
+
+       if (changed & SS_IOCARD) {
+               if (state->flags & SS_IOCARD) {
+                       if (s->sock == 1) {
+                           printk(KERN_INFO
+                                      "socket 1 can be only IC Memory card\n");
+                       } else {
+                               /* Reset the card and set as IO card */
+                               gcr |= HD64461_PCCGCR_PCCT;
+                               sp->IC_memory = 0;
+                       }
+               } else {
+                       /* Reset and set as memory card */
+                       gcr &= ~HD64461_PCCGCR_PCCT;
+                       sp->IC_memory = 1;
+               }
+       }
+
+       /* if bit 3 = 0 while pccard accessed, output 1 on pccreg */
+       if (changed & SS_RESET) {
+               if (state->flags & SS_RESET)
+                       gcr |= HD64461_PCCGCR_PCCR;
+               else
+                       gcr &= ~HD64461_PCCGCR_PCCR;
+       }
+
+       /* Set low level of external buffer */
+       if (changed & SS_OUTPUT_ENA) {
+               if (state->flags & SS_OUTPUT_ENA)
+                       gcr |= HD64461_PCCGCR_DRVE;
+               else
+                       gcr &= ~HD64461_PCCGCR_DRVE;
+       }
+
+       ctrl_outb(gcr, gcr_reg);
+
+       sp->state = *state;
+
+       local_irq_restore(flags);
+
+       return 0;
+}
+
+static int hd64461_set_io_map(struct pcmcia_socket *s, struct pccard_io_map 
*io)
+{
+       /* this is not needed due to static mappings */
+       io->start = 0xba000000;
+       io->stop  = 0xbc000000;
+
+       return 0;
+}
+
+static int hd64461_set_mem_map(struct pcmcia_socket *s,
+                              struct pccard_mem_map *mem)
+{
+       hd64461_socket_t *sp = container_of(s, struct hd64461_socket_t, socket);
+       struct pccard_mem_map *smem;
+       int map = mem->map;
+       unsigned long saddr;
+
+       if (map >= MAX_WIN)
+               return -EINVAL;
+
+       smem = &sp->mem_maps[map];
+       saddr = sp->mem_base + mem->card_start;
+
+       if (!(mem->flags & MAP_ATTRIB))
+               saddr += HD64461_PCC_WINDOW;
+
+       mem->static_start = saddr;
+
+       *smem = *mem;
+
+       return 0;
+}
+
+static struct pccard_operations hd64461_operations = {
+       .init = hd64461_init,
+       .suspend = hd64461_suspend,
+       .get_status = hd64461_get_status,
+       .set_socket = hd64461_set_socket,
+       .set_io_map = hd64461_set_io_map,
+       .set_mem_map = hd64461_set_mem_map,
+};
+
+static irqreturn_t hd64461_interrupt(int irq, void *dev)
+{
+       hd64461_socket_t *sp = (hd64461_socket_t *) dev;
+       unsigned events = 0;
+       unsigned char cscr;
+
+       cscr = ctrl_inb(HD64461_PCC0CSCR);
+
+       /* If IREQ pin is in low state */
+       if (cscr & HD64461_PCCCSCR_IREQ) {
+               cscr &= ~HD64461_PCCCSCR_IREQ;
+               /* silence interrupt source and hand over interrupt */
+               ctrl_outb(cscr, HD64461_PCC0CSCR);
+               return IRQ_NONE;
+       }
+
+       /* if both CD1 and CD2 has changed */
+       if ((cscr & HD64461_PCCCSCR_CDC)) {
+               /* silence it by writing a 0 to bit 3 */
+               cscr &= ~HD64461_PCCCSCR_CDC;
+               /* we've detected something being inserted or unplugged */
+               events |= SS_DETECT;
+
+               /* If card is ejected then cleanup */
+               if (((ctrl_inb(HD64461_PCC0ISR)) & ~HD64461_PCCISR_PCD_MASK)) {
+                       cscr &= ~(HD64461_PCCCSCR_RC | HD64461_PCCCSCR_BW |
+                                 HD64461_PCCCSCR_BD | HD64461_PCCCSCR_SC);
+
+               }
+       }
+
+       /* MEMORY CARD */
+       if (sp->IC_memory) {
+               if (cscr & HD64461_PCCCSCR_RC) {
+                       /* ? */
+                       cscr &= ~HD64461_PCCCSCR_RC;
+                       events |= SS_READY;
+               }
+
+               if (cscr & HD64461_PCCCSCR_BW) {
+                       /* battery warning */
+                       cscr &= ~HD64461_PCCCSCR_BW;
+                       events |= SS_BATWARN;
+               }
+
+               if (cscr & HD64461_PCCCSCR_BD) {
+                       /* battery dead */
+                       cscr &= ~HD64461_PCCCSCR_BD;
+                       events |= SS_BATDEAD;
+               }
+       } else { /* IO CARD */
+               if (cscr & HD64461_PCCCSCR_SC) {
+                       /* status changed */
+                       cscr &= ~HD64461_PCCCSCR_SC;
+                       events |= SS_STSCHG;
+               }
+       }
+       ctrl_outb(cscr, HD64461_PCC0CSCR);
+
+       /* make sure we push these changes into pcmcia events */
+       if (events)
+               pcmcia_parse_events(&sp->socket, events);
+
+       return IRQ_HANDLED;
+}
+
+int hd64461_init_socket(int sock, int irq, unsigned long mem_base,unsigned 
short io_offset)
+{
+       hd64461_socket_t *sp = &hd64461_sockets[sock];
+       unsigned gcr_reg = HD64461_PCC0GCR;
+       u8 gcr;
+       int i;
+
+       memset(sp, 0, sizeof(*sp));
+       sp->IC_memory = 1;
+       sp->irq = irq;
+       sp->mem_base = mem_base;
+       sp->socket.features = SS_CAP_PCCARD | SS_CAP_STATIC_MAP | 
SS_CAP_PAGE_REGS;
+       sp->socket.resource_ops = &pccard_static_ops;
+       sp->socket.ops = &hd64461_operations;
+       sp->socket.map_size = HD64461_PCC_WINDOW;       /* 16MB fixed window 
size */
+       sp->socket.pci_irq = irq;
+       sp->socket.io_offset = io_offset;
+       sp->socket.owner = THIS_MODULE;
+
+       for (i = 0; i != MAX_WIN; i++)
+               sp->mem_maps[i].map = i;
+
+       if ((request_irq(irq, hd64461_interrupt, IRQF_SHARED, "hd64461_ss-irq", 
sp)) < 0) {
+               printk(KERN_INFO "hd64461_init: request for irq %d: failed\n", 
sp->irq);
+               return -1;
+       }
+
+       gcr = inb(gcr_reg);
+       /* continuous 16MB area mode for both memory and I/O operations */
+       gcr |= HD64461_PCCGCR_PMMOD;
+       /* ??? */
+       gcr &= ~(HD64461_PCCGCR_PA25 | HD64461_PCCGCR_PA24);
+       outb(gcr, gcr_reg);
+
+       return 0;
+}
+
+void hd64461_exit_socket(int sock)
+{
+       hd64461_socket_t *sp = &hd64461_sockets[0];
+       unsigned cscier_reg = HD64461_PCC0CSCIER;
+
+       outb(0, cscier_reg);
+       hd64461_suspend(&sp->socket);
+
+}
+
+static int __devexit hd64461_pcmcia_drv_remove(struct platform_device *dev)
+{
+       /* Libpata handles CF slot (slot 1), so we only handle PCMCIA (slot 0) 
*/
+       pcmcia_unregister_socket(&hd64461_sockets[0].socket);
+       hd64461_exit_socket(0);
+
+       return 0;
+}
+
+#ifdef CONFIG_PM
+static int hd64461_pcmcia_drv_suspend(struct platform_device *dev, 
pm_message_t state)
+{
+       int ret = 0;
+       u32 cscier_reg = HD64461_PCC0CSCIER;
+
+       hd64461_sockets[0].cscier = inb(cscier_reg);
+       outb(0, cscier_reg);
+       ret = pcmcia_socket_dev_suspend(&dev->dev, state);
+
+       return ret;
+ }
+
+static int hd64461_pcmcia_drv_resume(struct platform_device *dev)
+{
+       int ret = 0;
+       u32 cscier_reg = HD64461_PCC0CSCIER;
+
+       outb(hd64461_sockets[0].cscier, cscier_reg);
+       ret = pcmcia_socket_dev_resume(&dev->dev);
+
+       return ret;
+}
+#endif
+
+static struct platform_driver hd64461_pcmcia_driver = {
+       .remove = __devexit_p(hd64461_pcmcia_drv_remove),
+#ifdef CONFIG_PM
+       .suspend = hd64461_pcmcia_drv_suspend,
+       .resume = hd64461_pcmcia_drv_resume,
+#endif
+       .driver         = {
+               .name   = "hd64461-pcmcia",
+       },
+};
+
+static struct platform_device *hd64461_pcmcia_device;
+
+static int __init init_hd64461_ss(void)
+{
+       int i;
+
+       printk(KERN_INFO "hd64461 host bridge driver\n");
+
+       if (platform_driver_register(&hd64461_pcmcia_driver))
+               return -ENODEV;
+
+       i = hd64461_init_socket(0, HD64461_IRQ_PCC0, HD64461_PCC0_BASE, 0xf000);
+       if (i < 0)
+               goto failed2;
+
+       hd64461_pcmcia_device = platform_device_alloc("hd64461-pcmcia",-1);
+       if(!hd64461_pcmcia_device) {
+               printk(KERN_INFO "hd64461_ss_init: Cannot find pcmcia host 
device!\n");
+               return -ENODEV;
+       }
+
+       i = platform_device_add(hd64461_pcmcia_device);
+       if (i) {
+               platform_device_put(hd64461_pcmcia_device);
+               goto failed2;
+       }
+
+       hd64461_sockets[0].socket.dev.parent = &hd64461_pcmcia_device->dev;
+
+       i = pcmcia_register_socket(&hd64461_sockets[0].socket);
+       return 0;
+
+/* Unregister driver nothing else */
+failed2:
+       printk(KERN_INFO "hd64461_ss_init: Failed to startup socket 0\n");
+       platform_driver_unregister(&hd64461_pcmcia_driver);
+       return i;
+}
+
+static void __exit exit_hd64461_ss(void)
+{
+       /* Only remove if there's something to remove */
+       if (hd64461_pcmcia_device) {
+               platform_device_unregister(hd64461_pcmcia_device);
+               platform_driver_unregister(&hd64461_pcmcia_driver);
+       }
+}
+module_init(init_hd64461_ss);
+module_exit(exit_hd64461_ss);
+
+MODULE_AUTHOR("Kristoffer Ericson <[EMAIL PROTECTED]>");
+MODULE_DESCRIPTION("PCMCIA driver for Hitachi HD64461 companion chip");
+MODULE_LICENSE("GPL");



Best wishes
Kristoffer



-- 
Kristoffer Ericson <[EMAIL PROTECTED]>

Attachment: hd64461_pcmcia.patch
Description: Binary data

_______________________________________________
Linux PCMCIA reimplementation list
http://lists.infradead.org/mailman/listinfo/linux-pcmcia

Reply via email to