Here is the code, tips and reviews would be appriciated too :)

/*
 * AVR32 SMC/CFC PATA Driver
 *
 * Copyright (C) 2007 Atmel Norway
 *
 * 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.
 */
#define DEBUG

#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/device.h>
#include <linux/platform_device.h>
#include <linux/delay.h>
#include <linux/interrupt.h>
#include <linux/irq.h>
#include <scsi/scsi_host.h>
#include <linux/ata.h>
#include <linux/libata.h>
#include <linux/err.h>
#include <linux/io.h>

#include <asm/gpio.h>
#include <asm/arch/board.h>
#include <asm/arch/smc.h>

#define DRV_NAME "pata_at32"
#define DRV_VERSION "0.0.2"

/*
 * CompactFlash controller memory layout relative to the base address:
 *
 *      Attribute memory:  0000 0000 -> 003f ffff
 *      Common memory:     0040 0000 -> 007f ffff
 *      I/O memory:        0080 0000 -> 00bf ffff
 *      True IDE Mode:     00c0 0000 -> 00df ffff
 *      Alt IDE Mode:      00e0 0000 -> 00ff ffff
 *
 * Only True IDE and Alt True IDE mode are needed for this driver.
 *
 *      True IDE mode     => CS0 = 0, CS1 = 1 (cmd, error, stat, etc)
 *      Alt True IDE mode => CS0 = 1, CS1 = 0 (ctl, alt_stat)
 */
#define CF_IDE_OFFSET     0x00c00000
#define CF_ALT_IDE_OFFSET 0x00e00000
#define CF_RES_SIZE       2048

/*
 * The CompactFlash controller in the current AP7000 series has a bug
 * that makes 8-bit data being put on both upper and lower byte on the
 * 16-bit bus. It should only be put on the lower byte.
 */
#define CF_BUGGY_IDE_MODE

/*
 * ATA PIO modes
 *
 *      Name    | Mb/s  | Min cycle time
 *      --------+-------+---------------
 *      Mode 0  | 3.3   | 600 ns
 *      Mode 1  | 5.2   | 383 ns
 *      Mode 2  | 8.3   | 240 ns
 *      Mode 3  | 11.1  | 180 ns
 *      Mode 4  | 16.7  | 120 ns
 *
 * Only PIO modes 0 is supported in version 0.0.2.
 */
#define DEFAULT_PIO_MODE (0)

/*
 * PIO mask for maximal PIO mode
 *
 *      Mode 0 => 0x01
 *      Mode 1 => 0x03
 *      Mode 2 => 0x07
 *      Mode 3 => 0x0f
 *      Mode 4 => 0x1f
 */
#define PIO_MASK (0x01)

/*
 * Define DEBUG_BUS if you are doing debugging of your own
 * EBI->PATA adaptor with an logic analyzer or similar.
 */
#undef DEBUG_BUS

/*
 * SMC timings to match ATAPI-4 PIO timing specs (given in ns)
 * See Figure 20 and Table 29 on pages 262-263 in T13/1153D revision 18
 */
static const int smc_tot_cycle[5] = {600, 390, 350, 190, 135}; /* >= t0 */
static const int smc_nrd_setup[5] = {100,  60,  40,  40,  35}; /* >= t1 */
static const int smc_nrd_pulse[5] = {400, 300, 290,  80,  70}; /* >= t2 */

/*
 * Setup SMC timings for the given PIO mode
 */
static int pata_at32_compute_timings(struct smc_config *smc, int pio_mode)
{
        const int ring_time = 100;

        if ((pio_mode < 0) || (4 < pio_mode))
                return -EINVAL;

        /* Total cycle time */
        smc->read_cycle  = smc_tot_cycle[pio_mode] + ring_time;
        smc->write_cycle = smc_tot_cycle[pio_mode] + ring_time;

        /* CFCE1 => CS0 and CFCE2 => CS1 timings */
        smc->ncs_read_setup  = 0;
        smc->ncs_read_pulse  = smc->read_cycle;
        smc->ncs_write_setup = 0;
        smc->ncs_write_pulse = smc->write_cycle;

        /* CFIOR => DIOR and CFIOW => DIOW timings */
        smc->nrd_setup = smc_nrd_setup[pio_mode] + ring_time;
        smc->nrd_pulse = smc_nrd_pulse[pio_mode];
        smc->nwe_setup = smc_nrd_setup[pio_mode] + ring_time;
        smc->nwe_pulse = smc_nrd_pulse[pio_mode];

        return 0;
}

