summaryrefslogtreecommitdiff
path: root/drivers
diff options
context:
space:
mode:
Diffstat (limited to 'drivers')
-rw-r--r--drivers/block/Makefile1
-rw-r--r--drivers/block/mxc_ata.c454
-rw-r--r--drivers/block/mxc_ata.h90
3 files changed, 545 insertions, 0 deletions
diff --git a/drivers/block/Makefile b/drivers/block/Makefile
index 3f6ad5c..0a5dc8c 100644
--- a/drivers/block/Makefile
+++ b/drivers/block/Makefile
@@ -36,6 +36,7 @@ COBJS-$(CONFIG_SATA_SIL3114) += sata_sil3114.o
COBJS-$(CONFIG_SCSI_AHCI) += ahci.o
COBJS-$(CONFIG_SCSI_SYM53C8XX) += sym53c8xx.o
COBJS-$(CONFIG_SYSTEMACE) += systemace.o
+COBJS-$(CONFIG_MXC_ATA) += mxc_ata.o
COBJS := $(COBJS-y)
SRCS := $(COBJS:.o=.c)
diff --git a/drivers/block/mxc_ata.c b/drivers/block/mxc_ata.c
new file mode 100644
index 0000000..aed7476
--- /dev/null
+++ b/drivers/block/mxc_ata.c
@@ -0,0 +1,454 @@
+/*
+ * (C) Copyright 2008-2009 Freescale Semiconductor, Inc.
+ *
+ * See file CREDITS for list of people who contributed to this
+ * project.
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
+ * MA 02111-1307 USA
+ */
+
+#include "mxc_ata.h"
+
+#include <pata.h>
+#include <libata.h>
+#include <common.h>
+#include <malloc.h>
+#include <asm/io.h>
+#include <linux/types.h>
+
+extern block_dev_desc_t pata_dev_desc[CONFIG_SYS_ATA_MAX_DEVICE];
+extern void setup_ata(void);
+
+struct mxc_ata_time_regs {
+ u8 time_off, time_on, time_1, time_2w;
+ u8 time_2r, time_ax, time_pio_rdx, time_4;
+ u8 time_9, time_m, time_jn, time_d;
+ u8 time_k, time_ack, time_env, time_rpx;
+ u8 time_zah, time_mlix, time_dvh, time_dzfs;
+ u8 time_dvs, time_cvh, time_ss, time_cyc;
+};
+
+/*
+ * This structure contains the timing parameters for
+ * ATA bus timing in the 5 PIO modes. The timings
+ * are in nanoseconds, and are converted to clock
+ * cycles before being stored in the ATA controller
+ * timing registers.
+ */
+static struct {
+ s16 t0, t1, t2_8, t2_16, t2i, t4, t9, tA;
+} pio_specs[] = {
+ [0] = {
+ .t0 = 600, .t1 = 70, .t2_8 = 290, .t2_16 = 165, .t2i = 40, .t4 =
+ 30, .t9 = 20, .tA = 50,},
+ [1] = {
+ .t0 = 383, .t1 = 50, .t2_8 = 290, .t2_16 = 125, .t2i = 0, .t4 =
+ 20, .t9 = 15, .tA = 50,},
+ [2] = {
+ .t0 = 240, .t1 = 30, .t2_8 = 290, .t2_16 = 100, .t2i = 0, .t4 =
+ 15, .t9 = 10, .tA = 50,},
+ [3] = {
+ .t0 = 180, .t1 = 30, .t2_8 = 80, .t2_16 = 80, .t2i = 0, .t4 =
+ 10, .t9 = 10, .tA = 50,},
+ [4] = {
+ .t0 = 120, .t1 = 25, .t2_8 = 70, .t2_16 = 70, .t2i = 0, .t4 =
+ 10, .t9 = 10, .tA = 50,},
+ };
+
+#define NR_PIO_SPECS (sizeof(pio_specs) / sizeof(pio_specs[0]))
+
+static inline void mdelay(u32 msec)
+{
+ u32 i;
+ for (i = 0; i < msec; i++)
+ udelay(1000);
+}
+
+static inline void sdelay(u32 sec)
+{
+ u32 i;
+ for (i = 0; i < sec; i++)
+ mdelay(1000);
+}
+
+void dprint_buffer(u8 *buf, s32 len)
+{
+ s32 i, j;
+
+ i = 0;
+ j = 0;
+ printf("\n\r");
+
+ for (i = 0; i < len; i++) {
+ printf("%02x ", *buf++);
+ j++;
+ if (j == 16) {
+ printf("\n\r");
+ j = 0;
+ }
+ }
+ printf("\n\r");
+}
+
+
+static void update_timing_config(struct mxc_ata_time_regs *tp)
+{
+ u32 *lp = (u32 *)tp;
+ u32 *ctlp = (u32 *) ATA_BASE_ADDR;
+ s32 i;
+
+ for (i = 0; i < 5; ++i) {
+ writel(*lp, ctlp);
+ lp++;
+ ctlp++;
+ }
+}
+
+static void set_ata_bus_timing(u8 xfer_mode)
+{
+ s32 speed = xfer_mode;
+ struct mxc_ata_time_regs tr = { 0 };
+ s32 T = 1 * 1000 * 1000 * 1000 / mxc_get_clock(MXC_IPG_CLK);
+
+ if (speed >= NR_PIO_SPECS)
+ return;
+
+ tr.time_off = 3;
+ tr.time_on = 3;
+
+ tr.time_1 = (pio_specs[speed].t1 + T) / T;
+ tr.time_2w = (pio_specs[speed].t2_8 + T) / T;
+
+ tr.time_2r = (pio_specs[speed].t2_8 + T) / T;
+ tr.time_ax = (pio_specs[speed].tA + T) / T + 2;
+ tr.time_pio_rdx = 1;
+ tr.time_4 = (pio_specs[speed].t4 + T) / T;
+
+ tr.time_9 = (pio_specs[speed].t9 + T) / T;
+
+ update_timing_config(&tr);
+}
+
+static u8 ata_sff_busy_wait(u32 bits, u32 max, u32 delay)
+{
+ u8 status;
+ u32 iterations = 1;
+
+ if (max != 0)
+ iterations = max;
+
+ do {
+ udelay(delay);
+ status = readb(CONFIG_SYS_ATA_BASE_ADDR + MXC_ATA_DCDR);
+ if (max != 0)
+ iterations--;
+ } while (status != 0xff && (status & bits) && (iterations > 0));
+
+ if (iterations == 0) {
+ printf("ata_sff_busy_wait timeout status = %x\n", status);
+ return 0xff;
+ }
+
+ return status;
+}
+
+static void ata_sff_exec_command(u16 cmd)
+{
+ writeb(cmd, CONFIG_SYS_ATA_BASE_ADDR + MXC_ATA_DCDR);
+ readb(CONFIG_SYS_ATA_BASE_ADDR + MXC_ATA_DRIVE_CONTROL);
+ udelay(4);
+}
+
+static s32 ata_identify(int dev, u16 *id)
+{
+ int i;
+ int CIS[256] = { 0 };
+
+ ata_sff_exec_command(ATA_CMD_ID_ATA);
+ if (ata_sff_busy_wait(ATA_BUSY, 5000, 500) == 0xff)
+ return -1;
+
+ for (i = 0 ; i < 256; ++i)
+ id[i] = (u16)readw(CONFIG_SYS_ATA_BASE_ADDR + MXC_ATA_DRIVE_DATA);
+
+ ata_swap_buf_le16(id, ATA_ID_WORDS);
+
+ return 0;
+}
+
+static s32 ata_dev_set_feature(int dev, u32 feature)
+{
+ u8 status;
+
+ writeb(feature, CONFIG_SYS_ATA_BASE_ADDR + MXC_ATA_DFTR);
+ /* Issue Set feature command */
+ ata_sff_exec_command(ATA_CMD_SET_FEATURES);
+ status = ata_sff_busy_wait(ATA_BUSY, 5000, 500);
+
+ if (status == 0xff)
+ return 1;
+ if (status & ATA_ERR)
+ return 1;
+
+ return 0;
+}
+
+static void write_sector_pio(u32 *addr, s32 num_of_sectors)
+{
+ int i, j;
+
+ for (i = 0; i < num_of_sectors; i++) {
+ for (j = 0; j < ATA_SECTOR_SIZE; j = j + 4) {
+ /* Write 4 bytes in each iteration */
+ writew((*addr & 0xFFFF),
+ ATA_BASE_ADDR + MXC_ATA_DRIVE_DATA) ;
+ writew(((*addr >> 16) & 0xFFFF),
+ ATA_BASE_ADDR + MXC_ATA_DRIVE_DATA) ;
+ addr++;
+ }
+ ata_sff_busy_wait(ATA_BUSY, 5000, 50);
+ }
+ readb(CONFIG_SYS_ATA_BASE_ADDR + MXC_ATA_DRIVE_CONTROL);
+}
+
+static void read_sector_pio(u32 *addr, s32 num_of_sectors)
+{
+ int i, j;
+ u32 data[2];
+
+ for (i = 0; i < num_of_sectors; i++) {
+ for (j = 0; j < ATA_SECTOR_SIZE; j = j + 4) {
+ /* Read 4 bytes in each iteration */
+ data[0] = readw(CONFIG_SYS_ATA_BASE_ADDR + MXC_ATA_DRIVE_DATA);
+ data[1] = readw(CONFIG_SYS_ATA_BASE_ADDR + MXC_ATA_DRIVE_DATA);
+ *addr = ((data[1] << 16) & 0xFFFF0000) | \
+ (data[0] & 0xFFFF);
+ addr++;
+ }
+ ata_sff_busy_wait(ATA_BUSY, 5000, 10);
+ }
+ readb(CONFIG_SYS_ATA_BASE_ADDR + MXC_ATA_DRIVE_CONTROL);
+}
+
+static s32 mxc_pata_reset(int dev)
+{
+ setup_ata();
+
+ /* Deassert the reset bit to enable the interface */
+ writel(MXC_ATA_CTRL_ATA_RST_B, \
+ CONFIG_SYS_ATA_BASE_ADDR + MXC_ATA_CONTROL);
+ writel(MXC_ATA_CTRL_ATA_RST_B | MXC_ATA_CTRL_FIFO_RST_B, \
+ CONFIG_SYS_ATA_BASE_ADDR + MXC_ATA_CONTROL);
+ /* Set initial timing and mode */
+ set_ata_bus_timing(PIO_XFER_MODE_4);
+ /* set fifo alarm to 20 halfwords, midway */
+ writeb(20, CONFIG_SYS_ATA_BASE_ADDR + MXC_ATA_FIFO_ALARM);
+
+ /* software reset */
+ writeb(ATA_NIEN, \
+ CONFIG_SYS_ATA_BASE_ADDR + MXC_ATA_DRIVE_CONTROL);
+ udelay(20);
+ writeb(ATA_NIEN | ATA_SRST, \
+ CONFIG_SYS_ATA_BASE_ADDR + MXC_ATA_DRIVE_CONTROL);
+ udelay(20);
+ writeb(ATA_NIEN, \
+ CONFIG_SYS_ATA_BASE_ADDR + MXC_ATA_DRIVE_CONTROL);
+
+ writeb(0, CONFIG_SYS_ATA_BASE_ADDR + MXC_ATA_DDHR);
+
+ if (ata_sff_busy_wait(ATA_BUSY | ATA_DRQ, \
+ 6000, 1000) == 0xff) {
+ puts("Failed to initialize the ATA drive\n");
+ return -1;
+ }
+
+ return 0;
+}
+
+/*
+ * PATA interface between low level driver and command layer
+ */
+int init_pata(int dev)
+{
+ if (dev < 0 || dev > (CONFIG_SYS_ATA_MAX_DEVICE - 1)) {
+ printf("the pata index %d is out of ranges\n\r", dev);
+ return -1;
+ }
+
+ return mxc_pata_reset(dev);
+}
+
+ulong pata_read(int dev, ulong blknr, ulong blkcnt, void *buffer)
+{
+ u8 lba_addr[4] = { 0 };
+ u32 num_of_sectors = 0;
+ u8 status = 0;
+ u32 total_blks = blkcnt;
+
+ while (blkcnt > 0) {
+ lba_addr[0] = blknr & 0xFF;
+ lba_addr[1] = (blknr >> 8) & 0xFF;
+ lba_addr[2] = (blknr >> 16) & 0xFF;
+ /* Enable the LBA bit */
+ lba_addr[3] = (1 << 6) | ((blknr >> 24) & 0xF);
+
+ if (blkcnt >= MAX_SECTORS)
+ num_of_sectors = 0;
+ else
+ num_of_sectors = blkcnt;
+
+ ata_sff_busy_wait(ATA_BUSY | ATA_DRQ, 5000, 50);
+ writeb(num_of_sectors, CONFIG_SYS_ATA_BASE_ADDR + MXC_ATA_DSCR);
+ writeb(lba_addr[0], CONFIG_SYS_ATA_BASE_ADDR + MXC_ATA_DSNR);
+ writeb(lba_addr[1], CONFIG_SYS_ATA_BASE_ADDR + MXC_ATA_DCLR);
+ writeb(lba_addr[2], CONFIG_SYS_ATA_BASE_ADDR + MXC_ATA_DCHR);
+ writeb(lba_addr[3], CONFIG_SYS_ATA_BASE_ADDR + MXC_ATA_DDHR);
+
+ /* Issue Read command */
+ ata_sff_exec_command(ATA_CMD_PIO_READ);
+ status = ata_sff_busy_wait(ATA_BUSY, 5000, 50);
+ if (status & ATA_ERR) {
+ puts("Error while issuing ATA Read command\n");
+ return -1;
+ }
+ if (num_of_sectors == 0) {
+ read_sector_pio((u32 *)buffer, MAX_SECTORS);
+ blkcnt -= MAX_SECTORS;
+ blknr += MAX_SECTORS;
+ buffer += (MAX_SECTORS * ATA_SECTOR_SIZE);
+ } else {
+ read_sector_pio((u32 *)buffer, num_of_sectors);
+ blkcnt -= num_of_sectors;
+ blknr += num_of_sectors;
+ buffer += (num_of_sectors * ATA_SECTOR_SIZE);
+ }
+ }
+
+ return total_blks;
+}
+
+ulong pata_write(int dev, ulong blknr, ulong blkcnt, const void *buffer)
+{
+ s32 lba_addr[4] = { 0 },
+ num_of_sectors = 0;
+ u8 status = 0;
+ u32 total_blks = blkcnt;
+
+ while (blkcnt > 0) {
+ lba_addr[0] = blknr & 0xFF;
+ lba_addr[1] = (blknr >> 8) & 0xFF;
+ lba_addr[2] = (blknr >> 16) & 0xFF;
+ /* Enable the LBA bit */
+ lba_addr[3] = (1 << 6) | ((blknr >> 24) & 0xF);
+
+ if (blkcnt >= MAX_SECTORS)
+ num_of_sectors = 0;
+ else
+ num_of_sectors = blkcnt;
+
+ ata_sff_busy_wait(ATA_BUSY | ATA_DRQ, 5000, 50);
+ writeb(num_of_sectors, CONFIG_SYS_ATA_BASE_ADDR + MXC_ATA_DSCR);
+ writeb(lba_addr[0], CONFIG_SYS_ATA_BASE_ADDR + MXC_ATA_DSNR);
+ writeb(lba_addr[1], CONFIG_SYS_ATA_BASE_ADDR + MXC_ATA_DCLR);
+ writeb(lba_addr[2], CONFIG_SYS_ATA_BASE_ADDR + MXC_ATA_DCHR);
+ writeb(lba_addr[3], CONFIG_SYS_ATA_BASE_ADDR + MXC_ATA_DDHR);
+
+ /* Issue Write command */
+ ata_sff_exec_command(ATA_CMD_PIO_WRITE);
+ ata_sff_busy_wait(ATA_BUSY, 5000, 50);
+ if (status & ATA_ERR) {
+ puts("Error while issuing ATA Write command\n");
+ return -1;
+ }
+ if (num_of_sectors == 0) {
+ write_sector_pio((u32 *)buffer, MAX_SECTORS);
+ blkcnt -= MAX_SECTORS;
+ blknr += MAX_SECTORS;
+ buffer += (MAX_SECTORS * ATA_SECTOR_SIZE);
+ } else {
+ write_sector_pio((u32 *)buffer, num_of_sectors);
+ blkcnt -= num_of_sectors;
+ blknr += num_of_sectors;
+ buffer += (num_of_sectors * ATA_SECTOR_SIZE);
+ }
+ }
+
+ return total_blks;
+}
+
+int scan_pata(int dev)
+{
+ u8 serial[ATA_ID_SERNO_LEN + 1];
+ u8 firmware[ATA_ID_FW_REV_LEN + 1];
+ u8 product[ATA_ID_PROD_LEN + 1];
+ u16 *id;
+ u64 n_sectors;
+
+ id = (u16 *)malloc(ATA_ID_WORDS * 2);
+ if (!id) {
+ printf("id malloc failed\n\r");
+ return -1;
+ }
+
+retry:
+ /* Identify device to get information */
+ ata_identify(dev, id);
+
+ if (ata_id_is_ata(id)) {
+ if ((id[2] == 0x37c8 || id[2] == 0x738c)) {
+ s32 err_mask = 0;
+
+ err_mask = ata_dev_set_feature(dev, SETFEATURES_SPINUP);
+ if (err_mask && id[2] != 0x738c) {
+ puts("ATA SPINUP Failed \n");
+ return -1;
+ }
+ if (id[2] == 0x37c8)
+ goto retry;
+ }
+ } else {
+ puts("ATA IDENTIFY DEVICE command Failed \n");
+ }
+
+ /* Serial number */
+ ata_id_c_string(id, serial, ATA_ID_SERNO, sizeof(serial));
+ memcpy(pata_dev_desc[dev].product, serial, sizeof(serial));
+
+ /* Firmware version */
+ ata_id_c_string(id, firmware, ATA_ID_FW_REV, sizeof(firmware));
+ memcpy(pata_dev_desc[dev].revision, firmware, sizeof(firmware));
+
+ /* Product model */
+ ata_id_c_string(id, product, ATA_ID_PROD, sizeof(product));
+ memcpy(pata_dev_desc[dev].vendor, product, sizeof(product));
+
+ /* Totoal sectors */
+ n_sectors = ata_id_n_sectors(id);
+ pata_dev_desc[dev].lba = (u32)n_sectors;
+
+ /* Check if support LBA48 */
+ if (ata_id_has_lba48(id)) {
+ pata_dev_desc[dev].lba48 = 1;
+ debug("Device support LBA48\n\r");
+ }
+
+#ifdef DEBUG
+ fsl_pata_identify(dev, id);
+ ata_dump_id(id);
+#endif
+ free((void *)id);
+ return 0;
+}
diff --git a/drivers/block/mxc_ata.h b/drivers/block/mxc_ata.h
new file mode 100644
index 0000000..5d198f1
--- /dev/null
+++ b/drivers/block/mxc_ata.h
@@ -0,0 +1,90 @@
+#ifndef _MXC_ATA_H_
+#define _MXC_ATA_H_
+
+/*
+ * (C) Copyright 2008-2009 Freescale Semiconductor, Inc.
+ *
+ * See file CREDITS for list of people who contributed to this
+ * project.
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
+ * MA 02111-1307 USA
+ */
+
+#define MXC_ATA_TIMING_REGS 0x00
+#define MXC_ATA_FIFO_FILL 0x20
+#define MXC_ATA_CONTROL 0x24
+#define MXC_ATA_INT_PEND 0x28
+#define MXC_ATA_INT_EN 0x2C
+#define MXC_ATA_INT_CLEAR 0x30
+#define MXC_ATA_FIFO_ALARM 0x34
+#define MXC_ATA_ADMA_ERROR_STATUS 0x38
+#define MXC_ATA_SYS_DMA_BADDR 0x3C
+#define MXC_ATA_ADMA_SYS_ADDR 0x40
+#define MXC_ATA_BLOCK_COUNT 0x48
+#define MXC_ATA_BURST_LENGTH 0x4C
+#define MXC_ATA_SECTOR_SIZE 0x50
+#define MXC_ATA_DRIVE_DATA 0xA0
+#define MXC_ATA_DFTR 0xA4
+#define MXC_ATA_DSCR 0xA8
+#define MXC_ATA_DSNR 0xAC
+#define MXC_ATA_DCLR 0xB0
+#define MXC_ATA_DCHR 0xB4
+#define MXC_ATA_DDHR 0xB8
+#define MXC_ATA_DCDR 0xBC
+#define MXC_ATA_DRIVE_CONTROL 0xD8
+
+/* bits within MXC_ATA_CONTROL */
+#define MXC_ATA_CTRL_DMA_SRST 0x1000
+#define MXC_ATA_CTRL_DMA_64ADMA 0x800
+#define MXC_ATA_CTRL_DMA_32ADMA 0x400
+#define MXC_ATA_CTRL_DMA_STAT_STOP 0x200
+#define MXC_ATA_CTRL_DMA_ENABLE 0x100
+#define MXC_ATA_CTRL_FIFO_RST_B 0x80
+#define MXC_ATA_CTRL_ATA_RST_B 0x40
+#define MXC_ATA_CTRL_FIFO_TX_EN 0x20
+#define MXC_ATA_CTRL_FIFO_RCV_EN 0x10
+#define MXC_ATA_CTRL_DMA_PENDING 0x08
+#define MXC_ATA_CTRL_DMA_ULTRA 0x04
+#define MXC_ATA_CTRL_DMA_WRITE 0x02
+#define MXC_ATA_CTRL_IORDY_EN 0x01
+
+/* bits within the interrupt control registers */
+#define MXC_ATA_INTR_ATA_INTRQ1 0x80
+#define MXC_ATA_INTR_FIFO_UNDERFLOW 0x40
+#define MXC_ATA_INTR_FIFO_OVERFLOW 0x20
+#define MXC_ATA_INTR_CTRL_IDLE 0x10
+#define MXC_ATA_INTR_ATA_INTRQ2 0x08
+#define MXC_ATA_INTR_DMA_ERR 0x04
+#define MXC_ATA_INTR_DMA_TRANS_OVER 0x02
+
+/* ADMA Addr Descriptor Attribute Filed */
+#define MXC_ADMA_DES_ATTR_VALID 0x01
+#define MXC_ADMA_DES_ATTR_END 0x02
+#define MXC_ADMA_DES_ATTR_INT 0x04
+#define MXC_ADMA_DES_ATTR_SET 0x10
+#define MXC_ADMA_DES_ATTR_TRAN 0x20
+#define MXC_ADMA_DES_ATTR_LINK 0x30
+
+#define PIO_XFER_MODE_0 0
+#define PIO_XFER_MODE_1 1
+#define PIO_XFER_MODE_2 2
+#define PIO_XFER_MODE_3 3
+#define PIO_XFER_MODE_4 4
+
+#define ATA_SECTOR_SIZE 512
+#define MAX_SECTORS 256
+
+#endif /* _IMX_ATA_H_ */