From 716ca530e1c4515d8683c9d5be3d56b301758b66 Mon Sep 17 00:00:00 2001 From: James <> Date: Wed, 4 Nov 2015 11:49:21 +0000 Subject: trunk-47381 --- .../uboot-oxnas/files/drivers/block/plxsata_ide.c | 1170 ++++++++++++++++++++ 1 file changed, 1170 insertions(+) create mode 100644 package/boot/uboot-oxnas/files/drivers/block/plxsata_ide.c (limited to 'package/boot/uboot-oxnas/files/drivers/block/plxsata_ide.c') diff --git a/package/boot/uboot-oxnas/files/drivers/block/plxsata_ide.c b/package/boot/uboot-oxnas/files/drivers/block/plxsata_ide.c new file mode 100644 index 0000000..7e0e78f --- /dev/null +++ b/package/boot/uboot-oxnas/files/drivers/block/plxsata_ide.c @@ -0,0 +1,1170 @@ +/* + * (C) Copyright 2005 + * Oxford Semiconductor Ltd + * + * 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 +#include + +/** + * SATA related definitions + */ +#define ATA_PORT_CTL 0 +#define ATA_PORT_FEATURE 1 +#define ATA_PORT_NSECT 2 +#define ATA_PORT_LBAL 3 +#define ATA_PORT_LBAM 4 +#define ATA_PORT_LBAH 5 +#define ATA_PORT_DEVICE 6 +#define ATA_PORT_COMMAND 7 + +/* The offsets to the SATA registers */ +#define SATA_ORB1_OFF 0 +#define SATA_ORB2_OFF 1 +#define SATA_ORB3_OFF 2 +#define SATA_ORB4_OFF 3 +#define SATA_ORB5_OFF 4 + +#define SATA_FIS_ACCESS 11 +#define SATA_INT_STATUS_OFF 12 /* Read only */ +#define SATA_INT_CLR_OFF 12 /* Write only */ +#define SATA_INT_ENABLE_OFF 13 /* Read only */ +#define SATA_INT_ENABLE_SET_OFF 13 /* Write only */ +#define SATA_INT_ENABLE_CLR_OFF 14 /* Write only */ +#define SATA_VERSION_OFF 15 +#define SATA_CONTROL_OFF 23 +#define SATA_COMMAND_OFF 24 +#define SATA_PORT_CONTROL_OFF 25 +#define SATA_DRIVE_CONTROL_OFF 26 + +/* The offsets to the link registers that are access in an asynchronous manner */ +#define SATA_LINK_DATA 28 +#define SATA_LINK_RD_ADDR 29 +#define SATA_LINK_WR_ADDR 30 +#define SATA_LINK_CONTROL 31 + +/* SATA interrupt status register fields */ +#define SATA_INT_STATUS_EOC_RAW_BIT ( 0 + 16) +#define SATA_INT_STATUS_ERROR_BIT ( 2 + 16) +#define SATA_INT_STATUS_EOADT_RAW_BIT ( 1 + 16) + +/* SATA core command register commands */ +#define SATA_CMD_WRITE_TO_ORB_REGS 2 +#define SATA_CMD_WRITE_TO_ORB_REGS_NO_COMMAND 4 + +#define SATA_CMD_BUSY_BIT 7 + +#define SATA_SCTL_CLR_ERR 0x00000316UL + +#define SATA_LBAL_BIT 0 +#define SATA_LBAM_BIT 8 +#define SATA_LBAH_BIT 16 +#define SATA_HOB_LBAH_BIT 24 +#define SATA_DEVICE_BIT 24 +#define SATA_NSECT_BIT 0 +#define SATA_HOB_NSECT_BIT 8 +#define SATA_LBA32_BIT 0 +#define SATA_LBA40_BIT 8 +#define SATA_FEATURE_BIT 16 +#define SATA_COMMAND_BIT 24 +#define SATA_CTL_BIT 24 + +/* ATA status (7) register field definitions */ +#define ATA_STATUS_BSY_BIT 7 +#define ATA_STATUS_DRDY_BIT 6 +#define ATA_STATUS_DF_BIT 5 +#define ATA_STATUS_DRQ_BIT 3 +#define ATA_STATUS_ERR_BIT 0 + +/* ATA device (6) register field definitions */ +#define ATA_DEVICE_FIXED_MASK 0xA0 +#define ATA_DEVICE_DRV_BIT 4 +#define ATA_DEVICE_DRV_NUM_BITS 1 +#define ATA_DEVICE_LBA_BIT 6 + +/* ATA Command register initiated commands */ +#define ATA_CMD_INIT 0x91 +#define ATA_CMD_IDENT 0xEC + +#define SATA_STD_ASYNC_REGS_OFF 0x20 +#define SATA_SCR_STATUS 0 +#define SATA_SCR_ERROR 1 +#define SATA_SCR_CONTROL 2 +#define SATA_SCR_ACTIVE 3 +#define SATA_SCR_NOTIFICAION 4 + +#define SATA_BURST_BUF_FORCE_EOT_BIT 0 +#define SATA_BURST_BUF_DATA_INJ_ENABLE_BIT 1 +#define SATA_BURST_BUF_DIR_BIT 2 +#define SATA_BURST_BUF_DATA_INJ_END_BIT 3 +#define SATA_BURST_BUF_FIFO_DIS_BIT 4 +#define SATA_BURST_BUF_DIS_DREQ_BIT 5 +#define SATA_BURST_BUF_DREQ_BIT 6 + +#define SATA_OPCODE_MASK 0x3 + +#define SATA_DMA_CHANNEL 0 + +#define DMA_CTRL_STATUS (0x0) +#define DMA_BASE_SRC_ADR (0x4) +#define DMA_BASE_DST_ADR (0x8) +#define DMA_BYTE_CNT (0xC) +#define DMA_CURRENT_SRC_ADR (0x10) +#define DMA_CURRENT_DST_ADR (0x14) +#define DMA_CURRENT_BYTE_CNT (0x18) +#define DMA_INTR_ID (0x1C) +#define DMA_INTR_CLEAR_REG (DMA_CURRENT_SRC_ADR) + +#define DMA_CALC_REG_ADR(channel, register) ((volatile u32*)(DMA_BASE + ((channel) << 5) + (register))) + +#define DMA_CTRL_STATUS_FAIR_SHARE_ARB (1 << 0) +#define DMA_CTRL_STATUS_IN_PROGRESS (1 << 1) +#define DMA_CTRL_STATUS_SRC_DREQ_MASK (0x0000003C) +#define DMA_CTRL_STATUS_SRC_DREQ_SHIFT (2) +#define DMA_CTRL_STATUS_DEST_DREQ_MASK (0x000003C0) +#define DMA_CTRL_STATUS_DEST_DREQ_SHIFT (6) +#define DMA_CTRL_STATUS_INTR (1 << 10) +#define DMA_CTRL_STATUS_NXT_FREE (1 << 11) +#define DMA_CTRL_STATUS_RESET (1 << 12) +#define DMA_CTRL_STATUS_DIR_MASK (0x00006000) +#define DMA_CTRL_STATUS_DIR_SHIFT (13) +#define DMA_CTRL_STATUS_SRC_ADR_MODE (1 << 15) +#define DMA_CTRL_STATUS_DEST_ADR_MODE (1 << 16) +#define DMA_CTRL_STATUS_TRANSFER_MODE_A (1 << 17) +#define DMA_CTRL_STATUS_TRANSFER_MODE_B (1 << 18) +#define DMA_CTRL_STATUS_SRC_WIDTH_MASK (0x00380000) +#define DMA_CTRL_STATUS_SRC_WIDTH_SHIFT (19) +#define DMA_CTRL_STATUS_DEST_WIDTH_MASK (0x01C00000) +#define DMA_CTRL_STATUS_DEST_WIDTH_SHIFT (22) +#define DMA_CTRL_STATUS_PAUSE (1 << 25) +#define DMA_CTRL_STATUS_INTERRUPT_ENABLE (1 << 26) +#define DMA_CTRL_STATUS_SOURCE_ADDRESS_FIXED (1 << 27) +#define DMA_CTRL_STATUS_DESTINATION_ADDRESS_FIXED (1 << 28) +#define DMA_CTRL_STATUS_STARVE_LOW_PRIORITY (1 << 29) +#define DMA_CTRL_STATUS_INTR_CLEAR_ENABLE (1 << 30) + +#define DMA_BYTE_CNT_MASK ((1 << 21) - 1) +#define DMA_BYTE_CNT_WR_EOT_MASK (1 << 30) +#define DMA_BYTE_CNT_RD_EOT_MASK (1 << 31) +#define DMA_BYTE_CNT_BURST_MASK (1 << 28) + +#define MAKE_FIELD(value, num_bits, bit_num) (((value) & ((1 << (num_bits)) - 1)) << (bit_num)) + +typedef enum oxnas_dma_mode { + OXNAS_DMA_MODE_FIXED, OXNAS_DMA_MODE_INC +} oxnas_dma_mode_t; + +typedef enum oxnas_dma_direction { + OXNAS_DMA_TO_DEVICE, OXNAS_DMA_FROM_DEVICE +} oxnas_dma_direction_t; + +/* The available buses to which the DMA controller is attached */ +typedef enum oxnas_dma_transfer_bus { + OXNAS_DMA_SIDE_A, OXNAS_DMA_SIDE_B +} oxnas_dma_transfer_bus_t; + +/* Direction of data flow between the DMA controller's pair of interfaces */ +typedef enum oxnas_dma_transfer_direction { + OXNAS_DMA_A_TO_A, OXNAS_DMA_B_TO_A, OXNAS_DMA_A_TO_B, OXNAS_DMA_B_TO_B +} oxnas_dma_transfer_direction_t; + +/* The available data widths */ +typedef enum oxnas_dma_transfer_width { + OXNAS_DMA_TRANSFER_WIDTH_8BITS, + OXNAS_DMA_TRANSFER_WIDTH_16BITS, + OXNAS_DMA_TRANSFER_WIDTH_32BITS +} oxnas_dma_transfer_width_t; + +/* The mode of the DMA transfer */ +typedef enum oxnas_dma_transfer_mode { + OXNAS_DMA_TRANSFER_MODE_SINGLE, OXNAS_DMA_TRANSFER_MODE_BURST +} oxnas_dma_transfer_mode_t; + +/* The available transfer targets */ +typedef enum oxnas_dma_dreq { + OXNAS_DMA_DREQ_SATA = 0, OXNAS_DMA_DREQ_MEMORY = 15 +} oxnas_dma_dreq_t; + +typedef struct oxnas_dma_device_settings { + unsigned long address_; + unsigned fifo_size_; // Chained transfers must take account of FIFO offset at end of previous transfer + unsigned char dreq_; + unsigned read_eot_ :1; + unsigned read_final_eot_ :1; + unsigned write_eot_ :1; + unsigned write_final_eot_ :1; + unsigned bus_ :1; + unsigned width_ :2; + unsigned transfer_mode_ :1; + unsigned address_mode_ :1; + unsigned address_really_fixed_ :1; +} oxnas_dma_device_settings_t; + +static const int MAX_NO_ERROR_LOOPS = 100000; /* 1 second in units of 10uS */ +static const int MAX_DMA_XFER_LOOPS = 300000; /* 30 seconds in units of 100uS */ +static const int MAX_DMA_ABORT_LOOPS = 10000; /* 0.1 second in units of 10uS */ +static const int MAX_SRC_READ_LOOPS = 10000; /* 0.1 second in units of 10uS */ +static const int MAX_SRC_WRITE_LOOPS = 10000; /* 0.1 second in units of 10uS */ +static const int MAX_NOT_BUSY_LOOPS = 10000; /* 1 second in units of 100uS */ + +/* The internal SATA drive on which we should attempt to find partitions */ +static volatile u32* sata_regs_base[2] = { (volatile u32*) SATA_0_REGS_BASE, + (volatile u32*) SATA_1_REGS_BASE, + +}; +static u32 wr_sata_orb1[2] = { 0, 0 }; +static u32 wr_sata_orb2[2] = { 0, 0 }; +static u32 wr_sata_orb3[2] = { 0, 0 }; +static u32 wr_sata_orb4[2] = { 0, 0 }; + +#ifdef CONFIG_LBA48 +/* need keeping a record of NSECT LBAL LBAM LBAH ide_outb values for lba48 support */ +#define OUT_HISTORY_BASE ATA_PORT_NSECT +#define OUT_HISTORY_MAX ATA_PORT_LBAH +static unsigned char out_history[2][OUT_HISTORY_MAX - OUT_HISTORY_BASE + 1] = {}; +#endif + +static oxnas_dma_device_settings_t oxnas_sata_dma_settings = { .address_ = + SATA_DATA_BASE, .fifo_size_ = 16, .dreq_ = OXNAS_DMA_DREQ_SATA, + .read_eot_ = 0, .read_final_eot_ = 1, .write_eot_ = 0, + .write_final_eot_ = 1, .bus_ = OXNAS_DMA_SIDE_B, .width_ = + OXNAS_DMA_TRANSFER_WIDTH_32BITS, .transfer_mode_ = + OXNAS_DMA_TRANSFER_MODE_BURST, .address_mode_ = + OXNAS_DMA_MODE_FIXED, .address_really_fixed_ = 0 }; + +oxnas_dma_device_settings_t oxnas_ram_dma_settings = { .address_ = 0, + .fifo_size_ = 0, .dreq_ = OXNAS_DMA_DREQ_MEMORY, .read_eot_ = 1, + .read_final_eot_ = 1, .write_eot_ = 1, .write_final_eot_ = 1, + .bus_ = OXNAS_DMA_SIDE_A, .width_ = + OXNAS_DMA_TRANSFER_WIDTH_32BITS, .transfer_mode_ = + OXNAS_DMA_TRANSFER_MODE_BURST, .address_mode_ = + OXNAS_DMA_MODE_FIXED, .address_really_fixed_ = 1 }; + +static void xfer_wr_shadow_to_orbs(int device) +{ + *(sata_regs_base[device] + SATA_ORB1_OFF) = wr_sata_orb1[device]; + *(sata_regs_base[device] + SATA_ORB2_OFF) = wr_sata_orb2[device]; + *(sata_regs_base[device] + SATA_ORB3_OFF) = wr_sata_orb3[device]; + *(sata_regs_base[device] + SATA_ORB4_OFF) = wr_sata_orb4[device]; +} + +static inline void device_select(int device) +{ + /* master/slave has no meaning to SATA core */ +} + +static int disk_present[CONFIG_SYS_IDE_MAXDEVICE]; + +#include + +unsigned char ide_inb(int device, int port) +{ + unsigned char val = 0; + + /* Only permit accesses to disks found to be present during ide_preinit() */ + if (!disk_present[device]) { + return ATA_STAT_FAULT; + } + + device_select(device); + + switch (port) { + case ATA_PORT_CTL: + val = (*(sata_regs_base[device] + SATA_ORB4_OFF) + & (0xFFUL << SATA_CTL_BIT)) >> SATA_CTL_BIT; + break; + case ATA_PORT_FEATURE: + val = (*(sata_regs_base[device] + SATA_ORB2_OFF) + & (0xFFUL << SATA_FEATURE_BIT)) >> SATA_FEATURE_BIT; + break; + case ATA_PORT_NSECT: + val = (*(sata_regs_base[device] + SATA_ORB2_OFF) + & (0xFFUL << SATA_NSECT_BIT)) >> SATA_NSECT_BIT; + break; + case ATA_PORT_LBAL: + val = (*(sata_regs_base[device] + SATA_ORB3_OFF) + & (0xFFUL << SATA_LBAL_BIT)) >> SATA_LBAL_BIT; + break; + case ATA_PORT_LBAM: + val = (*(sata_regs_base[device] + SATA_ORB3_OFF) + & (0xFFUL << SATA_LBAM_BIT)) >> SATA_LBAM_BIT; + break; + case ATA_PORT_LBAH: + val = (*(sata_regs_base[device] + SATA_ORB3_OFF) + & (0xFFUL << SATA_LBAH_BIT)) >> SATA_LBAH_BIT; + break; + case ATA_PORT_DEVICE: + val = (*(sata_regs_base[device] + SATA_ORB3_OFF) + & (0xFFUL << SATA_HOB_LBAH_BIT)) >> SATA_HOB_LBAH_BIT; + val |= (*(sata_regs_base[device] + SATA_ORB1_OFF) + & (0xFFUL << SATA_DEVICE_BIT)) >> SATA_DEVICE_BIT; + break; + case ATA_PORT_COMMAND: + val = (*(sata_regs_base[device] + SATA_ORB2_OFF) + & (0xFFUL << SATA_COMMAND_BIT)) >> SATA_COMMAND_BIT; + val |= ATA_STAT_DRQ; + break; + default: + printf("ide_inb() Unknown port = %d\n", port); + break; + } + + // printf("inb: %d:%01x => %02x\n", device, port, val); + + return val; +} + +/** + * Possible that ATA status will not become no-error, so must have timeout + * @returns An int which is zero on error + */ +static inline int wait_no_error(int device) +{ + int status = 0; + + /* Check for ATA core error */ + if (*(sata_regs_base[device] + SATA_INT_STATUS_OFF) + & (1 << SATA_INT_STATUS_ERROR_BIT)) { + printf("wait_no_error() SATA core flagged error\n"); + } else { + int loops = MAX_NO_ERROR_LOOPS; + do { + /* Check for ATA device error */ + if (!(ide_inb(device, ATA_PORT_COMMAND) + & (1 << ATA_STATUS_ERR_BIT))) { + status = 1; + break; + } + udelay(10); + } while (--loops); + + if (!loops) { + printf("wait_no_error() Timed out of wait for SATA no-error condition\n"); + } + } + + return status; +} + +/** + * Expect SATA command to always finish, perhaps with error + * @returns An int which is zero on error + */ +static inline int wait_sata_command_not_busy(int device) +{ + /* Wait for data to be available */ + int status = 0; + int loops = MAX_NOT_BUSY_LOOPS; + do { + if (!(*(sata_regs_base[device] + SATA_COMMAND_OFF) + & (1 << SATA_CMD_BUSY_BIT))) { + status = 1; + break; + } + udelay(100); + } while (--loops); + + if (!loops) { + printf("wait_sata_command_not_busy() Timed out of wait for SATA command to finish\n"); + } + + return status; +} + +void ide_outb(int device, int port, unsigned char val) +{ + typedef enum send_method { + SEND_NONE, SEND_SIMPLE, SEND_CMD, SEND_CTL, + } send_method_t; + + /* Only permit accesses to disks found to be present during ide_preinit() */ + if (!disk_present[device]) { + return; + } + + // printf("outb: %d:%01x <= %02x\n", device, port, val); + + device_select(device); + +#ifdef CONFIG_LBA48 + if (port >= OUT_HISTORY_BASE && port <= OUT_HISTORY_MAX) { + out_history[0][port - OUT_HISTORY_BASE] = + out_history[1][port - OUT_HISTORY_BASE]; + out_history[1][port - OUT_HISTORY_BASE] = val; + } +#endif + send_method_t send_regs = SEND_NONE; + switch (port) { + case ATA_PORT_CTL: + wr_sata_orb4[device] &= ~(0xFFUL << SATA_CTL_BIT); + wr_sata_orb4[device] |= (val << SATA_CTL_BIT); + send_regs = SEND_CTL; + break; + case ATA_PORT_FEATURE: + wr_sata_orb2[device] &= ~(0xFFUL << SATA_FEATURE_BIT); + wr_sata_orb2[device] |= (val << SATA_FEATURE_BIT); + send_regs = SEND_SIMPLE; + break; + case ATA_PORT_NSECT: + wr_sata_orb2[device] &= ~(0xFFUL << SATA_NSECT_BIT); + wr_sata_orb2[device] |= (val << SATA_NSECT_BIT); + send_regs = SEND_SIMPLE; + break; + case ATA_PORT_LBAL: + wr_sata_orb3[device] &= ~(0xFFUL << SATA_LBAL_BIT); + wr_sata_orb3[device] |= (val << SATA_LBAL_BIT); + send_regs = SEND_SIMPLE; + break; + case ATA_PORT_LBAM: + wr_sata_orb3[device] &= ~(0xFFUL << SATA_LBAM_BIT); + wr_sata_orb3[device] |= (val << SATA_LBAM_BIT); + send_regs = SEND_SIMPLE; + break; + case ATA_PORT_LBAH: + wr_sata_orb3[device] &= ~(0xFFUL << SATA_LBAH_BIT); + wr_sata_orb3[device] |= (val << SATA_LBAH_BIT); + send_regs = SEND_SIMPLE; + break; + case ATA_PORT_DEVICE: + wr_sata_orb1[device] &= ~(0xFFUL << SATA_DEVICE_BIT); + wr_sata_orb1[device] |= (val << SATA_DEVICE_BIT); + send_regs = SEND_SIMPLE; + break; + case ATA_PORT_COMMAND: + wr_sata_orb2[device] &= ~(0xFFUL << SATA_COMMAND_BIT); + wr_sata_orb2[device] |= (val << SATA_COMMAND_BIT); + send_regs = SEND_CMD; +#ifdef CONFIG_LBA48 + if (val == ATA_CMD_READ_EXT || val == ATA_CMD_WRITE_EXT) + { + /* fill high bytes of LBA48 && NSECT */ + wr_sata_orb2[device] &= ~(0xFFUL << SATA_HOB_NSECT_BIT); + wr_sata_orb2[device] |= + (out_history[0][ATA_PORT_NSECT - OUT_HISTORY_BASE] << SATA_HOB_NSECT_BIT); + + wr_sata_orb3[device] &= ~(0xFFUL << SATA_HOB_LBAH_BIT); + wr_sata_orb3[device] |= + (out_history[0][ATA_PORT_LBAL - OUT_HISTORY_BASE] << SATA_HOB_LBAH_BIT); + + wr_sata_orb4[device] &= ~(0xFFUL << SATA_LBA32_BIT); + wr_sata_orb4[device] |= + (out_history[0][ATA_PORT_LBAM - OUT_HISTORY_BASE] << SATA_LBA32_BIT); + + wr_sata_orb4[device] &= ~(0xFFUL << SATA_LBA40_BIT); + wr_sata_orb4[device] |= + (out_history[0][ATA_PORT_LBAH - OUT_HISTORY_BASE] << SATA_LBA40_BIT); + } +#endif + break; + default: + printf("ide_outb() Unknown port = %d\n", port); + } + + u32 command; + switch (send_regs) { + case SEND_CMD: + wait_sata_command_not_busy(device); + command = *(sata_regs_base[device] + SATA_COMMAND_OFF); + command &= ~SATA_OPCODE_MASK; + command |= SATA_CMD_WRITE_TO_ORB_REGS; + xfer_wr_shadow_to_orbs(device); + wait_sata_command_not_busy(device); + *(sata_regs_base[device] + SATA_COMMAND_OFF) = command; + if (!wait_no_error(device)) { + printf("ide_outb() Wait for ATA no-error timed-out\n"); + } + break; + case SEND_CTL: + wait_sata_command_not_busy(device); + command = *(sata_regs_base[device] + SATA_COMMAND_OFF); + command &= ~SATA_OPCODE_MASK; + command |= SATA_CMD_WRITE_TO_ORB_REGS_NO_COMMAND; + xfer_wr_shadow_to_orbs(device); + wait_sata_command_not_busy(device); + *(sata_regs_base[device] + SATA_COMMAND_OFF) = command; + if (!wait_no_error(device)) { + printf("ide_outb() Wait for ATA no-error timed-out\n"); + } + break; + default: + break; + } +} + +static u32 encode_start(u32 ctrl_status) +{ + return ctrl_status & ~DMA_CTRL_STATUS_PAUSE; +} + +/* start a paused DMA transfer in channel 0 of the SATA DMA core */ +static void dma_start(void) +{ + unsigned int reg; + reg = readl(SATA_DMA_REGS_BASE + DMA_CTRL_STATUS); + reg = encode_start(reg); + writel(reg, SATA_DMA_REGS_BASE + DMA_CTRL_STATUS); +} + +static unsigned long encode_control_status( + oxnas_dma_device_settings_t* src_settings, + oxnas_dma_device_settings_t* dst_settings) +{ + unsigned long ctrl_status; + oxnas_dma_transfer_direction_t direction; + + ctrl_status = DMA_CTRL_STATUS_PAUSE; // Paused + ctrl_status |= DMA_CTRL_STATUS_FAIR_SHARE_ARB; // High priority + ctrl_status |= (src_settings->dreq_ << DMA_CTRL_STATUS_SRC_DREQ_SHIFT); // Dreq + ctrl_status |= (dst_settings->dreq_ << DMA_CTRL_STATUS_DEST_DREQ_SHIFT); // Dreq + ctrl_status &= ~DMA_CTRL_STATUS_RESET; // !RESET + + // Use new interrupt clearing register + ctrl_status |= DMA_CTRL_STATUS_INTR_CLEAR_ENABLE; + + // Setup the transfer direction and burst/single mode for the two DMA busses + if (src_settings->bus_ == OXNAS_DMA_SIDE_A) { + // Set the burst/single mode for bus A based on src device's settings + if (src_settings->transfer_mode_ + == OXNAS_DMA_TRANSFER_MODE_BURST) { + ctrl_status |= DMA_CTRL_STATUS_TRANSFER_MODE_A; + } else { + ctrl_status &= ~DMA_CTRL_STATUS_TRANSFER_MODE_A; + } + + if (dst_settings->bus_ == OXNAS_DMA_SIDE_A) { + direction = OXNAS_DMA_A_TO_A; + } else { + direction = OXNAS_DMA_A_TO_B; + + // Set the burst/single mode for bus B based on dst device's settings + if (dst_settings->transfer_mode_ + == OXNAS_DMA_TRANSFER_MODE_BURST) { + ctrl_status |= DMA_CTRL_STATUS_TRANSFER_MODE_B; + } else { + ctrl_status &= ~DMA_CTRL_STATUS_TRANSFER_MODE_B; + } + } + } else { + // Set the burst/single mode for bus B based on src device's settings + if (src_settings->transfer_mode_ + == OXNAS_DMA_TRANSFER_MODE_BURST) { + ctrl_status |= DMA_CTRL_STATUS_TRANSFER_MODE_B; + } else { + ctrl_status &= ~DMA_CTRL_STATUS_TRANSFER_MODE_B; + } + + if (dst_settings->bus_ == OXNAS_DMA_SIDE_A) { + direction = OXNAS_DMA_B_TO_A; + + // Set the burst/single mode for bus A based on dst device's settings + if (dst_settings->transfer_mode_ + == OXNAS_DMA_TRANSFER_MODE_BURST) { + ctrl_status |= DMA_CTRL_STATUS_TRANSFER_MODE_A; + } else { + ctrl_status &= ~DMA_CTRL_STATUS_TRANSFER_MODE_A; + } + } else { + direction = OXNAS_DMA_B_TO_B; + } + } + ctrl_status |= (direction << DMA_CTRL_STATUS_DIR_SHIFT); + + // Setup source address mode fixed or increment + if (src_settings->address_mode_ == OXNAS_DMA_MODE_FIXED) { + // Fixed address + ctrl_status &= ~(DMA_CTRL_STATUS_SRC_ADR_MODE); + + // Set up whether fixed address is _really_ fixed + if (src_settings->address_really_fixed_) { + ctrl_status |= DMA_CTRL_STATUS_SOURCE_ADDRESS_FIXED; + } else { + ctrl_status &= ~DMA_CTRL_STATUS_SOURCE_ADDRESS_FIXED; + } + } else { + // Incrementing address + ctrl_status |= DMA_CTRL_STATUS_SRC_ADR_MODE; + ctrl_status &= ~DMA_CTRL_STATUS_SOURCE_ADDRESS_FIXED; + } + + // Setup destination address mode fixed or increment + if (dst_settings->address_mode_ == OXNAS_DMA_MODE_FIXED) { + // Fixed address + ctrl_status &= ~(DMA_CTRL_STATUS_DEST_ADR_MODE); + + // Set up whether fixed address is _really_ fixed + if (dst_settings->address_really_fixed_) { + ctrl_status |= + DMA_CTRL_STATUS_DESTINATION_ADDRESS_FIXED; + } else { + ctrl_status &= + ~DMA_CTRL_STATUS_DESTINATION_ADDRESS_FIXED; + } + } else { + // Incrementing address + ctrl_status |= DMA_CTRL_STATUS_DEST_ADR_MODE; + ctrl_status &= ~DMA_CTRL_STATUS_DESTINATION_ADDRESS_FIXED; + } + + // Set up the width of the transfers on the DMA buses + ctrl_status |= + (src_settings->width_ << DMA_CTRL_STATUS_SRC_WIDTH_SHIFT); + ctrl_status |= + (dst_settings->width_ << DMA_CTRL_STATUS_DEST_WIDTH_SHIFT); + + // Setup the priority arbitration scheme + ctrl_status &= ~DMA_CTRL_STATUS_STARVE_LOW_PRIORITY; // !Starve low priority + + return ctrl_status; +} + +static u32 encode_final_eot(oxnas_dma_device_settings_t* src_settings, + oxnas_dma_device_settings_t* dst_settings, + unsigned long length) +{ + // Write the length, with EOT configuration for a final transfer + unsigned long encoded = length; + if (dst_settings->write_final_eot_) { + encoded |= DMA_BYTE_CNT_WR_EOT_MASK; + } else { + encoded &= ~DMA_BYTE_CNT_WR_EOT_MASK; + } + if (src_settings->read_final_eot_) { + encoded |= DMA_BYTE_CNT_RD_EOT_MASK; + } else { + encoded &= ~DMA_BYTE_CNT_RD_EOT_MASK; + } + /* if((src_settings->transfer_mode_) || + (src_settings->transfer_mode_)) { + encoded |= DMA_BYTE_CNT_BURST_MASK; + } else { + encoded &= ~DMA_BYTE_CNT_BURST_MASK; + }*/ + return encoded; +} + +static void dma_start_write(const ulong* buffer, int num_bytes) +{ + // Assemble complete memory settings + oxnas_dma_device_settings_t mem_settings = oxnas_ram_dma_settings; + mem_settings.address_ = (unsigned long) buffer; + mem_settings.address_mode_ = OXNAS_DMA_MODE_INC; + + writel(encode_control_status(&mem_settings, &oxnas_sata_dma_settings), + SATA_DMA_REGS_BASE + DMA_CTRL_STATUS); + writel(mem_settings.address_, SATA_DMA_REGS_BASE + DMA_BASE_SRC_ADR); + writel(oxnas_sata_dma_settings.address_, + SATA_DMA_REGS_BASE + DMA_BASE_DST_ADR); + writel(encode_final_eot(&mem_settings, &oxnas_sata_dma_settings, + num_bytes), + SATA_DMA_REGS_BASE + DMA_BYTE_CNT); + + dma_start(); +} + +static void dma_start_read(ulong* buffer, int num_bytes) +{ + // Assemble complete memory settings + oxnas_dma_device_settings_t mem_settings = oxnas_ram_dma_settings; + mem_settings.address_ = (unsigned long) buffer; + mem_settings.address_mode_ = OXNAS_DMA_MODE_INC; + + writel(encode_control_status(&oxnas_sata_dma_settings, &mem_settings), + SATA_DMA_REGS_BASE + DMA_CTRL_STATUS); + writel(oxnas_sata_dma_settings.address_, + SATA_DMA_REGS_BASE + DMA_BASE_SRC_ADR); + writel(mem_settings.address_, SATA_DMA_REGS_BASE + DMA_BASE_DST_ADR); + writel(encode_final_eot(&oxnas_sata_dma_settings, &mem_settings, + num_bytes), + SATA_DMA_REGS_BASE + DMA_BYTE_CNT); + + dma_start(); +} + +static inline int dma_busy(void) +{ + return readl(SATA_DMA_REGS_BASE + DMA_CTRL_STATUS) + & DMA_CTRL_STATUS_IN_PROGRESS; +} + +static int wait_dma_not_busy(int device) +{ + unsigned int cleanup_required = 0; + + /* Poll for DMA completion */ + int loops = MAX_DMA_XFER_LOOPS; + do { + if (!dma_busy()) { + break; + } + udelay(100); + } while (--loops); + + if (!loops) { + printf("wait_dma_not_busy() Timed out of wait for DMA not busy\n"); + cleanup_required = 1; + } + + if (cleanup_required) { + /* Abort DMA to make sure it has finished. */ + unsigned int ctrl_status = readl( + SATA_DMA_CHANNEL + DMA_CTRL_STATUS); + ctrl_status |= DMA_CTRL_STATUS_RESET; + writel(ctrl_status, SATA_DMA_CHANNEL + DMA_CTRL_STATUS); + + // Wait for the channel to become idle - should be quick as should + // finish after the next AHB single or burst transfer + loops = MAX_DMA_ABORT_LOOPS; + do { + if (!dma_busy()) { + break; + } + udelay(10); + } while (--loops); + + if (!loops) { + printf("wait_dma_not_busy() Timed out of wait for DMA channel abort\n"); + } else { + /* Successfully cleanup the DMA channel */ + cleanup_required = 0; + } + + // Deassert reset for the channel + ctrl_status = readl(SATA_DMA_CHANNEL + DMA_CTRL_STATUS); + ctrl_status &= ~DMA_CTRL_STATUS_RESET; + writel(ctrl_status, SATA_DMA_CHANNEL + DMA_CTRL_STATUS); + } + + return !cleanup_required; +} + +/** + * Possible that ATA status will not become not-busy, so must have timeout + */ +static unsigned int wait_not_busy(int device, unsigned long timeout_secs) +{ + int busy = 1; + unsigned long loops = (timeout_secs * 1000) / 50; + do { + // Test the ATA status register BUSY flag + if (!((*(sata_regs_base[device] + SATA_ORB2_OFF) + >> SATA_COMMAND_BIT) & (1UL << ATA_STATUS_BSY_BIT))) { + /* Not busy, so stop polling */ + busy = 0; + break; + } + + // Wait for 50mS before sampling ATA status register again + udelay(50000); + } while (--loops); + + return busy; +} + +void ide_output_data(int device, const ulong *sect_buf, int words) +{ + /* Only permit accesses to disks found to be present during ide_preinit() */ + if (!disk_present[device]) { + return; + } + + /* Select the required internal SATA drive */ + device_select(device); + + /* Start the DMA channel sending data from the passed buffer to the SATA core */ + dma_start_write(sect_buf, words << 2); + + /* Don't know why we need this delay, but without it the wait for DMA not + busy times soemtimes out, e.g. when saving environment to second disk */ + udelay(1000); + + /* Wait for DMA to finish */ + if (!wait_dma_not_busy(device)) { + printf("Timed out of wait for DMA channel for SATA device %d to have in-progress clear\n", + device); + } + + /* Sata core should finish after DMA */ + if (wait_not_busy(device, 30)) { + printf("Timed out of wait for SATA device %d to have BUSY clear\n", + device); + } + if (!wait_no_error(device)) { + printf("oxnas_sata_output_data() Wait for ATA no-error timed-out\n"); + } +} + + +#define SATA_DM_DBG1 (SATA_HOST_REGS_BASE + 0) +#define SATA_DATACOUNT_PORT0 (SATA_HOST_REGS_BASE + 0x10) +#define SATA_DATACOUNT_PORT1 (SATA_HOST_REGS_BASE + 0x14) +#define SATA_DATA_MUX_RAM0 (SATA_HOST_REGS_BASE + 0x8000) +#define SATA_DATA_MUX_RAM1 (SATA_HOST_REGS_BASE + 0xA000) +/* Sata core debug1 register bits */ +#define SATA_CORE_PORT0_DATA_DIR_BIT 20 +#define SATA_CORE_PORT1_DATA_DIR_BIT 21 +#define SATA_CORE_PORT0_DATA_DIR (1 << SATA_CORE_PORT0_DATA_DIR_BIT) +#define SATA_CORE_PORT1_DATA_DIR (1 << SATA_CORE_PORT1_DATA_DIR_BIT) + +/** + * Ref bug-6320 + * + * This code is a work around for a DMA hardware bug that will repeat the + * penultimate 8-bytes on some reads. This code will check that the amount + * of data transferred is a multiple of 512 bytes, if not the in it will + * fetch the correct data from a buffer in the SATA core and copy it into + * memory. + * + */ +static void sata_bug_6320_workaround(int port, ulong *candidate) +{ + int is_read; + int quads_transferred; + int remainder; + int sector_quads_remaining; + + /* Only want to apply fix to reads */ + is_read = !(*((unsigned long*) SATA_DM_DBG1) + & (port ? SATA_CORE_PORT1_DATA_DIR : SATA_CORE_PORT0_DATA_DIR)); + + /* Check for an incomplete transfer, i.e. not a multiple of 512 bytes + transferred (datacount_port register counts quads transferred) */ + quads_transferred = *((unsigned long*) ( + port ? SATA_DATACOUNT_PORT1 : SATA_DATACOUNT_PORT0)); + + remainder = quads_transferred & 0x7f; + sector_quads_remaining = remainder ? (0x80 - remainder) : 0; + + if (is_read && (sector_quads_remaining == 2)) { + debug("SATA read fixup, only transfered %d quads, " + "sector_quads_remaining %d, port %d\n", + quads_transferred, sector_quads_remaining, port); + + int total_len = ATA_SECT_SIZE; + ulong *sata_data_ptr = (void*) ( + port ? SATA_DATA_MUX_RAM1 : SATA_DATA_MUX_RAM0) + + ((total_len - 8) % 2048); + + *candidate = *sata_data_ptr; + *(candidate + 1) = *(sata_data_ptr + 1); + } +} + + +void ide_input_data(int device, ulong *sect_buf, int words) +{ + /* Only permit accesses to disks found to be present during ide_preinit() */ + if (!disk_present[device]) { + return; + } + + /* Select the required internal SATA drive */ + device_select(device); + + /* Start the DMA channel receiving data from the SATA core into the passed buffer */ + dma_start_read(sect_buf, words << 2); + + /* Sata core should finish before DMA */ + if (wait_not_busy(device, 30)) { + printf("Timed out of wait for SATA device %d to have BUSY clear\n", + device); + } + if (!wait_no_error(device)) { + printf("oxnas_sata_output_data() Wait for ATA no-error timed-out\n"); + } + + /* Wait for DMA to finish */ + if (!wait_dma_not_busy(device)) { + printf("Timed out of wait for DMA channel for SATA device %d to have in-progress clear\n", + device); + } + + if (words == ATA_SECTORWORDS) + sata_bug_6320_workaround(device, sect_buf + words - 2); +} + +static u32 scr_read(int device, unsigned int sc_reg) +{ + /* Setup adr of required register. std regs start eight into async region */ + *(sata_regs_base[device] + SATA_LINK_RD_ADDR) = sc_reg + * 4+ SATA_STD_ASYNC_REGS_OFF; + + /* Wait for data to be available */ + int loops = MAX_SRC_READ_LOOPS; + do { + if (*(sata_regs_base[device] + SATA_LINK_CONTROL) & 1UL) { + break; + } + udelay(10); + } while (--loops); + + if (!loops) { + printf("scr_read() Timed out of wait for read completion\n"); + } + + /* Read the data from the async register */ + return *(sata_regs_base[device] + SATA_LINK_DATA); +} + +static void scr_write(int device, unsigned int sc_reg, u32 val) +{ + /* Setup the data for the write */ + *(sata_regs_base[device] + SATA_LINK_DATA) = val; + + /* Setup adr of required register. std regs start eight into async region */ + *(sata_regs_base[device] + SATA_LINK_WR_ADDR) = sc_reg + * 4+ SATA_STD_ASYNC_REGS_OFF; + + /* Wait for data to be written */ + int loops = MAX_SRC_WRITE_LOOPS; + do { + if (*(sata_regs_base[device] + SATA_LINK_CONTROL) & 1UL) { + break; + } + udelay(10); + } while (--loops); + + if (!loops) { + printf("scr_write() Timed out of wait for write completion\n"); + } +} +extern void workaround5458(void); + +#define PHY_LOOP_COUNT 25 /* Wait for upto 5 seconds for PHY to be found */ +#define LOS_AND_TX_LVL 0x2988 +#define TX_ATTEN 0x55629 + +static int phy_reset(int device) +{ + int phy_status = 0; + int loops = 0; + + scr_write(device, (0x60 - SATA_STD_ASYNC_REGS_OFF) / 4, LOS_AND_TX_LVL); + scr_write(device, (0x70 - SATA_STD_ASYNC_REGS_OFF) / 4, TX_ATTEN); + + /* limit it to Gen-1 SATA (1.5G) */ + scr_write(device, SATA_SCR_CONTROL, 0x311); /* Issue phy wake & core reset */ + scr_read(device, SATA_SCR_STATUS); /* Dummy read; flush */ + udelay(1000); + scr_write(device, SATA_SCR_CONTROL, 0x310); /* Issue phy wake & clear core reset */ + + /* Wait for upto 5 seconds for PHY to become ready */ + do { + udelay(200000); + if ((scr_read(device, SATA_SCR_STATUS) & 0xf) == 3) { + scr_write(device, SATA_SCR_ERROR, ~0); + phy_status = 1; + break; + } + //printf("No SATA PHY found status:0x%x\n", scr_read(device, SATA_SCR_STATUS)); + } while (++loops < PHY_LOOP_COUNT); + + if (phy_status) { + udelay(500000); /* wait half a second */ + } + + return phy_status; +} + +#define FIS_LOOP_COUNT 25 /* Wait for upto 5 seconds for FIS to be received */ +static int wait_FIS(int device) +{ + int status = 0; + int loops = 0; + + do { + udelay(200000); + if (ide_inb(device, ATA_PORT_NSECT) > 0) { + status = 1; + break; + } + } while (++loops < FIS_LOOP_COUNT); + + return status; +} + + +#define SATA_PHY_ASIC_STAT (0x44900000) +#define SATA_PHY_ASIC_DATA (0x44900004) + +/** + * initialise functions and macros for ASIC implementation + */ +#define PH_GAIN 2 +#define FR_GAIN 3 +#define PH_GAIN_OFFSET 6 +#define FR_GAIN_OFFSET 8 +#define PH_GAIN_MASK (0x3 << PH_GAIN_OFFSET) +#define FR_GAIN_MASK (0x3 << FR_GAIN_OFFSET) +#define USE_INT_SETTING (1<<5) + +#define CR_READ_ENABLE (1<<16) +#define CR_WRITE_ENABLE (1<<17) +#define CR_CAP_DATA (1<<18) + +static void wait_cr_ack(void) +{ + while ((readl(SATA_PHY_ASIC_STAT) >> 16) & 0x1f) + /* wait for an ack bit to be set */; +} + +static unsigned short read_cr(unsigned short address) +{ + writel(address, SATA_PHY_ASIC_STAT); + wait_cr_ack(); + writel(CR_READ_ENABLE, SATA_PHY_ASIC_DATA); + wait_cr_ack(); + return readl(SATA_PHY_ASIC_STAT); +} + +static void write_cr(unsigned short data, unsigned short address) +{ + writel(address, SATA_PHY_ASIC_STAT); + wait_cr_ack(); + writel((data | CR_CAP_DATA), SATA_PHY_ASIC_DATA); + wait_cr_ack(); + writel(CR_WRITE_ENABLE, SATA_PHY_ASIC_DATA); + wait_cr_ack(); + return; +} + +void workaround5458(void) +{ + unsigned i; + + for (i = 0; i < 2; i++) { + unsigned short rx_control = read_cr(0x201d + (i << 8)); + rx_control &= ~(PH_GAIN_MASK | FR_GAIN_MASK); + rx_control |= PH_GAIN << PH_GAIN_OFFSET; + rx_control |= FR_GAIN << FR_GAIN_OFFSET; + rx_control |= USE_INT_SETTING; + write_cr(rx_control, 0x201d + (i << 8)); + } +} + +int ide_preinit(void) +{ + int num_disks_found = 0; + + /* Initialise records of which disks are present to all present */ + int i; + for (i = 0; i < CONFIG_SYS_IDE_MAXDEVICE; i++) { + disk_present[i] = 1; + } + + /* Block reset SATA and DMA cores */ + reset_block(SYS_CTRL_RST_SATA, 1); + reset_block(SYS_CTRL_RST_SATA_LINK, 1); + reset_block(SYS_CTRL_RST_SATA_PHY, 1); + reset_block(SYS_CTRL_RST_SGDMA, 1); + + /* Enable clocks to SATA and DMA cores */ + enable_clock(SYS_CTRL_CLK_SATA); + enable_clock(SYS_CTRL_CLK_DMA); + + udelay(5000); + reset_block(SYS_CTRL_RST_SATA_PHY, 0); + udelay(50); + reset_block(SYS_CTRL_RST_SATA, 0); + reset_block(SYS_CTRL_RST_SATA_LINK, 0); + udelay(50); + reset_block(SYS_CTRL_RST_SGDMA, 0); + udelay(100); + /* Apply the Synopsis SATA PHY workarounds */ + workaround5458(); + udelay(10000); + + /* disable and clear core interrupts */ + *((unsigned long*) SATA_HOST_REGS_BASE + SATA_INT_ENABLE_CLR_OFF) = + ~0UL; + *((unsigned long*) SATA_HOST_REGS_BASE + SATA_INT_CLR_OFF) = ~0UL; + + int device; + for (device = 0; device < CONFIG_SYS_IDE_MAXDEVICE; device++) { + int found = 0; + int retries = 1; + + /* Disable SATA interrupts */ + *(sata_regs_base[device] + SATA_INT_ENABLE_CLR_OFF) = ~0UL; + + /* Clear any pending SATA interrupts */ + *(sata_regs_base[device] + SATA_INT_CLR_OFF) = ~0UL; + + do { + /* clear sector count register for FIS detection */ + ide_outb(device, ATA_PORT_NSECT, 0); + + /* Get the PHY working */ + if (!phy_reset(device)) { + printf("SATA PHY not ready for device %d\n", + device); + break; + } + + if (!wait_FIS(device)) { + printf("No FIS received from device %d\n", + device); + } else { + if ((scr_read(device, SATA_SCR_STATUS) & 0xf) + == 0x3) { + if (wait_not_busy(device, 30)) { + printf("Timed out of wait for SATA device %d to have BUSY clear\n", + device); + } else { + ++num_disks_found; + found = 1; + } + } else { + printf("No SATA device %d found, PHY status = 0x%08x\n", + device, + scr_read( + device, + SATA_SCR_STATUS)); + } + break; + } + } while (retries--); + + /* Record whether disk is present, so won't attempt to access it later */ + disk_present[device] = found; + } + + /* post disk detection clean-up */ + for (device = 0; device < CONFIG_SYS_IDE_MAXDEVICE; device++) { + if (disk_present[device]) { + /* set as ata-5 (28-bit) */ + *(sata_regs_base[device] + SATA_DRIVE_CONTROL_OFF) = + 0UL; + + /* clear phy/link errors */ + scr_write(device, SATA_SCR_ERROR, ~0); + + /* clear host errors */ + *(sata_regs_base[device] + SATA_CONTROL_OFF) |= + SATA_SCTL_CLR_ERR; + + /* clear interrupt register as this clears the error bit in the IDE + status register */ + *(sata_regs_base[device] + SATA_INT_CLR_OFF) = ~0UL; + } + } + + return !num_disks_found; +} + -- cgit v1.2.3