/*
 * Struct containing private information about device
 */
struct at32_ide_info {
        unsigned int            irq;
        struct resource         res_ide;
        struct resource         res_alt;
        void __iomem            *ide_addr;
        void __iomem            *alt_addr;
        int                     reset_pin;
        int                     dmarq_pin;
        int                     dmack_pin;
        unsigned int            cs;
        struct smc_config       smc;
        int                     smc_pio_mode;
};

static int pata_at32_hard_reset(struct device *dev, struct at32_ide_info *info)
{
        if (info->reset_pin < 0)
                return -EINVAL;

        /* Set RESET low for at least 25 us */
        gpio_set_value(info->reset_pin, 0);
        udelay(30);
        gpio_set_value(info->reset_pin, 1);

        /* The device needs time to stabilize after reset */
        udelay(10);

        return 0;
}

/*
 * Functions for libATA
 */
static void pata_at32_set_piomode(struct ata_port *ap, struct ata_device *adev)
{
        struct at32_ide_info *info = ap->host->private_data;
        int pio_mode = adev->pio_mode - XFER_PIO_0;
        int ret;

        /* Exit if the SMC is already configured for this PIO mode */
        if (pio_mode == info->smc_pio_mode)
                return;

        ret = pata_at32_compute_timings(&info->smc,  pio_mode);
        if (ret) {
                dev_warn(ap->dev, "Invalid PIO mode %d\n", pio_mode);
                return;
        }

        /* Only use IORDY/NWAIT for PIO mode 3 and 4 */
        if (pio_mode < 3)
                info->smc.nwait_mode = 0;
        else
                info->smc.nwait_mode = 3;

        ret = smc_set_configuration(info->cs, &info->smc);
        if (ret) {
                dev_warn(ap->dev, "Failed to setup SMC %d\n", ret);
                return;
        }

        info->smc_pio_mode = pio_mode;
}

static void pata_at32_phy_reset(struct ata_port *ap)
{
        pata_at32_hard_reset(ap->dev, ap->host->private_data);
}

static void pata_at32_irq_clear(struct ata_port *ap)
{
        ; /* Todo: Need to clear interrupt? */
}

static irqreturn_t pata_at32_interrupt(int irq, void *dev_instance)
{
        struct ata_host *host = dev_instance;
        irqreturn_t irq_ret = ata_interrupt(irq, dev_instance);

        /* We are the only ones listening for our IRQ */
        if (irq_ret == IRQ_NONE)
                dev_dbg(host->dev, "IRQ NOT HANDLED\n");

        return IRQ_HANDLED;
}

static struct scsi_host_template at32_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        = ata_scsi_slave_config,
        .slave_destroy          = ata_scsi_slave_destroy,
        .bios_param             = ata_std_bios_param,
};

static struct ata_port_operations at32_port_ops = {
        .port_disable           = ata_port_disable,
        .set_piomode            = pata_at32_set_piomode,
        .tf_load                = ata_tf_load,
        .tf_read                = ata_tf_read,
        .exec_command           = ata_exec_command,
        .check_status           = ata_check_status,
        .dev_select             = ata_std_dev_select,
        .phy_reset              = pata_at32_phy_reset,
        .cable_detect           = ata_cable_40wire,
        .freeze                 = ata_bmdma_freeze,
        .thaw                   = ata_bmdma_thaw,
        .error_handler          = ata_bmdma_error_handler,
        .post_internal_cmd      = ata_bmdma_post_internal_cmd,
        .qc_prep                = ata_qc_prep,
        .qc_issue               = ata_qc_issue_prot,
        .data_xfer              = ata_data_xfer,
        .irq_handler            = pata_at32_interrupt,
        .irq_clear              = pata_at32_irq_clear,
        .irq_on                 = ata_irq_on,
        .irq_ack                = ata_irq_ack,
        .port_start             = ata_port_start,
};

