/* * PATA OCIDEC DRIVER * * Copyright (C) 2014 Giorgio Delmondo LVD Systems srl * * Based on pata_platform - Copyright (C) 2006 - 2007 Paul Mundt * * This file is subject to the terms and conditions of the GNU General Public * License. See the file "COPYING" in the main directory of this archive * for more details. * * TODO: * - dmaengine support */ #include #include #include #include #include #include #include #include #include #include #define DRV_NAME "pata_ocidec" #define REG_SHIFT (2) #define PATA_OCIDEC_DRIVE_SFF (0x10<private_data; int mode; struct ata_timing t; const unsigned long T = 1000000 / 33; unsigned int mr = 0; unsigned int hold; if ( dev ) { mode = dev->pio_mode - XFER_PIO_0; } else { mode = 0; } if (ata_timing_compute(dev, dev->pio_mode, &t, 20000, 20000) < 0) { printk(KERN_ERR DRV_NAME ": mome computation failed.\n"); return; } if ( t.setup < 2 ) t.setup = 0; else t.setup -= 2; // t1 if ( t.active < 2 ) t.active = 0; else t.active -= 2; // t2 if ( t.recover < 2 ) t.recover = 0; else t.recover -= 2; // Teoc mr = t.setup | (t.active<<8) | (t.recover<<24); printk(KERN_INFO "pata_ocidec_set_mode 0x%08X %d\n", (unsigned int)priv, mode); ocidec_write32(priv->host_regs, PATA_OCIDEC_PFTR0, mr); ocidec_write32(priv->host_regs, PATA_OCIDEC_PFTR1, mr); ocidec_write32(priv->host_regs, PATA_OCIDEC_PCTR, mr); ocidec_set32(priv->host_regs, PATA_OCIDEC_CTRL, FAST_TIMING_DEV0_ENABLE | FAST_TIMING_DEV0_IORDY_ENABLE); ocidec_set32(priv->host_regs, PATA_OCIDEC_CTRL, FAST_TIMING_DEV1_ENABLE | FAST_TIMING_DEV1_IORDY_ENABLE); printk(KERN_INFO "pata_ocidec: 0x%08X 0x%08X 0x%08X 0x%08X\n", (unsigned int)priv->oc->np_pftr0, (unsigned int)priv->oc->np_ctrl, ocidec_read32(priv->host_regs,PATA_OCIDEC_PFTR0), ocidec_read32(priv->host_regs,PATA_OCIDEC_PCTR) ); printk(KERN_INFO "pata_ocidec: ctrl = 0x%08X\n", ocidec_read32(priv->host_regs,PATA_OCIDEC_CTRL) ); } static struct scsi_host_template pata_ocidec_sht = { ATA_PIO_SHT(DRV_NAME), }; static bool ocidec_sff_irq_check(struct ata_port *ap) { struct pata_ocidec_priv *priv; priv = ap->private_data; //printk("ocidec_sff_irq_check 0x%08X\n", priv->oc->np_stat ); return ocidec_read32(priv->host_regs, PATA_OCIDEC_STAT)&IDE_INTERRUPT_STATUS; } static void ocidec_sff_irq_clear(struct ata_port *ap) { struct pata_ocidec_priv *priv; priv = ap->private_data; //printk("ocidec_sff_irq_clear 0x%08X\n", priv->oc->np_stat); //ocidec_write32(priv->host_regs, PATA_OCIDEC_STAT, 0x0); } void ocidec_ata_sff_freeze(struct ata_port *ap); static struct ata_port_operations pata_ocidec_port_ops = { .inherits = &ata_sff_port_ops, .sff_data_xfer = ata_sff_data_xfer_noirq, //.sff_irq_check = ocidec_sff_irq_check, //.sff_irq_clear = ocidec_sff_irq_clear, .cable_detect = ata_cable_unknown, .set_piomode = pata_ocidec_set_mode, //.freeze = ocidec_ata_sff_freeze, //.port_start = ATA_OP_NULL, }; static void pata_ocidec_setup_port(struct ata_ioports *ioaddr, struct pata_ocidec_priv *priv) { //ioaddr->cmd_addr = priv->host_regs + PATA_OCIDEC_DRIVE_SFF; ioaddr->ctl_addr = priv->host_regs + PATA_OCIDEC_DRIVE_ASTS; ioaddr->altstatus_addr = priv->host_regs + PATA_OCIDEC_DRIVE_ASTS; ioaddr->data_addr = priv->host_regs + PATA_OCIDEC_DRIVE_SFF + (ATA_REG_DATA << REG_SHIFT); ioaddr->error_addr = priv->host_regs + PATA_OCIDEC_DRIVE_SFF + (ATA_REG_ERR << REG_SHIFT); ioaddr->feature_addr= priv->host_regs + PATA_OCIDEC_DRIVE_SFF + (ATA_REG_FEATURE << REG_SHIFT); ioaddr->nsect_addr = priv->host_regs + PATA_OCIDEC_DRIVE_SFF + (ATA_REG_NSECT << REG_SHIFT); ioaddr->lbal_addr = priv->host_regs + PATA_OCIDEC_DRIVE_SFF + (ATA_REG_LBAL << REG_SHIFT); ioaddr->lbam_addr = priv->host_regs + PATA_OCIDEC_DRIVE_SFF + (ATA_REG_LBAM << REG_SHIFT); ioaddr->lbah_addr = priv->host_regs + PATA_OCIDEC_DRIVE_SFF + (ATA_REG_LBAH << REG_SHIFT); ioaddr->device_addr = priv->host_regs + PATA_OCIDEC_DRIVE_SFF + (ATA_REG_DEVICE << REG_SHIFT); //PATA_OCIDEC_DRIVE_DEVADR ioaddr->status_addr = priv->host_regs + PATA_OCIDEC_DRIVE_SFF + (ATA_REG_STATUS << REG_SHIFT); ioaddr->command_addr= priv->host_regs + PATA_OCIDEC_DRIVE_SFF + (ATA_REG_CMD << REG_SHIFT); //ioaddr->cmd_addr = priv->host_regs + PATA_OCIDEC_DRIVE_SFF + (ATA_REG_CMD << REG_SHIFT); printk(KERN_INFO "cmd_addr = 0x%08X\n", ioaddr->cmd_addr); printk(KERN_INFO "ctl_addr = 0x%08X\n", ioaddr->ctl_addr); printk(KERN_INFO "altstatus_addr = 0x%08X\n", ioaddr->altstatus_addr); printk(KERN_INFO "data_addr = 0x%08X\n", ioaddr->data_addr); printk(KERN_INFO "error_addr = 0x%08X\n", ioaddr->error_addr); printk(KERN_INFO "feature_addr = 0x%08X\n", ioaddr->feature_addr); printk(KERN_INFO "nsect_addr = 0x%08X\n", ioaddr->nsect_addr); printk(KERN_INFO "lbal_addr = 0x%08X\n", ioaddr->lbal_addr); printk(KERN_INFO "lbam_addr = 0x%08X\n", ioaddr->lbam_addr); printk(KERN_INFO "lbah_addr = 0x%08X\n", ioaddr->lbah_addr); printk(KERN_INFO "device_addr = 0x%08X\n", ioaddr->device_addr); printk(KERN_INFO "status_addr = 0x%08X\n", ioaddr->status_addr); printk(KERN_INFO "command_addr = 0x%08X\n", ioaddr->command_addr); } /*static void dl3u_write32 ( unsigned int reg, unsigned int value ) { void __iomem *addr; addr = (void __iomem *)(((unsigned long)g_priv->dl3u_regs) + reg); //printk("writing at 0x%08X val = 0x%08X\n",(unsigned int)addr, reg); iowrite32(value,addr); } static unsigned int dl3u_read32 ( unsigned int reg ) { void __iomem *addr; unsigned int val; addr = (void __iomem *)(((unsigned long)g_priv->dl3u_regs) + reg); //printk("reading at 0x%08X\n",(unsigned int)addr); val = ioread32(addr); //printk("reading at 0x%08X val = 0x%08X\n",(unsigned int)addr, val); return val; }*/ static irqreturn_t ocidec_interrupt(int irq, void *dev_instance) { return ata_sff_interrupt(irq,dev_instance); } static int pata_ocidec_probe(struct platform_device *pdev) { struct ata_host *host; struct ata_port *ap; struct pata_ocidec_priv *priv; int irq = 0; struct resource *io_res; struct resource *ide_irq_res; int irqflags; printk(KERN_INFO "pata_ocidec probing\n"); io_res = platform_get_resource(pdev, IORESOURCE_MEM, 0); if (io_res == NULL) { printk(KERN_ERR "pata_ocidec: failed to get IORESOURCE_MEM\n"); return -EINVAL; } //irq = irq_of_parse_and_map(pdev->dev.of_node, 0); ide_irq_res = platform_get_resource(pdev, IORESOURCE_IRQ, 0); if ((unlikely(ide_irq_res == NULL))) { dev_err(&pdev->dev, "no IDE IRQ resource\n"); return -EINVAL; } irq = (int)ide_irq_res->start;//platform_get_irq(pdev, 0); irqflags = ide_irq_res->flags & IORESOURCE_BITS; if (irq <= 0) { printk(KERN_ERR "pata_ocidec: failed to get IRQ\n"); return -EINVAL; } priv = devm_kzalloc(&pdev->dev, sizeof(struct pata_ocidec_priv), GFP_KERNEL); if (!priv) return -ENOMEM; host = ata_host_alloc(&pdev->dev, 1); if (!host) goto free_priv; ap = host->ports[0]; ap->private_data = priv; ap->ops = &pata_ocidec_port_ops; ap->pio_mask = ATA_PIO6 | ATA_FLAG_NO_ATAPI; //ap->flags |= ATA_FLAG_SLAVE_POSS; printk(KERN_INFO "pata_ocidec: registered at %x %x irq %d\n", io_res->start, resource_size(io_res), irq); priv->host_regs = devm_ioremap(&pdev->dev, io_res->start, resource_size(io_res)); if (!priv->host_regs) { dev_err(&pdev->dev, "failed to map IO/CTL base\n"); goto free_priv; } printk("Mapped, setup port\n"); priv->oc = (np_OCIDEC*)priv->host_regs; pata_ocidec_setup_port(&ap->ioaddr, priv); ata_port_desc(ap, "cmd 0x%llx ctl 0x%llx", (unsigned long long)io_res->start + PATA_OCIDEC_DRIVE_DATA, (unsigned long long)io_res->start + PATA_OCIDEC_DRIVE_CONTROL); ocidec_write32(priv->host_regs, PATA_OCIDEC_CTRL, IDE_ENABLE); ocidec_write32(priv->host_regs, PATA_OCIDEC_CTRL, IDE_ENABLE | ATA_RESET); mdelay(200); ocidec_write32(priv->host_regs, PATA_OCIDEC_CTRL, IDE_ENABLE | COMPATIBLE_TIMING_IORDY_ENABLE | WRITE_PING_PONG_ENABLE); mdelay(200); ocidec_write32(priv->host_regs, PATA_OCIDEC_PCTR, MODE0_TIMING); ocidec_write32(priv->host_regs, PATA_OCIDEC_PFTR0, MODE0_TIMING); ocidec_write32(priv->host_regs, PATA_OCIDEC_PFTR1, MODE0_TIMING); printk(KERN_INFO "pata_ocidec probe: priv=0x%08x\n", (unsigned int)priv); printk("READBACK : PATA_OCIDEC_CTRL = 0x%08X\n", ocidec_read32(priv->host_regs, PATA_OCIDEC_CTRL) ); printk("READBACK : PATA_OCIDEC_PCTR = 0x%08X\n", ocidec_read32(priv->host_regs, PATA_OCIDEC_PCTR) ); printk("READBACK : PATA_OCIDEC_PCTR STS32 = 0x%08X\n", ocidec_read32(priv->host_regs, PATA_OCIDEC_DRIVE_ASTS) ); printk("READBACK : PATA_OCIDEC_PCTR STS8 = 0x%08X\n", ocidec_read8(priv->host_regs, PATA_OCIDEC_DRIVE_ASTS) ); priv->oc->np_stat = 0; udelay(500); g_priv = priv; /* activate */ return ata_host_activate(host, irq, ocidec_interrupt/*ata_sff_interrupt*/, irqflags, &pata_ocidec_sht); free_priv: return -ENOMEM; } static int pata_ocidec_remove(struct platform_device *pdev) { struct ata_host *host = dev_get_drvdata(&pdev->dev); //struct pata_imx_priv *priv = host->private_data; ata_host_detach(host); //free_irq(irq,&pata_ocidec_sht); //__raw_writel(0, priv->host_regs + PATA_IMX_ATA_INT_EN); //clk_disable_unprepare(priv->clk); //clk_put(priv->clk); return 0; } static struct platform_driver pata_ocidec_driver = { .probe = pata_ocidec_probe, .remove = pata_ocidec_remove, .driver = { .name = DRV_NAME, .owner = THIS_MODULE, }, }; /* module_init function */ static int __init ocidec_init(void) { return platform_driver_register(&pata_ocidec_driver); } /* module_exit function */ static void __exit ocidec_exit(void) { platform_driver_unregister(&pata_ocidec_driver); } module_init(ocidec_init); module_exit(ocidec_exit); MODULE_AUTHOR("Giorgio Delmondo "); MODULE_DESCRIPTION("low-level driver for OpenCores OCIDEC"); MODULE_LICENSE("GPL"); MODULE_ALIAS("platform:" DRV_NAME);