diff options
Diffstat (limited to 'drivers/block')
-rw-r--r-- | drivers/block/DAC960.c | 12 | ||||
-rw-r--r-- | drivers/block/Kconfig | 24 | ||||
-rw-r--r-- | drivers/block/Makefile | 4 | ||||
-rw-r--r-- | drivers/block/aoe/aoecmd.c | 23 | ||||
-rw-r--r-- | drivers/block/aoe/aoenet.c | 2 | ||||
-rw-r--r-- | drivers/block/cciss.c | 128 | ||||
-rw-r--r-- | drivers/block/cciss.h | 2 | ||||
-rw-r--r-- | drivers/block/cciss_cmd.h | 23 | ||||
-rw-r--r-- | drivers/block/cpqarray.c | 1 | ||||
-rw-r--r-- | drivers/block/floppy.c | 11 | ||||
-rw-r--r-- | drivers/block/hd.c | 2 | ||||
-rw-r--r-- | drivers/block/loop.c | 85 | ||||
-rw-r--r-- | drivers/block/mg_disk.c | 1005 | ||||
-rw-r--r-- | drivers/block/nbd.c | 112 | ||||
-rw-r--r-- | drivers/block/ps3vram.c | 2 | ||||
-rw-r--r-- | drivers/block/swim.c | 995 | ||||
-rw-r--r-- | drivers/block/swim_asm.S | 247 | ||||
-rw-r--r-- | drivers/block/sx8.c | 6 | ||||
-rw-r--r-- | drivers/block/ub.c | 11 | ||||
-rw-r--r-- | drivers/block/umem.c | 4 | ||||
-rw-r--r-- | drivers/block/xsysace.c | 30 |
21 files changed, 2622 insertions, 107 deletions
diff --git a/drivers/block/DAC960.c b/drivers/block/DAC960.c index f6a337c34ac4..f22ed6cc69f2 100644 --- a/drivers/block/DAC960.c +++ b/drivers/block/DAC960.c @@ -1169,9 +1169,9 @@ static bool DAC960_V1_EnableMemoryMailboxInterface(DAC960_Controller_T int i; - if (pci_set_dma_mask(Controller->PCIDevice, DMA_32BIT_MASK)) + if (pci_set_dma_mask(Controller->PCIDevice, DMA_BIT_MASK(32))) return DAC960_Failure(Controller, "DMA mask out of range"); - Controller->BounceBufferLimit = DMA_32BIT_MASK; + Controller->BounceBufferLimit = DMA_BIT_MASK(32); if ((hw_type == DAC960_PD_Controller) || (hw_type == DAC960_P_Controller)) { CommandMailboxesSize = 0; @@ -1372,10 +1372,10 @@ static bool DAC960_V2_EnableMemoryMailboxInterface(DAC960_Controller_T dma_addr_t CommandMailboxDMA; DAC960_V2_CommandStatus_T CommandStatus; - if (!pci_set_dma_mask(Controller->PCIDevice, DMA_64BIT_MASK)) - Controller->BounceBufferLimit = DMA_64BIT_MASK; - else if (!pci_set_dma_mask(Controller->PCIDevice, DMA_32BIT_MASK)) - Controller->BounceBufferLimit = DMA_32BIT_MASK; + if (!pci_set_dma_mask(Controller->PCIDevice, DMA_BIT_MASK(64))) + Controller->BounceBufferLimit = DMA_BIT_MASK(64); + else if (!pci_set_dma_mask(Controller->PCIDevice, DMA_BIT_MASK(32))) + Controller->BounceBufferLimit = DMA_BIT_MASK(32); else return DAC960_Failure(Controller, "DMA mask out of range"); diff --git a/drivers/block/Kconfig b/drivers/block/Kconfig index 0344a8a8321d..ddea8e485cc9 100644 --- a/drivers/block/Kconfig +++ b/drivers/block/Kconfig @@ -45,6 +45,13 @@ config MAC_FLOPPY If you have a SWIM-3 (Super Woz Integrated Machine 3; from Apple) floppy controller, say Y here. Most commonly found in PowerMacs. +config BLK_DEV_SWIM + tristate "Support for SWIM Macintosh floppy" + depends on M68K && MAC + help + You should select this option if you want floppy support + and you don't have a II, IIfx, Q900, Q950 or AV series. + config AMIGA_Z2RAM tristate "Amiga Zorro II ramdisk support" depends on ZORRO @@ -403,6 +410,23 @@ config ATA_OVER_ETH This driver provides Support for ATA over Ethernet block devices like the Coraid EtherDrive (R) Storage Blade. +config MG_DISK + tristate "mGine mflash, gflash support" + depends on ARM && ATA && GPIOLIB + help + mGine mFlash(gFlash) block device driver + +config MG_DISK_RES + int "Size of reserved area before MBR" + depends on MG_DISK + default 0 + help + Define size of reserved area that usually used for boot. Unit is KB. + All of the block device operation will be taken this value as start + offset + Examples: + 1024 => 1 MB + config SUNVDC tristate "Sun Virtual Disk Client support" depends on SUN_LDOMS diff --git a/drivers/block/Makefile b/drivers/block/Makefile index 87e120e0a79c..7755a5e2a85e 100644 --- a/drivers/block/Makefile +++ b/drivers/block/Makefile @@ -6,6 +6,7 @@ # obj-$(CONFIG_MAC_FLOPPY) += swim3.o +obj-$(CONFIG_BLK_DEV_SWIM) += swim_mod.o obj-$(CONFIG_BLK_DEV_FD) += floppy.o obj-$(CONFIG_AMIGA_FLOPPY) += amiflop.o obj-$(CONFIG_PS3_DISK) += ps3disk.o @@ -20,6 +21,7 @@ obj-$(CONFIG_BLK_CPQ_CISS_DA) += cciss.o obj-$(CONFIG_BLK_DEV_DAC960) += DAC960.o obj-$(CONFIG_XILINX_SYSACE) += xsysace.o obj-$(CONFIG_CDROM_PKTCDVD) += pktcdvd.o +obj-$(CONFIG_MG_DISK) += mg_disk.o obj-$(CONFIG_SUNVDC) += sunvdc.o obj-$(CONFIG_BLK_DEV_UMEM) += umem.o @@ -33,3 +35,5 @@ obj-$(CONFIG_BLK_DEV_UB) += ub.o obj-$(CONFIG_BLK_DEV_HD) += hd.o obj-$(CONFIG_XEN_BLKDEV_FRONTEND) += xen-blkfront.o + +swim_mod-objs := swim.o swim_asm.o diff --git a/drivers/block/aoe/aoecmd.c b/drivers/block/aoe/aoecmd.c index 45c5a33daf49..31693bc24444 100644 --- a/drivers/block/aoe/aoecmd.c +++ b/drivers/block/aoe/aoecmd.c @@ -4,6 +4,7 @@ * Filesystem request handling methods */ +#include <linux/ata.h> #include <linux/hdreg.h> #include <linux/blkdev.h> #include <linux/skbuff.h> @@ -267,7 +268,7 @@ aoecmd_ata_rw(struct aoedev *d) writebit = 0; } - ah->cmdstat = WIN_READ | writebit | extbit; + ah->cmdstat = ATA_CMD_PIO_READ | writebit | extbit; /* mark all tracking fields and load out */ buf->nframesout += 1; @@ -362,10 +363,10 @@ resend(struct aoedev *d, struct aoetgt *t, struct frame *f) switch (ah->cmdstat) { default: break; - case WIN_READ: - case WIN_READ_EXT: - case WIN_WRITE: - case WIN_WRITE_EXT: + case ATA_CMD_PIO_READ: + case ATA_CMD_PIO_READ_EXT: + case ATA_CMD_PIO_WRITE: + case ATA_CMD_PIO_WRITE_EXT: put_lba(ah, f->lba); n = f->bcnt; @@ -812,8 +813,8 @@ aoecmd_ata_rsp(struct sk_buff *skb) d->htgt = NULL; n = ahout->scnt << 9; switch (ahout->cmdstat) { - case WIN_READ: - case WIN_READ_EXT: + case ATA_CMD_PIO_READ: + case ATA_CMD_PIO_READ_EXT: if (skb->len - sizeof *hin - sizeof *ahin < n) { printk(KERN_ERR "aoe: %s. skb->len=%d need=%ld\n", @@ -823,8 +824,8 @@ aoecmd_ata_rsp(struct sk_buff *skb) return; } memcpy(f->bufaddr, ahin+1, n); - case WIN_WRITE: - case WIN_WRITE_EXT: + case ATA_CMD_PIO_WRITE: + case ATA_CMD_PIO_WRITE_EXT: ifp = getif(t, skb->dev); if (ifp) { ifp->lost = 0; @@ -838,7 +839,7 @@ aoecmd_ata_rsp(struct sk_buff *skb) goto xmit; } break; - case WIN_IDENTIFY: + case ATA_CMD_ID_ATA: if (skb->len - sizeof *hin - sizeof *ahin < 512) { printk(KERN_INFO "aoe: runt data size in ataid. skb->len=%d\n", @@ -914,7 +915,7 @@ aoecmd_ata_id(struct aoedev *d) /* set up ata header */ ah->scnt = 1; - ah->cmdstat = WIN_IDENTIFY; + ah->cmdstat = ATA_CMD_ID_ATA; ah->lba3 = 0xa0; skb->dev = t->ifp->nd; diff --git a/drivers/block/aoe/aoenet.c b/drivers/block/aoe/aoenet.c index c6099ba9a4b8..ce0d62cd71b2 100644 --- a/drivers/block/aoe/aoenet.c +++ b/drivers/block/aoe/aoenet.c @@ -151,7 +151,7 @@ exit: return 0; } -static struct packet_type aoe_pt = { +static struct packet_type aoe_pt __read_mostly = { .type = __constant_htons(ETH_P_AOE), .func = aoenet_rcv, }; diff --git a/drivers/block/cciss.c b/drivers/block/cciss.c index 4f9b6d792017..0ef6f08aa6ea 100644 --- a/drivers/block/cciss.c +++ b/drivers/block/cciss.c @@ -51,6 +51,7 @@ #include <scsi/scsi_ioctl.h> #include <linux/cdrom.h> #include <linux/scatterlist.h> +#include <linux/kthread.h> #define CCISS_DRIVER_VERSION(maj,min,submin) ((maj<<16)|(min<<8)|(submin)) #define DRIVER_NAME "HP CISS Driver (v 3.6.20)" @@ -186,6 +187,8 @@ static int sendcmd_withirq(__u8 cmd, int ctlr, void *buff, size_t size, __u8 page_code, int cmd_type); static void fail_all_cmds(unsigned long ctlr); +static int scan_thread(void *data); +static int check_for_unit_attention(ctlr_info_t *h, CommandList_struct *c); #ifdef CONFIG_PROC_FS static void cciss_procinit(int i); @@ -735,6 +738,12 @@ static int cciss_getgeo(struct block_device *bdev, struct hd_geometry *geo) return 0; } +static void check_ioctl_unit_attention(ctlr_info_t *host, CommandList_struct *c) +{ + if (c->err_info->CommandStatus == CMD_TARGET_STATUS && + c->err_info->ScsiStatus != SAM_STAT_CHECK_CONDITION) + (void)check_for_unit_attention(host, c); +} /* * ioctl */ @@ -1029,6 +1038,8 @@ static int cciss_ioctl(struct block_device *bdev, fmode_t mode, iocommand.buf_size, PCI_DMA_BIDIRECTIONAL); + check_ioctl_unit_attention(host, c); + /* Copy the error information out */ iocommand.error_info = *(c->err_info); if (copy_to_user @@ -1180,6 +1191,7 @@ static int cciss_ioctl(struct block_device *bdev, fmode_t mode, (dma_addr_t) temp64.val, buff_size[i], PCI_DMA_BIDIRECTIONAL); } + check_ioctl_unit_attention(host, c); /* Copy the error information out */ ioc->error_info = *(c->err_info); if (copy_to_user(argp, ioc, sizeof(*ioc))) { @@ -1287,6 +1299,7 @@ static void cciss_softirq_done(struct request *rq) { CommandList_struct *cmd = rq->completion_data; ctlr_info_t *h = hba[cmd->ctlr]; + unsigned int nr_bytes; unsigned long flags; u64bit temp64; int i, ddir; @@ -1308,7 +1321,14 @@ static void cciss_softirq_done(struct request *rq) printk("Done with %p\n", rq); #endif /* CCISS_DEBUG */ - if (blk_end_request(rq, (rq->errors == 0) ? 0 : -EIO, blk_rq_bytes(rq))) + /* + * Store the full size and set the residual count for pc requests + */ + nr_bytes = blk_rq_bytes(rq); + if (blk_pc_request(rq)) + rq->data_len = cmd->err_info->ResidualCnt; + + if (blk_end_request(rq, (rq->errors == 0) ? 0 : -EIO, nr_bytes)) BUG(); spin_lock_irqsave(&h->lock, flags); @@ -2585,12 +2605,14 @@ static inline unsigned int make_status_bytes(unsigned int scsi_status_byte, ((driver_byte & 0xff) << 24); } -static inline int evaluate_target_status(CommandList_struct *cmd) +static inline int evaluate_target_status(ctlr_info_t *h, + CommandList_struct *cmd, int *retry_cmd) { unsigned char sense_key; unsigned char status_byte, msg_byte, host_byte, driver_byte; int error_value; + *retry_cmd = 0; /* If we get in here, it means we got "target status", that is, scsi status */ status_byte = cmd->err_info->ScsiStatus; driver_byte = DRIVER_OK; @@ -2618,6 +2640,11 @@ static inline int evaluate_target_status(CommandList_struct *cmd) if (((sense_key == 0x0) || (sense_key == 0x1)) && !blk_pc_request(cmd->rq)) error_value = 0; + if (check_for_unit_attention(h, cmd)) { + *retry_cmd = !blk_pc_request(cmd->rq); + return 0; + } + if (!blk_pc_request(cmd->rq)) { /* Not SG_IO or similar? */ if (error_value != 0) printk(KERN_WARNING "cciss: cmd %p has CHECK CONDITION" @@ -2657,7 +2684,7 @@ static inline void complete_command(ctlr_info_t *h, CommandList_struct *cmd, switch (cmd->err_info->CommandStatus) { case CMD_TARGET_STATUS: - rq->errors = evaluate_target_status(cmd); + rq->errors = evaluate_target_status(h, cmd, &retry_cmd); break; case CMD_DATA_UNDERRUN: if (blk_fs_request(cmd->rq)) { @@ -3008,6 +3035,63 @@ static irqreturn_t do_cciss_intr(int irq, void *dev_id) return IRQ_HANDLED; } +static int scan_thread(void *data) +{ + ctlr_info_t *h = data; + int rc; + DECLARE_COMPLETION_ONSTACK(wait); + h->rescan_wait = &wait; + + for (;;) { + rc = wait_for_completion_interruptible(&wait); + if (kthread_should_stop()) + break; + if (!rc) + rebuild_lun_table(h, 0); + } + return 0; +} + +static int check_for_unit_attention(ctlr_info_t *h, CommandList_struct *c) +{ + if (c->err_info->SenseInfo[2] != UNIT_ATTENTION) + return 0; + + switch (c->err_info->SenseInfo[12]) { + case STATE_CHANGED: + printk(KERN_WARNING "cciss%d: a state change " + "detected, command retried\n", h->ctlr); + return 1; + break; + case LUN_FAILED: + printk(KERN_WARNING "cciss%d: LUN failure " + "detected, action required\n", h->ctlr); + return 1; + break; + case REPORT_LUNS_CHANGED: + printk(KERN_WARNING "cciss%d: report LUN data " + "changed\n", h->ctlr); + if (h->rescan_wait) + complete(h->rescan_wait); + return 1; + break; + case POWER_OR_RESET: + printk(KERN_WARNING "cciss%d: a power on " + "or device reset detected\n", h->ctlr); + return 1; + break; + case UNIT_ATTENTION_CLEARED: + printk(KERN_WARNING "cciss%d: unit attention " + "cleared by another initiator\n", h->ctlr); + return 1; + break; + default: + printk(KERN_WARNING "cciss%d: unknown " + "unit attention detected\n", h->ctlr); + return 1; + } +} + /* * We cannot read the structure directly, for portability we must use * the io functions. @@ -3181,12 +3265,21 @@ static int __devinit cciss_pci_init(ctlr_info_t *c, struct pci_dev *pdev) */ cciss_interrupt_mode(c, pdev, board_id); - /* - * Memory base addr is first addr , the second points to the config - * table - */ + /* find the memory BAR */ + for (i = 0; i < DEVICE_COUNT_RESOURCE; i++) { + if (pci_resource_flags(pdev, i) & IORESOURCE_MEM) + break; + } + if (i == DEVICE_COUNT_RESOURCE) { + printk(KERN_WARNING "cciss: No memory BAR found\n"); + err = -ENODEV; + goto err_out_free_res; + } + + c->paddr = pci_resource_start(pdev, i); /* addressing mode bits + * already removed + */ - c->paddr = pci_resource_start(pdev, 0); /* addressing mode bits already removed */ #ifdef CCISS_DEBUG printk("address 0 = %lx\n", c->paddr); #endif /* CCISS_DEBUG */ @@ -3637,9 +3730,9 @@ static int __devinit cciss_init_one(struct pci_dev *pdev, hba[i]->pdev = pdev; /* configure PCI DMA stuff */ - if (!pci_set_dma_mask(pdev, DMA_64BIT_MASK)) + if (!pci_set_dma_mask(pdev, DMA_BIT_MASK(64))) dac = 1; - else if (!pci_set_dma_mask(pdev, DMA_32BIT_MASK)) + else if (!pci_set_dma_mask(pdev, DMA_BIT_MASK(32))) dac = 0; else { printk(KERN_ERR "cciss: no suitable DMA available\n"); @@ -3753,6 +3846,11 @@ static int __devinit cciss_init_one(struct pci_dev *pdev, hba[i]->busy_initializing = 0; rebuild_lun_table(hba[i], 1); + hba[i]->cciss_scan_thread = kthread_run(scan_thread, hba[i], + "cciss_scan%02d", i); + if (IS_ERR(hba[i]->cciss_scan_thread)) + return PTR_ERR(hba[i]->cciss_scan_thread); + return 1; clean4: @@ -3828,6 +3926,7 @@ static void __devexit cciss_remove_one(struct pci_dev *pdev) printk(KERN_ERR "cciss: Unable to remove device \n"); return; } + tmp_ptr = pci_get_drvdata(pdev); i = tmp_ptr->ctlr; if (hba[i] == NULL) { @@ -3836,6 +3935,8 @@ static void __devexit cciss_remove_one(struct pci_dev *pdev) return; } + kthread_stop(hba[i]->cciss_scan_thread); + remove_proc_entry(hba[i]->devname, proc_cciss); unregister_blkdev(hba[i]->major, hba[i]->devname); @@ -3898,6 +3999,13 @@ static struct pci_driver cciss_pci_driver = { */ static int __init cciss_init(void) { + /* + * The hardware requires that commands are aligned on a 64-bit + * boundary. Given that we use pci_alloc_consistent() to allocate an + * array of them, the size must be a multiple of 8 bytes. + */ + BUILD_BUG_ON(sizeof(CommandList_struct) % 8); + printk(KERN_INFO DRIVER_NAME "\n"); /* Register for our PCI devices */ diff --git a/drivers/block/cciss.h b/drivers/block/cciss.h index 15e2b84734e3..703e08038fb9 100644 --- a/drivers/block/cciss.h +++ b/drivers/block/cciss.h @@ -121,6 +121,8 @@ struct ctlr_info struct sendcmd_reject_list scsi_rejects; #endif unsigned char alive; + struct completion *rescan_wait; + struct task_struct *cciss_scan_thread; }; /* Defining the diffent access_menthods */ diff --git a/drivers/block/cciss_cmd.h b/drivers/block/cciss_cmd.h index 24e22dea1a99..40b1b92dae7f 100644 --- a/drivers/block/cciss_cmd.h +++ b/drivers/block/cciss_cmd.h @@ -25,6 +25,29 @@ #define CMD_TIMEOUT 0x000B #define CMD_UNABORTABLE 0x000C +/* Unit Attentions ASC's as defined for the MSA2012sa */ +#define POWER_OR_RESET 0x29 +#define STATE_CHANGED 0x2a +#define UNIT_ATTENTION_CLEARED 0x2f +#define LUN_FAILED 0x3e +#define REPORT_LUNS_CHANGED 0x3f + +/* Unit Attentions ASCQ's as defined for the MSA2012sa */ + + /* These ASCQ's defined for ASC = POWER_OR_RESET */ +#define POWER_ON_RESET 0x00 +#define POWER_ON_REBOOT 0x01 +#define SCSI_BUS_RESET 0x02 +#define MSA_TARGET_RESET 0x03 +#define CONTROLLER_FAILOVER 0x04 +#define TRANSCEIVER_SE 0x05 +#define TRANSCEIVER_LVD 0x06 + + /* These ASCQ's defined for ASC = STATE_CHANGED */ +#define RESERVATION_PREEMPTED 0x03 +#define ASYM_ACCESS_CHANGED 0x06 +#define LUN_CAPACITY_CHANGED 0x09 + //transfer direction #define XFER_NONE 0x00 #define XFER_WRITE 0x01 diff --git a/drivers/block/cpqarray.c b/drivers/block/cpqarray.c index 5d39df14ed90..ca268ca11159 100644 --- a/drivers/block/cpqarray.c +++ b/drivers/block/cpqarray.c @@ -617,6 +617,7 @@ static int cpqarray_pci_init(ctlr_info_t *c, struct pci_dev *pdev) int i; c->pci_dev = pdev; + pci_set_master(pdev); if (pci_enable_device(pdev)) { printk(KERN_ERR "cpqarray: Unable to Enable PCI device\n"); return -1; diff --git a/drivers/block/floppy.c b/drivers/block/floppy.c index 83d8ed39433d..1300df6f1642 100644 --- a/drivers/block/floppy.c +++ b/drivers/block/floppy.c @@ -177,6 +177,7 @@ static int print_unex = 1; #include <linux/interrupt.h> #include <linux/init.h> #include <linux/platform_device.h> +#include <linux/mod_devicetable.h> #include <linux/buffer_head.h> /* for invalidate_buffers() */ #include <linux/mutex.h> @@ -4135,10 +4136,9 @@ static int have_no_fdc = -ENODEV; static ssize_t floppy_cmos_show(struct device *dev, struct device_attribute *attr, char *buf) { - struct platform_device *p; + struct platform_device *p = to_platform_device(dev); int drive; - p = container_of(dev, struct platform_device,dev); drive = p->id; return sprintf(buf, "%X\n", UDP->cmos); } @@ -4598,6 +4598,13 @@ MODULE_AUTHOR("Alain L. Knaff"); MODULE_SUPPORTED_DEVICE("fd"); MODULE_LICENSE("GPL"); +/* This doesn't actually get used other than for module information */ +static const struct pnp_device_id floppy_pnpids[] = { + { "PNP0700", 0 }, + { } +}; +MODULE_DEVICE_TABLE(pnp, floppy_pnpids); + #else __setup("floppy=", floppy_setup); diff --git a/drivers/block/hd.c b/drivers/block/hd.c index 482c0c4b964f..3c11f062a18c 100644 --- a/drivers/block/hd.c +++ b/drivers/block/hd.c @@ -42,6 +42,8 @@ #include <linux/ata.h> #include <linux/hdreg.h> +#define HD_IRQ 14 + #define REALLY_SLOW_IO #include <asm/system.h> #include <asm/io.h> diff --git a/drivers/block/loop.c b/drivers/block/loop.c index bf0345577672..ddae80825899 100644 --- a/drivers/block/loop.c +++ b/drivers/block/loop.c @@ -474,10 +474,35 @@ static int do_bio_filebacked(struct loop_device *lo, struct bio *bio) int ret; pos = ((loff_t) bio->bi_sector << 9) + lo->lo_offset; - if (bio_rw(bio) == WRITE) + + if (bio_rw(bio) == WRITE) { + int barrier = bio_barrier(bio); + struct file *file = lo->lo_backing_file; + + if (barrier) { + if (unlikely(!file->f_op->fsync)) { + ret = -EOPNOTSUPP; + goto out; + } + + ret = vfs_fsync(file, file->f_path.dentry, 0); + if (unlikely(ret)) { + ret = -EIO; + goto out; + } + } + ret = lo_send(lo, bio, pos); - else + + if (barrier && !ret) { + ret = vfs_fsync(file, file->f_path.dentry, 0); + if (unlikely(ret)) + ret = -EIO; + } + } else ret = lo_receive(lo, bio, lo->lo_blocksize, pos); + +out: return ret; } @@ -826,6 +851,9 @@ static int loop_set_fd(struct loop_device *lo, fmode_t mode, lo->lo_queue->queuedata = lo; lo->lo_queue->unplug_fn = loop_unplug; + if (!(lo_flags & LO_FLAGS_READ_ONLY) && file->f_op->fsync) + blk_queue_ordered(lo->lo_queue, QUEUE_ORDERED_DRAIN, NULL); + set_capacity(lo->lo_disk, size); bd_set_size(bdev, size << 9); @@ -941,11 +969,18 @@ static int loop_clr_fd(struct loop_device *lo, struct block_device *bdev) bd_set_size(bdev, 0); mapping_set_gfp_mask(filp->f_mapping, gfp); lo->lo_state = Lo_unbound; - fput(filp); /* This is safe: open() is still holding a reference. */ module_put(THIS_MODULE); if (max_part > 0) ioctl_by_bdev(bdev, BLKRRPART, 0); + mutex_unlock(&lo->lo_ctl_mutex); + /* + * Need not hold lo_ctl_mutex to fput backing file. + * Calling fput holding lo_ctl_mutex triggers a circular + * lock dependency possibility warning as fput can take + * bd_mutex which is usually taken before lo_ctl_mutex. + */ + fput(filp); return 0; } @@ -1157,13 +1192,37 @@ loop_get_status64(struct loop_device *lo, struct loop_info64 __user *arg) { return err; } +static int loop_set_capacity(struct loop_device *lo, struct block_device *bdev) +{ + int err; + sector_t sec; + loff_t sz; + + err = -ENXIO; + if (unlikely(lo->lo_state != Lo_bound)) + goto out; + err = figure_loop_size(lo); + if (unlikely(err)) + goto out; + sec = get_capacity(lo->lo_disk); + /* the width of sector_t may be narrow for bit-shift */ + sz = sec; + sz <<= 9; + mutex_lock(&bdev->bd_mutex); + bd_set_size(bdev, sz); + mutex_unlock(&bdev->bd_mutex); + + out: + return err; +} + static int lo_ioctl(struct block_device *bdev, fmode_t mode, unsigned int cmd, unsigned long arg) { struct loop_device *lo = bdev->bd_disk->private_data; int err; - mutex_lock(&lo->lo_ctl_mutex); + mutex_lock_nested(&lo->lo_ctl_mutex, 1); switch (cmd) { case LOOP_SET_FD: err = loop_set_fd(lo, mode, bdev, arg); @@ -1172,7 +1231,10 @@ static int lo_ioctl(struct block_device *bdev, fmode_t mode, err = loop_change_fd(lo, bdev, arg); break; case LOOP_CLR_FD: + /* loop_clr_fd would have unlocked lo_ctl_mutex on success */ err = loop_clr_fd(lo, bdev); + if (!err) + goto out_unlocked; break; case LOOP_SET_STATUS: err = loop_set_status_old(lo, (struct loop_info __user *) arg); @@ -1186,10 +1248,17 @@ static int lo_ioctl(struct block_device *bdev, fmode_t mode, case LOOP_GET_STATUS64: err = loop_get_status64(lo, (struct loop_info64 __user *) arg); break; + case LOOP_SET_CAPACITY: + err = -EPERM; + if ((mode & FMODE_WRITE) || capable(CAP_SYS_ADMIN)) + err = loop_set_capacity(lo, bdev); + break; default: err = lo->ioctl ? lo->ioctl(lo, cmd, arg) : -EINVAL; } mutex_unlock(&lo->lo_ctl_mutex); + +out_unlocked: return err; } @@ -1331,6 +1400,7 @@ static int lo_compat_ioctl(struct block_device *bdev, fmode_t mode, lo, (struct compat_loop_info __user *) arg); mutex_unlock(&lo->lo_ctl_mutex); break; + case LOOP_SET_CAPACITY: case LOOP_CLR_FD: case LOOP_GET_STATUS64: case LOOP_SET_STATUS64: @@ -1361,6 +1431,7 @@ static int lo_open(struct block_device *bdev, fmode_t mode) static int lo_release(struct gendisk *disk, fmode_t mode) { struct loop_device *lo = disk->private_data; + int err; mutex_lock(&lo->lo_ctl_mutex); @@ -1372,7 +1443,9 @@ static int lo_release(struct gendisk *disk, fmode_t mode) * In autoclear mode, stop the loop thread * and remove configuration after last close. */ - loop_clr_fd(lo, NULL); + err = loop_clr_fd(lo, NULL); + if (!err) + goto out_unlocked; } else { /* * Otherwise keep thread (if running) and config, @@ -1383,7 +1456,7 @@ static int lo_release(struct gendisk *disk, fmode_t mode) out: mutex_unlock(&lo->lo_ctl_mutex); - +out_unlocked: return 0; } diff --git a/drivers/block/mg_disk.c b/drivers/block/mg_disk.c new file mode 100644 index 000000000000..fb39d9aa3cdc --- /dev/null +++ b/drivers/block/mg_disk.c @@ -0,0 +1,1005 @@ +/* + * drivers/block/mg_disk.c + * + * Support for the mGine m[g]flash IO mode. + * Based on legacy hd.c + * + * (c) 2008 mGine Co.,LTD + * (c) 2008 unsik Kim <donari75@gmail.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/fs.h> +#include <linux/blkdev.h> +#include <linux/hdreg.h> +#include <linux/libata.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) + +static void mg_request(struct request_queue *); + +static void mg_dump_status(const char *msg, unsigned int stat, + struct mg_host *host) +{ + char *name = MG_DISK_NAME; + struct request *req; + + if (host->breq) { + req = elv_next_request(host->breq); + if (req) + name = req->rq_disk->disk_name; + } + + printk(KERN_ERR "%s: %s: status=0x%02x { ", name, msg, stat & 0xff); + if (stat & MG_REG_STATUS_BIT_BUSY) + printk("Busy "); + if (stat & MG_REG_STATUS_BIT_READY) + printk("DriveReady "); + if (stat & MG_REG_STATUS_BIT_WRITE_FAULT) + printk("WriteFault "); + if (stat & MG_REG_STATUS_BIT_SEEK_DONE) + printk("SeekComplete "); + if (stat & MG_REG_STATUS_BIT_DATA_REQ) + printk("DataRequest "); + if (stat & MG_REG_STATUS_BIT_CORRECTED_ERROR) + printk("CorrectedError "); + if (stat & MG_REG_STATUS_BIT_ERROR) + printk("Error "); + printk("}\n"); + if ((stat & MG_REG_STATUS_BIT_ERROR) == 0) { + host->error = 0; + } else { + host->error = inb((unsigned long)host->dev_base + MG_REG_ERROR); + printk(KERN_ERR "%s: %s: error=0x%02x { ", name, msg, + host->error & 0xff); + if (host->error & MG_REG_ERR_BBK) + printk("BadSector "); + if (host->error & MG_REG_ERR_UNC) + printk("UncorrectableError "); + if (host->error & MG_REG_ERR_IDNF) + printk("SectorIdNotFound "); + if (host->error & MG_REG_ERR_ABRT) + printk("DriveStatusError "); + if (host->error & MG_REG_ERR_AMNF) + printk("AddrMarkNotFound "); + printk("}"); + if (host->error & + (MG_REG_ERR_BBK | MG_REG_ERR_UNC | + MG_REG_ERR_IDNF | MG_REG_ERR_AMNF)) { + if (host->breq) { + req = elv_next_request(host->breq); + if (req) + printk(", sector=%ld", req->sector); + } + + } + printk("\n"); + } +} + +static unsigned int mg_wait(struct mg_host *host, u32 expect, u32 msec) +{ + u8 status; + unsigned long expire, cur_jiffies; + struct mg_drv_data *prv_data = host->dev->platform_data; + + host->error = MG_ERR_NONE; + expire = jiffies + msecs_to_jiffies(msec); + + status = inb((unsigned long)host->dev_base + MG_REG_STATUS); + + do { + cur_jiffies = jiffies; + if (status & MG_REG_STATUS_BIT_BUSY) { + if (expect == MG_REG_STATUS_BIT_BUSY) + break; + } else { + /* Check the error condition! */ + if (status & MG_REG_STATUS_BIT_ERROR) { + mg_dump_status("mg_wait", status, host); + break; + } + + if (expect == MG_STAT_READY) + if (MG_READY_OK(status)) + break; + + if (expect == MG_REG_STATUS_BIT_DATA_REQ) + if (status & MG_REG_STATUS_BIT_DATA_REQ) + break; + } + if (!msec) { + mg_dump_status("not ready", status, host); + return MG_ERR_INV_STAT; + } + if (prv_data->use_polling) + msleep(1); + + status = inb((unsigned long)host->dev_base + MG_REG_STATUS); + } while (time_before(cur_jiffies, expire)); + + if (time_after_eq(cur_jiffies, expire) && msec) + host->error = MG_ERR_TIMEOUT; + + return host->error; +} + +static unsigned int mg_wait_rstout(u32 rstout, u32 msec) +{ + unsigned long expire; + + expire = jiffies + msecs_to_jiffies(msec); + while (time_before(jiffies, expire)) { + if (gpio_get_value(rstout) == 1) + return MG_ERR_NONE; + msleep(10); + } + + return MG_ERR_RSTOUT; +} + +static void mg_unexpected_intr(struct mg_host *host) +{ + u32 status = inb((unsigned long)host->dev_base + MG_REG_STATUS); + + mg_dump_status("mg_unexpected_intr", status, host); +} + +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; + del_timer(&host->timer); + if (!handler) + handler = mg_unexpected_intr; + handler(host); + return IRQ_HANDLED; +} + +static int mg_get_disk_id(struct mg_host *host) +{ + u32 i; + s32 err; + const u16 *id = host->id; + struct mg_drv_data *prv_data = host->dev->platform_data; + char fwrev[ATA_ID_FW_REV_LEN + 1]; + char model[ATA_ID_PROD_LEN + 1]; + char serial[ATA_ID_SERNO_LEN + 1]; + + if (!prv_data->use_polling) + outb(MG_REG_CTRL_INTR_DISABLE, + (unsigned long)host->dev_base + + MG_REG_DRV_CTRL); + + outb(MG_CMD_ID, (unsigned long)host->dev_base + MG_REG_COMMAND); + err = mg_wait(host, MG_REG_STATUS_BIT_DATA_REQ, MG_TMAX_WAIT_RD_DRQ); + if (err) + return err; + + for (i = 0; i < (MG_SECTOR_SIZE >> 1); i++) + host->id[i] = le16_to_cpu(inw((unsigned long)host->dev_base + + MG_BUFF_OFFSET + i * 2)); + + outb(MG_CMD_RD_CONF, (unsigned long)host->dev_base + MG_REG_COMMAND); + err = mg_wait(host, MG_STAT_READY, MG_TMAX_CONF_TO_CMD); + if (err) + return err; + + if ((id[ATA_ID_FIELD_VALID] & 1) == 0) + return MG_ERR_TRANSLATION; + + host->n_sectors = ata_id_u32(id, ATA_ID_LBA_CAPACITY); + host->cyls = id[ATA_ID_CYLS]; + host->heads = id[ATA_ID_HEADS]; + host->sectors = id[ATA_ID_SECTORS]; + + if (MG_RES_SEC && host->heads && host->sectors) { + /* modify cyls, n_sectors */ + host->cyls = (host->n_sectors - MG_RES_SEC) / + host->heads / host->sectors; + host->nres_sectors = host->n_sectors - host->cyls * + host->heads * host->sectors; + 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)); + 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); + printk(KERN_INFO "mg_disk: %d + reserved %d sectors\n", + host->n_sectors, host->nres_sectors); + + if (!prv_data->use_polling) + outb(MG_REG_CTRL_INTR_ENABLE, (unsigned long)host->dev_base + + MG_REG_DRV_CTRL); + + return err; +} + + +static int mg_disk_init(struct mg_host *host) +{ + struct mg_drv_data *prv_data = host->dev->platform_data; + s32 err; + u8 init_status; + + /* hdd rst low */ + gpio_set_value(host->rst, 0); + err = mg_wait(host, MG_REG_STATUS_BIT_BUSY, MG_TMAX_RST_TO_BUSY); + if (err) + return err; + + /* hdd rst high */ + gpio_set_value(host->rst, 1); + err = mg_wait(host, MG_STAT_READY, MG_TMAX_HDRST_TO_RDY); + if (err) + return err; + + /* soft reset on */ + outb(MG_REG_CTRL_RESET | + (prv_data->use_polling ? MG_REG_CTRL_INTR_DISABLE : + MG_REG_CTRL_INTR_ENABLE), + (unsigned long)host->dev_base + MG_REG_DRV_CTRL); + err = mg_wait(host, MG_REG_STATUS_BIT_BUSY, MG_TMAX_RST_TO_BUSY); + if (err) + return err; + + /* soft reset off */ + outb(prv_data->use_polling ? MG_REG_CTRL_INTR_DISABLE : + MG_REG_CTRL_INTR_ENABLE, + (unsigned long)host->dev_base + MG_REG_DRV_CTRL); + err = mg_wait(host, MG_STAT_READY, MG_TMAX_SWRST_TO_RDY); + if (err) + return err; + + init_status = inb((unsigned long)host->dev_base + MG_REG_STATUS) & 0xf; + + if (init_status == 0xf) + return MG_ERR_INIT_STAT; + + return err; +} + +static void mg_bad_rw_intr(struct mg_host *host) +{ + struct request *req = elv_next_request(host->breq); + if (req != NULL) + if (++req->errors >= MG_MAX_ERRORS || + host->error == MG_ERR_TIMEOUT) + end_request(req, 0); +} + +static unsigned int mg_out(struct mg_host *host, + unsigned int sect_num, + unsigned int sect_cnt, + unsigned int cmd, + void (*intr_addr)(struct mg_host *)) +{ + struct mg_drv_data *prv_data = host->dev->platform_data; + + if (mg_wait(host, MG_STAT_READY, MG_TMAX_CONF_TO_CMD)) + return host->error; + + if (!prv_data->use_polling) { + host->mg_do_intr = intr_addr; + mod_timer(&host->timer, jiffies + 3 * HZ); + } + if (MG_RES_SEC) + sect_num += MG_RES_SEC; + outb((u8)sect_cnt, (unsigned long)host->dev_base + MG_REG_SECT_CNT); + outb((u8)sect_num, (unsigned long)host->dev_base + MG_REG_SECT_NUM); + outb((u8)(sect_num >> 8), (unsigned long)host->dev_base + + MG_REG_CYL_LOW); + outb((u8)(sect_num >> 16), (unsigned long)host->dev_base + + MG_REG_CYL_HIGH); + outb((u8)((sect_num >> 24) | MG_REG_HEAD_LBA_MODE), + (unsigned long)host->dev_base + MG_REG_DRV_HEAD); + outb(cmd, (unsigned long)host->dev_base + MG_REG_COMMAND); + return MG_ERR_NONE; +} + +static void mg_read(struct request *req) +{ + u32 remains, 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) != + MG_ERR_NONE) + mg_bad_rw_intr(host); + + MG_DBG("requested %d sects (from %ld), buffer=0x%p\n", + remains, req->sector, 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); + } + + outb(MG_CMD_RD_CONF, (unsigned long)host->dev_base + + MG_REG_COMMAND); + } +} + +static void mg_write(struct request *req) +{ + u32 remains, 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) != + 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) { + 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); + } + + outb(MG_CMD_WR_CONF, (unsigned long)host->dev_base + + MG_REG_COMMAND); + } +} + +static void mg_read_intr(struct mg_host *host) +{ + u32 i; + struct request *req; + + /* check status */ + do { + i = inb((unsigned long)host->dev_base + MG_REG_STATUS); + if (i & MG_REG_STATUS_BIT_BUSY) + break; + if (!MG_READY_OK(i)) + break; + if (i & MG_REG_STATUS_BIT_DATA_REQ) + goto ok_to_read; + } while (0); + mg_dump_status("mg_read_intr", i, host); + mg_bad_rw_intr(host); + mg_request(host->breq); + return; + +ok_to_read: + /* get current segment of request */ + req = elv_next_request(host->breq); + + /* 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; + } + + /* 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) + mg_request(host->breq); +} + +static void mg_write_intr(struct mg_host *host) +{ + u32 i, j; + u16 *buff; + struct request *req; + + /* get current segment of request */ + req = elv_next_request(host->breq); + + /* check status */ + do { + i = inb((unsigned long)host->dev_base + MG_REG_STATUS); + if (i & MG_REG_STATUS_BIT_BUSY) + break; + if (!MG_READY_OK(i)) + break; + if ((req->nr_sectors <= 1) || (i & MG_REG_STATUS_BIT_DATA_REQ)) + goto ok_to_write; + } while (0); + mg_dump_status("mg_write_intr", i, host); + mg_bad_rw_intr(host); + mg_request(host->breq); + 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) { + buff = (u16 *)req->buffer; + for (j = 0; j < MG_STORAGE_BUFFER_SIZE >> 1; j++) { + outw(*buff, (unsigned long)host->dev_base + + MG_BUFF_OFFSET + (j << 1)); + buff++; + } + MG_DBG("sector %ld, remaining=%ld, buffer=0x%p\n", + req->sector, req->nr_sectors, req->buffer); + host->mg_do_intr = mg_write_intr; + mod_timer(&host->timer, jiffies + 3 * HZ); + } + + /* send write confirm */ + outb(MG_CMD_WR_CONF, (unsigned long)host->dev_base + MG_REG_COMMAND); + + if (!i) + mg_request(host->breq); +} + +void mg_times_out(unsigned long data) +{ + struct mg_host *host = (struct mg_host *)data; + char *name; + struct request *req; + + req = elv_next_request(host->breq); + if (!req) + return; + + host->mg_do_intr = NULL; + + name = req->rq_disk->disk_name; + printk(KERN_DEBUG "%s: timeout\n", name); + + host->error = MG_ERR_TIMEOUT; + mg_bad_rw_intr(host); + + mg_request(host->breq); +} + +static void mg_request_poll(struct request_queue *q) +{ + struct request *req; + struct mg_host *host; + + while ((req = elv_next_request(q)) != NULL) { + host = req->rq_disk->private_data; + if (blk_fs_request(req)) { + switch (rq_data_dir(req)) { + case READ: + mg_read(req); + break; + case WRITE: + mg_write(req); + break; + default: + printk(KERN_WARNING "%s:%d unknown command\n", + __func__, __LINE__); + end_request(req, 0); + break; + } + } + } +} + +static unsigned int mg_issue_req(struct request *req, + struct mg_host *host, + unsigned int sect_num, + unsigned int sect_cnt) +{ + u16 *buff; + u32 i; + + switch (rq_data_dir(req)) { + case READ: + if (mg_out(host, sect_num, sect_cnt, MG_CMD_RD, &mg_read_intr) + != MG_ERR_NONE) { + mg_bad_rw_intr(host); + return host->error; + } + break; + case WRITE: + /* TODO : handler */ + outb(MG_REG_CTRL_INTR_DISABLE, + (unsigned long)host->dev_base + + MG_REG_DRV_CTRL); + if (mg_out(host, sect_num, sect_cnt, MG_CMD_WR, &mg_write_intr) + != MG_ERR_NONE) { + mg_bad_rw_intr(host); + return host->error; + } + del_timer(&host->timer); + mg_wait(host, MG_REG_STATUS_BIT_DATA_REQ, MG_TMAX_WAIT_WR_DRQ); + outb(MG_REG_CTRL_INTR_ENABLE, (unsigned long)host->dev_base + + MG_REG_DRV_CTRL); + if (host->error) { + mg_bad_rw_intr(host); + return host->error; + } + buff = (u16 *)req->buffer; + for (i = 0; i < MG_SECTOR_SIZE >> 1; i++) { + outw(*buff, (unsigned long)host->dev_base + + MG_BUFF_OFFSET + (i << 1)); + buff++; + } + mod_timer(&host->timer, jiffies + 3 * HZ); + 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; +} + +/* This function also called from IRQ context */ +static void mg_request(struct request_queue *q) +{ + struct request *req; + struct mg_host *host; + u32 sect_num, sect_cnt; + + while (1) { + req = elv_next_request(q); + if (!req) + return; + + host = req->rq_disk->private_data; + + /* check unwanted request call */ + if (host->mg_do_intr) + return; + + del_timer(&host->timer); + + sect_num = req->sector; + /* deal whole segments */ + sect_cnt = req->nr_sectors; + + /* sanity check */ + if (sect_num >= get_capacity(req->rq_disk) || + ((sect_num + sect_cnt) > + get_capacity(req->rq_disk))) { + printk(KERN_WARNING + "%s: bad access: sector=%d, count=%d\n", + req->rq_disk->disk_name, + sect_num, sect_cnt); + end_request(req, 0); + continue; + } + + if (!blk_fs_request(req)) + return; + + if (!mg_issue_req(req, host, sect_num, sect_cnt)) + return; + } +} + +static int mg_getgeo(struct block_device *bdev, struct hd_geometry *geo) +{ + struct mg_host *host = bdev->bd_disk->private_data; + + geo->cylinders = (unsigned short)host->cyls; + geo->heads = (unsigned char)host->heads; + geo->sectors = (unsigned char)host->sectors; + return 0; +} + +static struct block_device_operations mg_disk_ops = { + .getgeo = mg_getgeo +}; + +static int mg_suspend(struct platform_device *plat_dev, pm_message_t state) +{ + struct mg_drv_data *prv_data = plat_dev->dev.platform_data; + struct mg_host *host = prv_data->host; + + if (mg_wait(host, MG_STAT_READY, MG_TMAX_CONF_TO_CMD)) + return -EIO; + + if (!prv_data->use_polling) + outb(MG_REG_CTRL_INTR_DISABLE, + (unsigned long)host->dev_base + + MG_REG_DRV_CTRL); + + outb(MG_CMD_SLEEP, (unsigned long)host->dev_base + MG_REG_COMMAND); + /* wait until mflash deep sleep */ + msleep(1); + + if (mg_wait(host, MG_STAT_READY, MG_TMAX_CONF_TO_CMD)) { + if (!prv_data->use_polling) + outb(MG_REG_CTRL_INTR_ENABLE, + (unsigned long)host->dev_base + + MG_REG_DRV_CTRL); + return -EIO; + } + + return 0; +} + +static int mg_resume(struct platform_device *plat_dev) +{ + struct mg_drv_data *prv_data = plat_dev->dev.platform_data; + struct mg_host *host = prv_data->host; + + if (mg_wait(host, MG_STAT_READY, MG_TMAX_CONF_TO_CMD)) + return -EIO; + + outb(MG_CMD_WAKEUP, (unsigned long)host->dev_base + MG_REG_COMMAND); + /* wait until mflash wakeup */ + msleep(1); + + if (mg_wait(host, MG_STAT_READY, MG_TMAX_CONF_TO_CMD)) + return -EIO; + + if (!prv_data->use_polling) + outb(MG_REG_CTRL_INTR_ENABLE, (unsigned long)host->dev_base + + MG_REG_DRV_CTRL); + + return 0; +} + +static int mg_probe(struct platform_device *plat_dev) +{ + struct mg_host *host; + struct resource *rsc; + struct mg_drv_data *prv_data = plat_dev->dev.platform_data; + int err = 0; + + if (!prv_data) { + printk(KERN_ERR "%s:%d fail (no driver_data)\n", + __func__, __LINE__); + err = -EINVAL; + goto probe_err; + } + + /* alloc mg_host */ + host = kzalloc(sizeof(struct mg_host), GFP_KERNEL); + if (!host) { + printk(KERN_ERR "%s:%d fail (no memory for mg_host)\n", + __func__, __LINE__); + err = -ENOMEM; + goto probe_err; + } + host->major = MG_DISK_MAJ; + + /* link each other */ + prv_data->host = host; + host->dev = &plat_dev->dev; + + /* io remap */ + rsc = platform_get_resource(plat_dev, IORESOURCE_MEM, 0); + if (!rsc) { + printk(KERN_ERR "%s:%d platform_get_resource fail\n", + __func__, __LINE__); + err = -EINVAL; + goto probe_err_2; + } + host->dev_base = ioremap(rsc->start , rsc->end + 1); + if (!host->dev_base) { + printk(KERN_ERR "%s:%d ioremap fail\n", + __func__, __LINE__); + err = -EIO; + goto probe_err_2; + } + MG_DBG("dev_base = 0x%x\n", (u32)host->dev_base); + + /* get reset pin */ + rsc = platform_get_resource_byname(plat_dev, IORESOURCE_IO, + MG_RST_PIN); + if (!rsc) { + printk(KERN_ERR "%s:%d get reset pin fail\n", + __func__, __LINE__); + err = -EIO; + goto probe_err_3; + } + host->rst = rsc->start; + + /* init rst pin */ + err = gpio_request(host->rst, MG_RST_PIN); + if (err) + goto probe_err_3; + gpio_direction_output(host->rst, 1); + + /* reset out pin */ + if (!(prv_data->dev_attr & MG_DEV_MASK)) + goto probe_err_3a; + + if (prv_data->dev_attr != MG_BOOT_DEV) { + rsc = platform_get_resource_byname(plat_dev, IORESOURCE_IO, + MG_RSTOUT_PIN); + if (!rsc) { + printk(KERN_ERR "%s:%d get reset-out pin fail\n", + __func__, __LINE__); + err = -EIO; + goto probe_err_3a; + } + host->rstout = rsc->start; + err = gpio_request(host->rstout, MG_RSTOUT_PIN); + if (err) + goto probe_err_3a; + gpio_direction_input(host->rstout); + } + + /* disk reset */ + if (prv_data->dev_attr == MG_STORAGE_DEV) { + /* If POR seq. not yet finised, wait */ + err = mg_wait_rstout(host->rstout, MG_TMAX_RSTOUT); + if (err) + goto probe_err_3b; + err = mg_disk_init(host); + if (err) { + printk(KERN_ERR "%s:%d fail (err code : %d)\n", + __func__, __LINE__, err); + err = -EIO; + goto probe_err_3b; + } + } + + /* get irq resource */ + if (!prv_data->use_polling) { + host->irq = platform_get_irq(plat_dev, 0); + if (host->irq == -ENXIO) { + err = host->irq; + goto probe_err_3b; + } + err = request_irq(host->irq, mg_irq, + IRQF_DISABLED | IRQF_TRIGGER_RISING, + MG_DEV_NAME, host); + if (err) { + printk(KERN_ERR "%s:%d fail (request_irq err=%d)\n", + __func__, __LINE__, err); + goto probe_err_3b; + } + + } + + /* get disk id */ + err = mg_get_disk_id(host); + if (err) { + printk(KERN_ERR "%s:%d fail (err code : %d)\n", + __func__, __LINE__, err); + err = -EIO; + goto probe_err_4; + } + + err = register_blkdev(host->major, MG_DISK_NAME); + if (err < 0) { + printk(KERN_ERR "%s:%d register_blkdev fail (err code : %d)\n", + __func__, __LINE__, err); + goto probe_err_4; + } + if (!host->major) + host->major = err; + + spin_lock_init(&host->lock); + + if (prv_data->use_polling) + host->breq = blk_init_queue(mg_request_poll, &host->lock); + else + host->breq = blk_init_queue(mg_request, &host->lock); + + if (!host->breq) { + err = -ENOMEM; + printk(KERN_ERR "%s:%d (blk_init_queue) fail\n", + __func__, __LINE__); + goto probe_err_5; + } + + /* mflash is random device, thanx for the noop */ + elevator_exit(host->breq->elevator); + err = elevator_init(host->breq, "noop"); + if (err) { + printk(KERN_ERR "%s:%d (elevator_init) fail\n", + __func__, __LINE__); + goto probe_err_6; + } + blk_queue_max_sectors(host->breq, MG_MAX_SECTS); + blk_queue_hardsect_size(host->breq, MG_SECTOR_SIZE); + + init_timer(&host->timer); + host->timer.function = mg_times_out; + host->timer.data = (unsigned long)host; + + host->gd = alloc_disk(MG_DISK_MAX_PART); + if (!host->gd) { + printk(KERN_ERR "%s:%d (alloc_disk) fail\n", + __func__, __LINE__); + err = -ENOMEM; + goto probe_err_7; + } + host->gd->major = host->major; + host->gd->first_minor = 0; + host->gd->fops = &mg_disk_ops; + host->gd->queue = host->breq; + host->gd->private_data = host; + sprintf(host->gd->disk_name, MG_DISK_NAME"a"); + + set_capacity(host->gd, host->n_sectors); + + add_disk(host->gd); + + return err; + +probe_err_7: + del_timer_sync(&host->timer); +probe_err_6: + blk_cleanup_queue(host->breq); +probe_err_5: + unregister_blkdev(MG_DISK_MAJ, MG_DISK_NAME); +probe_err_4: + if (!prv_data->use_polling) + free_irq(host->irq, host); +probe_err_3b: + gpio_free(host->rstout); +probe_err_3a: + gpio_free(host->rst); +probe_err_3: + iounmap(host->dev_base); +probe_err_2: + kfree(host); +probe_err: + return err; +} + +static int mg_remove(struct platform_device *plat_dev) +{ + struct mg_drv_data *prv_data = plat_dev->dev.platform_data; + struct mg_host *host = prv_data->host; + int err = 0; + + /* delete timer */ + del_timer_sync(&host->timer); + + /* remove disk */ + if (host->gd) { + del_gendisk(host->gd); + put_disk(host->gd); + } + /* remove queue */ + if (host->breq) + blk_cleanup_queue(host->breq); + + /* unregister blk device */ + unregister_blkdev(host->major, MG_DISK_NAME); + + /* free irq */ + if (!prv_data->use_polling) + free_irq(host->irq, host); + + /* free reset-out pin */ + if (prv_data->dev_attr != MG_BOOT_DEV) + gpio_free(host->rstout); + + /* free rst pin */ + if (host->rst) + gpio_free(host->rst); + + /* unmap io */ + if (host->dev_base) + iounmap(host->dev_base); + + /* free mg_host */ + kfree(host); + + return err; +} + +static struct platform_driver mg_disk_driver = { + .probe = mg_probe, + .remove = mg_remove, + .suspend = mg_suspend, + .resume = mg_resume, + .driver = { + .name = MG_DEV_NAME, + .owner = THIS_MODULE, + } +}; + +/**************************************************************************** + * + * Module stuff + * + ****************************************************************************/ + +static int __init mg_init(void) +{ + printk(KERN_INFO "mGine mflash driver, (c) 2008 mGine Co.\n"); + return platform_driver_register(&mg_disk_driver); +} + +static void __exit mg_exit(void) +{ + printk(KERN_INFO "mflash driver : bye bye\n"); + platform_driver_unregister(&mg_disk_driver); +} + +module_init(mg_init); +module_exit(mg_exit); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("unsik Kim <donari75@gmail.com>"); +MODULE_DESCRIPTION("mGine m[g]flash device driver"); diff --git a/drivers/block/nbd.c b/drivers/block/nbd.c index 8299e2d3b611..4d6de4f15ccb 100644 --- a/drivers/block/nbd.c +++ b/drivers/block/nbd.c @@ -4,7 +4,7 @@ * Note that you can not swap over this thing, yet. Seems to work but * deadlocks sometimes - you can not swap over TCP in general. * - * Copyright 1997-2000 Pavel Machek <pavel@ucw.cz> + * Copyright 1997-2000, 2008 Pavel Machek <pavel@suse.cz> * Parts copyright 2001 Steven Whitehouse <steve@chygwyn.com> * * This file is released under GPLv2 or later. @@ -276,7 +276,7 @@ static int nbd_send_req(struct nbd_device *lo, struct request *req) return 0; error_out: - return 1; + return -EIO; } static struct request *nbd_find_request(struct nbd_device *lo, @@ -467,9 +467,7 @@ static void nbd_handle_req(struct nbd_device *lo, struct request *req) mutex_unlock(&lo->tx_lock); printk(KERN_ERR "%s: Attempted send on closed socket\n", lo->disk->disk_name); - req->errors++; - nbd_end_request(req); - return; + goto error_out; } lo->active_req = req; @@ -531,7 +529,7 @@ static int nbd_thread(void *data) * { printk( "Warning: Ignoring result!\n"); nbd_end_request( req ); } */ -static void do_nbd_request(struct request_queue * q) +static void do_nbd_request(struct request_queue *q) { struct request *req; @@ -568,27 +566,17 @@ static void do_nbd_request(struct request_queue * q) } } -static int nbd_ioctl(struct block_device *bdev, fmode_t mode, - unsigned int cmd, unsigned long arg) -{ - struct nbd_device *lo = bdev->bd_disk->private_data; - struct file *file; - int error; - struct request sreq ; - struct task_struct *thread; - - if (!capable(CAP_SYS_ADMIN)) - return -EPERM; - - BUG_ON(lo->magic != LO_MAGIC); - - /* Anyone capable of this syscall can do *real bad* things */ - dprintk(DBG_IOCTL, "%s: nbd_ioctl cmd=%s(0x%x) arg=%lu\n", - lo->disk->disk_name, ioctl_cmd_to_ascii(cmd), cmd, arg); +/* Must be called with tx_lock held */ +static int __nbd_ioctl(struct block_device *bdev, struct nbd_device *lo, + unsigned int cmd, unsigned long arg) +{ switch (cmd) { - case NBD_DISCONNECT: + case NBD_DISCONNECT: { + struct request sreq; + printk(KERN_INFO "%s: NBD_DISCONNECT\n", lo->disk->disk_name); + blk_rq_init(NULL, &sreq); sreq.cmd_type = REQ_TYPE_SPECIAL; nbd_cmd(&sreq) = NBD_CMD_DISC; @@ -599,29 +587,29 @@ static int nbd_ioctl(struct block_device *bdev, fmode_t mode, */ sreq.sector = 0; sreq.nr_sectors = 0; - if (!lo->sock) + if (!lo->sock) return -EINVAL; - mutex_lock(&lo->tx_lock); - nbd_send_req(lo, &sreq); - mutex_unlock(&lo->tx_lock); + nbd_send_req(lo, &sreq); return 0; + } - case NBD_CLEAR_SOCK: - error = 0; - mutex_lock(&lo->tx_lock); + case NBD_CLEAR_SOCK: { + struct file *file; + lo->sock = NULL; - mutex_unlock(&lo->tx_lock); file = lo->file; lo->file = NULL; nbd_clear_que(lo); BUG_ON(!list_empty(&lo->queue_head)); if (file) fput(file); - return error; - case NBD_SET_SOCK: + return 0; + } + + case NBD_SET_SOCK: { + struct file *file; if (lo->file) return -EBUSY; - error = -EINVAL; file = fget(arg); if (file) { struct inode *inode = file->f_path.dentry->d_inode; @@ -630,12 +618,14 @@ static int nbd_ioctl(struct block_device *bdev, fmode_t mode, lo->sock = SOCKET_I(inode); if (max_part > 0) bdev->bd_invalidated = 1; - error = 0; + return 0; } else { fput(file); } } - return error; + return -EINVAL; + } + case NBD_SET_BLKSIZE: lo->blksize = arg; lo->bytesize &= ~(lo->blksize-1); @@ -643,35 +633,50 @@ static int nbd_ioctl(struct block_device *bdev, fmode_t mode, set_blocksize(bdev, lo->blksize); set_capacity(lo->disk, lo->bytesize >> 9); return 0; + case NBD_SET_SIZE: lo->bytesize = arg & ~(lo->blksize-1); bdev->bd_inode->i_size = lo->bytesize; set_blocksize(bdev, lo->blksize); set_capacity(lo->disk, lo->bytesize >> 9); return 0; + case NBD_SET_TIMEOUT: lo->xmit_timeout = arg * HZ; return 0; + case NBD_SET_SIZE_BLOCKS: lo->bytesize = ((u64) arg) * lo->blksize; bdev->bd_inode->i_size = lo->bytesize; set_blocksize(bdev, lo->blksize); set_capacity(lo->disk, lo->bytesize >> 9); return 0; - case NBD_DO_IT: + + case NBD_DO_IT: { + struct task_struct *thread; + struct file *file; + int error; + if (lo->pid) return -EBUSY; if (!lo->file) return -EINVAL; + + mutex_unlock(&lo->tx_lock); + thread = kthread_create(nbd_thread, lo, lo->disk->disk_name); - if (IS_ERR(thread)) + if (IS_ERR(thread)) { + mutex_lock(&lo->tx_lock); return PTR_ERR(thread); + } wake_up_process(thread); error = nbd_do_it(lo); kthread_stop(thread); + + mutex_lock(&lo->tx_lock); if (error) return error; - sock_shutdown(lo, 1); + sock_shutdown(lo, 0); file = lo->file; lo->file = NULL; nbd_clear_que(lo); @@ -684,6 +689,8 @@ static int nbd_ioctl(struct block_device *bdev, fmode_t mode, if (max_part > 0) ioctl_by_bdev(bdev, BLKRRPART, 0); return lo->harderror; + } + case NBD_CLEAR_QUE: /* * This is for compatibility only. The queue is always cleared @@ -691,6 +698,7 @@ static int nbd_ioctl(struct block_device *bdev, fmode_t mode, */ BUG_ON(!lo->sock && !list_empty(&lo->queue_head)); return 0; + case NBD_PRINT_DEBUG: printk(KERN_INFO "%s: next = %p, prev = %p, head = %p\n", bdev->bd_disk->disk_name, @@ -698,7 +706,29 @@ static int nbd_ioctl(struct block_device *bdev, fmode_t mode, &lo->queue_head); return 0; } - return -EINVAL; + return -ENOTTY; +} + +static int nbd_ioctl(struct block_device *bdev, fmode_t mode, + unsigned int cmd, unsigned long arg) +{ + struct nbd_device *lo = bdev->bd_disk->private_data; + int error; + + if (!capable(CAP_SYS_ADMIN)) + return -EPERM; + + BUG_ON(lo->magic != LO_MAGIC); + + /* Anyone capable of this syscall can do *real bad* things */ + dprintk(DBG_IOCTL, "%s: nbd_ioctl cmd=%s(0x%x) arg=%lu\n", + lo->disk->disk_name, ioctl_cmd_to_ascii(cmd), cmd, arg); + + mutex_lock(&lo->tx_lock); + error = __nbd_ioctl(bdev, lo, cmd, arg); + mutex_unlock(&lo->tx_lock); + + return error; } static struct block_device_operations nbd_fops = diff --git a/drivers/block/ps3vram.c b/drivers/block/ps3vram.c index 393ed6760d78..8eddef373a91 100644 --- a/drivers/block/ps3vram.c +++ b/drivers/block/ps3vram.c @@ -551,8 +551,6 @@ static void __devinit ps3vram_proc_init(struct ps3_system_bus_device *dev) dev_warn(&dev->core, "failed to create /proc entry\n"); return; } - - pde->owner = THIS_MODULE; pde->data = priv; } diff --git a/drivers/block/swim.c b/drivers/block/swim.c new file mode 100644 index 000000000000..d22cc3856937 --- /dev/null +++ b/drivers/block/swim.c @@ -0,0 +1,995 @@ +/* + * Driver for SWIM (Sander Woz Integrated Machine) floppy controller + * + * Copyright (C) 2004,2008 Laurent Vivier <Laurent@lvivier.info> + * + * based on Alastair Bridgewater SWIM analysis, 2001 + * based on SWIM3 driver (c) Paul Mackerras, 1996 + * based on netBSD IWM driver (c) 1997, 1998 Hauke Fath. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + * + * 2004-08-21 (lv) - Initial implementation + * 2008-10-30 (lv) - Port to 2.6 + */ + +#include <linux/module.h> +#include <linux/fd.h> +#include <linux/blkdev.h> +#include <linux/hdreg.h> +#include <linux/kernel.h> +#include <linux/delay.h> +#include <linux/platform_device.h> + +#include <asm/macintosh.h> +#include <asm/mac_via.h> + +#define CARDNAME "swim" + +struct sector_header { + unsigned char side; + unsigned char track; + unsigned char sector; + unsigned char size; + unsigned char crc0; + unsigned char crc1; +} __attribute__((packed)); + +#define DRIVER_VERSION "Version 0.2 (2008-10-30)" + +#define REG(x) unsigned char x, x ## _pad[0x200 - 1]; + +struct swim { + REG(write_data) + REG(write_mark) + REG(write_CRC) + REG(write_parameter) + REG(write_phase) + REG(write_setup) + REG(write_mode0) + REG(write_mode1) + + REG(read_data) + REG(read_mark) + REG(read_error) + REG(read_parameter) + REG(read_phase) + REG(read_setup) + REG(read_status) + REG(read_handshake) +} __attribute__((packed)); + +#define swim_write(base, reg, v) out_8(&(base)->write_##reg, (v)) +#define swim_read(base, reg) in_8(&(base)->read_##reg) + +/* IWM registers */ + +struct iwm { + REG(ph0L) + REG(ph0H) + REG(ph1L) + REG(ph1H) + REG(ph2L) + REG(ph2H) + REG(ph3L) + REG(ph3H) + REG(mtrOff) + REG(mtrOn) + REG(intDrive) + REG(extDrive) + REG(q6L) + REG(q6H) + REG(q7L) + REG(q7H) +} __attribute__((packed)); + +#define iwm_write(base, reg, v) out_8(&(base)->reg, (v)) +#define iwm_read(base, reg) in_8(&(base)->reg) + +/* bits in phase register */ + +#define SEEK_POSITIVE 0x070 +#define SEEK_NEGATIVE 0x074 +#define STEP 0x071 +#define MOTOR_ON 0x072 +#define MOTOR_OFF 0x076 +#define INDEX 0x073 +#define EJECT 0x077 +#define SETMFM 0x171 +#define SETGCR 0x175 + +#define RELAX 0x033 +#define LSTRB 0x008 + +#define CA_MASK 0x077 + +/* Select values for swim_select and swim_readbit */ + +#define READ_DATA_0 0x074 +#define TWOMEG_DRIVE 0x075 +#define SINGLE_SIDED 0x076 +#define DRIVE_PRESENT 0x077 +#define DISK_IN 0x170 +#define WRITE_PROT 0x171 +#define TRACK_ZERO 0x172 +#define TACHO 0x173 +#define READ_DATA_1 0x174 +#define MFM_MODE 0x175 +#define SEEK_COMPLETE 0x176 +#define ONEMEG_MEDIA 0x177 + +/* Bits in handshake register */ + +#define MARK_BYTE 0x01 +#define CRC_ZERO 0x02 +#define RDDATA 0x04 +#define SENSE 0x08 +#define MOTEN 0x10 +#define ERROR 0x20 +#define DAT2BYTE 0x40 +#define DAT1BYTE 0x80 + +/* bits in setup register */ + +#define S_INV_WDATA 0x01 +#define S_3_5_SELECT 0x02 +#define S_GCR 0x04 +#define S_FCLK_DIV2 0x08 +#define S_ERROR_CORR 0x10 +#define S_IBM_DRIVE 0x20 +#define S_GCR_WRITE 0x40 +#define S_TIMEOUT 0x80 + +/* bits in mode register */ + +#define CLFIFO 0x01 +#define ENBL1 0x02 +#define ENBL2 0x04 +#define ACTION 0x08 +#define WRITE_MODE 0x10 +#define HEDSEL 0x20 +#define MOTON 0x80 + +/*----------------------------------------------------------------------------*/ + +enum drive_location { + INTERNAL_DRIVE = 0x02, + EXTERNAL_DRIVE = 0x04, +}; + +enum media_type { + DD_MEDIA, + HD_MEDIA, +}; + +struct floppy_state { + + /* physical properties */ + + enum drive_location location; /* internal or external drive */ + int head_number; /* single- or double-sided drive */ + + /* media */ + + int disk_in; + int ejected; + enum media_type type; + int write_protected; + + int total_secs; + int secpercyl; + int secpertrack; + + /* in-use information */ + + int track; + int ref_count; + + struct gendisk *disk; + + /* parent controller */ + + struct swim_priv *swd; +}; + +enum motor_action { + OFF, + ON, +}; + +enum head { + LOWER_HEAD = 0, + UPPER_HEAD = 1, +}; + +#define FD_MAX_UNIT 2 + +struct swim_priv { + struct swim __iomem *base; + spinlock_t lock; + struct request_queue *queue; + int floppy_count; + struct floppy_state unit[FD_MAX_UNIT]; +}; + +extern int swim_read_sector_header(struct swim __iomem *base, + struct sector_header *header); +extern int swim_read_sector_data(struct swim __iomem *base, + unsigned char *data); + +static inline void set_swim_mode(struct swim __iomem *base, int enable) +{ + struct iwm __iomem *iwm_base; + unsigned long flags; + + if (!enable) { + swim_write(base, mode0, 0xf8); + return; + } + + iwm_base = (struct iwm __iomem *)base; + local_irq_save(flags); + + iwm_read(iwm_base, q7L); + iwm_read(iwm_base, mtrOff); + iwm_read(iwm_base, q6H); + + iwm_write(iwm_base, q7H, 0x57); + iwm_write(iwm_base, q7H, 0x17); + iwm_write(iwm_base, q7H, 0x57); + iwm_write(iwm_base, q7H, 0x57); + + local_irq_restore(flags); +} + +static inline int get_swim_mode(struct swim __iomem *base) +{ + unsigned long flags; + + local_irq_save(flags); + + swim_write(base, phase, 0xf5); + if (swim_read(base, phase) != 0xf5) + goto is_iwm; + swim_write(base, phase, 0xf6); + if (swim_read(base, phase) != 0xf6) + goto is_iwm; + swim_write(base, phase, 0xf7); + if (swim_read(base, phase) != 0xf7) + goto is_iwm; + local_irq_restore(flags); + return 1; +is_iwm: + local_irq_restore(flags); + return 0; +} + +static inline void swim_select(struct swim __iomem *base, int sel) +{ + swim_write(base, phase, RELAX); + + via1_set_head(sel & 0x100); + + swim_write(base, phase, sel & CA_MASK); +} + +static inline void swim_action(struct swim __iomem *base, int action) +{ + unsigned long flags; + + local_irq_save(flags); + + swim_select(base, action); + udelay(1); + swim_write(base, phase, (LSTRB<<4) | LSTRB); + udelay(1); + swim_write(base, phase, (LSTRB<<4) | ((~LSTRB) & 0x0F)); + udelay(1); + + local_irq_restore(flags); +} + +static inline int swim_readbit(struct swim __iomem *base, int bit) +{ + int stat; + + swim_select(base, bit); + + udelay(10); + + stat = swim_read(base, handshake); + + return (stat & SENSE) == 0; +} + +static inline void swim_drive(struct swim __iomem *base, + enum drive_location location) +{ + if (location == INTERNAL_DRIVE) { + swim_write(base, mode0, EXTERNAL_DRIVE); /* clear drive 1 bit */ + swim_write(base, mode1, INTERNAL_DRIVE); /* set drive 0 bit */ + } else if (location == EXTERNAL_DRIVE) { + swim_write(base, mode0, INTERNAL_DRIVE); /* clear drive 0 bit */ + swim_write(base, mode1, EXTERNAL_DRIVE); /* set drive 1 bit */ + } +} + +static inline void swim_motor(struct swim __iomem *base, + enum motor_action action) +{ + if (action == ON) { + int i; + + swim_action(base, MOTOR_ON); + + for (i = 0; i < 2*HZ; i++) { + swim_select(base, RELAX); + if (swim_readbit(base, MOTOR_ON)) + break; + current->state = TASK_INTERRUPTIBLE; + schedule_timeout(1); + } + } else if (action == OFF) { + swim_action(base, MOTOR_OFF); + swim_select(base, RELAX); + } +} + +static inline void swim_eject(struct swim __iomem *base) +{ + int i; + + swim_action(base, EJECT); + + for (i = 0; i < 2*HZ; i++) { + swim_select(base, RELAX); + if (!swim_readbit(base, DISK_IN)) + break; + current->state = TASK_INTERRUPTIBLE; + schedule_timeout(1); + } + swim_select(base, RELAX); +} + +static inline void swim_head(struct swim __iomem *base, enum head head) +{ + /* wait drive is ready */ + + if (head == UPPER_HEAD) + swim_select(base, READ_DATA_1); + else if (head == LOWER_HEAD) + swim_select(base, READ_DATA_0); +} + +static inline int swim_step(struct swim __iomem *base) +{ + int wait; + + swim_action(base, STEP); + + for (wait = 0; wait < HZ; wait++) { + + current->state = TASK_INTERRUPTIBLE; + schedule_timeout(1); + + swim_select(base, RELAX); + if (!swim_readbit(base, STEP)) + return 0; + } + return -1; +} + +static inline int swim_track00(struct swim __iomem *base) +{ + int try; + + swim_action(base, SEEK_NEGATIVE); + + for (try = 0; try < 100; try++) { + + swim_select(base, RELAX); + if (swim_readbit(base, TRACK_ZERO)) + break; + + if (swim_step(base)) + return -1; + } + + if (swim_readbit(base, TRACK_ZERO)) + return 0; + + return -1; +} + +static inline int swim_seek(struct swim __iomem *base, int step) +{ + if (step == 0) + return 0; + + if (step < 0) { + swim_action(base, SEEK_NEGATIVE); + step = -step; + } else + swim_action(base, SEEK_POSITIVE); + + for ( ; step > 0; step--) { + if (swim_step(base)) + return -1; + } + + return 0; +} + +static inline int swim_track(struct floppy_state *fs, int track) +{ + struct swim __iomem *base = fs->swd->base; + int ret; + + ret = swim_seek(base, track - fs->track); + + if (ret == 0) + fs->track = track; + else { + swim_track00(base); + fs->track = 0; + } + + return ret; +} + +static int floppy_eject(struct floppy_state *fs) +{ + struct swim __iomem *base = fs->swd->base; + + swim_drive(base, fs->location); + swim_motor(base, OFF); + swim_eject(base); + + fs->disk_in = 0; + fs->ejected = 1; + + return 0; +} + +static inline int swim_read_sector(struct floppy_state *fs, + int side, int track, + int sector, unsigned char *buffer) +{ + struct swim __iomem *base = fs->swd->base; + unsigned long flags; + struct sector_header header; + int ret = -1; + short i; + + swim_track(fs, track); + + swim_write(base, mode1, MOTON); + swim_head(base, side); + swim_write(base, mode0, side); + + local_irq_save(flags); + for (i = 0; i < 36; i++) { + ret = swim_read_sector_header(base, &header); + if (!ret && (header.sector == sector)) { + /* found */ + + ret = swim_read_sector_data(base, buffer); + break; + } + } + local_irq_restore(flags); + + swim_write(base, mode0, MOTON); + + if ((header.side != side) || (header.track != track) || + (header.sector != sector)) + return 0; + + return ret; +} + +static int floppy_read_sectors(struct floppy_state *fs, + int req_sector, int sectors_nb, + unsigned char *buffer) +{ + struct swim __iomem *base = fs->swd->base; + int ret; + int side, track, sector; + int i, try; + + + swim_drive(base, fs->location); + for (i = req_sector; i < req_sector + sectors_nb; i++) { + int x; + track = i / fs->secpercyl; + x = i % fs->secpercyl; + side = x / fs->secpertrack; + sector = x % fs->secpertrack + 1; + + try = 5; + do { + ret = swim_read_sector(fs, side, track, sector, + buffer); + if (try-- == 0) + return -1; + } while (ret != 512); + + buffer += ret; + } + + return 0; +} + +static void redo_fd_request(struct request_queue *q) +{ + struct request *req; + struct floppy_state *fs; + + while ((req = elv_next_request(q))) { + + fs = req->rq_disk->private_data; + if (req->sector < 0 || req->sector >= fs->total_secs) { + end_request(req, 0); + continue; + } + if (req->current_nr_sectors == 0) { + end_request(req, 1); + continue; + } + if (!fs->disk_in) { + end_request(req, 0); + continue; + } + if (rq_data_dir(req) == WRITE) { + if (fs->write_protected) { + end_request(req, 0); + continue; + } + } + switch (rq_data_dir(req)) { + case WRITE: + /* NOT IMPLEMENTED */ + end_request(req, 0); + break; + case READ: + if (floppy_read_sectors(fs, req->sector, + req->current_nr_sectors, + req->buffer)) { + end_request(req, 0); + continue; + } + req->nr_sectors -= req->current_nr_sectors; + req->sector += req->current_nr_sectors; + req->buffer += req->current_nr_sectors * 512; + end_request(req, 1); + break; + } + } +} + +static void do_fd_request(struct request_queue *q) +{ + redo_fd_request(q); +} + +static struct floppy_struct floppy_type[4] = { + { 0, 0, 0, 0, 0, 0x00, 0x00, 0x00, 0x00, NULL }, /* no testing */ + { 720, 9, 1, 80, 0, 0x2A, 0x02, 0xDF, 0x50, NULL }, /* 360KB SS 3.5"*/ + { 1440, 9, 2, 80, 0, 0x2A, 0x02, 0xDF, 0x50, NULL }, /* 720KB 3.5" */ + { 2880, 18, 2, 80, 0, 0x1B, 0x00, 0xCF, 0x6C, NULL }, /* 1.44MB 3.5" */ +}; + +static int get_floppy_geometry(struct floppy_state *fs, int type, + struct floppy_struct **g) +{ + if (type >= ARRAY_SIZE(floppy_type)) + return -EINVAL; + + if (type) + *g = &floppy_type[type]; + else if (fs->type == HD_MEDIA) /* High-Density media */ + *g = &floppy_type[3]; + else if (fs->head_number == 2) /* double-sided */ + *g = &floppy_type[2]; + else + *g = &floppy_type[1]; + + return 0; +} + +static void setup_medium(struct floppy_state *fs) +{ + struct swim __iomem *base = fs->swd->base; + + if (swim_readbit(base, DISK_IN)) { + struct floppy_struct *g; + fs->disk_in = 1; + fs->write_protected = swim_readbit(base, WRITE_PROT); + fs->type = swim_readbit(base, ONEMEG_MEDIA); + + if (swim_track00(base)) + printk(KERN_ERR + "SWIM: cannot move floppy head to track 0\n"); + + swim_track00(base); + + get_floppy_geometry(fs, 0, &g); + fs->total_secs = g->size; + fs->secpercyl = g->head * g->sect; + fs->secpertrack = g->sect; + fs->track = 0; + } else { + fs->disk_in = 0; + } +} + +static int floppy_open(struct block_device *bdev, fmode_t mode) +{ + struct floppy_state *fs = bdev->bd_disk->private_data; + struct swim __iomem *base = fs->swd->base; + int err; + + if (fs->ref_count == -1 || (fs->ref_count && mode & FMODE_EXCL)) + return -EBUSY; + + if (mode & FMODE_EXCL) + fs->ref_count = -1; + else + fs->ref_count++; + + swim_write(base, setup, S_IBM_DRIVE | S_FCLK_DIV2); + udelay(10); + swim_drive(base, INTERNAL_DRIVE); + swim_motor(base, ON); + swim_action(base, SETMFM); + if (fs->ejected) + setup_medium(fs); + if (!fs->disk_in) { + err = -ENXIO; + goto out; + } + + if (mode & FMODE_NDELAY) + return 0; + + if (mode & (FMODE_READ|FMODE_WRITE)) { + check_disk_change(bdev); + if ((mode & FMODE_WRITE) && fs->write_protected) { + err = -EROFS; + goto out; + } + } + return 0; +out: + if (fs->ref_count < 0) + fs->ref_count = 0; + else if (fs->ref_count > 0) + --fs->ref_count; + + if (fs->ref_count == 0) + swim_motor(base, OFF); + return err; +} + +static int floppy_release(struct gendisk *disk, fmode_t mode) +{ + struct floppy_state *fs = disk->private_data; + struct swim __iomem *base = fs->swd->base; + + if (fs->ref_count < 0) + fs->ref_count = 0; + else if (fs->ref_count > 0) + --fs->ref_count; + + if (fs->ref_count == 0) + swim_motor(base, OFF); + + return 0; +} + +static int floppy_ioctl(struct block_device *bdev, fmode_t mode, + unsigned int cmd, unsigned long param) +{ + struct floppy_state *fs = bdev->bd_disk->private_data; + int err; + + if ((cmd & 0x80) && !capable(CAP_SYS_ADMIN)) + return -EPERM; + + switch (cmd) { + case FDEJECT: + if (fs->ref_count != 1) + return -EBUSY; + err = floppy_eject(fs); + return err; + + case FDGETPRM: + if (copy_to_user((void __user *) param, (void *) &floppy_type, + sizeof(struct floppy_struct))) + return -EFAULT; + break; + + default: + printk(KERN_DEBUG "SWIM floppy_ioctl: unknown cmd %d\n", + cmd); + return -ENOSYS; + } + return 0; +} + +static int floppy_getgeo(struct block_device *bdev, struct hd_geometry *geo) +{ + struct floppy_state *fs = bdev->bd_disk->private_data; + struct floppy_struct *g; + int ret; + + ret = get_floppy_geometry(fs, 0, &g); + if (ret) + return ret; + + geo->heads = g->head; + geo->sectors = g->sect; + geo->cylinders = g->track; + + return 0; +} + +static int floppy_check_change(struct gendisk *disk) +{ + struct floppy_state *fs = disk->private_data; + + return fs->ejected; +} + +static int floppy_revalidate(struct gendisk *disk) +{ + struct floppy_state *fs = disk->private_data; + struct swim __iomem *base = fs->swd->base; + + swim_drive(base, fs->location); + + if (fs->ejected) + setup_medium(fs); + + if (!fs->disk_in) + swim_motor(base, OFF); + else + fs->ejected = 0; + + return !fs->disk_in; +} + +static struct block_device_operations floppy_fops = { + .owner = THIS_MODULE, + .open = floppy_open, + .release = floppy_release, + .locked_ioctl = floppy_ioctl, + .getgeo = floppy_getgeo, + .media_changed = floppy_check_change, + .revalidate_disk = floppy_revalidate, +}; + +static struct kobject *floppy_find(dev_t dev, int *part, void *data) +{ + struct swim_priv *swd = data; + int drive = (*part & 3); + + if (drive > swd->floppy_count) + return NULL; + + *part = 0; + return get_disk(swd->unit[drive].disk); +} + +static int __devinit swim_add_floppy(struct swim_priv *swd, + enum drive_location location) +{ + struct floppy_state *fs = &swd->unit[swd->floppy_count]; + struct swim __iomem *base = swd->base; + + fs->location = location; + + swim_drive(base, location); + + swim_motor(base, OFF); + + if (swim_readbit(base, SINGLE_SIDED)) + fs->head_number = 1; + else + fs->head_number = 2; + fs->ref_count = 0; + fs->ejected = 1; + + swd->floppy_count++; + + return 0; +} + +static int __devinit swim_floppy_init(struct swim_priv *swd) +{ + int err; + int drive; + struct swim __iomem *base = swd->base; + + /* scan floppy drives */ + + swim_drive(base, INTERNAL_DRIVE); + if (swim_readbit(base, DRIVE_PRESENT)) + swim_add_floppy(swd, INTERNAL_DRIVE); + swim_drive(base, EXTERNAL_DRIVE); + if (swim_readbit(base, DRIVE_PRESENT)) + swim_add_floppy(swd, EXTERNAL_DRIVE); + + /* register floppy drives */ + + err = register_blkdev(FLOPPY_MAJOR, "fd"); + if (err) { + printk(KERN_ERR "Unable to get major %d for SWIM floppy\n", + FLOPPY_MAJOR); + return -EBUSY; + } + + for (drive = 0; drive < swd->floppy_count; drive++) { + swd->unit[drive].disk = alloc_disk(1); + if (swd->unit[drive].disk == NULL) { + err = -ENOMEM; + goto exit_put_disks; + } + swd->unit[drive].swd = swd; + } + + swd->queue = blk_init_queue(do_fd_request, &swd->lock); + if (!swd->queue) { + err = -ENOMEM; + goto exit_put_disks; + } + + for (drive = 0; drive < swd->floppy_count; drive++) { + swd->unit[drive].disk->flags = GENHD_FL_REMOVABLE; + swd->unit[drive].disk->major = FLOPPY_MAJOR; + swd->unit[drive].disk->first_minor = drive; + sprintf(swd->unit[drive].disk->disk_name, "fd%d", drive); + swd->unit[drive].disk->fops = &floppy_fops; + swd->unit[drive].disk->private_data = &swd->unit[drive]; + swd->unit[drive].disk->queue = swd->queue; + set_capacity(swd->unit[drive].disk, 2880); + add_disk(swd->unit[drive].disk); + } + + blk_register_region(MKDEV(FLOPPY_MAJOR, 0), 256, THIS_MODULE, + floppy_find, NULL, swd); + + return 0; + +exit_put_disks: + unregister_blkdev(FLOPPY_MAJOR, "fd"); + while (drive--) + put_disk(swd->unit[drive].disk); + return err; +} + +static int __devinit swim_probe(struct platform_device *dev) +{ + struct resource *res; + struct swim __iomem *swim_base; + struct swim_priv *swd; + int ret; + + res = platform_get_resource_byname(dev, IORESOURCE_MEM, "swim-regs"); + if (!res) { + ret = -ENODEV; + goto out; + } + + if (!request_mem_region(res->start, resource_size(res), CARDNAME)) { + ret = -EBUSY; + goto out; + } + + swim_base = ioremap(res->start, resource_size(res)); + if (!swim_base) { + return -ENOMEM; + goto out_release_io; + } + + /* probe device */ + + set_swim_mode(swim_base, 1); + if (!get_swim_mode(swim_base)) { + printk(KERN_INFO "SWIM device not found !\n"); + ret = -ENODEV; + goto out_iounmap; + } + + /* set platform driver data */ + + swd = kzalloc(sizeof(struct swim_priv), GFP_KERNEL); + if (!swd) { + ret = -ENOMEM; + goto out_iounmap; + } + platform_set_drvdata(dev, swd); + + swd->base = swim_base; + + ret = swim_floppy_init(swd); + if (ret) + goto out_kfree; + + return 0; + +out_kfree: + platform_set_drvdata(dev, NULL); + kfree(swd); +out_iounmap: + iounmap(swim_base); +out_release_io: + release_mem_region(res->start, resource_size(res)); +out: + return ret; +} + +static int __devexit swim_remove(struct platform_device *dev) +{ + struct swim_priv *swd = platform_get_drvdata(dev); + int drive; + struct resource *res; + + blk_unregister_region(MKDEV(FLOPPY_MAJOR, 0), 256); + + for (drive = 0; drive < swd->floppy_count; drive++) { + del_gendisk(swd->unit[drive].disk); + put_disk(swd->unit[drive].disk); + } + + unregister_blkdev(FLOPPY_MAJOR, "fd"); + + blk_cleanup_queue(swd->queue); + + /* eject floppies */ + + for (drive = 0; drive < swd->floppy_count; drive++) + floppy_eject(&swd->unit[drive]); + + iounmap(swd->base); + + res = platform_get_resource_byname(dev, IORESOURCE_MEM, "swim-regs"); + if (res) + release_mem_region(res->start, resource_size(res)); + + platform_set_drvdata(dev, NULL); + kfree(swd); + + return 0; +} + +static struct platform_driver swim_driver = { + .probe = swim_probe, + .remove = __devexit_p(swim_remove), + .driver = { + .name = CARDNAME, + .owner = THIS_MODULE, + }, +}; + +static int __init swim_init(void) +{ + printk(KERN_INFO "SWIM floppy driver %s\n", DRIVER_VERSION); + + return platform_driver_register(&swim_driver); +} +module_init(swim_init); + +static void __exit swim_exit(void) +{ + platform_driver_unregister(&swim_driver); +} +module_exit(swim_exit); + +MODULE_DESCRIPTION("Driver for SWIM floppy controller"); +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Laurent Vivier <laurent@lvivier.info>"); +MODULE_ALIAS_BLOCKDEV_MAJOR(FLOPPY_MAJOR); diff --git a/drivers/block/swim_asm.S b/drivers/block/swim_asm.S new file mode 100644 index 000000000000..c9668206857b --- /dev/null +++ b/drivers/block/swim_asm.S @@ -0,0 +1,247 @@ +/* + * low-level functions for the SWIM floppy controller + * + * needs assembly language because is very timing dependent + * this controller exists only on macintosh 680x0 based + * + * Copyright (C) 2004,2008 Laurent Vivier <Laurent@lvivier.info> + * + * based on Alastair Bridgewater SWIM analysis, 2001 + * based on netBSD IWM driver (c) 1997, 1998 Hauke Fath. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + * + * 2004-08-21 (lv) - Initial implementation + * 2008-11-05 (lv) - add get_swim_mode + */ + + .equ write_data, 0x0000 + .equ write_mark, 0x0200 + .equ write_CRC, 0x0400 + .equ write_parameter,0x0600 + .equ write_phase, 0x0800 + .equ write_setup, 0x0a00 + .equ write_mode0, 0x0c00 + .equ write_mode1, 0x0e00 + .equ read_data, 0x1000 + .equ read_mark, 0x1200 + .equ read_error, 0x1400 + .equ read_parameter, 0x1600 + .equ read_phase, 0x1800 + .equ read_setup, 0x1a00 + .equ read_status, 0x1c00 + .equ read_handshake, 0x1e00 + + .equ o_side, 0 + .equ o_track, 1 + .equ o_sector, 2 + .equ o_size, 3 + .equ o_crc0, 4 + .equ o_crc1, 5 + + .equ seek_time, 30000 + .equ max_retry, 40 + .equ sector_size, 512 + + .global swim_read_sector_header +swim_read_sector_header: + link %a6, #0 + moveml %d1-%d5/%a0-%a4,%sp@- + movel %a6@(0x0c), %a4 + bsr mfm_read_addrmark + moveml %sp@+, %d1-%d5/%a0-%a4 + unlk %a6 + rts + +sector_address_mark: + .byte 0xa1, 0xa1, 0xa1, 0xfe +sector_data_mark: + .byte 0xa1, 0xa1, 0xa1, 0xfb + +mfm_read_addrmark: + movel %a6@(0x08), %a3 + lea %a3@(read_handshake), %a2 + lea %a3@(read_mark), %a3 + moveq #-1, %d0 + movew #seek_time, %d2 + +wait_header_init: + tstb %a3@(read_error - read_mark) + moveb #0x18, %a3@(write_mode0 - read_mark) + moveb #0x01, %a3@(write_mode1 - read_mark) + moveb #0x01, %a3@(write_mode0 - read_mark) + tstb %a3@(read_error - read_mark) + moveb #0x08, %a3@(write_mode1 - read_mark) + + lea sector_address_mark, %a0 + moveq #3, %d1 + +wait_addr_mark_byte: + + tstb %a2@ + dbmi %d2, wait_addr_mark_byte + bpl header_exit + + moveb %a3@, %d3 + cmpb %a0@+, %d3 + dbne %d1, wait_addr_mark_byte + bne wait_header_init + + moveq #max_retry, %d2 + +amark0: tstb %a2@ + dbmi %d2, amark0 + bpl signal_nonyb + + moveb %a3@, %a4@(o_track) + + moveq #max_retry, %d2 + +amark1: tstb %a2@ + dbmi %d2, amark1 + bpl signal_nonyb + + moveb %a3@, %a4@(o_side) + + moveq #max_retry, %d2 + +amark2: tstb %a2@ + dbmi %d2, amark2 + bpl signal_nonyb + + moveb %a3@, %a4@(o_sector) + + moveq #max_retry, %d2 + +amark3: tstb %a2@ + dbmi %d2, amark3 + bpl signal_nonyb + + moveb %a3@, %a4@(o_size) + + moveq #max_retry, %d2 + +crc0: tstb %a2@ + dbmi %d2, crc0 + bpl signal_nonyb + + moveb %a3@, %a4@(o_crc0) + + moveq #max_retry, %d2 + +crc1: tstb %a2@ + dbmi %d2, crc1 + bpl signal_nonyb + + moveb %a3@, %a4@(o_crc1) + + tstb %a3@(read_error - read_mark) + +header_exit: + moveq #0, %d0 + moveb #0x18, %a3@(write_mode0 - read_mark) + rts +signal_nonyb: + moveq #-1, %d0 + moveb #0x18, %a3@(write_mode0 - read_mark) + rts + + .global swim_read_sector_data +swim_read_sector_data: + link %a6, #0 + moveml %d1-%d5/%a0-%a5,%sp@- + movel %a6@(0x0c), %a4 + bsr mfm_read_data + moveml %sp@+, %d1-%d5/%a0-%a5 + unlk %a6 + rts + +mfm_read_data: + movel %a6@(0x08), %a3 + lea %a3@(read_handshake), %a2 + lea %a3@(read_data), %a5 + lea %a3@(read_mark), %a3 + movew #seek_time, %d2 + +wait_data_init: + tstb %a3@(read_error - read_mark) + moveb #0x18, %a3@(write_mode0 - read_mark) + moveb #0x01, %a3@(write_mode1 - read_mark) + moveb #0x01, %a3@(write_mode0 - read_mark) + tstb %a3@(read_error - read_mark) + moveb #0x08, %a3@(write_mode1 - read_mark) + + lea sector_data_mark, %a0 + moveq #3, %d1 + + /* wait data address mark */ + +wait_data_mark_byte: + + tstb %a2@ + dbmi %d2, wait_data_mark_byte + bpl data_exit + + moveb %a3@, %d3 + cmpb %a0@+, %d3 + dbne %d1, wait_data_mark_byte + bne wait_data_init + + /* read data */ + + tstb %a3@(read_error - read_mark) + + movel #sector_size-1, %d4 /* sector size */ +read_new_data: + movew #max_retry, %d2 +read_data_loop: + moveb %a2@, %d5 + andb #0xc0, %d5 + dbne %d2, read_data_loop + beq data_exit + moveb %a5@, %a4@+ + andb #0x40, %d5 + dbne %d4, read_new_data + beq exit_loop + moveb %a5@, %a4@+ + dbra %d4, read_new_data +exit_loop: + + /* read CRC */ + + movew #max_retry, %d2 +data_crc0: + + tstb %a2@ + dbmi %d2, data_crc0 + bpl data_exit + + moveb %a3@, %d5 + + moveq #max_retry, %d2 + +data_crc1: + + tstb %a2@ + dbmi %d2, data_crc1 + bpl data_exit + + moveb %a3@, %d5 + + tstb %a3@(read_error - read_mark) + + moveb #0x18, %a3@(write_mode0 - read_mark) + + /* return number of bytes read */ + + movel #sector_size, %d0 + addw #1, %d4 + subl %d4, %d0 + rts +data_exit: + moveb #0x18, %a3@(write_mode0 - read_mark) + moveq #-1, %d0 + rts diff --git a/drivers/block/sx8.c b/drivers/block/sx8.c index a18e1ca0f761..ff0448e4bf03 100644 --- a/drivers/block/sx8.c +++ b/drivers/block/sx8.c @@ -1586,9 +1586,9 @@ static int carm_init_one (struct pci_dev *pdev, const struct pci_device_id *ent) goto err_out; #ifdef IF_64BIT_DMA_IS_POSSIBLE /* grrrr... */ - rc = pci_set_dma_mask(pdev, DMA_64BIT_MASK); + rc = pci_set_dma_mask(pdev, DMA_BIT_MASK(64)); if (!rc) { - rc = pci_set_consistent_dma_mask(pdev, DMA_64BIT_MASK); + rc = pci_set_consistent_dma_mask(pdev, DMA_BIT_MASK(64)); if (rc) { printk(KERN_ERR DRV_NAME "(%s): consistent DMA mask failure\n", pci_name(pdev)); @@ -1597,7 +1597,7 @@ static int carm_init_one (struct pci_dev *pdev, const struct pci_device_id *ent) pci_dac = 1; } else { #endif - rc = pci_set_dma_mask(pdev, DMA_32BIT_MASK); + rc = pci_set_dma_mask(pdev, DMA_BIT_MASK(32)); if (rc) { printk(KERN_ERR DRV_NAME "(%s): DMA mask failure\n", pci_name(pdev)); diff --git a/drivers/block/ub.c b/drivers/block/ub.c index 12fb816db7b0..69b7f8e77596 100644 --- a/drivers/block/ub.c +++ b/drivers/block/ub.c @@ -391,7 +391,7 @@ static int ub_probe_lun(struct ub_dev *sc, int lnum); */ #ifdef CONFIG_USB_LIBUSUAL -#define ub_usb_ids storage_usb_ids +#define ub_usb_ids usb_storage_usb_ids #else static struct usb_device_id ub_usb_ids[] = { @@ -2146,10 +2146,9 @@ static int ub_get_pipes(struct ub_dev *sc, struct usb_device *dev, ep = &altsetting->endpoint[i].desc; /* Is it a BULK endpoint? */ - if ((ep->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) - == USB_ENDPOINT_XFER_BULK) { + if (usb_endpoint_xfer_bulk(ep)) { /* BULK in or out? */ - if (ep->bEndpointAddress & USB_DIR_IN) { + if (usb_endpoint_dir_in(ep)) { if (ep_in == NULL) ep_in = ep; } else { @@ -2168,9 +2167,9 @@ static int ub_get_pipes(struct ub_dev *sc, struct usb_device *dev, sc->send_ctrl_pipe = usb_sndctrlpipe(dev, 0); sc->recv_ctrl_pipe = usb_rcvctrlpipe(dev, 0); sc->send_bulk_pipe = usb_sndbulkpipe(dev, - ep_out->bEndpointAddress & USB_ENDPOINT_NUMBER_MASK); + usb_endpoint_num(ep_out)); sc->recv_bulk_pipe = usb_rcvbulkpipe(dev, - ep_in->bEndpointAddress & USB_ENDPOINT_NUMBER_MASK); + usb_endpoint_num(ep_in)); return 0; } diff --git a/drivers/block/umem.c b/drivers/block/umem.c index c24e1bdbad43..9744d59a69f2 100644 --- a/drivers/block/umem.c +++ b/drivers/block/umem.c @@ -829,8 +829,8 @@ static int __devinit mm_pci_probe(struct pci_dev *dev, dev_printk(KERN_INFO, &dev->dev, "Micro Memory(tm) controller found (PCI Mem Module (Battery Backup))\n"); - if (pci_set_dma_mask(dev, DMA_64BIT_MASK) && - pci_set_dma_mask(dev, DMA_32BIT_MASK)) { + if (pci_set_dma_mask(dev, DMA_BIT_MASK(64)) && + pci_set_dma_mask(dev, DMA_BIT_MASK(32))) { dev_printk(KERN_WARNING, &dev->dev, "NO suitable DMA found\n"); return -ENOMEM; } diff --git a/drivers/block/xsysace.c b/drivers/block/xsysace.c index 119be3442f28..6cccdc3f5220 100644 --- a/drivers/block/xsysace.c +++ b/drivers/block/xsysace.c @@ -89,6 +89,7 @@ #include <linux/delay.h> #include <linux/slab.h> #include <linux/blkdev.h> +#include <linux/ata.h> #include <linux/hdreg.h> #include <linux/platform_device.h> #if defined(CONFIG_OF) @@ -208,7 +209,7 @@ struct ace_device { struct gendisk *gd; /* Inserted CF card parameters */ - struct hd_driveid cf_id; + u16 cf_id[ATA_ID_WORDS]; }; static int ace_major; @@ -402,21 +403,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 } @@ -614,7 +608,7 @@ static void ace_fsm_dostate(struct ace_device *ace) break; case ACE_FSM_STATE_IDENTIFY_COMPLETE: - ace_fix_driveid(&ace->cf_id); + ace_fix_driveid(&ace->cf_id[0]); ace_dump_mem(&ace->cf_id, 512); /* Debug: Dump out disk ID */ if (ace->data_result) { @@ -627,9 +621,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 */ @@ -928,12 +923,13 @@ static int ace_release(struct gendisk *disk, fmode_t mode) static int ace_getgeo(struct block_device *bdev, struct hd_geometry *geo) { struct ace_device *ace = bdev->bd_disk->private_data; + u16 *cf_id = &ace->cf_id[0]; dev_dbg(ace->dev, "ace_getgeo()\n"); - geo->heads = ace->cf_id.heads; - geo->sectors = ace->cf_id.sectors; - geo->cylinders = ace->cf_id.cyls; + geo->heads = cf_id[ATA_ID_HEADS]; + geo->sectors = cf_id[ATA_ID_SECTORS]; + geo->cylinders = cf_id[ATA_ID_CYLS]; return 0; } |