static int pata_at32_init_one(struct device *dev, struct at32_ide_info *info)
{
        struct ata_host *host;
        struct ata_port *ap;

        host = ata_host_alloc(dev, 1);
        if (!host)
                return -ENOMEM;

        ap = host->ports[0];

        /* Setup ATA bindings */
        ap->ops      = &at32_port_ops;
        ap->pio_mask = PIO_MASK;
        ap->flags   |= ATA_FLAG_MMIO;

        /* Run in polling mode if no irq has been assigned */
        if (!info->irq)
                ap->flags |= ATA_FLAG_PIO_POLLING;

        /*
         * Since all 8-bit taskfile transfers has to go on the lower byte
         * of the data bus and there is a bug in the SMC that makes it
         * impossible to alter the bus width during runtime, we need to
         * hardwire the address signals as follows:
         *
         *      A_IDE(2:0) <= A_EBI(3:1)
         *
         * This makes all addresses on the EBI even, thus all data will be
         * on the lower byte of the data bus.  All addresses used by libATA
         * need to be altered according to this, the offsets need to be
         * shifted on step up.
         *
         * A later version of the CompactFlash controller might fix this
         * issue. In that case the standard addresses may be used.
         */
#ifdef CF_BUGGY_IDE_MODE
        ap->ioaddr.cmd_addr       = info->ide_addr;
        ap->ioaddr.altstatus_addr = info->alt_addr + (0x06 << 1);
        ap->ioaddr.ctl_addr       = info->alt_addr + (0x06 << 1);

        ap->ioaddr.data_addr      = info->ide_addr + (ATA_REG_DATA << 1);
        ap->ioaddr.error_addr     = info->ide_addr + (ATA_REG_ERR << 1);
        ap->ioaddr.feature_addr   = info->ide_addr + (ATA_REG_FEATURE << 1);
        ap->ioaddr.nsect_addr     = info->ide_addr + (ATA_REG_NSECT << 1);
        ap->ioaddr.lbal_addr      = info->ide_addr + (ATA_REG_LBAL << 1);
        ap->ioaddr.lbam_addr      = info->ide_addr + (ATA_REG_LBAM << 1);
        ap->ioaddr.lbah_addr      = info->ide_addr + (ATA_REG_LBAH << 1);
        ap->ioaddr.device_addr    = info->ide_addr + (ATA_REG_DEVICE << 1);
        ap->ioaddr.status_addr    = info->ide_addr + (ATA_REG_STATUS << 1);
        ap->ioaddr.command_addr   = info->ide_addr + (ATA_REG_CMD << 1);
#else
        ap->ioaddr.cmd_addr       = info->ide_addr;
        ap->ioaddr.altstatus_addr = info->alt_addr + 0x06;
        ap->ioaddr.ctl_addr       = info->alt_addr + 0x06;

        ata_std_ports(&ap->ioaddr);
#endif

        /* Set info as private data of ATA host */
        host->private_data = info;

        /* Register ATA device and return */
        return ata_host_activate(host, info->irq, pata_at32_interrupt,
                                 IRQF_TRIGGER_HIGH, &at32_sht);
}

/*
 * The following two debugging functions might come in handy for
 * people analyzing their own EBI -> PATA adaptors.
 */
#ifdef DEBUG_BUS

static void __init pata_at32_debug_bus(struct device *dev,
                                       struct at32_ide_info *info)
{
        const int d1 = 0xff;
        const int d2 = 0x00;

        int i, ctld;
        int iod[8];

        iowrite8(d1, info->alt_addr + (0x06 << 1));
        iowrite8(d2, info->alt_addr + (0x06 << 1));

        for (i = 0; i < 8; i++) {
                iowrite8(d1, info->ide_addr + (i << 1));
                iowrite8(d2, info->ide_addr + (i << 1));
        }

        ctld = ioread8(info->alt_addr + (0x06 << 1));

        for (i = 0; i < 8; i++)
                iod[i] = ioread8(info->ide_addr + (i << 1));

        iowrite16(d1,      info->ide_addr);
        iowrite16(d1 << 8, info->ide_addr);

        iowrite16(d1,      info->ide_addr);
        iowrite16(d1 << 8, info->ide_addr);

        dev_dbg(dev, "CTL = %x\n", ctld);

        for (i = 0; i < 8; i++)
                dev_dbg(dev, "IO%d = %x\n", i, iod[i]);

