mg_disk: fix dependency on libata
[platform/kernel/linux-starfive.git] / drivers / block / mg_disk.c
index fb39d9a..8e53cae 100644 (file)
 #include <linux/fs.h>
 #include <linux/blkdev.h>
 #include <linux/hdreg.h>
-#include <linux/libata.h>
+#include <linux/ata.h>
 #include <linux/interrupt.h>
 #include <linux/delay.h>
 #include <linux/platform_device.h>
 #include <linux/gpio.h>
-#include <linux/mg_disk.h>
 
 #define MG_RES_SEC (CONFIG_MG_DISK_RES << 1)
 
+/* name for block device */
+#define MG_DISK_NAME "mgd"
+/* name for platform device */
+#define MG_DEV_NAME "mg_disk"
+
+#define MG_DISK_MAJ 0
+#define MG_DISK_MAX_PART 16
+#define MG_SECTOR_SIZE 512
+#define MG_MAX_SECTS 256
+
+/* Register offsets */
+#define MG_BUFF_OFFSET                 0x8000
+#define MG_STORAGE_BUFFER_SIZE         0x200
+#define MG_REG_OFFSET                  0xC000
+#define MG_REG_FEATURE                 (MG_REG_OFFSET + 2)     /* write case */
+#define MG_REG_ERROR                   (MG_REG_OFFSET + 2)     /* read case */
+#define MG_REG_SECT_CNT                        (MG_REG_OFFSET + 4)
+#define MG_REG_SECT_NUM                        (MG_REG_OFFSET + 6)
+#define MG_REG_CYL_LOW                 (MG_REG_OFFSET + 8)
+#define MG_REG_CYL_HIGH                        (MG_REG_OFFSET + 0xA)
+#define MG_REG_DRV_HEAD                        (MG_REG_OFFSET + 0xC)
+#define MG_REG_COMMAND                 (MG_REG_OFFSET + 0xE)   /* write case */
+#define MG_REG_STATUS                  (MG_REG_OFFSET + 0xE)   /* read  case */
+#define MG_REG_DRV_CTRL                        (MG_REG_OFFSET + 0x10)
+#define MG_REG_BURST_CTRL              (MG_REG_OFFSET + 0x12)
+
+/* "Drive Select/Head Register" bit values */
+#define MG_REG_HEAD_MUST_BE_ON         0xA0 /* These 2 bits are always on */
+#define MG_REG_HEAD_DRIVE_MASTER       (0x00 | MG_REG_HEAD_MUST_BE_ON)
+#define MG_REG_HEAD_DRIVE_SLAVE                (0x10 | MG_REG_HEAD_MUST_BE_ON)
+#define MG_REG_HEAD_LBA_MODE           (0x40 | MG_REG_HEAD_MUST_BE_ON)
+
+
+/* "Device Control Register" bit values */
+#define MG_REG_CTRL_INTR_ENABLE                        0x0
+#define MG_REG_CTRL_INTR_DISABLE               (0x1<<1)
+#define MG_REG_CTRL_RESET                      (0x1<<2)
+#define MG_REG_CTRL_INTR_POLA_ACTIVE_HIGH      0x0
+#define MG_REG_CTRL_INTR_POLA_ACTIVE_LOW       (0x1<<4)
+#define MG_REG_CTRL_DPD_POLA_ACTIVE_LOW                0x0
+#define MG_REG_CTRL_DPD_POLA_ACTIVE_HIGH       (0x1<<5)
+#define MG_REG_CTRL_DPD_DISABLE                        0x0
+#define MG_REG_CTRL_DPD_ENABLE                 (0x1<<6)
+
+/* Status register bit */
+/* error bit in status register */
+#define MG_REG_STATUS_BIT_ERROR                        0x01
+/* corrected error in status register */
+#define MG_REG_STATUS_BIT_CORRECTED_ERROR      0x04
+/* data request bit in status register */
+#define MG_REG_STATUS_BIT_DATA_REQ             0x08
+/* DSC - Drive Seek Complete */
+#define MG_REG_STATUS_BIT_SEEK_DONE            0x10
+/* DWF - Drive Write Fault */
+#define MG_REG_STATUS_BIT_WRITE_FAULT          0x20
+#define MG_REG_STATUS_BIT_READY                        0x40
+#define MG_REG_STATUS_BIT_BUSY                 0x80
+
+/* handy status */
+#define MG_STAT_READY  (MG_REG_STATUS_BIT_READY | MG_REG_STATUS_BIT_SEEK_DONE)
+#define MG_READY_OK(s) (((s) & (MG_STAT_READY | \
+                               (MG_REG_STATUS_BIT_BUSY | \
+                                MG_REG_STATUS_BIT_WRITE_FAULT | \
+                                MG_REG_STATUS_BIT_ERROR))) == MG_STAT_READY)
+
+/* Error register */
+#define MG_REG_ERR_AMNF                0x01
+#define MG_REG_ERR_ABRT                0x04
+#define MG_REG_ERR_IDNF                0x10
+#define MG_REG_ERR_UNC         0x40
+#define MG_REG_ERR_BBK         0x80
+
+/* error code for others */
+#define MG_ERR_NONE            0
+#define MG_ERR_TIMEOUT         0x100
+#define MG_ERR_INIT_STAT       0x101
+#define MG_ERR_TRANSLATION     0x102
+#define MG_ERR_CTRL_RST                0x103
+#define MG_ERR_INV_STAT                0x104
+#define MG_ERR_RSTOUT          0x105
+
+#define MG_MAX_ERRORS  6       /* Max read/write errors */
+
+/* command */
+#define MG_CMD_RD 0x20
+#define MG_CMD_WR 0x30
+#define MG_CMD_SLEEP 0x99
+#define MG_CMD_WAKEUP 0xC3
+#define MG_CMD_ID 0xEC
+#define MG_CMD_WR_CONF 0x3C
+#define MG_CMD_RD_CONF 0x40
+
+/* operation mode */
+#define MG_OP_CASCADE (1 << 0)
+#define MG_OP_CASCADE_SYNC_RD (1 << 1)
+#define MG_OP_CASCADE_SYNC_WR (1 << 2)
+#define MG_OP_INTERLEAVE (1 << 3)
+
+/* synchronous */
+#define MG_BURST_LAT_4 (3 << 4)
+#define MG_BURST_LAT_5 (4 << 4)
+#define MG_BURST_LAT_6 (5 << 4)
+#define MG_BURST_LAT_7 (6 << 4)
+#define MG_BURST_LAT_8 (7 << 4)
+#define MG_BURST_LEN_4 (1 << 1)
+#define MG_BURST_LEN_8 (2 << 1)
+#define MG_BURST_LEN_16 (3 << 1)
+#define MG_BURST_LEN_32 (4 << 1)
+#define MG_BURST_LEN_CONT (0 << 1)
+
+/* timeout value (unit: ms) */
+#define MG_TMAX_CONF_TO_CMD    1
+#define MG_TMAX_WAIT_RD_DRQ    10
+#define MG_TMAX_WAIT_WR_DRQ    500
+#define MG_TMAX_RST_TO_BUSY    10
+#define MG_TMAX_HDRST_TO_RDY   500
+#define MG_TMAX_SWRST_TO_RDY   500
+#define MG_TMAX_RSTOUT         3000
+
+/* device attribution */
+/* use mflash as boot device */
+#define MG_BOOT_DEV            (1 << 0)
+/* use mflash as storage device */
+#define MG_STORAGE_DEV         (1 << 1)
+/* same as MG_STORAGE_DEV, but bootloader already done reset sequence */
+#define MG_STORAGE_DEV_SKIP_RST        (1 << 2)
+
+#define MG_DEV_MASK (MG_BOOT_DEV | MG_STORAGE_DEV | MG_STORAGE_DEV_SKIP_RST)
+
+/* names of GPIO resource */
+#define MG_RST_PIN     "mg_rst"
+/* except MG_BOOT_DEV, reset-out pin should be assigned */
+#define MG_RSTOUT_PIN  "mg_rstout"
+
+/* private driver data */
+struct mg_drv_data {
+       /* disk resource */
+       u32 use_polling;
+
+       /* device attribution */
+       u32 dev_attr;
+
+       /* internally used */
+       struct mg_host *host;
+};
+
+/* main structure for mflash driver */
+struct mg_host {
+       struct device *dev;
+
+       struct request_queue *breq;
+       spinlock_t lock;
+       struct gendisk *gd;
+
+       struct timer_list timer;
+       void (*mg_do_intr) (struct mg_host *);
+
+       u16 id[ATA_ID_WORDS];
+
+       u16 cyls;
+       u16 heads;
+       u16 sectors;
+       u32 n_sectors;
+       u32 nres_sectors;
+
+       void __iomem *dev_base;
+       unsigned int irq;
+       unsigned int rst;
+       unsigned int rstout;
+
+       u32 major;
+       u32 error;
+};
+
+/*
+ * Debugging macro and defines
+ */
+#undef DO_MG_DEBUG
+#ifdef DO_MG_DEBUG
+#  define MG_DBG(fmt, args...) \
+       printk(KERN_DEBUG "%s:%d "fmt, __func__, __LINE__, ##args)
+#else /* CONFIG_MG_DEBUG */
+#  define MG_DBG(fmt, args...) do { } while (0)
+#endif /* CONFIG_MG_DEBUG */
+
 static void mg_request(struct request_queue *);
 
 static void mg_dump_status(const char *msg, unsigned int stat,
@@ -79,7 +263,7 @@ static void mg_dump_status(const char *msg, unsigned int stat,
                        if (host->breq) {
                                req = elv_next_request(host->breq);
                                if (req)
-                                       printk(", sector=%ld", req->sector);
+                                       printk(", sector=%u", (u32)req->sector);
                        }
 
                }
@@ -160,14 +344,55 @@ static irqreturn_t mg_irq(int irq, void *dev_id)
        struct mg_host *host = dev_id;
        void (*handler)(struct mg_host *) = host->mg_do_intr;
 
-       host->mg_do_intr = 0;
+       spin_lock(&host->lock);
+
+       host->mg_do_intr = NULL;
        del_timer(&host->timer);
        if (!handler)
                handler = mg_unexpected_intr;
        handler(host);
+
+       spin_unlock(&host->lock);
+
        return IRQ_HANDLED;
 }
 
+/* local copy of ata_id_string() */
+static void mg_id_string(const u16 *id, unsigned char *s,
+                        unsigned int ofs, unsigned int len)
+{
+       unsigned int c;
+
+       BUG_ON(len & 1);
+
+       while (len > 0) {
+               c = id[ofs] >> 8;
+               *s = c;
+               s++;
+
+               c = id[ofs] & 0xff;
+               *s = c;
+               s++;
+
+               ofs++;
+               len -= 2;
+       }
+}
+
+/* local copy of ata_id_c_string() */
+static void mg_id_c_string(const u16 *id, unsigned char *s,
+                          unsigned int ofs, unsigned int len)
+{
+       unsigned char *p;
+
+       mg_id_string(id, s, ofs, len - 1);
+
+       p = s + strnlen(s, len - 1);
+       while (p > s && p[-1] == ' ')
+               p--;
+       *p = '\0';
+}
+
 static int mg_get_disk_id(struct mg_host *host)
 {
        u32 i;
@@ -214,9 +439,9 @@ static int mg_get_disk_id(struct mg_host *host)
                host->n_sectors -= host->nres_sectors;
        }
 
-       ata_id_c_string(id, fwrev, ATA_ID_FW_REV, sizeof(fwrev));
-       ata_id_c_string(id, model, ATA_ID_PROD, sizeof(model));
-       ata_id_c_string(id, serial, ATA_ID_SERNO, sizeof(serial));
+       mg_id_c_string(id, fwrev, ATA_ID_FW_REV, sizeof(fwrev));
+       mg_id_c_string(id, model, ATA_ID_PROD, sizeof(model));
+       mg_id_c_string(id, serial, ATA_ID_SERNO, sizeof(serial));
        printk(KERN_INFO "mg_disk: model: %s\n", model);
        printk(KERN_INFO "mg_disk: firm: %.8s\n", fwrev);
        printk(KERN_INFO "mg_disk: serial: %s\n", serial);
@@ -280,7 +505,7 @@ static void mg_bad_rw_intr(struct mg_host *host)
        if (req != NULL)
                if (++req->errors >= MG_MAX_ERRORS ||
                                host->error == MG_ERR_TIMEOUT)
-                       end_request(req, 0);
+                       __blk_end_request_cur(req, -EIO);
 }
 
 static unsigned int mg_out(struct mg_host *host,
@@ -314,95 +539,68 @@ static unsigned int mg_out(struct mg_host *host,
 
 static void mg_read(struct request *req)
 {
-       u32 remains, j;
+       u32 j;
        struct mg_host *host = req->rq_disk->private_data;
 
-       remains = req->nr_sectors;
-
-       if (mg_out(host, req->sector, req->nr_sectors, MG_CMD_RD, 0) !=
+       if (mg_out(host, req->sector, req->nr_sectors, MG_CMD_RD, NULL) !=
                        MG_ERR_NONE)
                mg_bad_rw_intr(host);
 
        MG_DBG("requested %d sects (from %ld), buffer=0x%p\n",
-                       remains, req->sector, req->buffer);
+              req->nr_sectors, req->sector, req->buffer);
+
+       do {
+               u16 *buff = (u16 *)req->buffer;
 
-       while (remains) {
                if (mg_wait(host, MG_REG_STATUS_BIT_DATA_REQ,
                                        MG_TMAX_WAIT_RD_DRQ) != MG_ERR_NONE) {
                        mg_bad_rw_intr(host);
                        return;
                }
-               for (j = 0; j < MG_SECTOR_SIZE >> 1; j++) {
-                       *(u16 *)req->buffer =
-                               inw((unsigned long)host->dev_base +
-                                               MG_BUFF_OFFSET + (j << 1));
-                       req->buffer += 2;
-               }
-
-               req->sector++;
-               req->errors = 0;
-               remains = --req->nr_sectors;
-               --req->current_nr_sectors;
-
-               if (req->current_nr_sectors <= 0) {
-                       MG_DBG("remain : %d sects\n", remains);
-                       end_request(req, 1);
-                       if (remains > 0)
-                               req = elv_next_request(host->breq);
-               }
+               for (j = 0; j < MG_SECTOR_SIZE >> 1; j++)
+                       *buff++ = inw((unsigned long)host->dev_base +
+                                     MG_BUFF_OFFSET + (j << 1));
 
                outb(MG_CMD_RD_CONF, (unsigned long)host->dev_base +
                                MG_REG_COMMAND);
-       }
+       } while (__blk_end_request(req, 0, MG_SECTOR_SIZE));
 }
 
 static void mg_write(struct request *req)
 {
-       u32 remains, j;
+       u32 j;
        struct mg_host *host = req->rq_disk->private_data;
 
-       remains = req->nr_sectors;
-
-       if (mg_out(host, req->sector, req->nr_sectors, MG_CMD_WR, 0) !=
+       if (mg_out(host, req->sector, req->nr_sectors, MG_CMD_WR, NULL) !=
                        MG_ERR_NONE) {
                mg_bad_rw_intr(host);
                return;
        }
 
-
        MG_DBG("requested %d sects (from %ld), buffer=0x%p\n",
-                       remains, req->sector, req->buffer);
-       while (remains) {
+              req->nr_sectors, req->sector, req->buffer);
+
+       do {
+               u16 *buff = (u16 *)req->buffer;
+
                if (mg_wait(host, MG_REG_STATUS_BIT_DATA_REQ,
                                        MG_TMAX_WAIT_WR_DRQ) != MG_ERR_NONE) {
                        mg_bad_rw_intr(host);
                        return;
                }
-               for (j = 0; j < MG_SECTOR_SIZE >> 1; j++) {
-                       outw(*(u16 *)req->buffer,
-                                       (unsigned long)host->dev_base +
-                                       MG_BUFF_OFFSET + (j << 1));
-                       req->buffer += 2;
-               }
-               req->sector++;
-               remains = --req->nr_sectors;
-               --req->current_nr_sectors;
-
-               if (req->current_nr_sectors <= 0) {
-                       MG_DBG("remain : %d sects\n", remains);
-                       end_request(req, 1);
-                       if (remains > 0)
-                               req = elv_next_request(host->breq);
-               }
+               for (j = 0; j < MG_SECTOR_SIZE >> 1; j++)
+                       outw(*buff++, (unsigned long)host->dev_base +
+                                     MG_BUFF_OFFSET + (j << 1));
 
                outb(MG_CMD_WR_CONF, (unsigned long)host->dev_base +
                                MG_REG_COMMAND);
-       }
+       } while (__blk_end_request(req, 0, MG_SECTOR_SIZE));
 }
 
 static void mg_read_intr(struct mg_host *host)
 {
        u32 i;
+       u16 *buff;
        struct request *req;
 
        /* check status */
@@ -423,39 +621,24 @@ static void mg_read_intr(struct mg_host *host)
 ok_to_read:
        /* get current segment of request */
        req = elv_next_request(host->breq);
+       buff = (u16 *)req->buffer;
 
        /* read 1 sector */
-       for (i = 0; i < MG_SECTOR_SIZE >> 1; i++) {
-               *(u16 *)req->buffer =
-                       inw((unsigned long)host->dev_base + MG_BUFF_OFFSET +
-                                       (i << 1));
-               req->buffer += 2;
-       }
+       for (i = 0; i < MG_SECTOR_SIZE >> 1; i++)
+               *buff++ = inw((unsigned long)host->dev_base + MG_BUFF_OFFSET +
+                             (i << 1));
 
-       /* manipulate request */
        MG_DBG("sector %ld, remaining=%ld, buffer=0x%p\n",
                        req->sector, req->nr_sectors - 1, req->buffer);
 
-       req->sector++;
-       req->errors = 0;
-       i = --req->nr_sectors;
-       --req->current_nr_sectors;
-
-       /* let know if current segment done */
-       if (req->current_nr_sectors <= 0)
-               end_request(req, 1);
-
-       /* set handler if read remains */
-       if (i > 0) {
-               host->mg_do_intr = mg_read_intr;
-               mod_timer(&host->timer, jiffies + 3 * HZ);
-       }
-
        /* send read confirm */
        outb(MG_CMD_RD_CONF, (unsigned long)host->dev_base + MG_REG_COMMAND);
 
-       /* goto next request */
-       if (!i)
+       if (__blk_end_request(req, 0, MG_SECTOR_SIZE)) {
+               /* set handler if read remains */
+               host->mg_do_intr = mg_read_intr;
+               mod_timer(&host->timer, jiffies + 3 * HZ);
+       } else /* goto next request */
                mg_request(host->breq);
 }
 
@@ -464,6 +647,7 @@ static void mg_write_intr(struct mg_host *host)
        u32 i, j;
        u16 *buff;
        struct request *req;
+       bool rem;
 
        /* get current segment of request */
        req = elv_next_request(host->breq);
@@ -484,18 +668,8 @@ static void mg_write_intr(struct mg_host *host)
        return;
 
 ok_to_write:
-       /* manipulate request */
-       req->sector++;
-       i = --req->nr_sectors;
-       --req->current_nr_sectors;
-       req->buffer += MG_SECTOR_SIZE;
-
-       /* let know if current segment or all done */
-       if (!i || (req->bio && req->current_nr_sectors <= 0))
-               end_request(req, 1);
-
-       /* write 1 sector and set handler if remains */
-       if (i > 0) {
+       if ((rem = __blk_end_request(req, 0, MG_SECTOR_SIZE))) {
+               /* write 1 sector and set handler if remains */
                buff = (u16 *)req->buffer;
                for (j = 0; j < MG_STORAGE_BUFFER_SIZE >> 1; j++) {
                        outw(*buff, (unsigned long)host->dev_base +
@@ -511,7 +685,7 @@ ok_to_write:
        /* send write confirm */
        outb(MG_CMD_WR_CONF, (unsigned long)host->dev_base + MG_REG_COMMAND);
 
-       if (!i)
+       if (!rem)
                mg_request(host->breq);
 }
 
@@ -521,9 +695,11 @@ void mg_times_out(unsigned long data)
        char *name;
        struct request *req;
 
+       spin_lock_irq(&host->lock);
+
        req = elv_next_request(host->breq);
        if (!req)
-               return;
+               goto out_unlock;
 
        host->mg_do_intr = NULL;
 
@@ -534,6 +710,8 @@ void mg_times_out(unsigned long data)
        mg_bad_rw_intr(host);
 
        mg_request(host->breq);
+out_unlock:
+       spin_unlock_irq(&host->lock);
 }
 
 static void mg_request_poll(struct request_queue *q)
@@ -551,11 +729,6 @@ static void mg_request_poll(struct request_queue *q)
                        case WRITE:
                                mg_write(req);
                                break;
-                       default:
-                               printk(KERN_WARNING "%s:%d unknown command\n",
-                                               __func__, __LINE__);
-                               end_request(req, 0);
-                               break;
                        }
                }
        }
@@ -605,11 +778,6 @@ static unsigned int mg_issue_req(struct request *req,
                outb(MG_CMD_WR_CONF, (unsigned long)host->dev_base +
                                MG_REG_COMMAND);
                break;
-       default:
-               printk(KERN_WARNING "%s:%d unknown command\n",
-                               __func__, __LINE__);
-               end_request(req, 0);
-               break;
        }
        return MG_ERR_NONE;
 }
@@ -646,7 +814,7 @@ static void mg_request(struct request_queue *q)
                                        "%s: bad access: sector=%d, count=%d\n",
                                        req->rq_disk->disk_name,
                                        sect_num, sect_cnt);
-                       end_request(req, 0);
+                       __blk_end_request_cur(req, -EIO);
                        continue;
                }