From 9b13b682a68d5bcf09c75da73d4e61d92eba4c84 Mon Sep 17 00:00:00 2001 From: Alan Date: Thu, 7 Dec 2006 08:59:14 -0800 Subject: [PATCH] pata_it8213: Add new driver for the IT8213 card Add a driver for the IT8213 which is a single channel ICH-ish PATA controller. As it is very different to the IT8211/2 it gets its own driver. There is a legacy drivers/ide driver also available and I'll post that once I get time to test it all out (probably early January). If anyone else needs the drivers/ide driver and wants to do the merge for drivers/ide (Bart ??) then I'll forward it. [akpm@osdl.org: add PCI ID, constify needed_pio[]] Signed-off-by: Alan Cox Cc: Jeff Garzik Signed-off-by: Andrew Morton Signed-off-by: Jeff Garzik --- drivers/ata/Makefile | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers/ata/Makefile') diff --git a/drivers/ata/Makefile b/drivers/ata/Makefile index bc3d81ae757..50497ecc9b3 100644 --- a/drivers/ata/Makefile +++ b/drivers/ata/Makefile @@ -33,6 +33,7 @@ obj-$(CONFIG_PATA_HPT3X2N) += pata_hpt3x2n.o obj-$(CONFIG_PATA_HPT3X3) += pata_hpt3x3.o obj-$(CONFIG_PATA_ISAPNP) += pata_isapnp.o obj-$(CONFIG_PATA_IT821X) += pata_it821x.o +obj-$(CONFIG_PATA_IT8213) += pata_it8213.o obj-$(CONFIG_PATA_JMICRON) += pata_jmicron.o obj-$(CONFIG_PATA_NETCELL) += pata_netcell.o obj-$(CONFIG_PATA_NS87410) += pata_ns87410.o -- cgit v1.2.3-70-g09d2 From 155d2916d9474f81178f501664499f40833c59b2 Mon Sep 17 00:00:00 2001 From: Sylvain Munaut Date: Fri, 8 Dec 2006 00:14:16 +0100 Subject: [PATCH] libata: Add support for the MPC52xx ATA controller This patch adds initial libata support for the Freescale MPC5200 integrated IDE controller. Signed-off-by: Sylvain Munaut Signed-off-by: Jeff Garzik --- drivers/ata/Kconfig | 9 + drivers/ata/Makefile | 1 + drivers/ata/pata_mpc52xx.c | 563 +++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 573 insertions(+) create mode 100644 drivers/ata/pata_mpc52xx.c (limited to 'drivers/ata/Makefile') diff --git a/drivers/ata/Kconfig b/drivers/ata/Kconfig index afbd61575da..f72b3415d75 100644 --- a/drivers/ata/Kconfig +++ b/drivers/ata/Kconfig @@ -350,6 +350,15 @@ config PATA_MARVELL If unsure, say N. +config PATA_MPC52xx + tristate "Freescale MPC52xx SoC internal IDE" + depends on PPC_MPC52xx + help + This option enables support for integrated IDE controller + of the Freescale MPC52xx SoC. + + If unsure, say N. + config PATA_MPIIX tristate "Intel PATA MPIIX support" depends on PCI diff --git a/drivers/ata/Makefile b/drivers/ata/Makefile index 50497ecc9b3..a0df15d9e4a 100644 --- a/drivers/ata/Makefile +++ b/drivers/ata/Makefile @@ -39,6 +39,7 @@ obj-$(CONFIG_PATA_NETCELL) += pata_netcell.o obj-$(CONFIG_PATA_NS87410) += pata_ns87410.o obj-$(CONFIG_PATA_OPTI) += pata_opti.o obj-$(CONFIG_PATA_OPTIDMA) += pata_optidma.o +obj-$(CONFIG_PATA_MPC52xx) += pata_mpc52xx.o obj-$(CONFIG_PATA_MARVELL) += pata_marvell.o obj-$(CONFIG_PATA_MPIIX) += pata_mpiix.o obj-$(CONFIG_PATA_OLDPIIX) += pata_oldpiix.o diff --git a/drivers/ata/pata_mpc52xx.c b/drivers/ata/pata_mpc52xx.c new file mode 100644 index 00000000000..8b7019a2f19 --- /dev/null +++ b/drivers/ata/pata_mpc52xx.c @@ -0,0 +1,563 @@ +/* + * drivers/ata/pata_mpc52xx.c + * + * libata driver for the Freescale MPC52xx on-chip IDE interface + * + * Copyright (C) 2006 Sylvain Munaut + * Copyright (C) 2003 Mipsys - Benjamin Herrenschmidt + * + * This file is licensed under the terms of the GNU General Public License + * version 2. This program is licensed "as is" without any warranty of any + * kind, whether express or implied. + */ + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + + +#define DRV_NAME "mpc52xx_ata" +#define DRV_VERSION "0.1.0" + + +/* Private structures used by the driver */ +struct mpc52xx_ata_timings { + u32 pio1; + u32 pio2; +}; + +struct mpc52xx_ata_priv { + unsigned int ipb_period; + struct mpc52xx_ata __iomem * ata_regs; + int ata_irq; + struct mpc52xx_ata_timings timings[2]; + int csel; +}; + + +/* ATAPI-4 PIO specs (in ns) */ +static const int ataspec_t0[5] = {600, 383, 240, 180, 120}; +static const int ataspec_t1[5] = { 70, 50, 30, 30, 25}; +static const int ataspec_t2_8[5] = {290, 290, 290, 80, 70}; +static const int ataspec_t2_16[5] = {165, 125, 100, 80, 70}; +static const int ataspec_t2i[5] = { 0, 0, 0, 70, 25}; +static const int ataspec_t4[5] = { 30, 20, 15, 10, 10}; +static const int ataspec_ta[5] = { 35, 35, 35, 35, 35}; + +#define CALC_CLKCYC(c,v) ((((v)+(c)-1)/(c))) + + +/* Bit definitions inside the registers */ +#define MPC52xx_ATA_HOSTCONF_SMR 0x80000000UL /* State machine reset */ +#define MPC52xx_ATA_HOSTCONF_FR 0x40000000UL /* FIFO Reset */ +#define MPC52xx_ATA_HOSTCONF_IE 0x02000000UL /* Enable interrupt in PIO */ +#define MPC52xx_ATA_HOSTCONF_IORDY 0x01000000UL /* Drive supports IORDY protocol */ + +#define MPC52xx_ATA_HOSTSTAT_TIP 0x80000000UL /* Transaction in progress */ +#define MPC52xx_ATA_HOSTSTAT_UREP 0x40000000UL /* UDMA Read Extended Pause */ +#define MPC52xx_ATA_HOSTSTAT_RERR 0x02000000UL /* Read Error */ +#define MPC52xx_ATA_HOSTSTAT_WERR 0x01000000UL /* Write Error */ + +#define MPC52xx_ATA_FIFOSTAT_EMPTY 0x01 /* FIFO Empty */ + +#define MPC52xx_ATA_DMAMODE_WRITE 0x01 /* Write DMA */ +#define MPC52xx_ATA_DMAMODE_READ 0x02 /* Read DMA */ +#define MPC52xx_ATA_DMAMODE_UDMA 0x04 /* UDMA enabled */ +#define MPC52xx_ATA_DMAMODE_IE 0x08 /* Enable drive interrupt to CPU in DMA mode */ +#define MPC52xx_ATA_DMAMODE_FE 0x10 /* FIFO Flush enable in Rx mode */ +#define MPC52xx_ATA_DMAMODE_FR 0x20 /* FIFO Reset */ +#define MPC52xx_ATA_DMAMODE_HUT 0x40 /* Host UDMA burst terminate */ + + +/* Structure of the hardware registers */ +struct mpc52xx_ata { + + /* Host interface registers */ + u32 config; /* ATA + 0x00 Host configuration */ + u32 host_status; /* ATA + 0x04 Host controller status */ + u32 pio1; /* ATA + 0x08 PIO Timing 1 */ + u32 pio2; /* ATA + 0x0c PIO Timing 2 */ + u32 mdma1; /* ATA + 0x10 MDMA Timing 1 */ + u32 mdma2; /* ATA + 0x14 MDMA Timing 2 */ + u32 udma1; /* ATA + 0x18 UDMA Timing 1 */ + u32 udma2; /* ATA + 0x1c UDMA Timing 2 */ + u32 udma3; /* ATA + 0x20 UDMA Timing 3 */ + u32 udma4; /* ATA + 0x24 UDMA Timing 4 */ + u32 udma5; /* ATA + 0x28 UDMA Timing 5 */ + u32 share_cnt; /* ATA + 0x2c ATA share counter */ + u32 reserved0[3]; + + /* FIFO registers */ + u32 fifo_data; /* ATA + 0x3c */ + u8 fifo_status_frame; /* ATA + 0x40 */ + u8 fifo_status; /* ATA + 0x41 */ + u16 reserved7[1]; + u8 fifo_control; /* ATA + 0x44 */ + u8 reserved8[5]; + u16 fifo_alarm; /* ATA + 0x4a */ + u16 reserved9; + u16 fifo_rdp; /* ATA + 0x4e */ + u16 reserved10; + u16 fifo_wrp; /* ATA + 0x52 */ + u16 reserved11; + u16 fifo_lfrdp; /* ATA + 0x56 */ + u16 reserved12; + u16 fifo_lfwrp; /* ATA + 0x5a */ + + /* Drive TaskFile registers */ + u8 tf_control; /* ATA + 0x5c TASKFILE Control/Alt Status */ + u8 reserved13[3]; + u16 tf_data; /* ATA + 0x60 TASKFILE Data */ + u16 reserved14; + u8 tf_features; /* ATA + 0x64 TASKFILE Features/Error */ + u8 reserved15[3]; + u8 tf_sec_count; /* ATA + 0x68 TASKFILE Sector Count */ + u8 reserved16[3]; + u8 tf_sec_num; /* ATA + 0x6c TASKFILE Sector Number */ + u8 reserved17[3]; + u8 tf_cyl_low; /* ATA + 0x70 TASKFILE Cylinder Low */ + u8 reserved18[3]; + u8 tf_cyl_high; /* ATA + 0x74 TASKFILE Cylinder High */ + u8 reserved19[3]; + u8 tf_dev_head; /* ATA + 0x78 TASKFILE Device/Head */ + u8 reserved20[3]; + u8 tf_command; /* ATA + 0x7c TASKFILE Command/Status */ + u8 dma_mode; /* ATA + 0x7d ATA Host DMA Mode configuration */ + u8 reserved21[2]; +}; + + +/* ======================================================================== */ +/* Aux fns */ +/* ======================================================================== */ + + +/* MPC52xx low level hw control */ + +static int +mpc52xx_ata_compute_pio_timings(struct mpc52xx_ata_priv *priv, int dev, int pio) +{ + struct mpc52xx_ata_timings *timing = &priv->timings[dev]; + unsigned int ipb_period = priv->ipb_period; + unsigned int t0, t1, t2_8, t2_16, t2i, t4, ta; + + if ((pio<0) || (pio>4)) + return -EINVAL; + + t0 = CALC_CLKCYC(ipb_period, 1000 * ataspec_t0[pio]); + t1 = CALC_CLKCYC(ipb_period, 1000 * ataspec_t1[pio]); + t2_8 = CALC_CLKCYC(ipb_period, 1000 * ataspec_t2_8[pio]); + t2_16 = CALC_CLKCYC(ipb_period, 1000 * ataspec_t2_16[pio]); + t2i = CALC_CLKCYC(ipb_period, 1000 * ataspec_t2i[pio]); + t4 = CALC_CLKCYC(ipb_period, 1000 * ataspec_t4[pio]); + ta = CALC_CLKCYC(ipb_period, 1000 * ataspec_ta[pio]); + + timing->pio1 = (t0 << 24) | (t2_8 << 16) | (t2_16 << 8) | (t2i); + timing->pio2 = (t4 << 24) | (t1 << 16) | (ta << 8); + + return 0; +} + +static void +mpc52xx_ata_apply_timings(struct mpc52xx_ata_priv *priv, int device) +{ + struct mpc52xx_ata __iomem *regs = priv->ata_regs; + struct mpc52xx_ata_timings *timing = &priv->timings[device]; + + out_be32(®s->pio1, timing->pio1); + out_be32(®s->pio2, timing->pio2); + out_be32(®s->mdma1, 0); + out_be32(®s->mdma2, 0); + out_be32(®s->udma1, 0); + out_be32(®s->udma2, 0); + out_be32(®s->udma3, 0); + out_be32(®s->udma4, 0); + out_be32(®s->udma5, 0); + + priv->csel = device; +} + +static int +mpc52xx_ata_hw_init(struct mpc52xx_ata_priv *priv) +{ + struct mpc52xx_ata __iomem *regs = priv->ata_regs; + int tslot; + + /* Clear share_cnt (all sample code do this ...) */ + out_be32(®s->share_cnt, 0); + + /* Configure and reset host */ + out_be32(®s->config, + MPC52xx_ATA_HOSTCONF_IE | + MPC52xx_ATA_HOSTCONF_IORDY | + MPC52xx_ATA_HOSTCONF_SMR | + MPC52xx_ATA_HOSTCONF_FR); + + udelay(10); + + out_be32(®s->config, + MPC52xx_ATA_HOSTCONF_IE | + MPC52xx_ATA_HOSTCONF_IORDY); + + /* Set the time slot to 1us */ + tslot = CALC_CLKCYC(priv->ipb_period, 1000000); + out_be32(®s->share_cnt, tslot << 16 ); + + /* Init timings to PIO0 */ + memset(priv->timings, 0x00, 2*sizeof(struct mpc52xx_ata_timings)); + + mpc52xx_ata_compute_pio_timings(priv, 0, 0); + mpc52xx_ata_compute_pio_timings(priv, 1, 0); + + mpc52xx_ata_apply_timings(priv, 0); + + return 0; +} + + +/* ======================================================================== */ +/* libata driver */ +/* ======================================================================== */ + +static void +mpc52xx_ata_set_piomode(struct ata_port *ap, struct ata_device *adev) +{ + struct mpc52xx_ata_priv *priv = ap->host->private_data; + int pio, rv; + + pio = adev->pio_mode - XFER_PIO_0; + + rv = mpc52xx_ata_compute_pio_timings(priv, adev->devno, pio); + + if (rv) { + printk(KERN_ERR DRV_NAME + ": Trying to select invalid PIO mode %d\n", pio); + return; + } + + mpc52xx_ata_apply_timings(priv, adev->devno); +} +static void +mpc52xx_ata_dev_select(struct ata_port *ap, unsigned int device) +{ + struct mpc52xx_ata_priv *priv = ap->host->private_data; + + if (device != priv->csel) + mpc52xx_ata_apply_timings(priv, device); + + ata_std_dev_select(ap,device); +} + +static void +mpc52xx_ata_error_handler(struct ata_port *ap) +{ + ata_bmdma_drive_eh(ap, ata_std_prereset, ata_std_softreset, NULL, + ata_std_postreset); +} + + + +static struct scsi_host_template mpc52xx_ata_sht = { + .module = THIS_MODULE, + .name = DRV_NAME, + .ioctl = ata_scsi_ioctl, + .queuecommand = ata_scsi_queuecmd, + .can_queue = ATA_DEF_QUEUE, + .this_id = ATA_SHT_THIS_ID, + .sg_tablesize = LIBATA_MAX_PRD, + .max_sectors = ATA_MAX_SECTORS, + .cmd_per_lun = ATA_SHT_CMD_PER_LUN, + .emulated = ATA_SHT_EMULATED, + .use_clustering = ATA_SHT_USE_CLUSTERING, + .proc_name = DRV_NAME, + .dma_boundary = ATA_DMA_BOUNDARY, + .slave_configure = ata_scsi_slave_config, + .bios_param = ata_std_bios_param, +}; + +static struct ata_port_operations mpc52xx_ata_port_ops = { + .port_disable = ata_port_disable, + .set_piomode = mpc52xx_ata_set_piomode, + .dev_select = mpc52xx_ata_dev_select, + .tf_load = ata_tf_load, + .tf_read = ata_tf_read, + .check_status = ata_check_status, + .exec_command = ata_exec_command, + .freeze = ata_bmdma_freeze, + .thaw = ata_bmdma_thaw, + .error_handler = mpc52xx_ata_error_handler, + .qc_prep = ata_qc_prep, + .qc_issue = ata_qc_issue_prot, + .data_xfer = ata_mmio_data_xfer, + .irq_handler = ata_interrupt, + .irq_clear = ata_bmdma_irq_clear, + .port_start = ata_port_start, + .port_stop = ata_port_stop, + .host_stop = ata_host_stop, +}; + +static struct ata_probe_ent mpc52xx_ata_probe_ent = { + .port_ops = &mpc52xx_ata_port_ops, + .sht = &mpc52xx_ata_sht, + .n_ports = 1, + .pio_mask = 0x1f, /* Up to PIO4 */ + .mwdma_mask = 0x00, /* No MWDMA */ + .udma_mask = 0x00, /* No UDMA */ + .port_flags = ATA_FLAG_SLAVE_POSS | ATA_FLAG_SRST | ATA_FLAG_MMIO, + .irq_flags = 0, +}; + +static int __devinit +mpc52xx_ata_init_one(struct device *dev, struct mpc52xx_ata_priv *priv) +{ + struct ata_probe_ent *ae = &mpc52xx_ata_probe_ent; + struct ata_ioports *aio = &ae->port[0]; + int rv; + + INIT_LIST_HEAD(&ae->node); + ae->dev = dev; + ae->irq = priv->ata_irq; + + aio->cmd_addr = 0; /* Don't have a classic reg block */ + aio->altstatus_addr = (unsigned long)&priv->ata_regs->tf_control; + aio->ctl_addr = (unsigned long)&priv->ata_regs->tf_control; + aio->data_addr = (unsigned long)&priv->ata_regs->tf_data; + aio->error_addr = (unsigned long)&priv->ata_regs->tf_features; + aio->feature_addr = (unsigned long)&priv->ata_regs->tf_features; + aio->nsect_addr = (unsigned long)&priv->ata_regs->tf_sec_count; + aio->lbal_addr = (unsigned long)&priv->ata_regs->tf_sec_num; + aio->lbam_addr = (unsigned long)&priv->ata_regs->tf_cyl_low; + aio->lbah_addr = (unsigned long)&priv->ata_regs->tf_cyl_high; + aio->device_addr = (unsigned long)&priv->ata_regs->tf_dev_head; + aio->status_addr = (unsigned long)&priv->ata_regs->tf_command; + aio->command_addr = (unsigned long)&priv->ata_regs->tf_command; + + ae->private_data = priv; + + rv = ata_device_add(ae); + + return rv ? 0 : -EINVAL; +} + +static struct mpc52xx_ata_priv * +mpc52xx_ata_remove_one(struct device *dev) +{ + struct ata_host *host = dev_get_drvdata(dev); + struct mpc52xx_ata_priv *priv = host->private_data; + + ata_host_remove(host); + + return priv; +} + + +/* ======================================================================== */ +/* OF Platform driver */ +/* ======================================================================== */ + +static int __devinit +mpc52xx_ata_probe(struct of_device *op, const struct of_device_id *match) +{ + unsigned int ipb_freq; + struct resource res_mem; + int ata_irq = NO_IRQ; + struct mpc52xx_ata __iomem *ata_regs = NULL; + struct mpc52xx_ata_priv *priv = NULL; + int rv; + + /* Get ipb frequency */ + ipb_freq = mpc52xx_find_ipb_freq(op->node); + if (!ipb_freq) { + printk(KERN_ERR DRV_NAME ": " + "Unable to find IPB Bus frequency\n" ); + return -ENODEV; + } + + /* Get IRQ and register */ + rv = of_address_to_resource(op->node, 0, &res_mem); + if (rv) { + printk(KERN_ERR DRV_NAME ": " + "Error while parsing device node resource\n" ); + return rv; + } + + ata_irq = irq_of_parse_and_map(op->node, 0); + if (ata_irq == NO_IRQ) { + printk(KERN_ERR DRV_NAME ": " + "Error while mapping the irq\n"); + return -EINVAL; + } + + /* Request mem region */ + if (!request_mem_region(res_mem.start, + sizeof(struct mpc52xx_ata), DRV_NAME)) { + printk(KERN_ERR DRV_NAME ": " + "Error while requesting mem region\n"); + irq_dispose_mapping(ata_irq); + return -EBUSY; + } + + /* Remap registers */ + ata_regs = ioremap(res_mem.start, sizeof(struct mpc52xx_ata)); + if (!ata_regs) { + printk(KERN_ERR DRV_NAME ": " + "Error while mapping register set\n"); + rv = -ENOMEM; + goto err; + } + + /* Prepare our private structure */ + priv = kmalloc(sizeof(struct mpc52xx_ata_priv), GFP_ATOMIC); + if (!priv) { + printk(KERN_ERR DRV_NAME ": " + "Error while allocating private structure\n"); + rv = -ENOMEM; + goto err; + } + + priv->ipb_period = 1000000000 / (ipb_freq / 1000); + priv->ata_regs = ata_regs; + priv->ata_irq = ata_irq; + priv->csel = -1; + + /* Init the hw */ + rv = mpc52xx_ata_hw_init(priv); + if (rv) { + printk(KERN_ERR DRV_NAME ": Error during HW init\n"); + goto err; + } + + /* Register ourselves to libata */ + rv = mpc52xx_ata_init_one(&op->dev, priv); + if (rv) { + printk(KERN_ERR DRV_NAME ": " + "Error while registering to ATA layer\n"); + return rv; + } + + /* Done */ + return 0; + + /* Error path */ +err: + kfree(priv); + + if (ata_regs) + iounmap(ata_regs); + + release_mem_region(res_mem.start, sizeof(struct mpc52xx_ata)); + + irq_dispose_mapping(ata_irq); + + return rv; +} + +static int +mpc52xx_ata_remove(struct of_device *op) +{ + struct mpc52xx_ata_priv *priv; + struct resource res_mem; + int rv; + + /* Unregister */ + priv = mpc52xx_ata_remove_one(&op->dev); + + /* Free everything */ + iounmap(priv->ata_regs); + + rv = of_address_to_resource(op->node, 0, &res_mem); + if (rv) { + printk(KERN_ERR DRV_NAME ": " + "Error while parsing device node resource\n"); + printk(KERN_ERR DRV_NAME ": " + "Zone may not be properly released\n"); + } else + release_mem_region(res_mem.start, sizeof(struct mpc52xx_ata)); + + irq_dispose_mapping(priv->ata_irq); + + kfree(priv); + + return 0; +} + + +#ifdef CONFIG_PM + +static int +mpc52xx_ata_suspend(struct of_device *op, pm_message_t state) +{ + return 0; /* FIXME : What to do here ? */ +} + +static int +mpc52xx_ata_resume(struct of_device *op) +{ + return 0; /* FIXME : What to do here ? */ +} + +#endif + + +static struct of_device_id mpc52xx_ata_of_match[] = { + { + .compatible = "mpc5200-ata", + }, + { + .compatible = "mpc52xx-ata", + }, + {}, +}; + + +static struct of_platform_driver mpc52xx_ata_of_platform_driver = { + .owner = THIS_MODULE, + .name = DRV_NAME, + .match_table = mpc52xx_ata_of_match, + .probe = mpc52xx_ata_probe, + .remove = mpc52xx_ata_remove, +#ifdef CONFIG_PM + .suspend = mpc52xx_ata_suspend, + .resume = mpc52xx_ata_resume, +#endif + .driver = { + .name = DRV_NAME, + .owner = THIS_MODULE, + }, +}; + + +/* ======================================================================== */ +/* Module */ +/* ======================================================================== */ + +static int __init +mpc52xx_ata_init(void) +{ + printk(KERN_INFO "ata: MPC52xx IDE/ATA libata driver\n"); + return of_register_platform_driver(&mpc52xx_ata_of_platform_driver); +} + +static void __exit +mpc52xx_ata_exit(void) +{ + of_unregister_platform_driver(&mpc52xx_ata_of_platform_driver); +} + +module_init(mpc52xx_ata_init); +module_exit(mpc52xx_ata_exit); + +MODULE_AUTHOR("Sylvain Munaut "); +MODULE_DESCRIPTION("Freescale MPC52xx IDE/ATA libata driver"); +MODULE_LICENSE("GPL"); +MODULE_DEVICE_TABLE(of, mpc52xx_ata_of_match); +MODULE_VERSION(DRV_VERSION); + -- cgit v1.2.3-70-g09d2 From 1fd7a697a37bcd484b130a71326e43cd68ced90c Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Wed, 3 Jan 2007 17:32:45 +0900 Subject: sata_inic162x: finally, driver for initio 162x SATA controllers, take #2 Driver for Initio 162x SATA controllers. ATA r/w, ATAPI r, hotplug and suspend/resume work. ATAPI w (recording, that is) broken. Feel free to fix it, but be warned, this controller is weird. Signed-off-by: Tejun Heo Signed-off-by: Jeff Garzik --- drivers/ata/Kconfig | 6 + drivers/ata/Makefile | 1 + drivers/ata/sata_inic162x.c | 809 ++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 816 insertions(+) create mode 100644 drivers/ata/sata_inic162x.c (limited to 'drivers/ata/Makefile') diff --git a/drivers/ata/Kconfig b/drivers/ata/Kconfig index f72b3415d75..ea102c08922 100644 --- a/drivers/ata/Kconfig +++ b/drivers/ata/Kconfig @@ -147,6 +147,12 @@ config SATA_VITESSE If unsure, say N. +config SATA_INIC162X + tristate "Initio 162x SATA support (HIGHLY EXPERIMENTAL)" + depends on PCI && EXPERIMENTAL + help + This option enables support for Initio 162x Serial ATA. + config SATA_INTEL_COMBINED bool depends on IDE=y && !BLK_DEV_IDE_SATA && (SATA_AHCI || ATA_PIIX) diff --git a/drivers/ata/Makefile b/drivers/ata/Makefile index a0df15d9e4a..cd096f0c78a 100644 --- a/drivers/ata/Makefile +++ b/drivers/ata/Makefile @@ -15,6 +15,7 @@ obj-$(CONFIG_SATA_SX4) += sata_sx4.o obj-$(CONFIG_SATA_NV) += sata_nv.o obj-$(CONFIG_SATA_ULI) += sata_uli.o obj-$(CONFIG_SATA_MV) += sata_mv.o +obj-$(CONFIG_SATA_INIC162X) += sata_inic162x.o obj-$(CONFIG_PDC_ADMA) += pdc_adma.o obj-$(CONFIG_PATA_ALI) += pata_ali.o diff --git a/drivers/ata/sata_inic162x.c b/drivers/ata/sata_inic162x.c new file mode 100644 index 00000000000..b67817e440c --- /dev/null +++ b/drivers/ata/sata_inic162x.c @@ -0,0 +1,809 @@ +/* + * sata_inic162x.c - Driver for Initio 162x SATA controllers + * + * Copyright 2006 SUSE Linux Products GmbH + * Copyright 2006 Tejun Heo + * + * This file is released under GPL v2. + * + * This controller is eccentric and easily locks up if something isn't + * right. Documentation is available at initio's website but it only + * documents registers (not programming model). + * + * - ATA disks work. + * - Hotplug works. + * - ATAPI read works but burning doesn't. This thing is really + * peculiar about ATAPI and I couldn't figure out how ATAPI PIO and + * ATAPI DMA WRITE should be programmed. If you've got a clue, be + * my guest. + * - Both STR and STD work. + */ + +#include +#include +#include +#include +#include +#include +#include + +#define DRV_NAME "sata_inic162x" +#define DRV_VERSION "0.1" + +enum { + MMIO_BAR = 5, + + NR_PORTS = 2, + + HOST_CTL = 0x7c, + HOST_STAT = 0x7e, + HOST_IRQ_STAT = 0xbc, + HOST_IRQ_MASK = 0xbe, + + PORT_SIZE = 0x40, + + /* registers for ATA TF operation */ + PORT_TF = 0x00, + PORT_ALT_STAT = 0x08, + PORT_IRQ_STAT = 0x09, + PORT_IRQ_MASK = 0x0a, + PORT_PRD_CTL = 0x0b, + PORT_PRD_ADDR = 0x0c, + PORT_PRD_XFERLEN = 0x10, + + /* IDMA register */ + PORT_IDMA_CTL = 0x14, + + PORT_SCR = 0x20, + + /* HOST_CTL bits */ + HCTL_IRQOFF = (1 << 8), /* global IRQ off */ + HCTL_PWRDWN = (1 << 13), /* power down PHYs */ + HCTL_SOFTRST = (1 << 13), /* global reset (no phy reset) */ + HCTL_RPGSEL = (1 << 15), /* register page select */ + + HCTL_KNOWN_BITS = HCTL_IRQOFF | HCTL_PWRDWN | HCTL_SOFTRST | + HCTL_RPGSEL, + + /* HOST_IRQ_(STAT|MASK) bits */ + HIRQ_PORT0 = (1 << 0), + HIRQ_PORT1 = (1 << 1), + HIRQ_SOFT = (1 << 14), + HIRQ_GLOBAL = (1 << 15), /* STAT only */ + + /* PORT_IRQ_(STAT|MASK) bits */ + PIRQ_OFFLINE = (1 << 0), /* device unplugged */ + PIRQ_ONLINE = (1 << 1), /* device plugged */ + PIRQ_COMPLETE = (1 << 2), /* completion interrupt */ + PIRQ_FATAL = (1 << 3), /* fatal error */ + PIRQ_ATA = (1 << 4), /* ATA interrupt */ + PIRQ_REPLY = (1 << 5), /* reply FIFO not empty */ + PIRQ_PENDING = (1 << 7), /* port IRQ pending (STAT only) */ + + PIRQ_ERR = PIRQ_OFFLINE | PIRQ_ONLINE | PIRQ_FATAL, + + PIRQ_MASK_DMA_READ = PIRQ_REPLY | PIRQ_ATA, + PIRQ_MASK_OTHER = PIRQ_REPLY | PIRQ_COMPLETE, + PIRQ_MASK_FREEZE = 0xff, + + /* PORT_PRD_CTL bits */ + PRD_CTL_START = (1 << 0), + PRD_CTL_WR = (1 << 3), + PRD_CTL_DMAEN = (1 << 7), /* DMA enable */ + + /* PORT_IDMA_CTL bits */ + IDMA_CTL_RST_ATA = (1 << 2), /* hardreset ATA bus */ + IDMA_CTL_RST_IDMA = (1 << 5), /* reset IDMA machinary */ + IDMA_CTL_GO = (1 << 7), /* IDMA mode go */ + IDMA_CTL_ATA_NIEN = (1 << 8), /* ATA IRQ disable */ +}; + +struct inic_host_priv { + u16 cached_hctl; +}; + +struct inic_port_priv { + u8 dfl_prdctl; + u8 cached_prdctl; + u8 cached_pirq_mask; +}; + +static int inic_slave_config(struct scsi_device *sdev) +{ + /* This controller is braindamaged. dma_boundary is 0xffff + * like others but it will lock up the whole machine HARD if + * 65536 byte PRD entry is fed. Reduce maximum segment size. + */ + blk_queue_max_segment_size(sdev->request_queue, 65536 - 512); + + return ata_scsi_slave_config(sdev); +} + +static struct scsi_host_template inic_sht = { + .module = THIS_MODULE, + .name = DRV_NAME, + .ioctl = ata_scsi_ioctl, + .queuecommand = ata_scsi_queuecmd, + .can_queue = ATA_DEF_QUEUE, + .this_id = ATA_SHT_THIS_ID, + .sg_tablesize = LIBATA_MAX_PRD, + .cmd_per_lun = ATA_SHT_CMD_PER_LUN, + .emulated = ATA_SHT_EMULATED, + .use_clustering = ATA_SHT_USE_CLUSTERING, + .proc_name = DRV_NAME, + .dma_boundary = ATA_DMA_BOUNDARY, + .slave_configure = inic_slave_config, + .slave_destroy = ata_scsi_slave_destroy, + .bios_param = ata_std_bios_param, + .suspend = ata_scsi_device_suspend, + .resume = ata_scsi_device_resume, +}; + +static const int scr_map[] = { + [SCR_STATUS] = 0, + [SCR_ERROR] = 1, + [SCR_CONTROL] = 2, +}; + +static void __iomem * inic_port_base(struct ata_port *ap) +{ + return ap->host->mmio_base + ap->port_no * PORT_SIZE; +} + +static void __inic_set_pirq_mask(struct ata_port *ap, u8 mask) +{ + void __iomem *port_base = inic_port_base(ap); + struct inic_port_priv *pp = ap->private_data; + + writeb(mask, port_base + PORT_IRQ_MASK); + pp->cached_pirq_mask = mask; +} + +static void inic_set_pirq_mask(struct ata_port *ap, u8 mask) +{ + struct inic_port_priv *pp = ap->private_data; + + if (pp->cached_pirq_mask != mask) + __inic_set_pirq_mask(ap, mask); +} + +static void inic_reset_port(void __iomem *port_base) +{ + void __iomem *idma_ctl = port_base + PORT_IDMA_CTL; + u16 ctl; + + ctl = readw(idma_ctl); + ctl &= ~(IDMA_CTL_RST_IDMA | IDMA_CTL_ATA_NIEN | IDMA_CTL_GO); + + /* mask IRQ and assert reset */ + writew(ctl | IDMA_CTL_RST_IDMA | IDMA_CTL_ATA_NIEN, idma_ctl); + readw(idma_ctl); /* flush */ + + /* give it some time */ + msleep(1); + + /* release reset */ + writew(ctl | IDMA_CTL_ATA_NIEN, idma_ctl); + + /* clear irq */ + writeb(0xff, port_base + PORT_IRQ_STAT); + + /* reenable ATA IRQ, turn off IDMA mode */ + writew(ctl, idma_ctl); +} + +static u32 inic_scr_read(struct ata_port *ap, unsigned sc_reg) +{ + void __iomem *scr_addr = (void __iomem *)ap->ioaddr.scr_addr; + void __iomem *addr; + u32 val; + + if (unlikely(sc_reg >= ARRAY_SIZE(scr_map))) + return 0xffffffffU; + + addr = scr_addr + scr_map[sc_reg] * 4; + val = readl(scr_addr + scr_map[sc_reg] * 4); + + /* this controller has stuck DIAG.N, ignore it */ + if (sc_reg == SCR_ERROR) + val &= ~SERR_PHYRDY_CHG; + return val; +} + +static void inic_scr_write(struct ata_port *ap, unsigned sc_reg, u32 val) +{ + void __iomem *scr_addr = (void __iomem *)ap->ioaddr.scr_addr; + void __iomem *addr; + + if (unlikely(sc_reg >= ARRAY_SIZE(scr_map))) + return; + + addr = scr_addr + scr_map[sc_reg] * 4; + writel(val, scr_addr + scr_map[sc_reg] * 4); +} + +/* + * In TF mode, inic162x is very similar to SFF device. TF registers + * function the same. DMA engine behaves similary using the same PRD + * format as BMDMA but different command register, interrupt and event + * notification methods are used. The following inic_bmdma_*() + * functions do the impedance matching. + */ +static void inic_bmdma_setup(struct ata_queued_cmd *qc) +{ + struct ata_port *ap = qc->ap; + struct inic_port_priv *pp = ap->private_data; + void __iomem *port_base = inic_port_base(ap); + int rw = qc->tf.flags & ATA_TFLAG_WRITE; + + /* make sure device sees PRD table writes */ + wmb(); + + /* load transfer length */ + writel(qc->nbytes, port_base + PORT_PRD_XFERLEN); + + /* turn on DMA and specify data direction */ + pp->cached_prdctl = pp->dfl_prdctl | PRD_CTL_DMAEN; + if (!rw) + pp->cached_prdctl |= PRD_CTL_WR; + writeb(pp->cached_prdctl, port_base + PORT_PRD_CTL); + + /* issue r/w command */ + ap->ops->exec_command(ap, &qc->tf); +} + +static void inic_bmdma_start(struct ata_queued_cmd *qc) +{ + struct ata_port *ap = qc->ap; + struct inic_port_priv *pp = ap->private_data; + void __iomem *port_base = inic_port_base(ap); + + /* start host DMA transaction */ + pp->cached_prdctl |= PRD_CTL_START; + writeb(pp->cached_prdctl, port_base + PORT_PRD_CTL); +} + +static void inic_bmdma_stop(struct ata_queued_cmd *qc) +{ + struct ata_port *ap = qc->ap; + struct inic_port_priv *pp = ap->private_data; + void __iomem *port_base = inic_port_base(ap); + + /* stop DMA engine */ + writeb(pp->dfl_prdctl, port_base + PORT_PRD_CTL); +} + +static u8 inic_bmdma_status(struct ata_port *ap) +{ + /* event is already verified by the interrupt handler */ + return ATA_DMA_INTR; +} + +static void inic_irq_clear(struct ata_port *ap) +{ + /* noop */ +} + +static void inic_host_intr(struct ata_port *ap) +{ + void __iomem *port_base = inic_port_base(ap); + struct ata_eh_info *ehi = &ap->eh_info; + u8 irq_stat; + + /* fetch and clear irq */ + irq_stat = readb(port_base + PORT_IRQ_STAT); + writeb(irq_stat, port_base + PORT_IRQ_STAT); + + if (likely(!(irq_stat & PIRQ_ERR))) { + struct ata_queued_cmd *qc = ata_qc_from_tag(ap, ap->active_tag); + + if (unlikely(!qc || (qc->tf.flags & ATA_TFLAG_POLLING))) { + ata_chk_status(ap); /* clear ATA interrupt */ + return; + } + + if (likely(ata_host_intr(ap, qc))) + return; + + ata_chk_status(ap); /* clear ATA interrupt */ + ata_port_printk(ap, KERN_WARNING, "unhandled " + "interrupt, irq_stat=%x\n", irq_stat); + return; + } + + /* error */ + ata_ehi_push_desc(ehi, "irq_stat=0x%x", irq_stat); + + if (irq_stat & (PIRQ_OFFLINE | PIRQ_ONLINE)) { + ata_ehi_hotplugged(ehi); + ata_port_freeze(ap); + } else + ata_port_abort(ap); +} + +static irqreturn_t inic_interrupt(int irq, void *dev_instance) +{ + struct ata_host *host = dev_instance; + void __iomem *mmio_base = host->mmio_base; + u16 host_irq_stat; + int i, handled = 0;; + + host_irq_stat = readw(mmio_base + HOST_IRQ_STAT); + + if (unlikely(!(host_irq_stat & HIRQ_GLOBAL))) + goto out; + + spin_lock(&host->lock); + + for (i = 0; i < NR_PORTS; i++) { + struct ata_port *ap = host->ports[i]; + + if (!(host_irq_stat & (HIRQ_PORT0 << i))) + continue; + + if (likely(ap && !(ap->flags & ATA_FLAG_DISABLED))) { + inic_host_intr(ap); + handled++; + } else { + if (ata_ratelimit()) + dev_printk(KERN_ERR, host->dev, "interrupt " + "from disabled port %d (0x%x)\n", + i, host_irq_stat); + } + } + + spin_unlock(&host->lock); + + out: + return IRQ_RETVAL(handled); +} + +static unsigned int inic_qc_issue(struct ata_queued_cmd *qc) +{ + struct ata_port *ap = qc->ap; + + /* ATA IRQ doesn't wait for DMA transfer completion and vice + * versa. Mask IRQ selectively to detect command completion. + * Without it, ATA DMA read command can cause data corruption. + * + * Something similar might be needed for ATAPI writes. I + * tried a lot of combinations but couldn't find the solution. + */ + if (qc->tf.protocol == ATA_PROT_DMA && + !(qc->tf.flags & ATA_TFLAG_WRITE)) + inic_set_pirq_mask(ap, PIRQ_MASK_DMA_READ); + else + inic_set_pirq_mask(ap, PIRQ_MASK_OTHER); + + /* Issuing a command to yet uninitialized port locks up the + * controller. Most of the time, this happens for the first + * command after reset which are ATA and ATAPI IDENTIFYs. + * Fast fail if stat is 0x7f or 0xff for those commands. + */ + if (unlikely(qc->tf.command == ATA_CMD_ID_ATA || + qc->tf.command == ATA_CMD_ID_ATAPI)) { + u8 stat = ata_chk_status(ap); + if (stat == 0x7f || stat == 0xff) + return AC_ERR_HSM; + } + + return ata_qc_issue_prot(qc); +} + +static void inic_freeze(struct ata_port *ap) +{ + void __iomem *port_base = inic_port_base(ap); + + __inic_set_pirq_mask(ap, PIRQ_MASK_FREEZE); + + ata_chk_status(ap); + writeb(0xff, port_base + PORT_IRQ_STAT); + + readb(port_base + PORT_IRQ_STAT); /* flush */ +} + +static void inic_thaw(struct ata_port *ap) +{ + void __iomem *port_base = inic_port_base(ap); + + ata_chk_status(ap); + writeb(0xff, port_base + PORT_IRQ_STAT); + + __inic_set_pirq_mask(ap, PIRQ_MASK_OTHER); + + readb(port_base + PORT_IRQ_STAT); /* flush */ +} + +/* + * SRST and SControl hardreset don't give valid signature on this + * controller. Only controller specific hardreset mechanism works. + */ +static int inic_hardreset(struct ata_port *ap, unsigned int *class) +{ + void __iomem *port_base = inic_port_base(ap); + void __iomem *idma_ctl = port_base + PORT_IDMA_CTL; + const unsigned long *timing = sata_ehc_deb_timing(&ap->eh_context); + u16 val; + int rc; + + /* hammer it into sane state */ + inic_reset_port(port_base); + + if (ata_port_offline(ap)) { + *class = ATA_DEV_NONE; + return 0; + } + + val = readw(idma_ctl); + writew(val | IDMA_CTL_RST_ATA, idma_ctl); + readw(idma_ctl); /* flush */ + msleep(1); + writew(val & ~IDMA_CTL_RST_ATA, idma_ctl); + + rc = sata_phy_resume(ap, timing); + if (rc) { + ata_port_printk(ap, KERN_WARNING, "failed to resume " + "link for reset (errno=%d)\n", rc); + return rc; + } + + msleep(150); + + *class = ATA_DEV_NONE; + if (ata_port_online(ap)) { + struct ata_taskfile tf; + + if (ata_busy_sleep(ap, ATA_TMOUT_BOOT_QUICK, ATA_TMOUT_BOOT)) { + ata_port_printk(ap, KERN_WARNING, + "device busy after hardreset\n"); + return -EIO; + } + + ata_tf_read(ap, &tf); + *class = ata_dev_classify(&tf); + if (*class == ATA_DEV_UNKNOWN) + *class = ATA_DEV_NONE; + } + + return 0; +} + +static void inic_error_handler(struct ata_port *ap) +{ + void __iomem *port_base = inic_port_base(ap); + struct inic_port_priv *pp = ap->private_data; + unsigned long flags; + + /* reset PIO HSM and stop DMA engine */ + inic_reset_port(port_base); + + spin_lock_irqsave(ap->lock, flags); + ap->hsm_task_state = HSM_ST_IDLE; + writeb(pp->dfl_prdctl, port_base + PORT_PRD_CTL); + spin_unlock_irqrestore(ap->lock, flags); + + /* PIO and DMA engines have been stopped, perform recovery */ + ata_do_eh(ap, ata_std_prereset, NULL, inic_hardreset, + ata_std_postreset); +} + +static void inic_post_internal_cmd(struct ata_queued_cmd *qc) +{ + /* make DMA engine forget about the failed command */ + if (qc->err_mask) + inic_reset_port(inic_port_base(qc->ap)); +} + +static void inic_dev_config(struct ata_port *ap, struct ata_device *dev) +{ + /* inic can only handle upto LBA28 max sectors */ + if (dev->max_sectors > ATA_MAX_SECTORS) + dev->max_sectors = ATA_MAX_SECTORS; +} + +static void init_port(struct ata_port *ap) +{ + void __iomem *port_base = inic_port_base(ap); + + /* Setup PRD address */ + writel(ap->prd_dma, port_base + PORT_PRD_ADDR); +} + +static int inic_port_resume(struct ata_port *ap) +{ + init_port(ap); + return 0; +} + +static int inic_port_start(struct ata_port *ap) +{ + void __iomem *port_base = inic_port_base(ap); + struct inic_port_priv *pp; + u8 tmp; + int rc; + + /* alloc and initialize private data */ + pp = kzalloc(sizeof(*pp), GFP_KERNEL); + if (!pp) + return -ENOMEM; + ap->private_data = pp; + + /* default PRD_CTL value, DMAEN, WR and START off */ + tmp = readb(port_base + PORT_PRD_CTL); + tmp &= ~(PRD_CTL_DMAEN | PRD_CTL_WR | PRD_CTL_START); + pp->dfl_prdctl = tmp; + + /* Alloc resources */ + rc = ata_port_start(ap); + if (rc) { + kfree(pp); + return rc; + } + + init_port(ap); + + return 0; +} + +static void inic_port_stop(struct ata_port *ap) +{ + ata_port_stop(ap); + kfree(ap->private_data); +} + +static struct ata_port_operations inic_port_ops = { + .port_disable = ata_port_disable, + .tf_load = ata_tf_load, + .tf_read = ata_tf_read, + .check_status = ata_check_status, + .exec_command = ata_exec_command, + .dev_select = ata_std_dev_select, + + .scr_read = inic_scr_read, + .scr_write = inic_scr_write, + + .bmdma_setup = inic_bmdma_setup, + .bmdma_start = inic_bmdma_start, + .bmdma_stop = inic_bmdma_stop, + .bmdma_status = inic_bmdma_status, + + .irq_handler = inic_interrupt, + .irq_clear = inic_irq_clear, + + .qc_prep = ata_qc_prep, + .qc_issue = inic_qc_issue, + .data_xfer = ata_pio_data_xfer, + + .freeze = inic_freeze, + .thaw = inic_thaw, + .error_handler = inic_error_handler, + .post_internal_cmd = inic_post_internal_cmd, + .dev_config = inic_dev_config, + + .port_resume = inic_port_resume, + + .port_start = inic_port_start, + .port_stop = inic_port_stop, + .host_stop = ata_pci_host_stop +}; + +static struct ata_port_info inic_port_info = { + .sht = &inic_sht, + /* For some reason, ATA_PROT_ATAPI is broken on this + * controller, and no, PIO_POLLING does't fix it. It somehow + * manages to report the wrong ireason and ignoring ireason + * results in machine lock up. Tell libata to always prefer + * DMA. + */ + .flags = ATA_FLAG_SATA | ATA_FLAG_PIO_DMA, + .pio_mask = 0x1f, /* pio0-4 */ + .mwdma_mask = 0x07, /* mwdma0-2 */ + .udma_mask = 0x7f, /* udma0-6 */ + .port_ops = &inic_port_ops +}; + +static int init_controller(void __iomem *mmio_base, u16 hctl) +{ + int i; + u16 val; + + hctl &= ~HCTL_KNOWN_BITS; + + /* Soft reset whole controller. Spec says reset duration is 3 + * PCI clocks, be generous and give it 10ms. + */ + writew(hctl | HCTL_SOFTRST, mmio_base + HOST_CTL); + readw(mmio_base + HOST_CTL); /* flush */ + + for (i = 0; i < 10; i++) { + msleep(1); + val = readw(mmio_base + HOST_CTL); + if (!(val & HCTL_SOFTRST)) + break; + } + + if (val & HCTL_SOFTRST) + return -EIO; + + /* mask all interrupts and reset ports */ + for (i = 0; i < NR_PORTS; i++) { + void __iomem *port_base = mmio_base + i * PORT_SIZE; + + writeb(0xff, port_base + PORT_IRQ_MASK); + inic_reset_port(port_base); + } + + /* port IRQ is masked now, unmask global IRQ */ + writew(hctl & ~HCTL_IRQOFF, mmio_base + HOST_CTL); + val = readw(mmio_base + HOST_IRQ_MASK); + val &= ~(HIRQ_PORT0 | HIRQ_PORT1); + writew(val, mmio_base + HOST_IRQ_MASK); + + return 0; +} + +static int inic_pci_device_resume(struct pci_dev *pdev) +{ + struct ata_host *host = dev_get_drvdata(&pdev->dev); + struct inic_host_priv *hpriv = host->private_data; + void __iomem *mmio_base = host->mmio_base; + int rc; + + ata_pci_device_do_resume(pdev); + + if (pdev->dev.power.power_state.event == PM_EVENT_SUSPEND) { + printk("XXX\n"); + rc = init_controller(mmio_base, hpriv->cached_hctl); + if (rc) + return rc; + } + + ata_host_resume(host); + + return 0; +} + +static int inic_init_one(struct pci_dev *pdev, const struct pci_device_id *ent) +{ + static int printed_version; + struct ata_port_info *pinfo = &inic_port_info; + struct ata_probe_ent *probe_ent; + struct inic_host_priv *hpriv; + void __iomem *mmio_base; + int i, rc; + + if (!printed_version++) + dev_printk(KERN_DEBUG, &pdev->dev, "version " DRV_VERSION "\n"); + + rc = pci_enable_device(pdev); + if (rc) + return rc; + + rc = pci_request_regions(pdev, DRV_NAME); + if (rc) + goto err_out; + + rc = -ENOMEM; + mmio_base = pci_iomap(pdev, MMIO_BAR, 0); + if (!mmio_base) + goto err_out_regions; + + /* Set dma_mask. This devices doesn't support 64bit addressing. */ + rc = pci_set_dma_mask(pdev, DMA_32BIT_MASK); + if (rc) { + dev_printk(KERN_ERR, &pdev->dev, + "32-bit DMA enable failed\n"); + goto err_out_map; + } + + rc = pci_set_consistent_dma_mask(pdev, DMA_32BIT_MASK); + if (rc) { + dev_printk(KERN_ERR, &pdev->dev, + "32-bit consistent DMA enable failed\n"); + goto err_out_map; + } + + rc = -ENOMEM; + probe_ent = kzalloc(sizeof(*probe_ent), GFP_KERNEL); + if (!probe_ent) + goto err_out_map; + + hpriv = kzalloc(sizeof(*hpriv), GFP_KERNEL); + if (!hpriv) + goto err_out_ent; + + probe_ent->dev = &pdev->dev; + INIT_LIST_HEAD(&probe_ent->node); + + probe_ent->sht = pinfo->sht; + probe_ent->port_flags = pinfo->flags; + probe_ent->pio_mask = pinfo->pio_mask; + probe_ent->mwdma_mask = pinfo->mwdma_mask; + probe_ent->udma_mask = pinfo->udma_mask; + probe_ent->port_ops = pinfo->port_ops; + probe_ent->n_ports = NR_PORTS; + + probe_ent->irq = pdev->irq; + probe_ent->irq_flags = SA_SHIRQ; + + probe_ent->mmio_base = mmio_base; + + for (i = 0; i < NR_PORTS; i++) { + struct ata_ioports *port = &probe_ent->port[i]; + unsigned long port_base = + (unsigned long)mmio_base + i * PORT_SIZE; + + port->cmd_addr = pci_resource_start(pdev, 2 * i); + port->altstatus_addr = + port->ctl_addr = + pci_resource_start(pdev, 2 * i + 1) | ATA_PCI_CTL_OFS; + port->scr_addr = port_base + PORT_SCR; + + ata_std_ports(port); + } + + probe_ent->private_data = hpriv; + hpriv->cached_hctl = readw(mmio_base + HOST_CTL); + + rc = init_controller(mmio_base, hpriv->cached_hctl); + if (rc) { + dev_printk(KERN_ERR, &pdev->dev, + "failed to initialize controller\n"); + goto err_out_hpriv; + } + + pci_set_master(pdev); + + rc = -ENODEV; + if (!ata_device_add(probe_ent)) + goto err_out_hpriv; + + kfree(probe_ent); + + return 0; + + err_out_hpriv: + kfree(hpriv); + err_out_ent: + kfree(probe_ent); + err_out_map: + pci_iounmap(pdev, mmio_base); + err_out_regions: + pci_release_regions(pdev); + err_out: + pci_disable_device(pdev); + return rc; +} + +static const struct pci_device_id inic_pci_tbl[] = { + { PCI_VDEVICE(INIT, 0x1622), }, + { }, +}; + +static struct pci_driver inic_pci_driver = { + .name = DRV_NAME, + .id_table = inic_pci_tbl, + .suspend = ata_pci_device_suspend, + .resume = inic_pci_device_resume, + .probe = inic_init_one, + .remove = ata_pci_remove_one, +}; + +static int __init inic_init(void) +{ + return pci_register_driver(&inic_pci_driver); +} + +static void __exit inic_exit(void) +{ + pci_unregister_driver(&inic_pci_driver); +} + +MODULE_AUTHOR("Tejun Heo"); +MODULE_DESCRIPTION("low-level driver for Initio 162x SATA"); +MODULE_LICENSE("GPL v2"); +MODULE_DEVICE_TABLE(pci, inic_pci_tbl); +MODULE_VERSION(DRV_VERSION); + +module_init(inic_init); +module_exit(inic_exit); -- cgit v1.2.3-70-g09d2