        pata_at32_hard_reset(dev, info);
}

static void __init pata_at32_debug_disk(struct device *dev,
                                        struct at32_ide_info *info)
{
        void __iomem *nsect_addr
                = info->ide_addr + (ATA_REG_NSECT << 1);
        void __iomem *lbal_addr
                = info->ide_addr + (ATA_REG_LBAL << 1);
        void __iomem *status_addr
                = info->ide_addr + (ATA_REG_STATUS << 1);
        void __iomem *device_addr
                = info->ide_addr + (ATA_REG_DEVICE << 1);

        int i, status, nsect, lbal;

        pata_at32_hard_reset(dev, info);

        iowrite8(~0x10, device_addr);

        i = 0;
        do {
                status = ioread8(status_addr);
                i++;
        } while (status & 0x80);

        iowrite8(~0x10, device_addr);

        do {
                status = ioread8(status_addr);
                i--;
        } while (status & 0x80);

        iowrite8(0x55, nsect_addr);
        iowrite8(0xaa, lbal_addr);

        iowrite8(0x55, nsect_addr);
        iowrite8(0xaa, lbal_addr);

        iowrite8(0x55, nsect_addr);
        iowrite8(0xaa, lbal_addr);

        nsect = ioread8(nsect_addr);
        lbal  = ioread8(lbal_addr);

        dev_dbg(dev, "STATUS(%d) = %x, NSECT = %x, LBAL = %x\n",
                i, status, nsect, lbal);
}

#endif

static int __init pata_at32_probe(struct platform_device *pdev)
{
        struct at32_ide_info     *info;
        struct ide_platform_data *board = pdev->dev.platform_data;
        struct resource          *res;

        int irq;
        int ret;

        if (!board)
                return -ENXIO;

        res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
        if (!res)
                return -ENXIO;

        /* Retrive IRQ */
        irq = platform_get_irq(pdev, 0);
        if (irq < 0)
                return irq;

        /* Setup struct containg private infomation */
        info = kzalloc(sizeof(struct at32_ide_info), GFP_KERNEL);
        if (!info)
                return -ENOMEM;

        memset(info, 0, sizeof(struct at32_ide_info));

        info->irq = irq;
        info->cs = board->cs;

        info->reset_pin = -1;
        info->dmarq_pin = -1;
        info->dmack_pin = -1;

        /* Request reset pin */
        if (board->reset_pin) {
                ret = gpio_request(board->reset_pin, "reset");
                if (ret)
                        goto err_req_gpio;
                info->reset_pin = board->reset_pin;
        }

        /* Request DMA request pin */
        if (board->dmarq_pin) {
                ret = gpio_request(board->dmarq_pin, "dmarq");
                if (ret)
                        goto err_req_gpio;
                info->dmarq_pin = board->dmarq_pin;
        }

        /* Request DMA ack pin */
        if (board->dmack_pin) {
                ret = gpio_request(board->dmack_pin, "dmack");
                if (ret)
                        goto err_req_gpio;
                info->dmack_pin = board->dmack_pin;
        }

        dev_dbg(&pdev->dev, "RESET: %d DMARQ: %d DMACK: %d\n",
                info->reset_pin, info->dmarq_pin, info->dmack_pin);

        /* Request memory resources */
        dev_info(&pdev->dev, "Memory setup on CS%d\n", info->cs);

        info->res_ide.start = res->start + CF_IDE_OFFSET;
        info->res_ide.end   = info->res_ide.start + CF_RES_SIZE - 1;
        info->res_ide.name  = "ide";
        info->res_ide.flags = IORESOURCE_MEM;

        ret = request_resource(res, &info->res_ide);
        if (ret)
                goto err_req_res_ide;

        dev_dbg(&pdev->dev, "RESOURCE: %s: %x to %x flags %x\n",
                info->res_ide.name, (int) info->res_ide.start,
                (int) info->res_ide.end, (int) info->res_ide.flags);

        info->res_alt.start = res->start + CF_ALT_IDE_OFFSET;
        info->res_alt.end   = info->res_alt.start + CF_RES_SIZE - 1;
        info->res_alt.name  = "alt";
        info->res_alt.flags = IORESOURCE_MEM;

        ret = request_resource(res, &info->res_alt);
        if (ret)
                goto err_req_res_alt;

        dev_dbg(&pdev->dev, "RESOURCE: %s: %x to %x flags %x\n",
                info->res_alt.name, (int) info->res_alt.start,
                (int) info->res_alt.end, (int) info->res_alt.flags);

        /* Setup SMC, see Atmel AVR32 AP7000 datasheet chapter 27 */
        info->smc.bus_width       = 2; /* 16 bit data bus */
        info->smc.nrd_controlled  = 1; /* Sample data on rising edge of NRD */
        info->smc.nwe_controlled  = 0; /* Drive data on falling edge of NCS */
        info->smc.nwait_mode      = 0; /* NWAIT is only used for PIO 3 and 4 */
        info->smc.byte_write      = 0; /* Byte select access type */
        info->smc.tdf_mode        = 0; /* TDF optimization disabled */
        info->smc.tdf_cycles      = 0; /* 0 cycles before data is released */

        ret = pata_at32_compute_timings(&info->smc, DEFAULT_PIO_MODE);
        if (ret)
                goto err_smc_config;

        ret = smc_set_configuration(info->cs, &info->smc);
        if (ret)
                goto err_smc_config;

        info->smc_pio_mode = DEFAULT_PIO_MODE;

        /* Setup ATA addresses */
        ret = -ENOMEM;
        info->ide_addr = devm_ioport_map(&pdev->dev, info->res_ide.start, 16);
        info->alt_addr = devm_ioport_map(&pdev->dev, info->res_alt.start, 16);
        if (!info->ide_addr || !info->alt_addr)
                goto err_iomap;

        /* Call functions to debug EBI->PATA adaptor with logic analyzer */
#ifdef DEBUG_BUS
        pata_at32_debug_bus(&pdev->dev, info);
        pata_at32_debug_disk(&pdev->dev, info);
#endif

        /* Reset ATA device */
        pata_at32_hard_reset(&pdev->dev, info);

        /* Register ATA device */
        ret = pata_at32_init_one(&pdev->dev, info);
        if (ret)
                goto err_ata_device;

        return 0;

 err_ata_device:
 err_iomap:
 err_smc_config:
        release_resource(&info->res_alt);
 err_req_res_alt:
        release_resource(&info->res_ide);
 err_req_res_ide:
 err_req_gpio:
        if (info->reset_pin >= 0)
                gpio_free(info->reset_pin);
        if (info->dmarq_pin >= 0)
                gpio_free(info->dmarq_pin);
        if (info->dmack_pin >= 0)
                gpio_free(info->dmack_pin);

        kfree(info);

        return ret;
}

