xen/blkback: Make optional features be really optional.
[linux-2.6.git] / drivers / block / xsysace.c
index 732ec63..1a17e33 100644 (file)
 #include <linux/delay.h>
 #include <linux/slab.h>
 #include <linux/blkdev.h>
+#include <linux/mutex.h>
+#include <linux/ata.h>
 #include <linux/hdreg.h>
 #include <linux/platform_device.h>
+#if defined(CONFIG_OF)
+#include <linux/of_address.h>
+#include <linux/of_device.h>
+#include <linux/of_platform.h>
+#endif
 
 MODULE_AUTHOR("Grant Likely <grant.likely@secretlab.ca>");
 MODULE_DESCRIPTION("Xilinx SystemACE device driver");
@@ -158,6 +165,9 @@ MODULE_LICENSE("GPL");
 #define ACE_FIFO_SIZE (32)
 #define ACE_BUF_PER_SECTOR (ACE_SECTOR_SIZE / ACE_FIFO_SIZE)
 
+#define ACE_BUS_WIDTH_8  0
+#define ACE_BUS_WIDTH_16 1
+
 struct ace_reg_ops;
 
 struct ace_device {
@@ -187,8 +197,8 @@ struct ace_device {
        int in_irq;
 
        /* Details of hardware device */
-       unsigned long physaddr;
-       void *baseaddr;
+       resource_size_t physaddr;
+       void __iomem *baseaddr;
        int irq;
        int bus_width;          /* 0 := 8 bit; 1 := 16 bit */
        struct ace_reg_ops *reg_ops;
@@ -201,9 +211,10 @@ struct ace_device {
        struct gendisk *gd;
 
        /* Inserted CF card parameters */
-       struct hd_driveid cf_id;
+       u16 cf_id[ATA_ID_WORDS];
 };
 
+static DEFINE_MUTEX(xsysace_mutex);
 static int ace_major;
 
 /* ---------------------------------------------------------------------
@@ -220,20 +231,20 @@ struct ace_reg_ops {
 /* 8 Bit bus width */
 static u16 ace_in_8(struct ace_device *ace, int reg)
 {
-       void *r = ace->baseaddr + reg;
+       void __iomem *r = ace->baseaddr + reg;
        return in_8(r) | (in_8(r + 1) << 8);
 }
 
 static void ace_out_8(struct ace_device *ace, int reg, u16 val)
 {
-       void *r = ace->baseaddr + reg;
+       void __iomem *r = ace->baseaddr + reg;
        out_8(r, val);
        out_8(r + 1, val >> 8);
 }
 
 static void ace_datain_8(struct ace_device *ace)
 {
-       void *r = ace->baseaddr + 0x40;
+       void __iomem *r = ace->baseaddr + 0x40;
        u8 *dst = ace->data_ptr;
        int i = ACE_FIFO_SIZE;
        while (i--)
@@ -243,7 +254,7 @@ static void ace_datain_8(struct ace_device *ace)
 
 static void ace_dataout_8(struct ace_device *ace)
 {
-       void *r = ace->baseaddr + 0x40;
+       void __iomem *r = ace->baseaddr + 0x40;
        u8 *src = ace->data_ptr;
        int i = ACE_FIFO_SIZE;
        while (i--)
@@ -382,7 +393,8 @@ static inline void ace_dump_mem(void *base, int len)
 
 static void ace_dump_regs(struct ace_device *ace)
 {
-       dev_info(ace->dev, "    ctrl:  %.8x  seccnt/cmd: %.4x      ver:%.4x\n"
+       dev_info(ace->dev,
+                "    ctrl:  %.8x  seccnt/cmd: %.4x      ver:%.4x\n"
                 "    status:%.8x  mpu_lba:%.8x  busmode:%4x\n"
                 "    error: %.8x  cfg_lba:%.8x  fatstat:%.4x\n",
                 ace_in32(ace, ACE_CTRL),
@@ -395,21 +407,14 @@ static void ace_dump_regs(struct ace_device *ace)
                 ace_in32(ace, ACE_CFGLBA), ace_in(ace, ACE_FATSTAT));
 }
 
-void ace_fix_driveid(struct hd_driveid *id)
+void ace_fix_driveid(u16 *id)
 {
 #if defined(__BIG_ENDIAN)
-       u16 *buf = (void *)id;
        int i;
 
        /* All half words have wrong byte order; swap the bytes */
-       for (i = 0; i < sizeof(struct hd_driveid); i += 2, buf++)
-               *buf = le16_to_cpu(*buf);
-
-       /* Some of the data values are 32bit; swap the half words  */
-       id->lba_capacity = ((id->lba_capacity >> 16) & 0x0000FFFF) |
-           ((id->lba_capacity << 16) & 0xFFFF0000);
-       id->spg = ((id->spg >> 16) & 0x0000FFFF) |
-           ((id->spg << 16) & 0xFFFF0000);
+       for (i = 0; i < ATA_ID_WORDS; i++, id++)
+               *id = le16_to_cpu(*id);
 #endif
 }
 
@@ -451,21 +456,22 @@ static inline void ace_fsm_yieldirq(struct ace_device *ace)
 {
        dev_dbg(ace->dev, "ace_fsm_yieldirq()\n");
 
-       if (ace->irq == NO_IRQ)
+       if (!ace->irq)
                /* No IRQ assigned, so need to poll */
                tasklet_schedule(&ace->fsm_tasklet);
        ace->fsm_continue_flag = 0;
 }
 
 /* Get the next read/write request; ending requests that we don't handle */
-struct request *ace_get_next_request(request_queue_t * q)
+struct request *ace_get_next_request(struct request_queue * q)
 {
        struct request *req;
 
-       while ((req = elv_next_request(q)) != NULL) {
-               if (blk_fs_request(req))
+       while ((req = blk_peek_request(q)) != NULL) {
+               if (req->cmd_type == REQ_TYPE_FS)
                        break;
-               end_request(req, 0);
+               blk_start_request(req);
+               __blk_end_request_all(req, -EIO);
        }
        return req;
 }
@@ -476,13 +482,38 @@ static void ace_fsm_dostate(struct ace_device *ace)
        u32 status;
        u16 val;
        int count;
-       int i;
 
 #if defined(DEBUG)
        dev_dbg(ace->dev, "fsm_state=%i, id_req_count=%i\n",
                ace->fsm_state, ace->id_req_count);
 #endif
 
+       /* Verify that there is actually a CF in the slot. If not, then
+        * bail out back to the idle state and wake up all the waiters */
+       status = ace_in32(ace, ACE_STATUS);
+       if ((status & ACE_STATUS_CFDETECT) == 0) {
+               ace->fsm_state = ACE_FSM_STATE_IDLE;
+               ace->media_change = 1;
+               set_capacity(ace->gd, 0);
+               dev_info(ace->dev, "No CF in slot\n");
+
+               /* Drop all in-flight and pending requests */
+               if (ace->req) {
+                       __blk_end_request_all(ace->req, -EIO);
+                       ace->req = NULL;
+               }
+               while ((req = blk_fetch_request(ace->queue)) != NULL)
+                       __blk_end_request_all(req, -EIO);
+
+               /* Drop back to IDLE state and notify waiters */
+               ace->fsm_state = ACE_FSM_STATE_IDLE;
+               ace->id_result = -EIO;
+               while (ace->id_req_count) {
+                       complete(&ace->id_completion);
+                       ace->id_req_count--;
+               }
+       }
+
        switch (ace->fsm_state) {
        case ACE_FSM_STATE_IDLE:
                /* See if there is anything to do */
@@ -541,7 +572,7 @@ static void ace_fsm_dostate(struct ace_device *ace)
        case ACE_FSM_STATE_IDENTIFY_PREPARE:
                /* Send identify command */
                ace->fsm_task = ACE_TASK_IDENTIFY;
-               ace->data_ptr = &ace->cf_id;
+               ace->data_ptr = ace->cf_id;
                ace->data_count = ACE_BUF_PER_SECTOR;
                ace_out(ace, ACE_SECCNTCMD, ACE_SECCNTCMD_IDENTIFY);
 
@@ -586,11 +617,11 @@ static void ace_fsm_dostate(struct ace_device *ace)
                break;
 
        case ACE_FSM_STATE_IDENTIFY_COMPLETE:
-               ace_fix_driveid(&ace->cf_id);
-               ace_dump_mem(&ace->cf_id, 512); /* Debug: Dump out disk ID */
+               ace_fix_driveid(ace->cf_id);
+               ace_dump_mem(ace->cf_id, 512);  /* Debug: Dump out disk ID */
 
                if (ace->data_result) {
-                       /* Error occured, disable the disk */
+                       /* Error occurred, disable the disk */
                        ace->media_change = 1;
                        set_capacity(ace->gd, 0);
                        dev_err(ace->dev, "error fetching CF id (%i)\n",
@@ -599,9 +630,10 @@ static void ace_fsm_dostate(struct ace_device *ace)
                        ace->media_change = 0;
 
                        /* Record disk parameters */
-                       set_capacity(ace->gd, ace->cf_id.lba_capacity);
+                       set_capacity(ace->gd,
+                               ata_id_u32(ace->cf_id, ATA_ID_LBA_CAPACITY));
                        dev_info(ace->dev, "capacity: %i sectors\n",
-                                ace->cf_id.lba_capacity);
+                               ata_id_u32(ace->cf_id, ATA_ID_LBA_CAPACITY));
                }
 
                /* We're done, drop to IDLE state and notify waiters */
@@ -619,19 +651,21 @@ static void ace_fsm_dostate(struct ace_device *ace)
                        ace->fsm_state = ACE_FSM_STATE_IDLE;
                        break;
                }
+               blk_start_request(req);
 
                /* Okay, it's a data request, set it up for transfer */
                dev_dbg(ace->dev,
-                       "request: sec=%lx hcnt=%lx, ccnt=%x, dir=%i\n",
-                       req->sector, req->hard_nr_sectors,
-                       req->current_nr_sectors, rq_data_dir(req));
+                       "request: sec=%llx hcnt=%x, ccnt=%x, dir=%i\n",
+                       (unsigned long long)blk_rq_pos(req),
+                       blk_rq_sectors(req), blk_rq_cur_sectors(req),
+                       rq_data_dir(req));
 
                ace->req = req;
                ace->data_ptr = req->buffer;
-               ace->data_count = req->current_nr_sectors * ACE_BUF_PER_SECTOR;
-               ace_out32(ace, ACE_MPULBA, req->sector & 0x0FFFFFFF);
+               ace->data_count = blk_rq_cur_sectors(req) * ACE_BUF_PER_SECTOR;
+               ace_out32(ace, ACE_MPULBA, blk_rq_pos(req) & 0x0FFFFFFF);
 
-               count = req->hard_nr_sectors;
+               count = blk_rq_sectors(req);
                if (rq_data_dir(req)) {
                        /* Kick off write request */
                        dev_dbg(ace->dev, "write data\n");
@@ -665,7 +699,7 @@ static void ace_fsm_dostate(struct ace_device *ace)
                        dev_dbg(ace->dev,
                                "CFBSY set; t=%i iter=%i c=%i dc=%i irq=%i\n",
                                ace->fsm_task, ace->fsm_iter_num,
-                               ace->req->current_nr_sectors * 16,
+                               blk_rq_cur_sectors(ace->req) * 16,
                                ace->data_count, ace->in_irq);
                        ace_fsm_yield(ace);     /* need to poll CFBSY bit */
                        break;
@@ -674,14 +708,13 @@ static void ace_fsm_dostate(struct ace_device *ace)
                        dev_dbg(ace->dev,
                                "DATABUF not set; t=%i iter=%i c=%i dc=%i irq=%i\n",
                                ace->fsm_task, ace->fsm_iter_num,
-                               ace->req->current_nr_sectors * 16,
+                               blk_rq_cur_sectors(ace->req) * 16,
                                ace->data_count, ace->in_irq);
                        ace_fsm_yieldirq(ace);
                        break;
                }
 
                /* Transfer the next buffer */
-               i = 16;
                if (ace->fsm_task == ACE_TASK_WRITE)
                        ace->reg_ops->dataout(ace);
                else
@@ -695,14 +728,13 @@ static void ace_fsm_dostate(struct ace_device *ace)
                }
 
                /* bio finished; is there another one? */
-               i = ace->req->current_nr_sectors;
-               if (end_that_request_first(ace->req, 1, i)) {
-                       /* dev_dbg(ace->dev, "next block; h=%li c=%i\n",
-                        *      ace->req->hard_nr_sectors,
-                        *      ace->req->current_nr_sectors);
+               if (__blk_end_request_cur(ace->req, 0)) {
+                       /* dev_dbg(ace->dev, "next block; h=%u c=%u\n",
+                        *      blk_rq_sectors(ace->req),
+                        *      blk_rq_cur_sectors(ace->req));
                         */
                        ace->data_ptr = ace->req->buffer;
-                       ace->data_count = ace->req->current_nr_sectors * 16;
+                       ace->data_count = blk_rq_cur_sectors(ace->req) * 16;
                        ace_fsm_yieldirq(ace);
                        break;
                }
@@ -711,9 +743,6 @@ static void ace_fsm_dostate(struct ace_device *ace)
                break;
 
        case ACE_FSM_STATE_REQ_COMPLETE:
-               /* Complete the block request */
-               blkdev_dequeue_request(ace->req);
-               end_that_request_last(ace->req, 1);
                ace->req = NULL;
 
                /* Finished request; go to idle state */
@@ -772,7 +801,7 @@ static int ace_interrupt_checkstate(struct ace_device *ace)
        u32 sreg = ace_in32(ace, ACE_STATUS);
        u16 creg = ace_in(ace, ACE_CTRL);
 
-       /* Check for error occurance */
+       /* Check for error occurrence */
        if ((sreg & (ACE_STATUS_CFGERROR | ACE_STATUS_CFCERROR)) &&
            (creg & ACE_CTRL_ERRORIRQ)) {
                dev_err(ace->dev, "transfer failure\n");
@@ -825,7 +854,7 @@ static irqreturn_t ace_interrupt(int irq, void *dev_id)
 /* ---------------------------------------------------------------------
  * Block ops
  */
-static void ace_request(request_queue_t * q)
+static void ace_request(struct request_queue * q)
 {
        struct request *req;
        struct ace_device *ace;
@@ -838,12 +867,12 @@ static void ace_request(request_queue_t * q)
        }
 }
 
-static int ace_media_changed(struct gendisk *gd)
+static unsigned int ace_check_events(struct gendisk *gd, unsigned int clearing)
 {
        struct ace_device *ace = gd->private_data;
-       dev_dbg(ace->dev, "ace_media_changed(): %i\n", ace->media_change);
+       dev_dbg(ace->dev, "ace_check_events(): %i\n", ace->media_change);
 
-       return ace->media_change;
+       return ace->media_change ? DISK_EVENT_MEDIA_CHANGE : 0;
 }
 
 static int ace_revalidate_disk(struct gendisk *gd)
@@ -868,30 +897,33 @@ static int ace_revalidate_disk(struct gendisk *gd)
        return ace->id_result;
 }
 
-static int ace_open(struct inode *inode, struct file *filp)
+static int ace_open(struct block_device *bdev, fmode_t mode)
 {
-       struct ace_device *ace = inode->i_bdev->bd_disk->private_data;
+       struct ace_device *ace = bdev->bd_disk->private_data;
        unsigned long flags;
 
        dev_dbg(ace->dev, "ace_open() users=%i\n", ace->users + 1);
 
-       filp->private_data = ace;
+       mutex_lock(&xsysace_mutex);
        spin_lock_irqsave(&ace->lock, flags);
        ace->users++;
        spin_unlock_irqrestore(&ace->lock, flags);
 
-       check_disk_change(inode->i_bdev);
+       check_disk_change(bdev);
+       mutex_unlock(&xsysace_mutex);
+
        return 0;
 }
 
-static int ace_release(struct inode *inode, struct file *filp)
+static int ace_release(struct gendisk *disk, fmode_t mode)
 {
-       struct ace_device *ace = inode->i_bdev->bd_disk->private_data;
+       struct ace_device *ace = disk->private_data;
        unsigned long flags;
        u16 val;
 
        dev_dbg(ace->dev, "ace_release() users=%i\n", ace->users - 1);
 
+       mutex_lock(&xsysace_mutex);
        spin_lock_irqsave(&ace->lock, flags);
        ace->users--;
        if (ace->users == 0) {
@@ -899,38 +931,31 @@ static int ace_release(struct inode *inode, struct file *filp)
                ace_out(ace, ACE_CTRL, val & ~ACE_CTRL_LOCKREQ);
        }
        spin_unlock_irqrestore(&ace->lock, flags);
+       mutex_unlock(&xsysace_mutex);
        return 0;
 }
 
-static int ace_ioctl(struct inode *inode, struct file *filp,
-                    unsigned int cmd, unsigned long arg)
+static int ace_getgeo(struct block_device *bdev, struct hd_geometry *geo)
 {
-       struct ace_device *ace = inode->i_bdev->bd_disk->private_data;
-       struct hd_geometry __user *geo = (struct hd_geometry __user *)arg;
-       struct hd_geometry g;
-       dev_dbg(ace->dev, "ace_ioctl()\n");
-
-       switch (cmd) {
-       case HDIO_GETGEO:
-               g.heads = ace->cf_id.heads;
-               g.sectors = ace->cf_id.sectors;
-               g.cylinders = ace->cf_id.cyls;
-               g.start = 0;
-               return copy_to_user(geo, &g, sizeof(g)) ? -EFAULT : 0;
+       struct ace_device *ace = bdev->bd_disk->private_data;
+       u16 *cf_id = ace->cf_id;
 
-       default:
-               return -ENOTTY;
-       }
-       return -ENOTTY;
+       dev_dbg(ace->dev, "ace_getgeo()\n");
+
+       geo->heads      = cf_id[ATA_ID_HEADS];
+       geo->sectors    = cf_id[ATA_ID_SECTORS];
+       geo->cylinders  = cf_id[ATA_ID_CYLS];
+
+       return 0;
 }
 
-static struct block_device_operations ace_fops = {
+static const struct block_device_operations ace_fops = {
        .owner = THIS_MODULE,
        .open = ace_open,
        .release = ace_release,
-       .media_changed = ace_media_changed,
+       .check_events = ace_check_events,
        .revalidate_disk = ace_revalidate_disk,
-       .ioctl = ace_ioctl,
+       .getgeo = ace_getgeo,
 };
 
 /* --------------------------------------------------------------------
@@ -940,9 +965,12 @@ static int __devinit ace_setup(struct ace_device *ace)
 {
        u16 version;
        u16 val;
-
        int rc;
 
+       dev_dbg(ace->dev, "ace_setup(ace=0x%p)\n", ace);
+       dev_dbg(ace->dev, "physaddr=0x%llx irq=%i\n",
+               (unsigned long long)ace->physaddr, ace->irq);
+
        spin_lock_init(&ace->lock);
        init_completion(&ace->id_completion);
 
@@ -953,15 +981,6 @@ static int __devinit ace_setup(struct ace_device *ace)
        if (!ace->baseaddr)
                goto err_ioremap;
 
-       if (ace->irq != NO_IRQ) {
-               rc = request_irq(ace->irq, ace_interrupt, 0, "systemace", ace);
-               if (rc) {
-                       /* Failure - fall back to polled mode */
-                       dev_err(ace->dev, "request_irq failed\n");
-                       ace->irq = NO_IRQ;
-               }
-       }
-
        /*
         * Initialize the state machine tasklet and stall timer
         */
@@ -974,7 +993,7 @@ static int __devinit ace_setup(struct ace_device *ace)
        ace->queue = blk_init_queue(ace_request, &ace->lock);
        if (ace->queue == NULL)
                goto err_blk_initq;
-       blk_queue_hardsect_size(ace->queue, 512);
+       blk_queue_logical_block_size(ace->queue, 512);
 
        /*
         * Allocate and initialize GD structure
@@ -991,7 +1010,7 @@ static int __devinit ace_setup(struct ace_device *ace)
        snprintf(ace->gd->disk_name, 32, "xs%c", ace->id + 'a');
 
        /* set bus width */
-       if (ace->bus_width == 1) {
+       if (ace->bus_width == ACE_BUS_WIDTH_16) {
                /* 0x0101 should work regardless of endianess */
                ace_out_le16(ace, ACE_BUSMODE, 0x0101);
 
@@ -1014,6 +1033,16 @@ static int __devinit ace_setup(struct ace_device *ace)
        ace_out(ace, ACE_CTRL, ACE_CTRL_FORCECFGMODE |
                ACE_CTRL_DATABUFRDYIRQ | ACE_CTRL_ERRORIRQ);
 
+       /* Now we can hook up the irq handler */
+       if (ace->irq) {
+               rc = request_irq(ace->irq, ace_interrupt, 0, "systemace", ace);
+               if (rc) {
+                       /* Failure - fall back to polled mode */
+                       dev_err(ace->dev, "request_irq failed\n");
+                       ace->irq = 0;
+               }
+       }
+
        /* Enable interrupts */
        val = ace_in(ace, ACE_CTRL);
        val |= ACE_CTRL_DATABUFRDYIRQ | ACE_CTRL_ERRORIRQ;
@@ -1022,8 +1051,8 @@ static int __devinit ace_setup(struct ace_device *ace)
        /* Print the identification */
        dev_info(ace->dev, "Xilinx SystemACE revision %i.%i.%i\n",
                 (version >> 12) & 0xf, (version >> 8) & 0x0f, version & 0xff);
-       dev_dbg(ace->dev, "physaddr 0x%lx, mapped to 0x%p, irq=%i\n",
-               ace->physaddr, ace->baseaddr, ace->irq);
+       dev_dbg(ace->dev, "physaddr 0x%llx, mapped to 0x%p, irq=%i\n",
+               (unsigned long long) ace->physaddr, ace->baseaddr, ace->irq);
 
        ace->media_change = 1;
        ace_revalidate_disk(ace->gd);
@@ -1033,17 +1062,15 @@ static int __devinit ace_setup(struct ace_device *ace)
 
        return 0;
 
-      err_read:
+err_read:
        put_disk(ace->gd);
-      err_alloc_disk:
+err_alloc_disk:
        blk_cleanup_queue(ace->queue);
-      err_blk_initq:
+err_blk_initq:
        iounmap(ace->baseaddr);
-       if (ace->irq != NO_IRQ)
-               free_irq(ace->irq, ace);
-      err_ioremap:
-       printk(KERN_INFO "xsysace: error initializing device at 0x%lx\n",
-              ace->physaddr);
+err_ioremap:
+       dev_info(ace->dev, "xsysace: error initializing device at 0x%llx\n",
+                (unsigned long long) ace->physaddr);
        return -ENOMEM;
 }
 
@@ -1059,83 +1086,130 @@ static void __devexit ace_teardown(struct ace_device *ace)
 
        tasklet_kill(&ace->fsm_tasklet);
 
-       if (ace->irq != NO_IRQ)
+       if (ace->irq)
                free_irq(ace->irq, ace);
 
        iounmap(ace->baseaddr);
 }
 
-/* ---------------------------------------------------------------------
- * Platform Bus Support
- */
-
-static int __devinit ace_probe(struct device *device)
+static int __devinit
+ace_alloc(struct device *dev, int id, resource_size_t physaddr,
+         int irq, int bus_width)
 {
-       struct platform_device *dev = to_platform_device(device);
        struct ace_device *ace;
-       int i;
+       int rc;
+       dev_dbg(dev, "ace_alloc(%p)\n", dev);
 
-       dev_dbg(device, "ace_probe(%p)\n", device);
+       if (!physaddr) {
+               rc = -ENODEV;
+               goto err_noreg;
+       }
 
-       /*
-        * Allocate the ace device structure
-        */
+       /* Allocate and initialize the ace device structure */
        ace = kzalloc(sizeof(struct ace_device), GFP_KERNEL);
-       if (!ace)
+       if (!ace) {
+               rc = -ENOMEM;
                goto err_alloc;
-
-       ace->dev = device;
-       ace->id = dev->id;
-       ace->irq = NO_IRQ;
-
-       for (i = 0; i < dev->num_resources; i++) {
-               if (dev->resource[i].flags & IORESOURCE_MEM)
-                       ace->physaddr = dev->resource[i].start;
-               if (dev->resource[i].flags & IORESOURCE_IRQ)
-                       ace->irq = dev->resource[i].start;
        }
 
-       /* FIXME: Should get bus_width from the platform_device struct */
-       ace->bus_width = 1;
+       ace->dev = dev;
+       ace->id = id;
+       ace->physaddr = physaddr;
+       ace->irq = irq;
+       ace->bus_width = bus_width;
 
-       dev_set_drvdata(&dev->dev, ace);
-
-       /* Call the bus-independant setup code */
-       if (ace_setup(ace) != 0)
+       /* Call the setup code */
+       rc = ace_setup(ace);
+       if (rc)
                goto err_setup;
 
+       dev_set_drvdata(dev, ace);
        return 0;
 
-      err_setup:
-       dev_set_drvdata(&dev->dev, NULL);
+err_setup:
+       dev_set_drvdata(dev, NULL);
        kfree(ace);
-      err_alloc:
-       printk(KERN_ERR "xsysace: could not initialize device\n");
-       return -ENOMEM;
+err_alloc:
+err_noreg:
+       dev_err(dev, "could not initialize device, err=%i\n", rc);
+       return rc;
 }
 
-/*
- * Platform bus remove() method
- */
-static int __devexit ace_remove(struct device *device)
+static void __devexit ace_free(struct device *dev)
 {
-       struct ace_device *ace = dev_get_drvdata(device);
-
-       dev_dbg(device, "ace_remove(%p)\n", device);
+       struct ace_device *ace = dev_get_drvdata(dev);
+       dev_dbg(dev, "ace_free(%p)\n", dev);
 
        if (ace) {
                ace_teardown(ace);
+               dev_set_drvdata(dev, NULL);
                kfree(ace);
        }
+}
+
+/* ---------------------------------------------------------------------
+ * Platform Bus Support
+ */
+
+static int __devinit ace_probe(struct platform_device *dev)
+{
+       resource_size_t physaddr = 0;
+       int bus_width = ACE_BUS_WIDTH_16; /* FIXME: should not be hard coded */
+       u32 id = dev->id;
+       int irq = 0;
+       int i;
+
+       dev_dbg(&dev->dev, "ace_probe(%p)\n", dev);
+
+       /* device id and bus width */
+       of_property_read_u32(dev->dev.of_node, "port-number", &id);
+       if (id < 0)
+               id = 0;
+       if (of_find_property(dev->dev.of_node, "8-bit", NULL))
+               bus_width = ACE_BUS_WIDTH_8;
+
+       for (i = 0; i < dev->num_resources; i++) {
+               if (dev->resource[i].flags & IORESOURCE_MEM)
+                       physaddr = dev->resource[i].start;
+               if (dev->resource[i].flags & IORESOURCE_IRQ)
+                       irq = dev->resource[i].start;
+       }
 
+       /* Call the bus-independent setup code */
+       return ace_alloc(&dev->dev, id, physaddr, irq, bus_width);
+}
+
+/*
+ * Platform bus remove() method
+ */
+static int __devexit ace_remove(struct platform_device *dev)
+{
+       ace_free(&dev->dev);
        return 0;
 }
 
-static struct device_driver ace_driver = {
-       .name = "xsysace",
-       .bus = &platform_bus_type,
+#if defined(CONFIG_OF)
+/* Match table for of_platform binding */
+static const struct of_device_id ace_of_match[] __devinitconst = {
+       { .compatible = "xlnx,opb-sysace-1.00.b", },
+       { .compatible = "xlnx,opb-sysace-1.00.c", },
+       { .compatible = "xlnx,xps-sysace-1.00.a", },
+       { .compatible = "xlnx,sysace", },
+       {},
+};
+MODULE_DEVICE_TABLE(of, ace_of_match);
+#else /* CONFIG_OF */
+#define ace_of_match NULL
+#endif /* CONFIG_OF */
+
+static struct platform_driver ace_platform_driver = {
        .probe = ace_probe,
        .remove = __devexit_p(ace_remove),
+       .driver = {
+               .owner = THIS_MODULE,
+               .name = "xsysace",
+               .of_match_table = ace_of_match,
+       },
 };
 
 /* ---------------------------------------------------------------------
@@ -1143,22 +1217,33 @@ static struct device_driver ace_driver = {
  */
 static int __init ace_init(void)
 {
+       int rc;
+
        ace_major = register_blkdev(ace_major, "xsysace");
        if (ace_major <= 0) {
-               printk(KERN_WARNING "xsysace: register_blkdev() failed\n");
-               return ace_major;
+               rc = -ENOMEM;
+               goto err_blk;
        }
 
-       pr_debug("Registering Xilinx SystemACE driver, major=%i\n", ace_major);
-       return driver_register(&ace_driver);
+       rc = platform_driver_register(&ace_platform_driver);
+       if (rc)
+               goto err_plat;
+
+       pr_info("Xilinx SystemACE device driver, major=%i\n", ace_major);
+       return 0;
+
+err_plat:
+       unregister_blkdev(ace_major, "xsysace");
+err_blk:
+       printk(KERN_ERR "xsysace: registration failed; err=%i\n", rc);
+       return rc;
 }
+module_init(ace_init);
 
 static void __exit ace_exit(void)
 {
        pr_debug("Unregistering Xilinx SystemACE driver\n");
-       driver_unregister(&ace_driver);
+       platform_driver_unregister(&ace_platform_driver);
        unregister_blkdev(ace_major, "xsysace");
 }
-
-module_init(ace_init);
 module_exit(ace_exit);