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