diff options
author | Sonic Zhang <Sonic.Zhang@analog.com> | 2008-11-26 22:16:45 -0500 |
---|---|---|
committer | Mike Frysinger <vapier@gentoo.org> | 2009-02-02 12:27:14 -0500 |
commit | 8a6b272596d43de0db4943eac7af58c3534c4026 (patch) | |
tree | 9cd5498465898a78b0bec75fc8d4091b81cd1192 /drivers | |
parent | be9d8c780e6831cb84b7d4590ceae03dca8fc10b (diff) |
Blackfin: add driver for on-chip ATAPI controller
This is a port of the Linux Blackfin on-chip ATAPI driver to U-Boot.
Signed-off-by: Sonic Zhang <Sonic.Zhang@analog.com>
Signed-off-by: Mike Frysinger <vapier@gentoo.org>
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/block/Makefile | 1 | ||||
-rw-r--r-- | drivers/block/pata_bfin.c | 1201 | ||||
-rw-r--r-- | drivers/block/pata_bfin.h | 173 |
3 files changed, 1375 insertions, 0 deletions
diff --git a/drivers/block/Makefile b/drivers/block/Makefile index 642582b0881..59388d9d452 100644 --- a/drivers/block/Makefile +++ b/drivers/block/Makefile @@ -29,6 +29,7 @@ COBJS-$(CONFIG_SCSI_AHCI) += ahci.o COBJS-$(CONFIG_ATA_PIIX) += ata_piix.o COBJS-$(CONFIG_FSL_SATA) += fsl_sata.o COBJS-$(CONFIG_LIBATA) += libata.o +COBJS-$(CONFIG_PATA_BFIN) += pata_bfin.o COBJS-$(CONFIG_SATA_SIL3114) += sata_sil3114.o COBJS-$(CONFIG_IDE_SIL680) += sil680.o COBJS-$(CONFIG_SCSI_SYM53C8XX) += sym53c8xx.o diff --git a/drivers/block/pata_bfin.c b/drivers/block/pata_bfin.c new file mode 100644 index 00000000000..f16dabeba79 --- /dev/null +++ b/drivers/block/pata_bfin.c @@ -0,0 +1,1201 @@ +/* + * Driver for Blackfin on-chip ATAPI controller. + * + * Enter bugs at http://blackfin.uclinux.org/ + * + * Copyright (c) 2008 Analog Devices Inc. + * + * Licensed under the GPL-2 or later. + */ + +#include <common.h> +#include <command.h> +#include <config.h> +#include <asm/byteorder.h> +#include <asm/io.h> +#include <asm/errno.h> +#include <asm/mach-common/bits/pata.h> +#include <ata.h> +#include <libata.h> +#include "pata_bfin.h" + +static struct ata_port port[CONFIG_SYS_SATA_MAX_DEVICE]; + +/** + * PIO Mode - Frequency compatibility + */ +/* mode: 0 1 2 3 4 */ +static const u32 pio_fsclk[] = +{ 33333333, 33333333, 33333333, 33333333, 33333333 }; + +/** + * MDMA Mode - Frequency compatibility + */ +/* mode: 0 1 2 */ +static const u32 mdma_fsclk[] = { 33333333, 33333333, 33333333 }; + +/** + * UDMA Mode - Frequency compatibility + * + * UDMA5 - 100 MB/s - SCLK = 133 MHz + * UDMA4 - 66 MB/s - SCLK >= 80 MHz + * UDMA3 - 44.4 MB/s - SCLK >= 50 MHz + * UDMA2 - 33 MB/s - SCLK >= 40 MHz + */ +/* mode: 0 1 2 3 4 5 */ +static const u32 udma_fsclk[] = +{ 33333333, 33333333, 40000000, 50000000, 80000000, 133333333 }; + +/** + * Register transfer timing table + */ +/* mode: 0 1 2 3 4 */ +/* Cycle Time */ +static const u32 reg_t0min[] = { 600, 383, 330, 180, 120 }; +/* DIOR/DIOW to end cycle */ +static const u32 reg_t2min[] = { 290, 290, 290, 70, 25 }; +/* DIOR/DIOW asserted pulse width */ +static const u32 reg_teocmin[] = { 290, 290, 290, 80, 70 }; + +/** + * PIO timing table + */ +/* mode: 0 1 2 3 4 */ +/* Cycle Time */ +static const u32 pio_t0min[] = { 600, 383, 240, 180, 120 }; +/* Address valid to DIOR/DIORW */ +static const u32 pio_t1min[] = { 70, 50, 30, 30, 25 }; +/* DIOR/DIOW to end cycle */ +static const u32 pio_t2min[] = { 165, 125, 100, 80, 70 }; +/* DIOR/DIOW asserted pulse width */ +static const u32 pio_teocmin[] = { 165, 125, 100, 70, 25 }; +/* DIOW data hold */ +static const u32 pio_t4min[] = { 30, 20, 15, 10, 10 }; + +/* ****************************************************************** + * Multiword DMA timing table + * ****************************************************************** + */ +/* mode: 0 1 2 */ +/* Cycle Time */ +static const u32 mdma_t0min[] = { 480, 150, 120 }; +/* DIOR/DIOW asserted pulse width */ +static const u32 mdma_tdmin[] = { 215, 80, 70 }; +/* DMACK to read data released */ +static const u32 mdma_thmin[] = { 20, 15, 10 }; +/* DIOR/DIOW to DMACK hold */ +static const u32 mdma_tjmin[] = { 20, 5, 5 }; +/* DIOR negated pulse width */ +static const u32 mdma_tkrmin[] = { 50, 50, 25 }; +/* DIOR negated pulse width */ +static const u32 mdma_tkwmin[] = { 215, 50, 25 }; +/* CS[1:0] valid to DIOR/DIOW */ +static const u32 mdma_tmmin[] = { 50, 30, 25 }; +/* DMACK to read data released */ +static const u32 mdma_tzmax[] = { 20, 25, 25 }; + +/** + * Ultra DMA timing table + */ +/* mode: 0 1 2 3 4 5 */ +static const u32 udma_tcycmin[] = { 112, 73, 54, 39, 25, 17 }; +static const u32 udma_tdvsmin[] = { 70, 48, 31, 20, 7, 5 }; +static const u32 udma_tenvmax[] = { 70, 70, 70, 55, 55, 50 }; +static const u32 udma_trpmin[] = { 160, 125, 100, 100, 100, 85 }; +static const u32 udma_tmin[] = { 5, 5, 5, 5, 3, 3 }; + + +static const u32 udma_tmlimin = 20; +static const u32 udma_tzahmin = 20; +static const u32 udma_tenvmin = 20; +static const u32 udma_tackmin = 20; +static const u32 udma_tssmin = 50; + +static void msleep(int count) +{ + int i; + + for (i = 0; i < count; i++) + udelay(1000); +} + +/** + * + * Function: num_clocks_min + * + * Description: + * calculate number of SCLK cycles to meet minimum timing + */ +static unsigned short num_clocks_min(unsigned long tmin, + unsigned long fsclk) +{ + unsigned long tmp ; + unsigned short result; + + tmp = tmin * (fsclk/1000/1000) / 1000; + result = (unsigned short)tmp; + if ((tmp*1000*1000) < (tmin*(fsclk/1000))) + result++; + + return result; +} + +/** + * bfin_set_piomode - Initialize host controller PATA PIO timings + * @ap: Port whose timings we are configuring + * @pio_mode: mode + * + * Set PIO mode for device. + * + * LOCKING: + * None (inherited from caller). + */ + +static void bfin_set_piomode(struct ata_port *ap, int pio_mode) +{ + int mode = pio_mode - XFER_PIO_0; + void __iomem *base = (void __iomem *)ap->ioaddr.ctl_addr; + unsigned int fsclk = get_sclk(); + unsigned short teoc_reg, t2_reg, teoc_pio; + unsigned short t4_reg, t2_pio, t1_reg; + unsigned short n0, n6, t6min = 5; + + /* the most restrictive timing value is t6 and tc, the DIOW - data hold + * If one SCLK pulse is longer than this minimum value then register + * transfers cannot be supported at this frequency. + */ + n6 = num_clocks_min(t6min, fsclk); + if (mode >= 0 && mode <= 4 && n6 >= 1) { + debug("set piomode: mode=%d, fsclk=%ud\n", mode, fsclk); + /* calculate the timing values for register transfers. */ + while (mode > 0 && pio_fsclk[mode] > fsclk) + mode--; + + /* DIOR/DIOW to end cycle time */ + t2_reg = num_clocks_min(reg_t2min[mode], fsclk); + /* DIOR/DIOW asserted pulse width */ + teoc_reg = num_clocks_min(reg_teocmin[mode], fsclk); + /* Cycle Time */ + n0 = num_clocks_min(reg_t0min[mode], fsclk); + + /* increase t2 until we meed the minimum cycle length */ + if (t2_reg + teoc_reg < n0) + t2_reg = n0 - teoc_reg; + + /* calculate the timing values for pio transfers. */ + + /* DIOR/DIOW to end cycle time */ + t2_pio = num_clocks_min(pio_t2min[mode], fsclk); + /* DIOR/DIOW asserted pulse width */ + teoc_pio = num_clocks_min(pio_teocmin[mode], fsclk); + /* Cycle Time */ + n0 = num_clocks_min(pio_t0min[mode], fsclk); + + /* increase t2 until we meed the minimum cycle length */ + if (t2_pio + teoc_pio < n0) + t2_pio = n0 - teoc_pio; + + /* Address valid to DIOR/DIORW */ + t1_reg = num_clocks_min(pio_t1min[mode], fsclk); + + /* DIOW data hold */ + t4_reg = num_clocks_min(pio_t4min[mode], fsclk); + + ATAPI_SET_REG_TIM_0(base, (teoc_reg<<8 | t2_reg)); + ATAPI_SET_PIO_TIM_0(base, (t4_reg<<12 | t2_pio<<4 | t1_reg)); + ATAPI_SET_PIO_TIM_1(base, teoc_pio); + if (mode > 2) { + ATAPI_SET_CONTROL(base, + ATAPI_GET_CONTROL(base) | IORDY_EN); + } else { + ATAPI_SET_CONTROL(base, + ATAPI_GET_CONTROL(base) & ~IORDY_EN); + } + + /* Disable host ATAPI PIO interrupts */ + ATAPI_SET_INT_MASK(base, ATAPI_GET_INT_MASK(base) + & ~(PIO_DONE_MASK | HOST_TERM_XFER_MASK)); + SSYNC(); + } +} + +/** + * + * Function: wait_complete + * + * Description: Waits the interrupt from device + * + */ +static inline void wait_complete(void __iomem *base, unsigned short mask) +{ + unsigned short status; + unsigned int i = 0; + + for (i = 0; i < PATA_BFIN_WAIT_TIMEOUT; i++) { + status = ATAPI_GET_INT_STATUS(base) & mask; + if (status) + break; + } + + ATAPI_SET_INT_STATUS(base, mask); +} + +/** + * + * Function: write_atapi_register + * + * Description: Writes to ATA Device Resgister + * + */ + +static void write_atapi_register(void __iomem *base, + unsigned long ata_reg, unsigned short value) +{ + /* Program the ATA_DEV_TXBUF register with write data (to be + * written into the device). + */ + ATAPI_SET_DEV_TXBUF(base, value); + + /* Program the ATA_DEV_ADDR register with address of the + * device register (0x01 to 0x0F). + */ + ATAPI_SET_DEV_ADDR(base, ata_reg); + + /* Program the ATA_CTRL register with dir set to write (1) + */ + ATAPI_SET_CONTROL(base, (ATAPI_GET_CONTROL(base) | XFER_DIR)); + + /* ensure PIO DMA is not set */ + ATAPI_SET_CONTROL(base, (ATAPI_GET_CONTROL(base) & ~PIO_USE_DMA)); + + /* and start the transfer */ + ATAPI_SET_CONTROL(base, (ATAPI_GET_CONTROL(base) | PIO_START)); + + /* Wait for the interrupt to indicate the end of the transfer. + * (We need to wait on and clear rhe ATA_DEV_INT interrupt status) + */ + wait_complete(base, PIO_DONE_INT); +} + +/** + * + * Function: read_atapi_register + * + *Description: Reads from ATA Device Resgister + * + */ + +static unsigned short read_atapi_register(void __iomem *base, + unsigned long ata_reg) +{ + /* Program the ATA_DEV_ADDR register with address of the + * device register (0x01 to 0x0F). + */ + ATAPI_SET_DEV_ADDR(base, ata_reg); + + /* Program the ATA_CTRL register with dir set to read (0) and + */ + ATAPI_SET_CONTROL(base, (ATAPI_GET_CONTROL(base) & ~XFER_DIR)); + + /* ensure PIO DMA is not set */ + ATAPI_SET_CONTROL(base, (ATAPI_GET_CONTROL(base) & ~PIO_USE_DMA)); + + /* and start the transfer */ + ATAPI_SET_CONTROL(base, (ATAPI_GET_CONTROL(base) | PIO_START)); + + /* Wait for the interrupt to indicate the end of the transfer. + * (PIO_DONE interrupt is set and it doesn't seem to matter + * that we don't clear it) + */ + wait_complete(base, PIO_DONE_INT); + + /* Read the ATA_DEV_RXBUF register with write data (to be + * written into the device). + */ + return ATAPI_GET_DEV_RXBUF(base); +} + +/** + * + * Function: write_atapi_register_data + * + * Description: Writes to ATA Device Resgister + * + */ + +static void write_atapi_data(void __iomem *base, + int len, unsigned short *buf) +{ + int i; + + /* Set transfer length to 1 */ + ATAPI_SET_XFER_LEN(base, 1); + + /* Program the ATA_DEV_ADDR register with address of the + * ATA_REG_DATA + */ + ATAPI_SET_DEV_ADDR(base, ATA_REG_DATA); + + /* Program the ATA_CTRL register with dir set to write (1) + */ + ATAPI_SET_CONTROL(base, (ATAPI_GET_CONTROL(base) | XFER_DIR)); + + /* ensure PIO DMA is not set */ + ATAPI_SET_CONTROL(base, (ATAPI_GET_CONTROL(base) & ~PIO_USE_DMA)); + + for (i = 0; i < len; i++) { + /* Program the ATA_DEV_TXBUF register with write data (to be + * written into the device). + */ + ATAPI_SET_DEV_TXBUF(base, buf[i]); + + /* and start the transfer */ + ATAPI_SET_CONTROL(base, (ATAPI_GET_CONTROL(base) | PIO_START)); + + /* Wait for the interrupt to indicate the end of the transfer. + * (We need to wait on and clear rhe ATA_DEV_INT + * interrupt status) + */ + wait_complete(base, PIO_DONE_INT); + } +} + +/** + * + * Function: read_atapi_register_data + * + * Description: Reads from ATA Device Resgister + * + */ + +static void read_atapi_data(void __iomem *base, + int len, unsigned short *buf) +{ + int i; + + /* Set transfer length to 1 */ + ATAPI_SET_XFER_LEN(base, 1); + + /* Program the ATA_DEV_ADDR register with address of the + * ATA_REG_DATA + */ + ATAPI_SET_DEV_ADDR(base, ATA_REG_DATA); + + /* Program the ATA_CTRL register with dir set to read (0) and + */ + ATAPI_SET_CONTROL(base, (ATAPI_GET_CONTROL(base) & ~XFER_DIR)); + + /* ensure PIO DMA is not set */ + ATAPI_SET_CONTROL(base, (ATAPI_GET_CONTROL(base) & ~PIO_USE_DMA)); + + for (i = 0; i < len; i++) { + /* and start the transfer */ + ATAPI_SET_CONTROL(base, (ATAPI_GET_CONTROL(base) | PIO_START)); + + /* Wait for the interrupt to indicate the end of the transfer. + * (PIO_DONE interrupt is set and it doesn't seem to matter + * that we don't clear it) + */ + wait_complete(base, PIO_DONE_INT); + + /* Read the ATA_DEV_RXBUF register with write data (to be + * written into the device). + */ + buf[i] = ATAPI_GET_DEV_RXBUF(base); + } +} + +/** + * bfin_check_status - Read device status reg & clear interrupt + * @ap: port where the device is + * + * Note: Original code is ata_check_status(). + */ + +static u8 bfin_check_status(struct ata_port *ap) +{ + void __iomem *base = (void __iomem *)ap->ioaddr.ctl_addr; + return read_atapi_register(base, ATA_REG_STATUS); +} + +/** + * bfin_check_altstatus - Read device alternate status reg + * @ap: port where the device is + */ + +static u8 bfin_check_altstatus(struct ata_port *ap) +{ + void __iomem *base = (void __iomem *)ap->ioaddr.ctl_addr; + return read_atapi_register(base, ATA_REG_ALTSTATUS); +} + +/** + * bfin_ata_busy_wait - Wait for a port status register + * @ap: Port to wait for. + * @bits: bits that must be clear + * @max: number of 10uS waits to perform + * + * Waits up to max*10 microseconds for the selected bits in the port's + * status register to be cleared. + * Returns final value of status register. + * + * LOCKING: + * Inherited from caller. + */ +static inline u8 bfin_ata_busy_wait(struct ata_port *ap, unsigned int bits, + unsigned int max, u8 usealtstatus) +{ + u8 status; + + do { + udelay(10); + if (usealtstatus) + status = bfin_check_altstatus(ap); + else + status = bfin_check_status(ap); + max--; + } while (status != 0xff && (status & bits) && (max > 0)); + + return status; +} + +/** + * bfin_ata_busy_sleep - sleep until BSY clears, or timeout + * @ap: port containing status register to be polled + * @tmout_pat: impatience timeout in msecs + * @tmout: overall timeout in msecs + * + * Sleep until ATA Status register bit BSY clears, + * or a timeout occurs. + * + * RETURNS: + * 0 on success, -errno otherwise. + */ +static int bfin_ata_busy_sleep(struct ata_port *ap, + long tmout_pat, unsigned long tmout) +{ + u8 status; + + status = bfin_ata_busy_wait(ap, ATA_BUSY, 300, 0); + while (status != 0xff && (status & ATA_BUSY) && tmout_pat > 0) { + msleep(50); + tmout_pat -= 50; + status = bfin_ata_busy_wait(ap, ATA_BUSY, 3, 0); + } + + if (status != 0xff && (status & ATA_BUSY)) + printf("port is slow to respond, please be patient " + "(Status 0x%x)\n", status); + + while (status != 0xff && (status & ATA_BUSY) && tmout_pat > 0) { + msleep(50); + tmout_pat -= 50; + status = bfin_check_status(ap); + } + + if (status == 0xff) + return -ENODEV; + + if (status & ATA_BUSY) { + printf("port failed to respond " + "(%lu secs, Status 0x%x)\n", + DIV_ROUND_UP(tmout, 1000), status); + return -EBUSY; + } + + return 0; +} + +/** + * bfin_dev_select - Select device 0/1 on ATA bus + * @ap: ATA channel to manipulate + * @device: ATA device (numbered from zero) to select + * + * Note: Original code is ata_sff_dev_select(). + */ + +static void bfin_dev_select(struct ata_port *ap, unsigned int device) +{ + void __iomem *base = (void __iomem *)ap->ioaddr.ctl_addr; + u8 tmp; + + + if (device == 0) + tmp = ATA_DEVICE_OBS; + else + tmp = ATA_DEVICE_OBS | ATA_DEV1; + + write_atapi_register(base, ATA_REG_DEVICE, tmp); + udelay(1); +} + +/** + * bfin_devchk - PATA device presence detection + * @ap: ATA channel to examine + * @device: Device to examine (starting at zero) + * + * Note: Original code is ata_devchk(). + */ + +static unsigned int bfin_devchk(struct ata_port *ap, + unsigned int device) +{ + void __iomem *base = (void __iomem *)ap->ioaddr.ctl_addr; + u8 nsect, lbal; + + bfin_dev_select(ap, device); + + write_atapi_register(base, ATA_REG_NSECT, 0x55); + write_atapi_register(base, ATA_REG_LBAL, 0xaa); + + write_atapi_register(base, ATA_REG_NSECT, 0xaa); + write_atapi_register(base, ATA_REG_LBAL, 0x55); + + write_atapi_register(base, ATA_REG_NSECT, 0x55); + write_atapi_register(base, ATA_REG_LBAL, 0xaa); + + nsect = read_atapi_register(base, ATA_REG_NSECT); + lbal = read_atapi_register(base, ATA_REG_LBAL); + + if ((nsect == 0x55) && (lbal == 0xaa)) + return 1; /* we found a device */ + + return 0; /* nothing found */ +} + +/** + * bfin_bus_post_reset - PATA device post reset + * + * Note: Original code is ata_bus_post_reset(). + */ + +static void bfin_bus_post_reset(struct ata_port *ap, unsigned int devmask) +{ + void __iomem *base = (void __iomem *)ap->ioaddr.ctl_addr; + unsigned int dev0 = devmask & (1 << 0); + unsigned int dev1 = devmask & (1 << 1); + long deadline; + + /* if device 0 was found in ata_devchk, wait for its + * BSY bit to clear + */ + if (dev0) + bfin_ata_busy_sleep(ap, ATA_TMOUT_BOOT_QUICK, ATA_TMOUT_BOOT); + + /* if device 1 was found in ata_devchk, wait for + * register access, then wait for BSY to clear + */ + deadline = ATA_TMOUT_BOOT; + while (dev1) { + u8 nsect, lbal; + + bfin_dev_select(ap, 1); + nsect = read_atapi_register(base, ATA_REG_NSECT); + lbal = read_atapi_register(base, ATA_REG_LBAL); + if ((nsect == 1) && (lbal == 1)) + break; + if (deadline <= 0) { + dev1 = 0; + break; + } + msleep(50); /* give drive a breather */ + deadline -= 50; + } + if (dev1) + bfin_ata_busy_sleep(ap, ATA_TMOUT_BOOT_QUICK, ATA_TMOUT_BOOT); + + /* is all this really necessary? */ + bfin_dev_select(ap, 0); + if (dev1) + bfin_dev_select(ap, 1); + if (dev0) + bfin_dev_select(ap, 0); +} + +/** + * bfin_bus_softreset - PATA device software reset + * + * Note: Original code is ata_bus_softreset(). + */ + +static unsigned int bfin_bus_softreset(struct ata_port *ap, + unsigned int devmask) +{ + void __iomem *base = (void __iomem *)ap->ioaddr.ctl_addr; + + /* software reset. causes dev0 to be selected */ + write_atapi_register(base, ATA_REG_CTRL, ap->ctl_reg); + udelay(20); + write_atapi_register(base, ATA_REG_CTRL, ap->ctl_reg | ATA_SRST); + udelay(20); + write_atapi_register(base, ATA_REG_CTRL, ap->ctl_reg); + + /* spec mandates ">= 2ms" before checking status. + * We wait 150ms, because that was the magic delay used for + * ATAPI devices in Hale Landis's ATADRVR, for the period of time + * between when the ATA command register is written, and then + * status is checked. Because waiting for "a while" before + * checking status is fine, post SRST, we perform this magic + * delay here as well. + * + * Old drivers/ide uses the 2mS rule and then waits for ready + */ + msleep(150); + + /* Before we perform post reset processing we want to see if + * the bus shows 0xFF because the odd clown forgets the D7 + * pulldown resistor. + */ + if (bfin_check_status(ap) == 0xFF) + return 0; + + bfin_bus_post_reset(ap, devmask); + + return 0; +} + +/** + * bfin_softreset - reset host port via ATA SRST + * @ap: port to reset + * + * Note: Original code is ata_sff_softreset(). + */ + +static int bfin_softreset(struct ata_port *ap) +{ + unsigned int err_mask; + + ap->dev_mask = 0; + + /* determine if device 0/1 are present. + * only one device is supported on one port by now. + */ + if (bfin_devchk(ap, 0)) + ap->dev_mask |= (1 << 0); + else if (bfin_devchk(ap, 1)) + ap->dev_mask |= (1 << 1); + else + return -ENODEV; + + /* select device 0 again */ + bfin_dev_select(ap, 0); + + /* issue bus reset */ + err_mask = bfin_bus_softreset(ap, ap->dev_mask); + if (err_mask) { + printf("SRST failed (err_mask=0x%x)\n", + err_mask); + ap->dev_mask = 0; + return -EIO; + } + + return 0; +} + +/** + * bfin_irq_clear - Clear ATAPI interrupt. + * @ap: Port associated with this ATA transaction. + * + * Note: Original code is ata_sff_irq_clear(). + */ + +static void bfin_irq_clear(struct ata_port *ap) +{ + void __iomem *base = (void __iomem *)ap->ioaddr.ctl_addr; + + ATAPI_SET_INT_STATUS(base, ATAPI_GET_INT_STATUS(base)|ATAPI_DEV_INT + | MULTI_DONE_INT | UDMAIN_DONE_INT | UDMAOUT_DONE_INT + | MULTI_TERM_INT | UDMAIN_TERM_INT | UDMAOUT_TERM_INT); +} + +static u8 bfin_wait_for_irq(struct ata_port *ap, unsigned int max) +{ + void __iomem *base = (void __iomem *)ap->ioaddr.ctl_addr; + + do { + if (ATAPI_GET_INT_STATUS(base) & (ATAPI_DEV_INT + | MULTI_DONE_INT | UDMAIN_DONE_INT | UDMAOUT_DONE_INT + | MULTI_TERM_INT | UDMAIN_TERM_INT | UDMAOUT_TERM_INT)) { + break; + } + udelay(1000); + max--; + } while ((max > 0)); + + return max == 0; +} + +/** + * bfin_ata_reset_port - initialize BFIN ATAPI port. + */ + +static int bfin_ata_reset_port(struct ata_port *ap) +{ + void __iomem *base = (void __iomem *)ap->ioaddr.ctl_addr; + int count; + unsigned short status; + + /* Disable all ATAPI interrupts */ + ATAPI_SET_INT_MASK(base, 0); + SSYNC(); + + /* Assert the RESET signal 25us*/ + ATAPI_SET_CONTROL(base, ATAPI_GET_CONTROL(base) | DEV_RST); + udelay(30); + + /* Negate the RESET signal for 2ms*/ + ATAPI_SET_CONTROL(base, ATAPI_GET_CONTROL(base) & ~DEV_RST); + msleep(2); + + /* Wait on Busy flag to clear */ + count = 10000000; + do { + status = read_atapi_register(base, ATA_REG_STATUS); + } while (--count && (status & ATA_BUSY)); + + /* Enable only ATAPI Device interrupt */ + ATAPI_SET_INT_MASK(base, 1); + SSYNC(); + + return !count; +} + +/** + * + * Function: bfin_config_atapi_gpio + * + * Description: Configures the ATAPI pins for use + * + */ +static int bfin_config_atapi_gpio(struct ata_port *ap) +{ + bfin_write_PORTH_FER(bfin_read_PORTH_FER() | 0x4); + bfin_write_PORTH_MUX(bfin_read_PORTH_MUX() & ~0x30); + bfin_write_PORTH_DIR_SET(0x4); + + bfin_write_PORTJ_FER(0x7f8); + bfin_write_PORTJ_MUX(bfin_read_PORTI_MUX() & ~0x3fffc0); + bfin_write_PORTJ_DIR_SET(0x5f8); + bfin_write_PORTJ_DIR_CLEAR(0x200); + bfin_write_PORTJ_INEN(0x200); + + bfin_write_PINT2_ASSIGN(0x0707); + bfin_write_PINT2_MASK_SET(0x200); + SSYNC(); + + return 0; +} + +/** + * bfin_atapi_probe - attach a bfin atapi interface + * @pdev: platform device + * + * Register a bfin atapi interface. + * + * + * Platform devices are expected to contain 2 resources per port: + * + * - I/O Base (IORESOURCE_IO) + * - IRQ (IORESOURCE_IRQ) + * + */ +static int bfin_ata_probe_port(struct ata_port *ap) +{ + if (bfin_config_atapi_gpio(ap)) { + printf("Requesting Peripherals faild\n"); + return -EFAULT; + } + + if (bfin_ata_reset_port(ap)) { + printf("Fail to reset ATAPI device\n"); + return -EFAULT; + } + + if (ap->ata_mode >= XFER_PIO_0 && ap->ata_mode <= XFER_PIO_4) + bfin_set_piomode(ap, ap->ata_mode); + else { + printf("Given ATA data transfer mode is not supported.\n"); + return -EFAULT; + } + + return 0; +} + +#define ATA_SECTOR_WORDS (ATA_SECT_SIZE/2) + +static void bfin_ata_identify(struct ata_port *ap, int dev) +{ + void __iomem *base = (void __iomem *)ap->ioaddr.ctl_addr; + u8 status = 0; + static u16 iobuf[ATA_SECTOR_WORDS]; + u64 n_sectors = 0; + hd_driveid_t *iop = (hd_driveid_t *)iobuf; + + memset(iobuf, 0, sizeof(iobuf)); + + if (!(ap->dev_mask & (1 << dev))) + return; + + debug("port=%d dev=%d\n", ap->port_no, dev); + + bfin_dev_select(ap, dev); + + status = 0; + /* Device Identify Command */ + write_atapi_register(base, ATA_REG_CMD, ATA_CMD_ID_ATA); + bfin_check_altstatus(ap); + udelay(10); + + status = bfin_ata_busy_wait(ap, ATA_BUSY, 1000, 0); + if (status & ATA_ERR) { + printf("\ndevice not responding\n"); + ap->dev_mask &= ~(1 << dev); + return; + } + + read_atapi_data(base, ATA_SECTOR_WORDS, iobuf); + + ata_swap_buf_le16(iobuf, ATA_SECTOR_WORDS); + + /* we require LBA and DMA support (bits 8 & 9 of word 49) */ + if (!ata_id_has_dma(iobuf) || !ata_id_has_lba(iobuf)) + printf("ata%u: no dma/lba\n", ap->port_no); + +#ifdef DEBUG + ata_dump_id(iobuf); +#endif + + n_sectors = ata_id_n_sectors(iobuf); + + if (n_sectors == 0) { + ap->dev_mask &= ~(1 << dev); + return; + } + + ata_id_c_string(iobuf, (unsigned char *)sata_dev_desc[ap->port_no].revision, + ATA_ID_FW_REV, sizeof(sata_dev_desc[ap->port_no].revision)); + ata_id_c_string(iobuf, (unsigned char *)sata_dev_desc[ap->port_no].vendor, + ATA_ID_PROD, sizeof(sata_dev_desc[ap->port_no].vendor)); + ata_id_c_string(iobuf, (unsigned char *)sata_dev_desc[ap->port_no].product, + ATA_ID_SERNO, sizeof(sata_dev_desc[ap->port_no].product)); + + if ((iop->config & 0x0080) == 0x0080) + sata_dev_desc[ap->port_no].removable = 1; + else + sata_dev_desc[ap->port_no].removable = 0; + + sata_dev_desc[ap->port_no].lba = (u32) n_sectors; + debug("lba=0x%x\n", sata_dev_desc[ap->port_no].lba); + +#ifdef CONFIG_LBA48 + if (iop->command_set_2 & 0x0400) + sata_dev_desc[ap->port_no].lba48 = 1; + else + sata_dev_desc[ap->port_no].lba48 = 0; +#endif + + /* assuming HD */ + sata_dev_desc[ap->port_no].type = DEV_TYPE_HARDDISK; + sata_dev_desc[ap->port_no].blksz = ATA_SECT_SIZE; + sata_dev_desc[ap->port_no].lun = 0; /* just to fill something in... */ + + printf("PATA device#%d %s is found on ata port#%d.\n", + ap->port_no%PATA_DEV_NUM_PER_PORT, + sata_dev_desc[ap->port_no].vendor, + ap->port_no/PATA_DEV_NUM_PER_PORT); +} + +static void bfin_ata_set_Feature_cmd(struct ata_port *ap, int dev) +{ + void __iomem *base = (void __iomem *)ap->ioaddr.ctl_addr; + u8 status = 0; + + if (!(ap->dev_mask & (1 << dev))) + return; + + bfin_dev_select(ap, dev); + + write_atapi_register(base, ATA_REG_FEATURE, SETFEATURES_XFER); + write_atapi_register(base, ATA_REG_NSECT, ap->ata_mode); + write_atapi_register(base, ATA_REG_LBAL, 0); + write_atapi_register(base, ATA_REG_LBAM, 0); + write_atapi_register(base, ATA_REG_LBAH, 0); + + write_atapi_register(base, ATA_REG_DEVICE, ATA_DEVICE_OBS); + write_atapi_register(base, ATA_REG_CMD, ATA_CMD_SET_FEATURES); + + udelay(50); + msleep(150); + + status = bfin_ata_busy_wait(ap, ATA_BUSY, 5000, 0); + if ((status & (ATA_BUSY | ATA_ERR))) { + printf("Error : status 0x%02x\n", status); + ap->dev_mask &= ~(1 << dev); + } +} + +int scan_sata(int dev) +{ + /* dev is the index of each ata device in the system. one PATA port + * contains 2 devices. one element in scan_done array indicates one + * PATA port. device connected to one PATA port is selected by + * bfin_dev_select() before access. + */ + struct ata_port *ap = &port[dev]; + static int scan_done[(CONFIG_SYS_SATA_MAX_DEVICE+1)/PATA_DEV_NUM_PER_PORT]; + + if (scan_done[dev/PATA_DEV_NUM_PER_PORT]) + return 0; + + /* Check for attached device */ + if (!bfin_ata_probe_port(ap)) { + if (bfin_softreset(ap)) { + /* soft reset failed, try a hard one */ + bfin_ata_reset_port(ap); + if (bfin_softreset(ap)) + scan_done[dev/PATA_DEV_NUM_PER_PORT] = 1; + } else { + scan_done[dev/PATA_DEV_NUM_PER_PORT] = 1; + } + } + if (scan_done[dev/PATA_DEV_NUM_PER_PORT]) { + /* Probe device and set xfer mode */ + bfin_ata_identify(ap, dev%PATA_DEV_NUM_PER_PORT); + bfin_ata_set_Feature_cmd(ap, dev%PATA_DEV_NUM_PER_PORT); + init_part(&sata_dev_desc[dev]); + return 0; + } + + printf("PATA device#%d is not present on ATA port#%d.\n", + ap->port_no%PATA_DEV_NUM_PER_PORT, + ap->port_no/PATA_DEV_NUM_PER_PORT); + + return -1; +} + +int init_sata(int dev) +{ + struct ata_port *ap = &port[dev]; + static u8 init_done; + int res = 1; + + if (init_done) + return res; + + init_done = 1; + + switch (dev/PATA_DEV_NUM_PER_PORT) { + case 0: + ap->ioaddr.ctl_addr = ATAPI_CONTROL; + ap->ata_mode = CONFIG_BFIN_ATA_MODE; + break; + default: + printf("Tried to scan unknown port %d.\n", dev); + return res; + } + + if (ap->ata_mode < XFER_PIO_0 || ap->ata_mode > XFER_PIO_4) { + ap->ata_mode = XFER_PIO_4; + printf("DMA mode is not supported. Set to PIO mode 4.\n"); + } + + ap->port_no = dev; + ap->ctl_reg = 0x8; /*Default value of control reg */ + + res = 0; + return res; +} + +/* Read up to 255 sectors + * + * Returns sectors read +*/ +static u8 do_one_read(struct ata_port *ap, u64 blknr, u8 blkcnt, u16 *buffer, + uchar lba48) +{ + void __iomem *base = (void __iomem *)ap->ioaddr.ctl_addr; + u8 sr = 0; + u8 status; + u16 err = 0; + + if (!(bfin_check_status(ap) & ATA_DRDY)) { + printf("Device ata%d not ready\n", ap->port_no); + return 0; + } + + /* Set up transfer */ +#ifdef CONFIG_LBA48 + if (lba48) { + /* write high bits */ + write_atapi_register(base, ATA_REG_NSECT, 0); + write_atapi_register(base, ATA_REG_LBAL, (blknr >> 24) & 0xFF); + write_atapi_register(base, ATA_REG_LBAM, (blknr >> 32) & 0xFF); + write_atapi_register(base, ATA_REG_LBAH, (blknr >> 40) & 0xFF); + } +#endif + write_atapi_register(base, ATA_REG_NSECT, blkcnt); + write_atapi_register(base, ATA_REG_LBAL, (blknr >> 0) & 0xFF); + write_atapi_register(base, ATA_REG_LBAM, (blknr >> 8) & 0xFF); + write_atapi_register(base, ATA_REG_LBAH, (blknr >> 16) & 0xFF); + +#ifdef CONFIG_LBA48 + if (lba48) { + write_atapi_register(base, ATA_REG_DEVICE, ATA_LBA); + write_atapi_register(base, ATA_REG_CMD, ATA_CMD_PIO_READ_EXT); + } else +#endif + { + write_atapi_register(base, ATA_REG_DEVICE, ATA_LBA | ((blknr >> 24) & 0xF)); + write_atapi_register(base, ATA_REG_CMD, ATA_CMD_PIO_READ); + } + status = bfin_ata_busy_wait(ap, ATA_BUSY, 500000, 1); + + if (status & (ATA_BUSY | ATA_ERR)) { + printf("Device %d not responding status 0x%x.\n", ap->port_no, status); + err = read_atapi_register(base, ATA_REG_ERR); + printf("Error reg = 0x%x\n", err); + return sr; + } + + while (blkcnt--) { + if (bfin_wait_for_irq(ap, 500)) { + printf("ata%u irq failed\n", ap->port_no); + return sr; + } + + status = bfin_check_status(ap); + if (status & ATA_ERR) { + err = read_atapi_register(base, ATA_REG_ERR); + printf("ata%u error %d\n", ap->port_no, err); + return sr; + } + bfin_irq_clear(ap); + + /* Read one sector */ + read_atapi_data(base, ATA_SECTOR_WORDS, buffer); + buffer += ATA_SECTOR_WORDS; + sr++; + } + + return sr; +} + +ulong sata_read(int dev, ulong block, ulong blkcnt, void *buff) +{ + struct ata_port *ap = &port[dev]; + ulong n = 0, sread; + u16 *buffer = (u16 *) buff; + u8 status = 0; + u64 blknr = (u64) block; + unsigned char lba48 = 0; + +#ifdef CONFIG_LBA48 + if (blknr > 0xfffffff) { + if (!sata_dev_desc[dev].lba48) { + printf("Drive doesn't support 48-bit addressing\n"); + return 0; + } + /* more than 28 bits used, use 48bit mode */ + lba48 = 1; + } +#endif + bfin_dev_select(ap, dev%PATA_DEV_NUM_PER_PORT); + + while (blkcnt > 0) { + + if (blkcnt > 255) + sread = 255; + else + sread = blkcnt; + + status = do_one_read(ap, blknr, sread, buffer, lba48); + if (status != sread) { + printf("Read failed\n"); + return n; + } + + blkcnt -= sread; + blknr += sread; + n += sread; + buffer += sread * ATA_SECTOR_WORDS; + } + return n; +} + +ulong sata_write(int dev, ulong block, ulong blkcnt, const void *buff) +{ + struct ata_port *ap = &port[dev]; + void __iomem *base = (void __iomem *)ap->ioaddr.ctl_addr; + ulong n = 0; + u16 *buffer = (u16 *) buff; + unsigned char status = 0; + u64 blknr = (u64) block; +#ifdef CONFIG_LBA48 + unsigned char lba48 = 0; + + if (blknr > 0xfffffff) { + if (!sata_dev_desc[dev].lba48) { + printf("Drive doesn't support 48-bit addressing\n"); + return 0; + } + /* more than 28 bits used, use 48bit mode */ + lba48 = 1; + } +#endif + + bfin_dev_select(ap, dev%PATA_DEV_NUM_PER_PORT); + + while (blkcnt-- > 0) { + status = bfin_ata_busy_wait(ap, ATA_BUSY, 50000, 0); + if (status & ATA_BUSY) { + printf("ata%u failed to respond\n", ap->port_no); + return n; + } +#ifdef CONFIG_LBA48 + if (lba48) { + /* write high bits */ + write_atapi_register(base, ATA_REG_NSECT, 0); + write_atapi_register(base, ATA_REG_LBAL, + (blknr >> 24) & 0xFF); + write_atapi_register(base, ATA_REG_LBAM, + (blknr >> 32) & 0xFF); + write_atapi_register(base, ATA_REG_LBAH, + (blknr >> 40) & 0xFF); + } +#endif + write_atapi_register(base, ATA_REG_NSECT, 1); + write_atapi_register(base, ATA_REG_LBAL, (blknr >> 0) & 0xFF); + write_atapi_register(base, ATA_REG_LBAM, (blknr >> 8) & 0xFF); + write_atapi_register(base, ATA_REG_LBAH, (blknr >> 16) & 0xFF); +#ifdef CONFIG_LBA48 + if (lba48) { + write_atapi_register(base, ATA_REG_DEVICE, ATA_LBA); + write_atapi_register(base, ATA_REG_CMD, + ATA_CMD_PIO_WRITE_EXT); + } else +#endif + { + write_atapi_register(base, ATA_REG_DEVICE, + ATA_LBA | ((blknr >> 24) & 0xF)); + write_atapi_register(base, ATA_REG_CMD, + ATA_CMD_PIO_WRITE); + } + + /*may take up to 5 sec */ + status = bfin_ata_busy_wait(ap, ATA_BUSY, 50000, 0); + if ((status & (ATA_DRQ | ATA_BUSY | ATA_ERR)) != ATA_DRQ) { + printf("Error no DRQ dev %d blk %ld: sts 0x%02x\n", + ap->port_no, (ulong) blknr, status); + return n; + } + + write_atapi_data(base, ATA_SECTOR_WORDS, buffer); + bfin_check_altstatus(ap); + udelay(1); + + ++n; + ++blknr; + buffer += ATA_SECTOR_WORDS; + } + return n; +} diff --git a/drivers/block/pata_bfin.h b/drivers/block/pata_bfin.h new file mode 100644 index 00000000000..2b3425bc954 --- /dev/null +++ b/drivers/block/pata_bfin.h @@ -0,0 +1,173 @@ +/* + * Driver for Blackfin on-chip ATAPI controller. + * + * Enter bugs at http://blackfin.uclinux.org/ + * + * Copyright (c) 2008 Analog Devices Inc. + * + * Licensed under the GPL-2 or later. + */ + +#ifndef PATA_BFIN_H +#define PATA_BFIN_H + +#include <asm/blackfin_local.h> + +struct ata_ioports { + unsigned long cmd_addr; + unsigned long data_addr; + unsigned long error_addr; + unsigned long feature_addr; + unsigned long nsect_addr; + unsigned long lbal_addr; + unsigned long lbam_addr; + unsigned long lbah_addr; + unsigned long device_addr; + unsigned long status_addr; + unsigned long command_addr; + unsigned long altstatus_addr; + unsigned long ctl_addr; + unsigned long bmdma_addr; + unsigned long scr_addr; +}; + +struct ata_port { + unsigned int port_no; /* primary=0, secondary=1 */ + struct ata_ioports ioaddr; /* ATA cmd/ctl/dma reg blks */ + unsigned long flag; + unsigned int ata_mode; + unsigned char ctl_reg; + unsigned char last_ctl; + unsigned char dev_mask; +}; + +extern block_dev_desc_t sata_dev_desc[CONFIG_SYS_SATA_MAX_DEVICE]; + +#define DRV_NAME "pata-bfin" +#define DRV_VERSION "0.9" +#define __iomem + +#define ATA_REG_CTRL 0x0E +#define ATA_REG_ALTSTATUS ATA_REG_CTRL +#define ATA_TMOUT_BOOT 30000 +#define ATA_TMOUT_BOOT_QUICK 7000 + +#define PATA_BFIN_WAIT_TIMEOUT 10000 +#define PATA_DEV_NUM_PER_PORT 2 + +/* These are the offset of the controller's registers */ +#define ATAPI_OFFSET_CONTROL 0x00 +#define ATAPI_OFFSET_STATUS 0x04 +#define ATAPI_OFFSET_DEV_ADDR 0x08 +#define ATAPI_OFFSET_DEV_TXBUF 0x0c +#define ATAPI_OFFSET_DEV_RXBUF 0x10 +#define ATAPI_OFFSET_INT_MASK 0x14 +#define ATAPI_OFFSET_INT_STATUS 0x18 +#define ATAPI_OFFSET_XFER_LEN 0x1c +#define ATAPI_OFFSET_LINE_STATUS 0x20 +#define ATAPI_OFFSET_SM_STATE 0x24 +#define ATAPI_OFFSET_TERMINATE 0x28 +#define ATAPI_OFFSET_PIO_TFRCNT 0x2c +#define ATAPI_OFFSET_DMA_TFRCNT 0x30 +#define ATAPI_OFFSET_UMAIN_TFRCNT 0x34 +#define ATAPI_OFFSET_UDMAOUT_TFRCNT 0x38 +#define ATAPI_OFFSET_REG_TIM_0 0x40 +#define ATAPI_OFFSET_PIO_TIM_0 0x44 +#define ATAPI_OFFSET_PIO_TIM_1 0x48 +#define ATAPI_OFFSET_MULTI_TIM_0 0x50 +#define ATAPI_OFFSET_MULTI_TIM_1 0x54 +#define ATAPI_OFFSET_MULTI_TIM_2 0x58 +#define ATAPI_OFFSET_ULTRA_TIM_0 0x60 +#define ATAPI_OFFSET_ULTRA_TIM_1 0x64 +#define ATAPI_OFFSET_ULTRA_TIM_2 0x68 +#define ATAPI_OFFSET_ULTRA_TIM_3 0x6c + + +#define ATAPI_GET_CONTROL(base)\ + bfin_read16(base + ATAPI_OFFSET_CONTROL) +#define ATAPI_SET_CONTROL(base, val)\ + bfin_write16(base + ATAPI_OFFSET_CONTROL, val) +#define ATAPI_GET_STATUS(base)\ + bfin_read16(base + ATAPI_OFFSET_STATUS) +#define ATAPI_GET_DEV_ADDR(base)\ + bfin_read16(base + ATAPI_OFFSET_DEV_ADDR) +#define ATAPI_SET_DEV_ADDR(base, val)\ + bfin_write16(base + ATAPI_OFFSET_DEV_ADDR, val) +#define ATAPI_GET_DEV_TXBUF(base)\ + bfin_read16(base + ATAPI_OFFSET_DEV_TXBUF) +#define ATAPI_SET_DEV_TXBUF(base, val)\ + bfin_write16(base + ATAPI_OFFSET_DEV_TXBUF, val) +#define ATAPI_GET_DEV_RXBUF(base)\ + bfin_read16(base + ATAPI_OFFSET_DEV_RXBUF) +#define ATAPI_SET_DEV_RXBUF(base, val)\ + bfin_write16(base + ATAPI_OFFSET_DEV_RXBUF, val) +#define ATAPI_GET_INT_MASK(base)\ + bfin_read16(base + ATAPI_OFFSET_INT_MASK) +#define ATAPI_SET_INT_MASK(base, val)\ + bfin_write16(base + ATAPI_OFFSET_INT_MASK, val) +#define ATAPI_GET_INT_STATUS(base)\ + bfin_read16(base + ATAPI_OFFSET_INT_STATUS) +#define ATAPI_SET_INT_STATUS(base, val)\ + bfin_write16(base + ATAPI_OFFSET_INT_STATUS, val) +#define ATAPI_GET_XFER_LEN(base)\ + bfin_read16(base + ATAPI_OFFSET_XFER_LEN) +#define ATAPI_SET_XFER_LEN(base, val)\ + bfin_write16(base + ATAPI_OFFSET_XFER_LEN, val) +#define ATAPI_GET_LINE_STATUS(base)\ + bfin_read16(base + ATAPI_OFFSET_LINE_STATUS) +#define ATAPI_GET_SM_STATE(base)\ + bfin_read16(base + ATAPI_OFFSET_SM_STATE) +#define ATAPI_GET_TERMINATE(base)\ + bfin_read16(base + ATAPI_OFFSET_TERMINATE) +#define ATAPI_SET_TERMINATE(base, val)\ + bfin_write16(base + ATAPI_OFFSET_TERMINATE, val) +#define ATAPI_GET_PIO_TFRCNT(base)\ + bfin_read16(base + ATAPI_OFFSET_PIO_TFRCNT) +#define ATAPI_GET_DMA_TFRCNT(base)\ + bfin_read16(base + ATAPI_OFFSET_DMA_TFRCNT) +#define ATAPI_GET_UMAIN_TFRCNT(base)\ + bfin_read16(base + ATAPI_OFFSET_UMAIN_TFRCNT) +#define ATAPI_GET_UDMAOUT_TFRCNT(base)\ + bfin_read16(base + ATAPI_OFFSET_UDMAOUT_TFRCNT) +#define ATAPI_GET_REG_TIM_0(base)\ + bfin_read16(base + ATAPI_OFFSET_REG_TIM_0) +#define ATAPI_SET_REG_TIM_0(base, val)\ + bfin_write16(base + ATAPI_OFFSET_REG_TIM_0, val) +#define ATAPI_GET_PIO_TIM_0(base)\ + bfin_read16(base + ATAPI_OFFSET_PIO_TIM_0) +#define ATAPI_SET_PIO_TIM_0(base, val)\ + bfin_write16(base + ATAPI_OFFSET_PIO_TIM_0, val) +#define ATAPI_GET_PIO_TIM_1(base)\ + bfin_read16(base + ATAPI_OFFSET_PIO_TIM_1) +#define ATAPI_SET_PIO_TIM_1(base, val)\ + bfin_write16(base + ATAPI_OFFSET_PIO_TIM_1, val) +#define ATAPI_GET_MULTI_TIM_0(base)\ + bfin_read16(base + ATAPI_OFFSET_MULTI_TIM_0) +#define ATAPI_SET_MULTI_TIM_0(base, val)\ + bfin_write16(base + ATAPI_OFFSET_MULTI_TIM_0, val) +#define ATAPI_GET_MULTI_TIM_1(base)\ + bfin_read16(base + ATAPI_OFFSET_MULTI_TIM_1) +#define ATAPI_SET_MULTI_TIM_1(base, val)\ + bfin_write16(base + ATAPI_OFFSET_MULTI_TIM_1, val) +#define ATAPI_GET_MULTI_TIM_2(base)\ + bfin_read16(base + ATAPI_OFFSET_MULTI_TIM_2) +#define ATAPI_SET_MULTI_TIM_2(base, val)\ + bfin_write16(base + ATAPI_OFFSET_MULTI_TIM_2, val) +#define ATAPI_GET_ULTRA_TIM_0(base)\ + bfin_read16(base + ATAPI_OFFSET_ULTRA_TIM_0) +#define ATAPI_SET_ULTRA_TIM_0(base, val)\ + bfin_write16(base + ATAPI_OFFSET_ULTRA_TIM_0, val) +#define ATAPI_GET_ULTRA_TIM_1(base)\ + bfin_read16(base + ATAPI_OFFSET_ULTRA_TIM_1) +#define ATAPI_SET_ULTRA_TIM_1(base, val)\ + bfin_write16(base + ATAPI_OFFSET_ULTRA_TIM_1, val) +#define ATAPI_GET_ULTRA_TIM_2(base)\ + bfin_read16(base + ATAPI_OFFSET_ULTRA_TIM_2) +#define ATAPI_SET_ULTRA_TIM_2(base, val)\ + bfin_write16(base + ATAPI_OFFSET_ULTRA_TIM_2, val) +#define ATAPI_GET_ULTRA_TIM_3(base)\ + bfin_read16(base + ATAPI_OFFSET_ULTRA_TIM_3) +#define ATAPI_SET_ULTRA_TIM_3(base, val)\ + bfin_write16(base + ATAPI_OFFSET_ULTRA_TIM_3, val) + +#endif |