]> pilppa.org Git - linux-2.6-omap-h63xx.git/blobdiff - drivers/mmc/wbsd.c
Merge git://oss.sgi.com:8090/oss/git/xfs-2.6
[linux-2.6-omap-h63xx.git] / drivers / mmc / wbsd.c
index b7fbd30b49a0de18d1b05cf30bcfe62c0f64efac..dec01d38c782fce5935ffb575ca90122934260e7 100644 (file)
@@ -42,7 +42,7 @@
 #include "wbsd.h"
 
 #define DRIVER_NAME "wbsd"
-#define DRIVER_VERSION "1.2"
+#define DRIVER_VERSION "1.4"
 
 #ifdef CONFIG_MMC_DEBUG
 #define DBG(x...) \
 #define DBGF(x...)     do { } while (0)
 #endif
 
-#ifdef CONFIG_MMC_DEBUG
-void DBG_REG(int reg, u8 value)
-{
-       int i;
-       
-       printk(KERN_DEBUG "wbsd: Register %d: 0x%02X %3d '%c' ",
-               reg, (int)value, (int)value, (value < 0x20)?'.':value);
-       
-       for (i = 7;i >= 0;i--)
-       {
-               if (value & (1 << i))
-                       printk("x");
-               else
-                       printk(".");
-       }
-       
-       printk("\n");
-}
-#else
-#define DBG_REG(r, v) do {}  while (0)
-#endif
-
 /*
  * Device resources
  */
@@ -92,6 +70,13 @@ MODULE_DEVICE_TABLE(pnp, pnp_dev_table);
 
 #endif /* CONFIG_PNP */
 
+static const int config_ports[] = { 0x2E, 0x4E };
+static const int unlock_codes[] = { 0x83, 0x87 };
+
+static const int valid_ids[] = {
+       0x7112,
+       };
+
 #ifdef CONFIG_PNP
 static unsigned int nopnp = 0;
 #else
@@ -735,11 +720,28 @@ static void wbsd_prepare_data(struct wbsd_host* host, struct mmc_data* data)
         * calculate CRC.
         *
         * Space for CRC must be included in the size.
+        * Two bytes are needed for each data line.
         */
-       blksize = (1 << data->blksz_bits) + 2;
+       if (host->bus_width == MMC_BUS_WIDTH_1)
+       {
+               blksize = (1 << data->blksz_bits) + 2;
+
+               wbsd_write_index(host, WBSD_IDX_PBSMSB, (blksize >> 4) & 0xF0);
+               wbsd_write_index(host, WBSD_IDX_PBSLSB, blksize & 0xFF);
+       }
+       else if (host->bus_width == MMC_BUS_WIDTH_4)
+       {
+               blksize = (1 << data->blksz_bits) + 2 * 4;
        
-       wbsd_write_index(host, WBSD_IDX_PBSMSB, (blksize >> 4) & 0xF0);
-       wbsd_write_index(host, WBSD_IDX_PBSLSB, blksize & 0xFF);
+               wbsd_write_index(host, WBSD_IDX_PBSMSB, ((blksize >> 4) & 0xF0)
+                       | WBSD_DATA_WIDTH);
+               wbsd_write_index(host, WBSD_IDX_PBSLSB, blksize & 0xFF);
+       }
+       else
+       {
+               data->error = MMC_ERR_INVALID;
+               return;
+       }
 
        /*
         * Clear the FIFO. This is needed even for DMA
@@ -975,8 +977,9 @@ static void wbsd_set_ios(struct mmc_host* mmc, struct mmc_ios* ios)
        struct wbsd_host* host = mmc_priv(mmc);
        u8 clk, setup, pwr;
        
-       DBGF("clock %uHz busmode %u powermode %u Vdd %u\n",
-               ios->clock, ios->bus_mode, ios->power_mode, ios->vdd);
+       DBGF("clock %uHz busmode %u powermode %u cs %u Vdd %u width %u\n",
+            ios->clock, ios->bus_mode, ios->power_mode, ios->chip_select,
+            ios->vdd, ios->bus_width);
 
        spin_lock_bh(&host->lock);
 
@@ -1018,30 +1021,63 @@ static void wbsd_set_ios(struct mmc_host* mmc, struct mmc_ios* ios)
 
        /*
         * MMC cards need to have pin 1 high during init.
-        * Init time corresponds rather nicely with the bus mode.
         * It wreaks havoc with the card detection though so
-        * that needs to be disabed.
+        * that needs to be disabled.
         */
        setup = wbsd_read_index(host, WBSD_IDX_SETUP);
-       if ((ios->power_mode == MMC_POWER_ON) &&
-               (ios->bus_mode == MMC_BUSMODE_OPENDRAIN))
+       if (ios->chip_select == MMC_CS_HIGH)
        {
+               BUG_ON(ios->bus_width != MMC_BUS_WIDTH_1);
                setup |= WBSD_DAT3_H;
                host->flags |= WBSD_FIGNORE_DETECT;
        }
        else
        {
                setup &= ~WBSD_DAT3_H;
-               host->flags &= ~WBSD_FIGNORE_DETECT;
+
+               /*
+                * We cannot resume card detection immediatly
+                * because of capacitance and delays in the chip.
+                */
+               mod_timer(&host->ignore_timer, jiffies + HZ/100);
        }
        wbsd_write_index(host, WBSD_IDX_SETUP, setup);
        