static int __exit pata_at32_remove(struct platform_device *pdev)
{
        struct ata_host *host = platform_get_drvdata(pdev);
        struct at32_ide_info *info;

        if (!host)
                return 0;

        info = host->private_data;
        ata_host_detach(host);

        if (!info)
                return 0;

        pata_at32_hard_reset(&pdev->dev, info);

        release_resource(&info->res_ide);
        release_resource(&info->res_alt);

        if (info->reset_pin >= 0)
                gpio_free(info->reset_pin);
        if (info->dmarq_pin >= 0)
                gpio_free(info->dmarq_pin);
        if (info->dmack_pin >= 0)
                gpio_free(info->dmack_pin);

        kfree(info);

        return 0;
}

static struct platform_driver pata_at32_driver = {
        .remove        = __exit_p(pata_at32_remove),
        .driver        = {
                .name  = "at32_ide",
                .owner = THIS_MODULE,
        },
};

static int __init pata_at32_init(void)
{
        return platform_driver_probe(&pata_at32_driver, pata_at32_probe);
}

static void __exit pata_at32_exit(void)
{
        platform_driver_unregister(&pata_at32_driver);
}

module_init(pata_at32_init);
module_exit(pata_at32_exit);

MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("AVR32 SMC IDE Driver");
MODULE_AUTHOR("Kristoffer Nyborg Gregertsen <[EMAIL PROTECTED]>");
MODULE_VERSION(DRV_VERSION);
-
To unsubscribe from this list: send the line "unsubscribe linux-ide" in
the body of a message to [EMAIL PROTECTED]
More majordomo info at  http://vger.kernel.org/majordomo-info.html

Reply via email to