+       /*
+        * Store bus width for later. Will be used when
+        * setting up the data transfer.
+        */
+       host->bus_width = ios->bus_width;
+
+       spin_unlock_bh(&host->lock);
+}
+
+static int wbsd_get_ro(struct mmc_host* mmc)
+{
+       struct wbsd_host* host = mmc_priv(mmc);
+       u8 csr;
+
+       spin_lock_bh(&host->lock);
+
+       csr = inb(host->base + WBSD_CSR);
+       csr |= WBSD_MSLED;
+       outb(csr, host->base + WBSD_CSR);
+
+       mdelay(1);
+
+       csr = inb(host->base + WBSD_CSR);
+       csr &= ~WBSD_MSLED;
+       outb(csr, host->base + WBSD_CSR);
+
        spin_unlock_bh(&host->lock);
+
+       return csr & WBSD_WRPT;
 }
 
 static struct mmc_host_ops wbsd_ops = {
        .request        = wbsd_request,
        .set_ios        = wbsd_set_ios,
+       .get_ro         = wbsd_get_ro,
 };
 
 /*****************************************************************************\
@@ -1050,11 +1086,50 @@ static struct mmc_host_ops wbsd_ops = {
  *                                                                           *
 \*****************************************************************************/
 
+/*
+ * Helper function to reset detection ignore
+ */
+
+static void wbsd_reset_ignore(unsigned long data)
+{
+       struct wbsd_host *host = (struct wbsd_host*)data;
+
+       BUG_ON(host == NULL);
+
+       DBG("Resetting card detection ignore\n");
+
+       spin_lock_bh(&host->lock);
+
+       host->flags &= ~WBSD_FIGNORE_DETECT;
+
+       /*
+        * Card status might have changed during the
+        * blackout.
+        */
+       tasklet_schedule(&host->card_tasklet);
+
+       spin_unlock_bh(&host->lock);
+}
+
+/*
+ * Helper function for card detection
+ */
+static void wbsd_detect_card(unsigned long data)
+{
+       struct wbsd_host *host = (struct wbsd_host*)data;
+       
+       BUG_ON(host == NULL);
+       
+       DBG("Executing card detection\n");
+       
+       mmc_detect_change(host->mmc);   
+}
+
 /*
  * Tasklets
  */
 
-inline static struct mmc_data* wbsd_get_data(struct wbsd_host* host)
+static inline struct mmc_data* wbsd_get_data(struct wbsd_host* host)
 {
        WARN_ON(!host->mrq);
        if (!host->mrq)
@@ -1075,7 +1150,6 @@ static void wbsd_tasklet_card(unsigned long param)
 {
        struct wbsd_host* host = (struct wbsd_host*)param;
        u8 csr;
-       int change = 0;
        
        spin_lock(&host->lock);
        
@@ -1094,14 +1168,20 @@ static void wbsd_tasklet_card(unsigned long param)
                {
                        DBG("Card inserted\n");
                        host->flags |= WBSD_FCARD_PRESENT;
-                       change = 1;
+                       
+                       /*
+                        * Delay card detection to allow electrical connections
+                        * to stabilise.
+                        */
+                       mod_timer(&host->detect_timer, jiffies + HZ/2);
                }
+               
+               spin_unlock(&host->lock);
        }
        else if (host->flags & WBSD_FCARD_PRESENT)
        {
                DBG("Card removed\n");
                host->flags &= ~WBSD_FCARD_PRESENT;
-               change = 1;
                
                if (host->mrq)
                {
@@ -1112,15 +1192,16 @@ static void wbsd_tasklet_card(unsigned long param)
                        host->mrq->cmd->error = MMC_ERR_FAILED;
                        tasklet_schedule(&host->finish_tasklet);
                }
-       }
-       
-       /*
-        * Unlock first since we might get a call back.
-        */
-       spin_unlock(&host->lock);
+               
+               /*
+                * Unlock first since we might get a call back.
+                */
+               spin_unlock(&host->lock);
 
-       if (change)
                mmc_detect_change(host->mmc);
+       }
+       else
+               spin_unlock(&host->lock);
 }
 
 static void wbsd_tasklet_fifo(unsigned long param)
@@ -1321,9 +1402,21 @@ static int __devinit wbsd_alloc_mmc(struct device* dev)
        mmc->f_min = 375000;
        mmc->f_max = 24000000;
        mmc->ocr_avail = MMC_VDD_32_33|MMC_VDD_33_34;
+       mmc->caps = MMC_CAP_4_BIT_DATA;
        
        spin_lock_init(&host->lock);
        
+       /*
+        * Set up timers
+        */
+       init_timer(&host->detect_timer);
+       host->detect_timer.data = (unsigned long)host;
+       host->detect_timer.function = wbsd_detect_card;
+
+       init_timer(&host->ignore_timer);
+       host->ignore_timer.data = (unsigned long)host;
+       host->ignore_timer.function = wbsd_reset_ignore;
+       
        /*
         * Maximum number of segments. Worst case is one sector per segment
         * so this will be 64kB/512.
@@ -1351,11 +1444,18 @@ static int __devinit wbsd_alloc_mmc(struct device* dev)
 static void __devexit wbsd_free_mmc(struct device* dev)
 {
        struct mmc_host* mmc;
+       struct wbsd_host* host;
        
        mmc = dev_get_drvdata(dev);
        if (!mmc)
                return;
        
+       host = mmc_priv(mmc);
+       BUG_ON(host == NULL);
+       
+       del_timer_sync(&host->ignore_timer);
+       del_timer_sync(&host->detect_timer);
+       
        mmc_free_host(mmc);
        
        dev_set_drvdata(dev, NULL);
@@ -1780,7 +1880,7 @@ static int __devinit wbsd_init(struct device* dev, int base, int irq, int dma,
        
        mmc_add_host(mmc);
 
-       printk(KERN_INFO "%s: W83L51xD", mmc->host_name);
+       printk(KERN_INFO "%s: W83L51xD", mmc_hostname(mmc));
        if (host->chip_id != 0)
                printk(" id %x", (int)host->chip_id);
        printk(" at 0x%x irq %d", (int)host->base, (int)host->irq);