/branches/gl-inet/target/linux/oxnas/files/drivers/ata/sata_oxnas.c |
@@ -0,0 +1,2505 @@ |
/* |
* sata_oxnas |
* A driver to interface the 934 based sata core present in the ox820 |
* with libata and scsi |
* based on sata_oxnas driver by Ma Haijun <mahaijuns@gmail.com> |
* based on ox820 sata code by: |
* Copyright (c) 2007 Oxford Semiconductor Ltd. |
* |
* 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, 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. |
*/ |
|
#include <linux/ata.h> |
#include <linux/libata.h> |
#include <linux/of_platform.h> |
#include <linux/delay.h> |
#include <linux/module.h> |
#include <linux/slab.h> |
#include <linux/spinlock.h> |
#include <linux/of_address.h> |
#include <linux/of_irq.h> |
#include <linux/clk.h> |
#include <linux/reset.h> |
|
#include <linux/io.h> |
#include <linux/sizes.h> |
|
static inline void oxnas_register_clear_mask(void __iomem *p, unsigned mask) |
{ |
u32 val = readl_relaxed(p); |
|
val &= ~mask; |
writel_relaxed(val, p); |
} |
|
static inline void oxnas_register_set_mask(void __iomem *p, unsigned mask) |
{ |
u32 val = readl_relaxed(p); |
|
val |= mask; |
writel_relaxed(val, p); |
} |
|
static inline void oxnas_register_value_mask(void __iomem *p, |
unsigned mask, unsigned new_value) |
{ |
/* TODO sanity check mask & new_value = new_value */ |
u32 val = readl_relaxed(p); |
|
val &= ~mask; |
val |= new_value; |
writel_relaxed(val, p); |
} |
|
/* sgdma request structure */ |
struct sgdma_request { |
volatile u32 qualifier; |
volatile u32 control; |
dma_addr_t src_pa; |
dma_addr_t dst_pa; |
} __packed __aligned(4); |
|
|
/* Controller information */ |
enum { |
SATA_OXNAS_MAX_PRD = 254, |
SATA_OXNAS_DMA_SIZE = SATA_OXNAS_MAX_PRD * |
sizeof(struct ata_bmdma_prd) + |
sizeof(struct sgdma_request), |
SATA_OXNAS_MAX_PORTS = 2, |
/** The different Oxsemi SATA core version numbers */ |
SATA_OXNAS_CORE_VERSION = 0x1f3, |
SATA_OXNAS_IRQ_FLAG = IRQF_SHARED, |
SATA_OXNAS_HOST_FLAGS = (ATA_FLAG_SATA | ATA_FLAG_PIO_DMA | |
ATA_FLAG_NO_ATAPI /*| ATA_FLAG_NCQ*/), |
SATA_OXNAS_QUEUE_DEPTH = 32, |
|
SATA_OXNAS_DMA_BOUNDARY = 0xFFFFFFFF, |
}; |
|
|
/* |
* SATA Port Registers |
*/ |
enum { |
/** sata host port register offsets */ |
ORB1 = 0x00, |
ORB2 = 0x04, |
ORB3 = 0x08, |
ORB4 = 0x0C, |
ORB5 = 0x10, |
MASTER_STATUS = 0x10, |
FIS_CTRL = 0x18, |
FIS_DATA = 0x1C, |
INT_STATUS = 0x30, |
INT_CLEAR = 0x30, |
INT_ENABLE = 0x34, |
INT_DISABLE = 0x38, |
VERSION = 0x3C, |
SATA_CONTROL = 0x5C, |
SATA_COMMAND = 0x60, |
HID_FEATURES = 0x64, |
PORT_CONTROL = 0x68, |
DRIVE_CONTROL = 0x6C, |
/** These registers allow access to the link layer registers |
that reside in a different clock domain to the processor bus */ |
LINK_DATA = 0x70, |
LINK_RD_ADDR = 0x74, |
LINK_WR_ADDR = 0x78, |
LINK_CONTROL = 0x7C, |
/* window control */ |
WIN1LO = 0x80, |
WIN1HI = 0x84, |
WIN2LO = 0x88, |
WIN2HI = 0x8C, |
WIN0_CONTROL = 0x90, |
}; |
|
/** sata port register bits */ |
enum{ |
/** |
* commands to issue in the master status to tell it to move shadow , |
* registers to the actual device , |
*/ |
SATA_OPCODE_MASK = 0x00000007, |
CMD_WRITE_TO_ORB_REGS_NO_COMMAND = 0x4, |
CMD_WRITE_TO_ORB_REGS = 0x2, |
CMD_SYNC_ESCAPE = 0x7, |
CMD_CORE_BUSY = (1 << 7), |
CMD_DRIVE_SELECT_SHIFT = 12, |
CMD_DRIVE_SELECT_MASK = (0xf << CMD_DRIVE_SELECT_SHIFT), |
|
/** interrupt bits */ |
INT_END_OF_CMD = 1 << 0, |
INT_LINK_SERROR = 1 << 1, |
INT_ERROR = 1 << 2, |
INT_LINK_IRQ = 1 << 3, |
INT_REG_ACCESS_ERR = 1 << 7, |
INT_BIST_FIS = 1 << 11, |
INT_MASKABLE = INT_END_OF_CMD | |
INT_LINK_SERROR | |
INT_ERROR | |
INT_LINK_IRQ | |
INT_REG_ACCESS_ERR | |
INT_BIST_FIS, |
INT_WANT = INT_END_OF_CMD | |
INT_LINK_SERROR | |
INT_REG_ACCESS_ERR | |
INT_ERROR, |
INT_ERRORS = INT_LINK_SERROR | |
INT_REG_ACCESS_ERR | |
INT_ERROR, |
|
/** raw interrupt bits, unmaskable, but do not generate interrupts */ |
RAW_END_OF_CMD = INT_END_OF_CMD << 16, |
RAW_LINK_SERROR = INT_LINK_SERROR << 16, |
RAW_ERROR = INT_ERROR << 16, |
RAW_LINK_IRQ = INT_LINK_IRQ << 16, |
RAW_REG_ACCESS_ERR = INT_REG_ACCESS_ERR << 16, |
RAW_BIST_FIS = INT_BIST_FIS << 16, |
RAW_WANT = INT_WANT << 16, |
RAW_ERRORS = INT_ERRORS << 16, |
|
/** |
* variables to write to the device control register to set the current |
* device, ie. master or slave. |
*/ |
DR_CON_48 = 2, |
DR_CON_28 = 0, |
|
SATA_CTL_ERR_MASK = 0x00000016, |
|
}; |
|
/* ATA SGDMA register offsets */ |
enum { |
SGDMA_CONTROL = 0x0, |
SGDMA_STATUS = 0x4, |
SGDMA_REQUESTPTR = 0x8, |
SGDMA_RESETS = 0xC, |
SGDMA_CORESIZE = 0x10, |
}; |
|
/* DMA controller register offsets */ |
enum { |
DMA_CONTROL = 0x0, |
DMA_CORESIZE = 0x20, |
|
DMA_CONTROL_RESET = (1 << 12), |
}; |
|
enum { |
/* see DMA core docs for the values. Out means from memory (bus A) out |
* to disk (bus B) */ |
SGDMA_REQCTL0OUT = 0x0497c03d, |
/* burst mode disabled when no micro code used */ |
SGDMA_REQCTL0IN = 0x0493a3c1, |
SGDMA_REQCTL1OUT = 0x0497c07d, |
SGDMA_REQCTL1IN = 0x0497a3c5, |
SGDMA_CONTROL_NOGO = 0x3e, |
SGDMA_CONTROL_GO = SGDMA_CONTROL_NOGO | 1, |
SGDMA_ERRORMASK = 0x3f, |
SGDMA_BUSY = 0x80, |
|
SGDMA_RESETS_CTRL = 1 << 0, |
SGDMA_RESETS_ARBT = 1 << 1, |
SGDMA_RESETS_AHB = 1 << 2, |
SGDMA_RESETS_ALL = SGDMA_RESETS_CTRL | |
SGDMA_RESETS_ARBT | |
SGDMA_RESETS_AHB, |
|
/* Final EOTs */ |
SGDMA_REQQUAL = 0x00220001, |
|
}; |
|
/** SATA core register offsets */ |
enum { |
DM_DBG1 = 0x000, |
RAID_SET = 0x004, |
DM_DBG2 = 0x008, |
DATACOUNT_PORT0 = 0x010, |
DATACOUNT_PORT1 = 0x014, |
CORE_INT_STATUS = 0x030, |
CORE_INT_CLEAR = 0x030, |
CORE_INT_ENABLE = 0x034, |
CORE_INT_DISABLE = 0x038, |
CORE_REBUILD_ENABLE = 0x050, |
CORE_FAILED_PORT_R = 0x054, |
DEVICE_CONTROL = 0x068, |
EXCESS = 0x06C, |
RAID_SIZE_LOW = 0x070, |
RAID_SIZE_HIGH = 0x074, |
PORT_ERROR_MASK = 0x078, |
IDLE_STATUS = 0x07C, |
RAID_CONTROL = 0x090, |
DATA_PLANE_CTRL = 0x0AC, |
CORE_DATAPLANE_STAT = 0x0b8, |
PROC_PC = 0x100, |
CONFIG_IN = 0x3d8, |
PROC_START = 0x3f0, |
PROC_RESET = 0x3f4, |
UCODE_STORE = 0x1000, |
RAID_WP_BOT_LOW = 0x1FF0, |
RAID_WP_BOT_HIGH = 0x1FF4, |
RAID_WP_TOP_LOW = 0x1FF8, |
RAID_WP_TOP_HIGH = 0x1FFC, |
DATA_MUX_RAM0 = 0x8000, |
DATA_MUX_RAM1 = 0xA000, |
PORT_SIZE = 0x10000, |
}; |
|
enum { |
/* Sata core debug1 register bits */ |
CORE_PORT0_DATA_DIR_BIT = 20, |
CORE_PORT1_DATA_DIR_BIT = 21, |
CORE_PORT0_DATA_DIR = 1 << CORE_PORT0_DATA_DIR_BIT, |
CORE_PORT1_DATA_DIR = 1 << CORE_PORT1_DATA_DIR_BIT, |
|
/** sata core control register bits */ |
SCTL_CLR_ERR = 0x00003016, |
RAID_CLR_ERR = 0x0000011e, |
|
/* Interrupts direct from the ports */ |
NORMAL_INTS_WANTED = 0x00000303, |
|
/* shift these left by port number */ |
COREINT_HOST = 0x00000001, |
COREINT_END = 0x00000100, |
CORERAW_HOST = COREINT_HOST << 16, |
CORERAW_END = COREINT_END << 16, |
|
/* Interrupts from the RAID controller only */ |
RAID_INTS_WANTED = 0x00008300, |
|
/* The bits in the IDLE_STATUS that, when set indicate an idle core */ |
IDLE_CORES = (1 << 18) | (1 << 19), |
|
/* Data plane control error-mask mask and bit, these bit in the data |
* plane control mask out errors from the ports that prevent the SGDMA |
* care from sending an interrupt */ |
DPC_ERROR_MASK = 0x00000300, |
DPC_ERROR_MASK_BIT = 0x00000100, |
/* enable jbod micro-code */ |
DPC_JBOD_UCODE = 1 << 0, |
DPC_FIS_SWCH = 1 << 1, |
|
/** Device Control register bits */ |
DEVICE_CONTROL_DMABT = 1 << 4, |
DEVICE_CONTROL_ABORT = 1 << 2, |
DEVICE_CONTROL_PAD = 1 << 3, |
DEVICE_CONTROL_PADPAT = 1 << 16, |
DEVICE_CONTROL_PRTRST = 1 << 8, |
DEVICE_CONTROL_RAMRST = 1 << 12, |
DEVICE_CONTROL_ATA_ERR_OVERRIDE = 1 << 28, |
|
/** oxsemi HW raid modes */ |
OXNASSATA_NOTRAID = 0, |
OXNASSATA_RAID0 = 1, |
OXNASSATA_RAID1 = 2, |
/** OX820 specific HW-RAID register values */ |
RAID_TWODISKS = 3, |
UNKNOWN_MODE = ~0, |
|
CONFIG_IN_RESUME = 2, |
}; |
|
/* SATA PHY Registers */ |
enum { |
PHY_STAT = 0x00, |
PHY_DATA = 0x04, |
}; |
|
enum { |
STAT_READ_VALID = (1 << 21), |
STAT_CR_ACK = (1 << 20), |
STAT_CR_READ = (1 << 19), |
STAT_CR_WRITE = (1 << 18), |
STAT_CAP_DATA = (1 << 17), |
STAT_CAP_ADDR = (1 << 16), |
|
STAT_ACK_ANY = STAT_CR_ACK | |
STAT_CR_READ | |
STAT_CR_WRITE | |
STAT_CAP_DATA | |
STAT_CAP_ADDR, |
|
CR_READ_ENABLE = (1 << 16), |
CR_WRITE_ENABLE = (1 << 17), |
CR_CAP_DATA = (1 << 18), |
}; |
|
enum { |
/* Link layer registers */ |
SERROR_IRQ_MASK = 5, |
}; |
|
enum { |
OXNAS_SATA_SOFTRESET = 1, |
OXNAS_SATA_REINIT = 2, |
}; |
|
enum { |
OXNAS_SATA_UCODE_RAID0, |
OXNAS_SATA_UCODE_RAID1, |
OXNAS_SATA_UCODE_JBOD, |
OXNAS_SATA_UCODE_NONE, |
}; |
|
enum { |
SATA_UNLOCKED, |
SATA_WRITER, |
SATA_READER, |
SATA_REBUILD, |
SATA_HWRAID, |
SATA_SCSI_STACK |
}; |
|
typedef irqreturn_t (*oxnas_sata_isr_callback_t)(int, unsigned long, int); |
|
struct sata_oxnas_host_priv { |
void __iomem *port_base; |
void __iomem *dmactl_base; |
void __iomem *sgdma_base; |
void __iomem *core_base; |
void __iomem *phy_base; |
dma_addr_t dma_base; |
void __iomem *dma_base_va; |
size_t dma_size; |
int irq; |
int n_ports; |
int current_ucode; |
u32 port_frozen; |
u32 port_in_eh; |
struct clk *clk; |
struct reset_control *rst_sata; |
struct reset_control *rst_link; |
struct reset_control *rst_phy; |
spinlock_t phy_lock; |
spinlock_t core_lock; |
int core_locked; |
int reentrant_port_no; |
int hw_lock_count; |
int direct_lock_count; |
void *locker_uid; |
int current_locker_type; |
int scsi_nonblocking_attempts; |
oxnas_sata_isr_callback_t isr_callback; |
void *isr_arg; |
wait_queue_head_t fast_wait_queue; |
wait_queue_head_t scsi_wait_queue; |
}; |
|
|
struct sata_oxnas_port_priv { |
void __iomem *port_base; |
void __iomem *dmactl_base; |
void __iomem *sgdma_base; |
void __iomem *core_base; |
struct sgdma_request *sgdma_request; |
dma_addr_t sgdma_request_pa; |
}; |
|
static u8 sata_oxnas_check_status(struct ata_port *ap); |
static int sata_oxnas_cleanup(struct ata_host *ah); |
static void sata_oxnas_tf_load(struct ata_port *ap, |
const struct ata_taskfile *tf); |
static void sata_oxnas_irq_on(struct ata_port *ap); |
static void sata_oxnas_post_reset_init(struct ata_port *ap); |
|
static int sata_oxnas_acquire_hw(struct ata_port *ap, int may_sleep, |
int timeout_jiffies); |
static void sata_oxnas_release_hw(struct ata_port *ap); |
|
static const void *HW_LOCKER_UID = (void *)0xdeadbeef; |
|
/*************************************************************************** |
* ASIC access |
***************************************************************************/ |
static void wait_cr_ack(void __iomem *phy_base) |
{ |
while ((ioread32(phy_base + PHY_STAT) >> 16) & 0x1f) |
; /* wait for an ack bit to be set */ |
} |
|
static u16 read_cr(void __iomem *phy_base, u16 address) |
{ |
iowrite32((u32)address, phy_base + PHY_STAT); |
wait_cr_ack(phy_base); |
iowrite32(CR_READ_ENABLE, phy_base + PHY_DATA); |
wait_cr_ack(phy_base); |
return (u16)ioread32(phy_base + PHY_STAT); |
} |
|
static void write_cr(void __iomem *phy_base, u16 data, u16 address) |
{ |
iowrite32((u32)address, phy_base + PHY_STAT); |
wait_cr_ack(phy_base); |
iowrite32((data | CR_CAP_DATA), phy_base + PHY_DATA); |
wait_cr_ack(phy_base); |
iowrite32(CR_WRITE_ENABLE, phy_base + PHY_DATA); |
wait_cr_ack(phy_base); |
} |
|
#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) |
|
void workaround5458(struct ata_host *ah) |
{ |
struct sata_oxnas_host_priv *hd = ah->private_data; |
void __iomem *phy_base = hd->phy_base; |
u16 rx_control; |
unsigned i; |
|
for (i = 0; i < 2; i++) { |
rx_control = read_cr(phy_base, 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) | USE_INT_SETTING; |
write_cr(phy_base, rx_control, 0x201d+(i<<8)); |
} |
} |
|
/** |
* allows access to the link layer registers |
* @param link_reg the link layer register to access (oxsemi indexing ie |
* 00 = static config, 04 = phy ctrl) |
*/ |
void sata_oxnas_link_write(struct ata_port *ap, unsigned int link_reg, u32 val) |
{ |
struct sata_oxnas_port_priv *pd = ap->private_data; |
struct sata_oxnas_host_priv *hd = ap->host->private_data; |
void __iomem *port_base = pd->port_base; |
u32 patience; |
unsigned long flags; |
|
DPRINTK("P%d [0x%02x]->0x%08x\n", ap->port_no, link_reg, val); |
|
spin_lock_irqsave(&hd->phy_lock, flags); |
iowrite32(val, port_base + LINK_DATA); |
|
/* accessed twice as a work around for a bug in the SATA abp bridge |
* hardware (bug 6828) */ |
iowrite32(link_reg , port_base + LINK_WR_ADDR); |
ioread32(port_base + LINK_WR_ADDR); |
|
for (patience = 0x100000; patience > 0; --patience) { |
if (ioread32(port_base + LINK_CONTROL) & 0x00000001) |
break; |
} |
spin_unlock_irqrestore(&hd->phy_lock, flags); |
} |
|
static int sata_oxnas_scr_write_port(struct ata_port *ap, unsigned int sc_reg, |
u32 val) |
{ |
sata_oxnas_link_write(ap, 0x20 + (sc_reg * 4), val); |
return 0; |
} |
|
static int sata_oxnas_scr_write(struct ata_link *link, unsigned int sc_reg, |
u32 val) |
{ |
return sata_oxnas_scr_write_port(link->ap, sc_reg, val); |
} |
|
u32 sata_oxnas_link_read(struct ata_port *ap, unsigned int link_reg) |
{ |
struct sata_oxnas_port_priv *pd = ap->private_data; |
struct sata_oxnas_host_priv *hd = ap->host->private_data; |
void __iomem *port_base = pd->port_base; |
u32 result; |
u32 patience; |
unsigned long flags; |
|
spin_lock_irqsave(&hd->phy_lock, flags); |
/* accessed twice as a work around for a bug in the SATA abp bridge |
* hardware (bug 6828) */ |
iowrite32(link_reg, port_base + LINK_RD_ADDR); |
ioread32(port_base + LINK_RD_ADDR); |
|
for (patience = 0x100000; patience > 0; --patience) { |
if (ioread32(port_base + LINK_CONTROL) & 0x00000001) |
break; |
} |
if (patience == 0) |
DPRINTK("link read timed out for port %d\n", ap->port_no); |
|
result = ioread32(port_base + LINK_DATA); |
spin_unlock_irqrestore(&hd->phy_lock, flags); |
|
return result; |
} |
|
static int sata_oxnas_scr_read_port(struct ata_port *ap, unsigned int sc_reg, |
u32 *val) |
{ |
*val = sata_oxnas_link_read(ap, 0x20 + (sc_reg*4)); |
return 0; |
} |
|
static int sata_oxnas_scr_read(struct ata_link *link, |
unsigned int sc_reg, u32 *val) |
{ |
return sata_oxnas_scr_read_port(link->ap, sc_reg, val); |
} |
|
/** |
* sata_oxnas_irq_clear is called during probe just before the interrupt handler is |
* registered, to be sure hardware is quiet. It clears and masks interrupt bits |
* in the SATA core. |
* |
* @param ap hardware with the registers in |
*/ |
static void sata_oxnas_irq_clear(struct ata_port *ap) |
{ |
struct sata_oxnas_port_priv *port_priv = ap->private_data; |
|
/* clear pending interrupts */ |
iowrite32(~0, port_priv->port_base + INT_CLEAR); |
iowrite32(COREINT_END, port_priv->core_base + CORE_INT_CLEAR); |
} |
|
/** |
* qc_issue is used to make a command active, once the hardware and S/G tables |
* have been prepared. IDE BMDMA drivers use the helper function |
* ata_qc_issue_prot() for taskfile protocol-based dispatch. More advanced |
* drivers roll their own ->qc_issue implementation, using this as the |
* "issue new ATA command to hardware" hook. |
* @param qc the queued command to issue |
*/ |
static unsigned int sata_oxnas_qc_issue(struct ata_queued_cmd *qc) |
{ |
struct sata_oxnas_port_priv *pd = qc->ap->private_data; |
struct sata_oxnas_host_priv *hd = qc->ap->host->private_data; |
|
void __iomem *port_base = pd->port_base; |
void __iomem *core_base = pd->core_base; |
int port_no = qc->ap->port_no; |
int no_microcode = (hd->current_ucode == UNKNOWN_MODE); |
u32 reg; |
|
/* check the core is idle */ |
if (ioread32(port_base + SATA_COMMAND) & CMD_CORE_BUSY) { |
int count = 0; |
|
DPRINTK("core busy for a command on port %d\n", |
qc->ap->port_no); |
do { |
mdelay(1); |
if (++count > 100) { |
DPRINTK("core busy for a command on port %d\n", |
qc->ap->port_no); |
/* CrazyDumpDebug(); */ |
sata_oxnas_cleanup(qc->ap->host); |
} |
} while (ioread32(port_base + SATA_COMMAND) & CMD_CORE_BUSY); |
} |
|
/* enable passing of error signals to DMA sub-core by clearing the |
* appropriate bit */ |
reg = ioread32(core_base + DATA_PLANE_CTRL); |
if (no_microcode) |
reg |= (DPC_ERROR_MASK_BIT | (DPC_ERROR_MASK_BIT << 1)); |
reg &= ~(DPC_ERROR_MASK_BIT << port_no); |
iowrite32(reg, core_base + DATA_PLANE_CTRL); |
|
/* Disable all interrupts for ports and RAID controller */ |
iowrite32(~0, port_base + INT_DISABLE); |
|
/* Disable all interrupts for core */ |
iowrite32(~0, core_base + CORE_INT_DISABLE); |
wmb(); |
|
/* Load the command settings into the orb registers */ |
sata_oxnas_tf_load(qc->ap, &qc->tf); |
|
/* both pio and dma commands use dma */ |
if (ata_is_dma(qc->tf.protocol) || ata_is_pio(qc->tf.protocol)) { |
/* Start the DMA */ |
iowrite32(SGDMA_CONTROL_GO, pd->sgdma_base + SGDMA_CONTROL); |
wmb(); |
} |
|
/* enable End of command interrupt */ |
iowrite32(INT_WANT, port_base + INT_ENABLE); |
iowrite32(COREINT_END, core_base + CORE_INT_ENABLE); |
wmb(); |
|
/* Start the command */ |
reg = ioread32(port_base + SATA_COMMAND); |
reg &= ~SATA_OPCODE_MASK; |
reg |= CMD_WRITE_TO_ORB_REGS; |
iowrite32(reg , port_base + SATA_COMMAND); |
wmb(); |
|
return 0; |
} |
|
/** |
* Will schedule the libATA error handler on the premise that there has |
* been a hotplug event on the port specified |
*/ |
void sata_oxnas_checkforhotplug(struct ata_port *ap) |
{ |
DPRINTK("ENTER\n"); |
|
ata_ehi_hotplugged(&ap->link.eh_info); |
ata_port_freeze(ap); |
} |
|
|
/**************************************************************************/ |
/* Locking */ |
/**************************************************************************/ |
/** |
* The underlying function that controls access to the sata core |
* |
* @return non-zero indicates that you have acquired exclusive access to the |
* sata core. |
*/ |
static int __acquire_sata_core( |
struct ata_host *ah, |
int port_no, |
oxnas_sata_isr_callback_t callback, |
void *arg, |
int may_sleep, |
int timeout_jiffies, |
int hw_access, |
void *uid, |
int locker_type) |
{ |
unsigned long end = jiffies + timeout_jiffies; |
int acquired = 0; |
unsigned long flags; |
int timed_out = 0; |
struct sata_oxnas_host_priv *hd; |
|
DEFINE_WAIT(wait); |
|
if (!ah) |
return acquired; |
|
hd = ah->private_data; |
|
spin_lock_irqsave(&hd->core_lock, flags); |
|
DPRINTK("Entered uid %p, port %d, h/w count %d, d count %d, " |
"callback %p, hw_access %d, core_locked %d, " |
"reentrant_port_no %d, isr_callback %p\n", |
uid, port_no, hd->hw_lock_count, hd->direct_lock_count, |
callback, hw_access, hd->core_locked, hd->reentrant_port_no, |
hd->isr_callback); |
|
while (!timed_out) { |
if (hd->core_locked || |
(!hw_access && hd->scsi_nonblocking_attempts)) { |
/* Can only allow access if from SCSI/SATA stack and if |
* reentrant access is allowed and this access is to the |
* same port for which the lock is current held |
*/ |
if (hw_access && (port_no == hd->reentrant_port_no)) { |
BUG_ON(!hd->hw_lock_count); |
++(hd->hw_lock_count); |
|
DPRINTK("Allow SCSI/SATA re-entrant access to " |
"uid %p port %d\n", uid, port_no); |
acquired = 1; |
break; |
} else if (!hw_access) { |
if ((locker_type == SATA_READER) && |
(hd->current_locker_type == SATA_READER)) { |
WARN(1, |
"Already locked by reader, " |
"uid %p, locker_uid %p, " |
"port %d, h/w count %d, " |
"d count %d, hw_access %d\n", |
uid, hd->locker_uid, port_no, |
hd->hw_lock_count, |
hd->direct_lock_count, |
hw_access); |
goto check_uid; |
} |
|
if ((locker_type != SATA_READER) && |
(locker_type != SATA_WRITER)) { |
goto wait_for_lock; |
} |
|
check_uid: |
WARN(uid == hd->locker_uid, "Attempt to lock " |
"by locker type %d uid %p, already " |
"locked by locker type %d with " |
"locker_uid %p, port %d, " |
"h/w count %d, d count %d, " |
"hw_access %d\n", locker_type, uid, |
hd->current_locker_type, |
hd->locker_uid, port_no, |
hd->hw_lock_count, |
hd->direct_lock_count, hw_access); |
} |
} else { |
WARN(hd->hw_lock_count || hd->direct_lock_count, |
"Core unlocked but counts non-zero: uid %p, " |
"locker_uid %p, port %d, h/w count %d, " |
"d count %d, hw_access %d\n", uid, |
hd->locker_uid, port_no, hd->hw_lock_count, |
hd->direct_lock_count, hw_access); |
|
BUG_ON(hd->current_locker_type != SATA_UNLOCKED); |
|
WARN(hd->locker_uid, "Attempt to lock uid %p when " |
"locker_uid %p is non-zero, port %d, " |
"h/w count %d, d count %d, hw_access %d\n", |
uid, hd->locker_uid, port_no, hd->hw_lock_count, |
hd->direct_lock_count, hw_access); |
|
if (!hw_access) { |
/* Direct access attempting to acquire |
* non-contented lock |
*/ |
/* Must have callback for direct access */ |
BUG_ON(!callback); |
/* Sanity check lock state */ |
BUG_ON(hd->reentrant_port_no != -1); |
|
hd->isr_callback = callback; |
hd->isr_arg = arg; |
++(hd->direct_lock_count); |
|
hd->current_locker_type = locker_type; |
} else { |
/* SCSI/SATA attempting to acquire |
* non-contented lock |
*/ |
/* No callbacks for SCSI/SATA access */ |
BUG_ON(callback); |
/* No callback args for SCSI/SATA access */ |
BUG_ON(arg); |
|
/* Sanity check lock state */ |
BUG_ON(hd->isr_callback); |
BUG_ON(hd->isr_arg); |
|
++(hd->hw_lock_count); |
hd->reentrant_port_no = port_no; |
|
hd->current_locker_type = SATA_SCSI_STACK; |
} |
|
hd->core_locked = 1; |
hd->locker_uid = uid; |
acquired = 1; |
break; |
} |
|
wait_for_lock: |
if (!may_sleep) { |
DPRINTK("Denying for uid %p locker_type %d, " |
"hw_access %d, port %d, current_locker_type %d as " |
"cannot sleep\n", uid, locker_type, hw_access, port_no, |
hd->current_locker_type); |
|
if (hw_access) |
++(hd->scsi_nonblocking_attempts); |
|
break; |
} |
|
/* Core is locked and we're allowed to sleep, so wait to be |
* awoken when the core is unlocked |
*/ |
for (;;) { |
prepare_to_wait(hw_access ? &hd->scsi_wait_queue : |
&hd->fast_wait_queue, |
&wait, TASK_UNINTERRUPTIBLE); |
if (!hd->core_locked && |
!(!hw_access && hd->scsi_nonblocking_attempts)) { |
/* We're going to use variables that will have |
* been changed by the waker prior to clearing |
* core_locked so we need to ensure we see |
* changes to all those variables |
*/ |
smp_rmb(); |
break; |
} |
if (time_after(jiffies, end)) { |
printk(KERN_WARNING "__acquire_sata_core() " |
"uid %p failing for port %d timed out, " |
"locker_uid %p, h/w count %d, " |
"d count %d, callback %p, hw_access %d, " |
"core_locked %d, reentrant_port_no %d, " |
"isr_callback %p, isr_arg %p\n", uid, |
port_no, hd->locker_uid, |
hd->hw_lock_count, |
hd->direct_lock_count, callback, |
hw_access, hd->core_locked, |
hd->reentrant_port_no, hd->isr_callback, |
hd->isr_arg); |
timed_out = 1; |
break; |
} |
spin_unlock_irqrestore(&hd->core_lock, flags); |
if (!schedule_timeout(4*HZ)) { |
printk(KERN_INFO "__acquire_sata_core() uid %p, " |
"locker_uid %p, timed-out of " |
"schedule(), checking overall timeout\n", |
uid, hd->locker_uid); |
} |
spin_lock_irqsave(&hd->core_lock, flags); |
} |
finish_wait(hw_access ? &hd->scsi_wait_queue : |
&hd->fast_wait_queue, &wait); |
} |
|
if (hw_access && acquired) { |
if (hd->scsi_nonblocking_attempts) |
hd->scsi_nonblocking_attempts = 0; |
|
/* Wake any other SCSI/SATA waiters so they can get reentrant |
* access to the same port if appropriate. This is because if |
* the SATA core is locked by fast access, or SCSI/SATA access |
* to other port, then can have >1 SCSI/SATA waiters on the wait |
* list so want to give reentrant accessors a chance to get |
* access ASAP |
*/ |
if (!list_empty(&hd->scsi_wait_queue.head)) |
wake_up(&hd->scsi_wait_queue); |
} |
|
DPRINTK("Leaving uid %p with acquired = %d, port %d, callback %p\n", |
uid, acquired, port_no, callback); |
|
spin_unlock_irqrestore(&hd->core_lock, flags); |
|
return acquired; |
} |
|
int sata_core_has_fast_waiters(struct ata_host *ah) |
{ |
int has_waiters; |
unsigned long flags; |
struct sata_oxnas_host_priv *hd = ah->private_data; |
|
spin_lock_irqsave(&hd->core_lock, flags); |
has_waiters = !list_empty(&hd->fast_wait_queue.head); |
spin_unlock_irqrestore(&hd->core_lock, flags); |
|
return has_waiters; |
} |
EXPORT_SYMBOL(sata_core_has_fast_waiters); |
|
int sata_core_has_scsi_waiters(struct ata_host *ah) |
{ |
int has_waiters; |
unsigned long flags; |
struct sata_oxnas_host_priv *hd = ah->private_data; |
|
spin_lock_irqsave(&hd->core_lock, flags); |
has_waiters = hd->scsi_nonblocking_attempts || |
!list_empty(&hd->scsi_wait_queue.head); |
spin_unlock_irqrestore(&hd->core_lock, flags); |
|
return has_waiters; |
} |
EXPORT_SYMBOL(sata_core_has_scsi_waiters); |
|
/* |
* ata_port operation to gain ownership of the SATA hardware prior to issuing |
* a command against a SATA host. Allows any number of users of the port against |
* which the lock was first acquired, thus enforcing that only one SATA core |
* port may be operated on at once. |
*/ |
static int sata_oxnas_acquire_hw( |
struct ata_port *ap, |
int may_sleep, |
int timeout_jiffies) |
{ |
return __acquire_sata_core(ap->host, ap->port_no, NULL, 0, may_sleep, |
timeout_jiffies, 1, (void *)HW_LOCKER_UID, |
SATA_SCSI_STACK); |
} |
|
/* |
* operation to release ownership of the SATA hardware |
*/ |
static void sata_oxnas_release_hw(struct ata_port *ap) |
{ |
unsigned long flags; |
int released = 0; |
struct sata_oxnas_host_priv *hd = ap->host->private_data; |
|
spin_lock_irqsave(&hd->core_lock, flags); |
|
DPRINTK("Entered port_no = %d, h/w count %d, d count %d, " |
"core locked = %d, reentrant_port_no = %d, isr_callback %p\n", |
ap->port_no, hd->hw_lock_count, hd->direct_lock_count, |
hd->core_locked, hd->reentrant_port_no, hd->isr_callback); |
|
if (!hd->core_locked) { |
/* Nobody holds the SATA lock */ |
printk(KERN_WARNING "Nobody holds SATA lock, port_no %d\n", |
ap->port_no); |
released = 1; |
} else if (!hd->hw_lock_count) { |
/* SCSI/SATA has released without holding the lock */ |
printk(KERN_WARNING "SCSI/SATA does not hold SATA lock, " |
"port_no %d\n", ap->port_no); |
} else { |
/* Trap incorrect usage */ |
BUG_ON(hd->reentrant_port_no == -1); |
BUG_ON(ap->port_no != hd->reentrant_port_no); |
BUG_ON(hd->direct_lock_count); |
BUG_ON(hd->current_locker_type != SATA_SCSI_STACK); |
|
WARN(!hd->locker_uid || (hd->locker_uid != HW_LOCKER_UID), |
"Invalid locker uid %p, h/w count %d, d count %d, " |
"reentrant_port_no %d, core_locked %d, " |
"isr_callback %p\n", hd->locker_uid, hd->hw_lock_count, |
hd->direct_lock_count, hd->reentrant_port_no, |
hd->core_locked, hd->isr_callback); |
|
if (--(hd->hw_lock_count)) { |
DPRINTK("Still nested port_no %d\n", ap->port_no); |
} else { |
DPRINTK("Release port_no %d\n", ap->port_no); |
hd->reentrant_port_no = -1; |
hd->isr_callback = NULL; |
hd->current_locker_type = SATA_UNLOCKED; |
hd->locker_uid = 0; |
hd->core_locked = 0; |
released = 1; |
wake_up(!list_empty(&hd->scsi_wait_queue.head) ? |
&hd->scsi_wait_queue : |
&hd->fast_wait_queue); |
} |
} |
|
DPRINTK("Leaving, port_no %d, count %d\n", ap->port_no, |
hd->hw_lock_count); |
|
spin_unlock_irqrestore(&hd->core_lock, flags); |
|
/* CONFIG_SATA_OX820_DIRECT_HWRAID */ |
/* if (released) |
ox820hwraid_restart_queue(); |
} */ |
} |
|
static inline int sata_oxnas_is_host_frozen(struct ata_host *ah) |
{ |
struct sata_oxnas_host_priv *hd = ah->private_data; |
|
smp_rmb(); |
return hd->port_in_eh || hd->port_frozen; |
} |
|
|
static inline u32 sata_oxnas_hostportbusy(struct ata_port *ap) |
{ |
struct sata_oxnas_host_priv *hd = ap->host->private_data; |
|
return (ioread32(hd->port_base + SATA_COMMAND) & CMD_CORE_BUSY) || |
(hd->n_ports > 1 && |
(ioread32(hd->port_base + PORT_SIZE + SATA_COMMAND) & |
CMD_CORE_BUSY)); |
} |
|
static inline u32 sata_oxnas_hostdmabusy(struct ata_port *ap) |
{ |
struct sata_oxnas_port_priv *pd = ap->private_data; |
|
return ioread32(pd->sgdma_base + SGDMA_STATUS) & SGDMA_BUSY; |
} |
|
|
/** |
* Turns on the cores clock and resets it |
*/ |
static void sata_oxnas_reset_core(struct ata_host *ah) |
{ |
struct sata_oxnas_host_priv *host_priv = ah->private_data; |
int n; |
|
DPRINTK("ENTER\n"); |
clk_prepare_enable(host_priv->clk); |
|
reset_control_assert(host_priv->rst_sata); |
reset_control_assert(host_priv->rst_link); |
reset_control_assert(host_priv->rst_phy); |
|
udelay(50); |
|
/* un-reset the PHY, then Link and Controller */ |
reset_control_deassert(host_priv->rst_phy); |
udelay(50); |
|
reset_control_deassert(host_priv->rst_sata); |
reset_control_deassert(host_priv->rst_link); |
udelay(50); |
|
workaround5458(ah); |
/* tune for sata compatibility */ |
sata_oxnas_link_write(ah->ports[0], 0x60, 0x2988); |
|
for (n = 0; n < host_priv->n_ports; n++) { |
/* each port in turn */ |
sata_oxnas_link_write(ah->ports[n], 0x70, 0x55629); |
} |
udelay(50); |
} |
|
|
/** |
* Called after an identify device command has worked out what kind of device |
* is on the port |
* |
* @param port The port to configure |
* @param pdev The hardware associated with controlling the port |
*/ |
static void sata_oxnas_dev_config(struct ata_device *pdev) |
{ |
struct sata_oxnas_port_priv *pd = pdev->link->ap->private_data; |
void __iomem *port_base = pd->port_base; |
u32 reg; |
|
DPRINTK("ENTER\n"); |
/* Set the bits to put the port into 28 or 48-bit node */ |
reg = ioread32(port_base + DRIVE_CONTROL); |
reg &= ~3; |
reg |= (pdev->flags & ATA_DFLAG_LBA48) ? DR_CON_48 : DR_CON_28; |
iowrite32(reg, port_base + DRIVE_CONTROL); |
|
/* if this is an ATA-6 disk, put port into ATA-5 auto translate mode */ |
if (pdev->flags & ATA_DFLAG_LBA48) { |
reg = ioread32(port_base + PORT_CONTROL); |
reg |= 2; |
iowrite32(reg, port_base + PORT_CONTROL); |
} |
} |
/** |
* called to write a taskfile into the ORB registers |
* @param ap hardware with the registers in |
* @param tf taskfile to write to the registers |
*/ |
static void sata_oxnas_tf_load(struct ata_port *ap, |
const struct ata_taskfile *tf) |
{ |
u32 count = 0; |
u32 Orb1 = 0; |
u32 Orb2 = 0; |
u32 Orb3 = 0; |
u32 Orb4 = 0; |
u32 Command_Reg; |
|
struct sata_oxnas_port_priv *port_priv = ap->private_data; |
void __iomem *port_base = port_priv->port_base; |
unsigned int is_addr = tf->flags & ATA_TFLAG_ISADDR; |
|
/* wait a maximum of 10ms for the core to be idle */ |
do { |
Command_Reg = ioread32(port_base + SATA_COMMAND); |
if (!(Command_Reg & CMD_CORE_BUSY)) |
break; |
count++; |
udelay(50); |
} while (count < 200); |
|
/* check if the ctl register has interrupts disabled or enabled and |
* modify the interrupt enable registers on the ata core as required */ |
if (tf->ctl & ATA_NIEN) { |
/* interrupts disabled */ |
u32 mask = (COREINT_END << ap->port_no); |
|
iowrite32(mask, port_priv->core_base + CORE_INT_DISABLE); |
sata_oxnas_irq_clear(ap); |
} else { |
sata_oxnas_irq_on(ap); |
} |
|
Orb2 |= (tf->command) << 24; |
|
/* write 48 or 28 bit tf parameters */ |
if (is_addr) { |
/* set LBA bit as it's an address */ |
Orb1 |= (tf->device & ATA_LBA) << 24; |
|
if (tf->flags & ATA_TFLAG_LBA48) { |
Orb1 |= ATA_LBA << 24; |
Orb2 |= (tf->hob_nsect) << 8; |
Orb3 |= (tf->hob_lbal) << 24; |
Orb4 |= (tf->hob_lbam) << 0; |
Orb4 |= (tf->hob_lbah) << 8; |
Orb4 |= (tf->hob_feature) << 16; |
} else { |
Orb3 |= (tf->device & 0xf) << 24; |
} |
|
/* write 28-bit lba */ |
Orb2 |= (tf->nsect) << 0; |
Orb2 |= (tf->feature) << 16; |
Orb3 |= (tf->lbal) << 0; |
Orb3 |= (tf->lbam) << 8; |
Orb3 |= (tf->lbah) << 16; |
Orb4 |= (tf->ctl) << 24; |
} |
|
if (tf->flags & ATA_TFLAG_DEVICE) |
Orb1 |= (tf->device) << 24; |
|
ap->last_ctl = tf->ctl; |
|
/* write values to registers */ |
iowrite32(Orb1, port_base + ORB1); |
iowrite32(Orb2, port_base + ORB2); |
iowrite32(Orb3, port_base + ORB3); |
iowrite32(Orb4, port_base + ORB4); |
} |
|
|
void sata_oxnas_set_mode(struct ata_host *ah, u32 mode, u32 force) |
{ |
struct sata_oxnas_host_priv *host_priv = ah->private_data; |
void __iomem *core_base = host_priv->core_base; |
|
unsigned int *src; |
void __iomem *dst; |
unsigned int progmicrocode = 0; |
unsigned int changeparameters = 0; |
|
u32 previous_mode; |
|
/* these micro-code programs _should_ include the version word */ |
|
/* JBOD */ |
static const unsigned int jbod[] = { |
0x07B400AC, 0x0228A280, 0x00200001, 0x00204002, 0x00224001, |
0x00EE0009, 0x00724901, 0x01A24903, 0x00E40009, 0x00224001, |
0x00621120, 0x0183C908, 0x00E20005, 0x00718908, 0x0198A206, |
0x00621124, 0x0183C908, 0x00E20046, 0x00621104, 0x0183C908, |
0x00E20015, 0x00EE009D, 0x01A3E301, 0x00E2001B, 0x0183C900, |
0x00E2001B, 0x00210001, 0x00EE0020, 0x01A3E302, 0x00E2009D, |
0x0183C901, 0x00E2009D, 0x00210002, 0x0235D700, 0x0208A204, |
0x0071C908, 0x000F8207, 0x000FC207, 0x0071C920, 0x000F8507, |
0x000FC507, 0x0228A240, 0x02269A40, 0x00094004, 0x00621104, |
0x0180C908, 0x00E40031, 0x00621112, 0x01A3C801, 0x00E2002B, |
0x00294000, 0x0228A220, 0x01A69ABF, 0x002F8000, 0x002FC000, |
0x0198A204, 0x0001C022, 0x01B1A220, 0x0001C106, 0x00088007, |
0x0183C903, 0x00E2009D, 0x0228A220, 0x0071890C, 0x0208A206, |
0x0198A206, 0x0001C022, 0x01B1A220, 0x0001C106, 0x00088007, |
0x00EE009D, 0x00621104, 0x0183C908, 0x00E2004A, 0x00EE009D, |
0x01A3C901, 0x00E20050, 0x0021E7FF, 0x0183E007, 0x00E2009D, |
0x00EE0054, 0x0061600B, 0x0021E7FF, 0x0183C507, 0x00E2009D, |
0x01A3E301, 0x00E2005A, 0x0183C900, 0x00E2005A, 0x00210001, |
0x00EE005F, 0x01A3E302, 0x00E20005, 0x0183C901, 0x00E20005, |
0x00210002, 0x0235D700, 0x0208A204, 0x000F8109, 0x000FC109, |
0x0071C918, 0x000F8407, 0x000FC407, 0x0001C022, 0x01A1A2BF, |
0x0001C106, 0x00088007, 0x02269A40, 0x00094004, 0x00621112, |
0x01A3C801, 0x00E4007F, 0x00621104, 0x0180C908, 0x00E4008D, |
0x00621128, 0x0183C908, 0x00E2006C, 0x01A3C901, 0x00E2007B, |
0x0021E7FF, 0x0183E007, 0x00E2007F, 0x00EE006C, 0x0061600B, |
0x0021E7FF, 0x0183C507, 0x00E4006C, 0x00621111, 0x01A3C801, |
0x00E2007F, 0x00621110, 0x01A3C801, 0x00E20082, 0x0228A220, |
0x00621119, 0x01A3C801, 0x00E20086, 0x0001C022, 0x01B1A220, |
0x0001C106, 0x00088007, 0x0198A204, 0x00294000, 0x01A69ABF, |
0x002F8000, 0x002FC000, 0x0183C903, 0x00E20005, 0x0228A220, |
0x0071890C, 0x0208A206, 0x0198A206, 0x0001C022, 0x01B1A220, |
0x0001C106, 0x00088007, 0x00EE009D, 0x00621128, 0x0183C908, |
0x00E20005, 0x00621104, 0x0183C908, 0x00E200A6, 0x0062111C, |
0x0183C908, 0x00E20005, 0x0071890C, 0x0208A206, 0x0198A206, |
0x00718908, 0x0208A206, 0x00EE0005, ~0 |
}; |
|
/* Bi-Modal RAID-0/1 */ |
static const unsigned int raid[] = { |
0x00F20145, 0x00EE20FA, 0x00EE20A7, 0x0001C009, 0x00EE0004, |
0x00220000, 0x0001000B, 0x037003FF, 0x00700018, 0x037003FE, |
0x037043FD, 0x00704118, 0x037043FC, 0x01A3D240, 0x00E20017, |
0x00B3C235, 0x00E40018, 0x0093C104, 0x00E80014, 0x0093C004, |
0x00E80017, 0x01020000, 0x00274020, 0x00EE0083, 0x0080C904, |
0x0093C104, 0x00EA0020, 0x0093C103, 0x00EC001F, 0x00220002, |
0x00924104, 0x0005C009, 0x00EE0058, 0x0093CF04, 0x00E80026, |
0x00900F01, 0x00600001, 0x00910400, 0x00EE0058, 0x00601604, |
0x01A00003, 0x00E2002C, 0x01018000, 0x00274040, 0x00EE0083, |
0x0093CF03, 0x00EC0031, 0x00220003, 0x00924F04, 0x0005C009, |
0x00810104, 0x00B3C235, 0x00E20037, 0x0022C000, 0x00218210, |
0x00EE0039, 0x0022C001, 0x00218200, 0x00600401, 0x00A04901, |
0x00604101, 0x01A0C401, 0x00E20040, 0x00216202, 0x00EE0041, |
0x00216101, 0x02018506, 0x00EE2141, 0x00904901, 0x00E20049, |
0x00A00401, 0x00600001, 0x02E0C301, 0x00EE2141, 0x00216303, |
0x037003EE, 0x01A3C001, 0x00E40105, 0x00250080, 0x00204000, |
0x002042F1, 0x0004C001, 0x00230001, 0x00100006, 0x02C18605, |
0x00100006, 0x01A3D502, 0x00E20055, 0x00EE0053, 0x00004009, |
0x00000004, 0x00B3C235, 0x00E40062, 0x0022C001, 0x0020C000, |
0x00EE2141, 0x0020C001, 0x00EE2141, 0x00EE006B, 0x0022C000, |
0x0060D207, 0x00EE2141, 0x00B3C242, 0x00E20069, 0x01A3D601, |
0x00E2006E, 0x02E0C301, 0x00EE2141, 0x00230001, 0x00301303, |
0x00EE007B, 0x00218210, 0x01A3C301, 0x00E20073, 0x00216202, |
0x00EE0074, 0x00216101, 0x02018506, 0x00214000, 0x037003EE, |
0x01A3C001, 0x00E40108, 0x00230001, 0x00100006, 0x00250080, |
0x00204000, 0x002042F1, 0x0004C001, 0x00EE007F, 0x0024C000, |
0x01A3D1F0, 0x00E20088, 0x00230001, 0x00300000, 0x01A3D202, |
0x00E20085, 0x00EE00A5, 0x00B3C800, 0x00E20096, 0x00218000, |
0x00924709, 0x0005C009, 0x00B20802, 0x00E40093, 0x037103FD, |
0x00710418, 0x037103FC, 0x00EE0006, 0x00220000, 0x0001000F, |
0x00EE0006, 0x00800B0C, 0x00B00001, 0x00204000, 0x00208550, |
0x00208440, 0x002083E0, 0x00208200, 0x00208100, 0x01008000, |
0x037083EE, 0x02008212, 0x02008216, 0x01A3C201, 0x00E400A5, |
0x0100C000, 0x00EE20FA, 0x02800000, 0x00208000, 0x00B24C00, |
0x00E400AD, 0x00224001, 0x00724910, 0x0005C009, 0x00B3CDC4, |
0x00E200D5, 0x00B3CD29, 0x00E200D5, 0x00B3CD20, 0x00E200D5, |
0x00B3CD24, 0x00E200D5, 0x00B3CDC5, 0x00E200D2, 0x00B3CD39, |
0x00E200D2, 0x00B3CD30, 0x00E200D2, 0x00B3CD34, 0x00E200D2, |
0x00B3CDCA, 0x00E200CF, 0x00B3CD35, 0x00E200CF, 0x00B3CDC8, |
0x00E200CC, 0x00B3CD25, 0x00E200CC, 0x00B3CD40, 0x00E200CB, |
0x00B3CD42, 0x00E200CB, 0x01018000, 0x00EE0083, 0x0025C000, |
0x036083EE, 0x0000800D, 0x00EE00D8, 0x036083EE, 0x00208035, |
0x00EE00DA, 0x036083EE, 0x00208035, 0x00EE00DA, 0x00208007, |
0x036083EE, 0x00208025, 0x036083EF, 0x02400000, 0x01A3D208, |
0x00E200D8, 0x0067120A, 0x0021C000, 0x0021C224, 0x00220000, |
0x00404B1C, 0x00600105, 0x00800007, 0x0020C00E, 0x00214000, |
0x01004000, 0x01A0411F, 0x00404E01, 0x01A3C101, 0x00E200F1, |
0x00B20800, 0x00E400D8, 0x00220001, 0x0080490B, 0x00B04101, |
0x0040411C, 0x00EE00E1, 0x02269A01, 0x01020000, 0x02275D80, |
0x01A3D202, 0x00E200F4, 0x01B75D80, 0x01030000, 0x01B69A01, |
0x00EE00D8, 0x01A3D204, 0x00E40104, 0x00224000, 0x0020C00E, |
0x0020001E, 0x00214000, 0x01004000, 0x0212490E, 0x00214001, |
0x01004000, 0x02400000, 0x00B3D702, 0x00E80112, 0x00EE010E, |
0x00B3D702, 0x00E80112, 0x00B3D702, 0x00E4010E, 0x00230001, |
0x00EE0140, 0x00200005, 0x036003EE, 0x00204001, 0x00EE0116, |
0x00230001, 0x00100006, 0x02C18605, 0x00100006, 0x01A3D1F0, |
0x00E40083, 0x037003EE, 0x01A3C002, 0x00E20121, 0x0020A300, |
0x0183D102, 0x00E20124, 0x037003EE, 0x01A00005, 0x036003EE, |
0x01A0910F, 0x00B3C20F, 0x00E2012F, 0x01A3D502, 0x00E20116, |
0x01A3C002, 0x00E20116, 0x00B3D702, 0x00E4012C, 0x00300000, |
0x00EE011F, 0x02C18605, 0x00100006, 0x00EE0116, 0x01A3D1F0, |
0x00E40083, 0x037003EE, 0x01A3C004, 0x00E20088, 0x00200003, |
0x036003EE, 0x01A3D502, 0x00E20136, 0x00230001, 0x00B3C101, |
0x00E4012C, 0x00100006, 0x02C18605, 0x00100006, 0x00204000, |
0x00EE0116, 0x00100006, 0x01A3D1F0, 0x00E40083, 0x01000000, |
0x02400000, ~0 |
}; |
|
DPRINTK("ENTER: mode:%d, force:%d\n", mode, force); |
|
if (force) |
previous_mode = UNKNOWN_MODE; |
else |
previous_mode = host_priv->current_ucode; |
|
if (mode == previous_mode) |
return; |
|
host_priv->current_ucode = mode; |
|
/* decide what needs to be done using the STD in my logbook */ |
switch (previous_mode) { |
case OXNASSATA_RAID1: |
switch (mode) { |
case OXNASSATA_RAID0: |
changeparameters = 1; |
break; |
case OXNASSATA_NOTRAID: |
changeparameters = 1; |
progmicrocode = 1; |
break; |
} |
break; |
case OXNASSATA_RAID0: |
switch (mode) { |
case OXNASSATA_RAID1: |
changeparameters = 1; |
break; |
case OXNASSATA_NOTRAID: |
changeparameters = 1; |
progmicrocode = 1; |
break; |
} |
break; |
case OXNASSATA_NOTRAID: |
switch (mode) { |
case OXNASSATA_RAID0: |
case OXNASSATA_RAID1: |
changeparameters = 1; |
progmicrocode = 1; |
break; |
} |
break; |
case UNKNOWN_MODE: |
changeparameters = 1; |
progmicrocode = 1; |
break; |
} |
|
/* no need to reprogram everything if already in the right mode */ |
if (progmicrocode) { |
/* reset micro-code processor */ |
iowrite32(1, core_base + PROC_RESET); |
wmb(); |
|
/* select micro-code */ |
switch (mode) { |
case OXNASSATA_RAID1: |
case OXNASSATA_RAID0: |
VPRINTK("Loading RAID micro-code\n"); |
src = (unsigned int *)&raid[1]; |
break; |
case OXNASSATA_NOTRAID: |
VPRINTK("Loading JBOD micro-code\n"); |
src = (unsigned int *)&jbod[1]; |
break; |
default: |
BUG(); |
break; |
} |
|
/* load micro code */ |
dst = core_base + UCODE_STORE; |
while (*src != ~0) { |
iowrite32(*src, dst); |
src++; |
dst += sizeof(*src); |
} |
wmb(); |
} |
|
if (changeparameters) { |
u32 reg; |
/* set other mode dependent flags */ |
switch (mode) { |
case OXNASSATA_RAID1: |
/* clear JBOD mode */ |
reg = ioread32(core_base + DATA_PLANE_CTRL); |
reg |= DPC_JBOD_UCODE; |
reg &= ~DPC_FIS_SWCH; |
iowrite32(reg, core_base + DATA_PLANE_CTRL); |
wmb(); |
|
/* set the hardware up for RAID-1 */ |
iowrite32(0, core_base + RAID_WP_BOT_LOW); |
iowrite32(0, core_base + RAID_WP_BOT_HIGH); |
iowrite32(0xffffffff, core_base + RAID_WP_TOP_LOW); |
iowrite32(0x7fffffff, core_base + RAID_WP_TOP_HIGH); |
iowrite32(0, core_base + RAID_SIZE_LOW); |
iowrite32(0, core_base + RAID_SIZE_HIGH); |
wmb(); |
break; |
case OXNASSATA_RAID0: |
/* clear JBOD mode */ |
reg = ioread32(core_base + DATA_PLANE_CTRL); |
reg |= DPC_JBOD_UCODE; |
reg &= ~DPC_FIS_SWCH; |
iowrite32(reg, core_base + DATA_PLANE_CTRL); |
wmb(); |
|
/* set the hardware up for RAID-1 */ |
iowrite32(0, core_base + RAID_WP_BOT_LOW); |
iowrite32(0, core_base + RAID_WP_BOT_HIGH); |
iowrite32(0xffffffff, core_base + RAID_WP_TOP_LOW); |
iowrite32(0x7fffffff, core_base + RAID_WP_TOP_HIGH); |
iowrite32(0xffffffff, core_base + RAID_SIZE_LOW); |
iowrite32(0x7fffffff, core_base + RAID_SIZE_HIGH); |
wmb(); |
break; |
case OXNASSATA_NOTRAID: |
/* enable jbod mode */ |
reg = ioread32(core_base + DATA_PLANE_CTRL); |
reg &= ~DPC_JBOD_UCODE; |
reg &= ~DPC_FIS_SWCH; |
iowrite32(reg, core_base + DATA_PLANE_CTRL); |
wmb(); |
|
/* start micro-code processor*/ |
iowrite32(1, core_base + PROC_START); |
break; |
default: |
reg = ioread32(core_base + DATA_PLANE_CTRL); |
reg |= DPC_JBOD_UCODE; |
reg &= ~DPC_FIS_SWCH; |
iowrite32(reg, core_base + DATA_PLANE_CTRL); |
wmb(); |
break; |
} |
} |
} |
|
/** |
* sends a sync-escape if there is a link present |
*/ |
static inline void sata_oxnas_send_sync_escape(struct ata_port *ap) |
{ |
struct sata_oxnas_port_priv *pd = ap->private_data; |
u32 reg; |
|
/* read the SSTATUS register and only send a sync escape if there is a |
* link active */ |
if ((sata_oxnas_link_read(ap, 0x20) & 3) == 3) { |
reg = ioread32(pd->port_base + SATA_COMMAND); |
reg &= ~SATA_OPCODE_MASK; |
reg |= CMD_SYNC_ESCAPE; |
iowrite32(reg, pd->port_base + SATA_COMMAND); |
} |
} |
|
/* clears errors */ |
static inline void sata_oxnas_clear_CS_error(struct ata_port *ap) |
{ |
struct sata_oxnas_port_priv *pd = ap->private_data; |
u32 *base = pd->port_base; |
u32 reg; |
|
reg = ioread32(base + SATA_CONTROL); |
reg &= SATA_CTL_ERR_MASK; |
iowrite32(reg, base + SATA_CONTROL); |
} |
|
static inline void sata_oxnas_reset_sgdma(struct ata_port *ap) |
{ |
struct sata_oxnas_port_priv *pd = ap->private_data; |
|
iowrite32(SGDMA_RESETS_CTRL, pd->sgdma_base + SGDMA_RESETS); |
} |
|
static inline void sata_oxnas_reset_dma(struct ata_port *ap, int assert) |
{ |
struct sata_oxnas_port_priv *pd = ap->private_data; |
u32 reg; |
|
reg = ioread32(pd->dmactl_base + DMA_CONTROL); |
if (assert) |
reg |= DMA_CONTROL_RESET; |
else |
reg &= ~DMA_CONTROL_RESET; |
|
iowrite32(reg, pd->dmactl_base + DMA_CONTROL); |
}; |
|
/** |
* Clears the error caused by the core's registers being accessed when the |
* core is busy. |
*/ |
static inline void sata_oxnas_clear_reg_access_error(struct ata_port *ap) |
{ |
struct sata_oxnas_port_priv *pd = ap->private_data; |
u32 *base = pd->port_base; |
u32 reg; |
|
reg = ioread32(base + INT_STATUS); |
|
DPRINTK("ENTER\n"); |
if (reg & INT_REG_ACCESS_ERR) { |
DPRINTK("clearing register access error on port %d\n", |
ap->port_no); |
iowrite32(INT_REG_ACCESS_ERR, base + INT_STATUS); |
} |
reg = ioread32(base + INT_STATUS); |
if (reg & INT_REG_ACCESS_ERR) |
DPRINTK("register access error didn't clear\n"); |
} |
|
static inline void sata_oxnas_clear_sctl_error(struct ata_port *ap) |
{ |
struct sata_oxnas_port_priv *pd = ap->private_data; |
u32 *base = pd->port_base; |
u32 reg; |
|
reg = ioread32(base + SATA_CONTROL); |
reg |= SCTL_CLR_ERR; |
iowrite32(reg, base + SATA_CONTROL); |
} |
|
static inline void sata_oxnas_clear_raid_error(struct ata_host *ah) |
{ |
return; |
}; |
|
/** |
* Clean up all the state machines in the sata core. |
* @return post cleanup action required |
*/ |
static int sata_oxnas_cleanup(struct ata_host *ah) |
{ |
struct sata_oxnas_host_priv *hd = ah->private_data; |
int actions_required = 0; |
int n; |
|
printk(KERN_INFO "sata_oxnas: resetting SATA core\n"); |
/* core not recovering, reset it */ |
mdelay(5); |
sata_oxnas_reset_core(ah); |
mdelay(5); |
actions_required |= OXNAS_SATA_REINIT; |
/* Perform any SATA core re-initialisation after reset post reset init |
* needs to be called for both ports as there's one reset for both |
* ports */ |
for (n = 0; n < hd->n_ports; n++) |
sata_oxnas_post_reset_init(ah->ports[n]); |
|
|
return actions_required; |
} |
|
/** |
* ata_qc_new - Request an available ATA command, for queueing |
* @ap: Port associated with device @dev |
* @return non zero will refuse a new command, zero will may grant on subject |
* to conditions elsewhere. |
* |
*/ |
static int sata_oxnas_qc_new(struct ata_port *ap) |
{ |
struct sata_oxnas_host_priv *hd = ap->host->private_data; |
|
DPRINTK("port %d\n", ap->port_no); |
smp_rmb(); |
if (hd->port_frozen || hd->port_in_eh) |
return 1; |
else |
return !sata_oxnas_acquire_hw(ap, 0, 0); |
} |
|
/** |
* releases the lock on the port the command used |
*/ |
static void sata_oxnas_qc_free(struct ata_queued_cmd *qc) |
{ |
DPRINTK("\n"); |
sata_oxnas_release_hw(qc->ap); |
} |
|
static void sata_oxnas_freeze(struct ata_port *ap) |
{ |
struct sata_oxnas_host_priv *hd = ap->host->private_data; |
|
DPRINTK("\n"); |
hd->port_frozen |= BIT(ap->port_no); |
smp_wmb(); |
} |
|
static void sata_oxnas_thaw(struct ata_port *ap) |
{ |
struct sata_oxnas_host_priv *hd = ap->host->private_data; |
|
DPRINTK("\n"); |
hd->port_frozen &= ~BIT(ap->port_no); |
smp_wmb(); |
} |
|
void sata_oxnas_freeze_host(struct ata_port *ap) |
{ |
struct sata_oxnas_host_priv *hd = ap->host->private_data; |
|
DPRINTK("ENTER\n"); |
hd->port_in_eh |= BIT(ap->port_no); |
smp_wmb(); |
} |
|
void sata_oxnas_thaw_host(struct ata_port *ap) |
{ |
struct sata_oxnas_host_priv *hd = ap->host->private_data; |
|
DPRINTK("ENTER\n"); |
hd->port_in_eh &= ~BIT(ap->port_no); |
smp_wmb(); |
} |
|
static void sata_oxnas_post_internal_cmd(struct ata_queued_cmd *qc) |
{ |
DPRINTK("ENTER\n"); |
/* If the core is busy here, make it idle */ |
if (qc->flags & ATA_QCFLAG_FAILED) |
sata_oxnas_cleanup(qc->ap->host); |
} |
|
|
/** |
* turn on the interrupts |
* |
* @param ap Hardware with the registers in |
*/ |
static void sata_oxnas_irq_on(struct ata_port *ap) |
{ |
struct sata_oxnas_port_priv *pd = ap->private_data; |
u32 mask = (COREINT_END << ap->port_no); |
|
/* Clear pending interrupts */ |
iowrite32(~0, pd->port_base + INT_CLEAR); |
iowrite32(mask, pd->core_base + CORE_INT_STATUS); |
wmb(); |
|
/* enable End of command interrupt */ |
iowrite32(INT_WANT, pd->port_base + INT_ENABLE); |
iowrite32(mask, pd->core_base + CORE_INT_ENABLE); |
} |
|
|
/** @return true if the port has a cable connected */ |
int sata_oxnas_check_link(struct ata_port *ap) |
{ |
int reg; |
|
sata_oxnas_scr_read_port(ap, SCR_STATUS, ®); |
/* Check for the cable present indicated by SCR status bit-0 set */ |
return reg & 0x1; |
} |
|
/** |
* ata_std_postreset - standard postreset callback |
* @link: the target ata_link |
* @classes: classes of attached devices |
* |
* This function is invoked after a successful reset. Note that |
* the device might have been reset more than once using |
* different reset methods before postreset is invoked. |
* |
* LOCKING: |
* Kernel thread context (may sleep) |
*/ |
static void sata_oxnas_postreset(struct ata_link *link, unsigned int *classes) |
{ |
struct ata_port *ap = link->ap; |
struct sata_oxnas_host_priv *hd = ap->host->private_data; |
|
unsigned int dev; |
|
DPRINTK("ENTER\n"); |
ata_std_postreset(link, classes); |
|
/* turn on phy error detection by removing the masks */ |
sata_oxnas_link_write(ap->host->ports[0], 0x0c, 0x30003); |
if (hd->n_ports > 1) |
sata_oxnas_link_write(ap->host->ports[1], 0x0c, 0x30003); |
|
/* bail out if no device is present */ |
if (classes[0] == ATA_DEV_NONE && classes[1] == ATA_DEV_NONE) { |
DPRINTK("EXIT, no device\n"); |
return; |
} |
|
/* go through all the devices and configure them */ |
for (dev = 0; dev < ATA_MAX_DEVICES; ++dev) { |
if (ap->link.device[dev].class == ATA_DEV_ATA) |
sata_oxnas_dev_config(&(ap->link.device[dev])); |
} |
|
DPRINTK("EXIT\n"); |
} |
|
/** |
* Called to read the hardware registers / DMA buffers, to |
* obtain the current set of taskfile register values. |
* @param ap hardware with the registers in |
* @param tf taskfile to read the registers into |
*/ |
static void sata_oxnas_tf_read(struct ata_port *ap, struct ata_taskfile *tf) |
{ |
struct sata_oxnas_port_priv *port_priv = ap->private_data; |
void __iomem *port_base = port_priv->port_base; |
/* read the orb registers */ |
u32 Orb1 = ioread32(port_base + ORB1); |
u32 Orb2 = ioread32(port_base + ORB2); |
u32 Orb3 = ioread32(port_base + ORB3); |
u32 Orb4 = ioread32(port_base + ORB4); |
|
/* read common 28/48 bit tf parameters */ |
tf->device = (Orb1 >> 24); |
tf->nsect = (Orb2 >> 0); |
tf->feature = (Orb2 >> 16); |
tf->command = sata_oxnas_check_status(ap); |
|
/* read 48 or 28 bit tf parameters */ |
if (tf->flags & ATA_TFLAG_LBA48) { |
tf->hob_nsect = (Orb2 >> 8); |
tf->lbal = (Orb3 >> 0); |
tf->lbam = (Orb3 >> 8); |
tf->lbah = (Orb3 >> 16); |
tf->hob_lbal = (Orb3 >> 24); |
tf->hob_lbam = (Orb4 >> 0); |
tf->hob_lbah = (Orb4 >> 8); |
/* feature ext and control are write only */ |
} else { |
/* read 28-bit lba */ |
tf->lbal = (Orb3 >> 0); |
tf->lbam = (Orb3 >> 8); |
tf->lbah = (Orb3 >> 16); |
} |
} |
|
/** |
* Read a result task-file from the sata core registers. |
*/ |
static bool sata_oxnas_qc_fill_rtf(struct ata_queued_cmd *qc) |
{ |
/* Read the most recently received FIS from the SATA core ORB registers |
and convert to an ATA taskfile */ |
sata_oxnas_tf_read(qc->ap, &qc->result_tf); |
return true; |
} |
|
/** |
* Reads the Status ATA shadow register from hardware. |
* |
* @return The status register |
*/ |
static u8 sata_oxnas_check_status(struct ata_port *ap) |
{ |
u32 Reg; |
u8 status; |
struct sata_oxnas_port_priv *port_priv = ap->private_data; |
void __iomem *port_base = port_priv->port_base; |
|
/* read byte 3 of Orb2 register */ |
status = ioread32(port_base + ORB2) >> 24; |
|
/* check for the drive going missing indicated by SCR status bits |
* 0-3 = 0 */ |
sata_oxnas_scr_read_port(ap, SCR_STATUS, &Reg); |
|
if (!(Reg & 0x1)) { |
status |= ATA_DF; |
status |= ATA_ERR; |
} |
|
return status; |
} |
|
static inline void sata_oxnas_reset_ucode(struct ata_host *ah, int force, |
int no_microcode) |
{ |
struct sata_oxnas_host_priv *hd = ah->private_data; |
|
DPRINTK("ENTER\n"); |
if (no_microcode) { |
u32 reg; |
|
sata_oxnas_set_mode(ah, UNKNOWN_MODE, force); |
reg = ioread32(hd->core_base + DEVICE_CONTROL); |
reg |= DEVICE_CONTROL_ATA_ERR_OVERRIDE; |
iowrite32(reg, hd->core_base + DEVICE_CONTROL); |
} else { |
/* JBOD uCode */ |
sata_oxnas_set_mode(ah, OXNASSATA_NOTRAID, force); |
/* Turn the work around off as it may have been left on by any |
* HW-RAID code that we've been working with */ |
iowrite32(0x0, hd->core_base + PORT_ERROR_MASK); |
} |
} |
|
/** |
* Prepare as much as possible for a command without involving anything that is |
* shared between ports. |
*/ |
static void sata_oxnas_qc_prep(struct ata_queued_cmd *qc) |
{ |
struct sata_oxnas_port_priv *pd; |
int port_no = qc->ap->port_no; |
|
/* if the port's not connected, complete now with an error */ |
if (!sata_oxnas_check_link(qc->ap)) { |
ata_port_err(qc->ap, |
"port %d not connected completing with error\n", |
port_no); |
qc->err_mask |= AC_ERR_ATA_BUS; |
ata_qc_complete(qc); |
} |
|
sata_oxnas_reset_ucode(qc->ap->host, 0, 0); |
|
/* both pio and dma commands use dma */ |
if (ata_is_dma(qc->tf.protocol) || ata_is_pio(qc->tf.protocol)) { |
|
/* program the scatterlist into the prd table */ |
ata_bmdma_qc_prep(qc); |
|
/* point the sgdma controller at the dma request structure */ |
pd = qc->ap->private_data; |
|
iowrite32(pd->sgdma_request_pa, |
pd->sgdma_base + SGDMA_REQUESTPTR); |
|
/* setup the request table */ |
if (port_no == 0) { |
pd->sgdma_request->control = |
(qc->dma_dir == DMA_FROM_DEVICE) ? |
SGDMA_REQCTL0IN : SGDMA_REQCTL0OUT; |
} else { |
pd->sgdma_request->control = |
(qc->dma_dir == DMA_FROM_DEVICE) ? |
SGDMA_REQCTL1IN : SGDMA_REQCTL1OUT; |
} |
pd->sgdma_request->qualifier = SGDMA_REQQUAL; |
pd->sgdma_request->src_pa = qc->ap->bmdma_prd_dma; |
pd->sgdma_request->dst_pa = qc->ap->bmdma_prd_dma; |
smp_wmb(); |
|
/* tell it to wait */ |
iowrite32(SGDMA_CONTROL_NOGO, pd->sgdma_base + SGDMA_CONTROL); |
} |
} |
|
static int sata_oxnas_port_start(struct ata_port *ap) |
{ |
struct sata_oxnas_host_priv *host_priv = ap->host->private_data; |
struct device *dev = ap->host->dev; |
struct sata_oxnas_port_priv *pp; |
void *mem; |
dma_addr_t mem_dma; |
|
DPRINTK("ENTER\n"); |
|
pp = kzalloc(sizeof(*pp), GFP_KERNEL); |
if (!pp) |
return -ENOMEM; |
|
pp->port_base = host_priv->port_base + |
(ap->port_no ? PORT_SIZE : 0); |
pp->dmactl_base = host_priv->dmactl_base + |
(ap->port_no ? DMA_CORESIZE : 0); |
pp->sgdma_base = host_priv->sgdma_base + |
(ap->port_no ? SGDMA_CORESIZE : 0); |
pp->core_base = host_priv->core_base; |
|
/* preallocated */ |
if (host_priv->dma_size >= SATA_OXNAS_DMA_SIZE * host_priv->n_ports) { |
DPRINTK("using preallocated DMA\n"); |
mem_dma = host_priv->dma_base + |
(ap->port_no ? SATA_OXNAS_DMA_SIZE : 0); |
mem = ioremap(mem_dma, SATA_OXNAS_DMA_SIZE); |
} else { |
mem = dma_alloc_coherent(dev, SATA_OXNAS_DMA_SIZE, &mem_dma, |
GFP_KERNEL); |
} |
if (!mem) |
goto err_ret; |
|
pp->sgdma_request_pa = mem_dma; |
pp->sgdma_request = mem; |
|
ap->bmdma_prd_dma = mem_dma + sizeof(struct sgdma_request); |
ap->bmdma_prd = mem + sizeof(struct sgdma_request); |
|
ap->private_data = pp; |
|
sata_oxnas_post_reset_init(ap); |
|
return 0; |
|
err_ret: |
kfree(pp); |
return -ENOMEM; |
|
} |
|
static void sata_oxnas_port_stop(struct ata_port *ap) |
{ |
struct device *dev = ap->host->dev; |
struct sata_oxnas_port_priv *pp = ap->private_data; |
struct sata_oxnas_host_priv *host_priv = ap->host->private_data; |
|
DPRINTK("ENTER\n"); |
ap->private_data = NULL; |
if (host_priv->dma_size) { |
iounmap(pp->sgdma_request); |
} else { |
dma_free_coherent(dev, SATA_OXNAS_DMA_SIZE, |
pp->sgdma_request, pp->sgdma_request_pa); |
} |
|
kfree(pp); |
} |
|
|
static void sata_oxnas_post_reset_init(struct ata_port *ap) |
{ |
uint dev; |
|
/* force to load u-code only once after reset */ |
sata_oxnas_reset_ucode(ap->host, !ap->port_no, 0); |
|
/* turn on phy error detection by removing the masks */ |
sata_oxnas_link_write(ap, 0x0C, 0x30003); |
|
/* enable hotplug event detection */ |
sata_oxnas_scr_write_port(ap, SCR_ERROR, ~0); |
sata_oxnas_scr_write_port(ap, SERROR_IRQ_MASK, 0x03feffff); |
sata_oxnas_scr_write_port(ap, SCR_ACTIVE, ~0 & ~(1 << 26) & ~(1 << 16)); |
|
/* enable interrupts for ports */ |
sata_oxnas_irq_on(ap); |
|
/* go through all the devices and configure them */ |
for (dev = 0; dev < ATA_MAX_DEVICES; ++dev) { |
if (ap->link.device[dev].class == ATA_DEV_ATA) { |
sata_std_hardreset(&ap->link, NULL, jiffies + HZ); |
sata_oxnas_dev_config(&(ap->link.device[dev])); |
} |
} |
|
/* clean up any remaining errors */ |
sata_oxnas_scr_write_port(ap, SCR_ERROR, ~0); |
VPRINTK("done\n"); |
} |
|
/** |
* host_stop() is called when the rmmod or hot unplug process begins. The |
* hook must stop all hardware interrupts, DMA engines, etc. |
* |
* @param ap hardware with the registers in |
*/ |
static void sata_oxnas_host_stop(struct ata_host *host_set) |
{ |
DPRINTK("\n"); |
} |
|
|
#define ERROR_HW_ACQUIRE_TIMEOUT_JIFFIES (10 * HZ) |
static void sata_oxnas_error_handler(struct ata_port *ap) |
{ |
DPRINTK("Enter port_no %d\n", ap->port_no); |
sata_oxnas_freeze_host(ap); |
|
/* If the core is busy here, make it idle */ |
sata_oxnas_cleanup(ap->host); |
|
ata_std_error_handler(ap); |
|
sata_oxnas_thaw_host(ap); |
} |
|
static int sata_oxnas_softreset(struct ata_link *link, unsigned int *class, |
unsigned long deadline) |
{ |
struct ata_port *ap = link->ap; |
struct sata_oxnas_port_priv *pd = ap->private_data; |
void __iomem *port_base = pd->port_base; |
int rc; |
|
struct ata_taskfile tf; |
u32 Command_Reg; |
|
DPRINTK("ENTER\n"); |
|
port_base = pd->port_base; |
|
if (ata_link_offline(link)) { |
DPRINTK("PHY reports no device\n"); |
*class = ATA_DEV_NONE; |
goto out; |
} |
|
/* write value to register */ |
iowrite32(0, port_base + ORB1); |
iowrite32(0, port_base + ORB2); |
iowrite32(0, port_base + ORB3); |
iowrite32((ap->ctl) << 24, port_base + ORB4); |
|
/* command the core to send a control FIS */ |
Command_Reg = ioread32(port_base + SATA_COMMAND); |
Command_Reg &= ~SATA_OPCODE_MASK; |
Command_Reg |= CMD_WRITE_TO_ORB_REGS_NO_COMMAND; |
iowrite32(Command_Reg, port_base + SATA_COMMAND); |
udelay(20); /* FIXME: flush */ |
|
/* write value to register */ |
iowrite32((ap->ctl | ATA_SRST) << 24, port_base + ORB4); |
|
/* command the core to send a control FIS */ |
Command_Reg &= ~SATA_OPCODE_MASK; |
Command_Reg |= CMD_WRITE_TO_ORB_REGS_NO_COMMAND; |
iowrite32(Command_Reg, port_base + SATA_COMMAND); |
udelay(20); /* FIXME: flush */ |
|
/* write value to register */ |
iowrite32((ap->ctl) << 24, port_base + ORB4); |
|
/* command the core to send a control FIS */ |
Command_Reg &= ~SATA_OPCODE_MASK; |
Command_Reg |= CMD_WRITE_TO_ORB_REGS_NO_COMMAND; |
iowrite32(Command_Reg, port_base + SATA_COMMAND); |
|
msleep(150); |
|
rc = ata_sff_wait_ready(link, deadline); |
|
/* if link is occupied, -ENODEV too is an error */ |
if (rc && (rc != -ENODEV || sata_scr_valid(link))) { |
ata_link_printk(link, KERN_ERR, "SRST failed (errno=%d)\n", rc); |
return rc; |
} |
|
/* determine by signature whether we have ATA or ATAPI devices */ |
sata_oxnas_tf_read(ap, &tf); |
*class = ata_dev_classify(&tf); |
|
if (*class == ATA_DEV_UNKNOWN) |
*class = ATA_DEV_NONE; |
|
out: |
DPRINTK("EXIT, class=%u\n", *class); |
return 0; |
} |
|
|
int sata_oxnas_init_controller(struct ata_host *host) |
{ |
return 0; |
} |
|
/** |
* 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. |
* |
* @param port SATA port to check and if necessary, correct. |
*/ |
static int sata_oxnas_bug_6320_detect(struct ata_port *ap) |
{ |
struct sata_oxnas_port_priv *pd = ap->private_data; |
void __iomem *core_base = pd->core_base; |
int is_read; |
int quads_transferred; |
int remainder; |
int sector_quads_remaining; |
int bug_present = 0; |
|
/* Only want to apply fix to reads */ |
is_read = !(ioread32(core_base + DM_DBG1) & (ap->port_no ? |
BIT(CORE_PORT1_DATA_DIR_BIT) : |
BIT(CORE_PORT0_DATA_DIR_BIT))); |
|
/* Check for an incomplete transfer, i.e. not a multiple of 512 bytes |
transferred (datacount_port register counts quads transferred) */ |
quads_transferred = |
ioread32(core_base + (ap->port_no ? |
DATACOUNT_PORT1 : DATACOUNT_PORT0)); |
|
remainder = quads_transferred & 0x7f; |
sector_quads_remaining = remainder ? (0x80 - remainder) : 0; |
|
if (is_read && (sector_quads_remaining == 2)) { |
bug_present = 1; |
} else if (sector_quads_remaining) { |
if (is_read) { |
ata_port_warn(ap, "SATA read fixup cannot deal with " |
"%d quads remaining\n", |
sector_quads_remaining); |
} else { |
ata_port_warn(ap, "SATA write fixup of %d quads " |
"remaining not supported\n", |
sector_quads_remaining); |
} |
} |
|
return bug_present; |
} |
|
/* This port done an interrupt */ |
static void sata_oxnas_port_irq(struct ata_port *ap, int force_error) |
{ |
struct ata_queued_cmd *qc; |
struct sata_oxnas_port_priv *pd = ap->private_data; |
void __iomem *port_base = pd->port_base; |
|
u32 int_status; |
unsigned long flags = 0; |
|
DPRINTK("ENTER port %d irqstatus %x\n", ap->port_no, |
ioread32(port_base + INT_STATUS)); |
|
if (ap->qc_active & (1 << ATA_TAG_INTERNAL)) { |
qc = ata_qc_from_tag(ap, ATA_TAG_INTERNAL); |
DPRINTK("completing non-ncq cmd\n"); |
|
if (qc) |
ata_qc_complete(qc); |
|
return; |
} |
|
qc = ata_qc_from_tag(ap, ap->link.active_tag); |
|
|
/* record the port's interrupt */ |
int_status = ioread32(port_base + INT_STATUS); |
|
/* If there's no command associated with this IRQ, ignore it. We may get |
* spurious interrupts when cleaning-up after a failed command, ignore |
* these too. */ |
if (likely(qc)) { |
/* get the status before any error cleanup */ |
qc->err_mask = ac_err_mask(sata_oxnas_check_status(ap)); |
if (force_error) { |
/* Pretend there has been a link error */ |
qc->err_mask |= AC_ERR_ATA_BUS; |
DPRINTK(" ####force error####\n"); |
} |
/* tell libata we're done */ |
local_irq_save(flags); |
sata_oxnas_irq_clear(ap); |
local_irq_restore(flags); |
ata_qc_complete(qc); |
} else { |
VPRINTK("Ignoring interrupt, can't find the command tag=" |
"%d %08x\n", ap->link.active_tag, ap->qc_active); |
} |
|
/* maybe a hotplug event */ |
if (unlikely(int_status & INT_LINK_SERROR)) { |
u32 serror; |
|
sata_oxnas_scr_read_port(ap, SCR_ERROR, &serror); |
if (serror & (SERR_DEV_XCHG | SERR_PHYRDY_CHG)) { |
ata_ehi_hotplugged(&ap->link.eh_info); |
ata_port_freeze(ap); |
} |
} |
} |
|
/** |
* irq_handler is the interrupt handling routine registered with the system, |
* by libata. |
*/ |
static irqreturn_t sata_oxnas_interrupt(int irq, void *dev_instance) |
{ |
struct ata_host *ah = dev_instance; |
struct sata_oxnas_host_priv *hd = ah->private_data; |
void __iomem *core_base = hd->core_base; |
|
u32 int_status; |
irqreturn_t ret = IRQ_NONE; |
u32 port_no; |
u32 mask; |
int bug_present; |
|
/* loop until there are no more interrupts */ |
while ((int_status = (ioread32(core_base + CORE_INT_STATUS)) & |
(COREINT_END | (COREINT_END << 1)))) { |
|
/* clear any interrupt */ |
iowrite32(int_status, core_base + CORE_INT_CLEAR); |
|
/* Only need workaround_bug_6320 for single disk systems as dual |
* disk will use uCode which prevents this read underrun problem |
* from occurring. |
* All single disk systems will use port 0 */ |
for (port_no = 0; port_no < hd->n_ports; ++port_no) { |
/* check the raw end of command interrupt to see if the |
* port is done */ |
mask = (COREINT_END << port_no); |
if (!(int_status & mask)) |
continue; |
|
/* this port had an interrupt, clear it */ |
iowrite32(mask, core_base + CORE_INT_CLEAR); |
/* check for bug 6320 only if no microcode was loaded */ |
bug_present = (hd->current_ucode == UNKNOWN_MODE) && |
sata_oxnas_bug_6320_detect(ah->ports[port_no]); |
|
sata_oxnas_port_irq(ah->ports[port_no], |
bug_present); |
ret = IRQ_HANDLED; |
} |
} |
|
return ret; |
} |
|
/* |
* scsi mid-layer and libata interface structures |
*/ |
static struct scsi_host_template sata_oxnas_sht = { |
ATA_NCQ_SHT("sata_oxnas"), |
.can_queue = SATA_OXNAS_QUEUE_DEPTH, |
.sg_tablesize = SATA_OXNAS_MAX_PRD, |
.dma_boundary = ATA_DMA_BOUNDARY, |
.unchecked_isa_dma = 0, |
}; |
|
|
static struct ata_port_operations sata_oxnas_ops = { |
.inherits = &sata_port_ops, |
.qc_prep = sata_oxnas_qc_prep, |
.qc_issue = sata_oxnas_qc_issue, |
.qc_fill_rtf = sata_oxnas_qc_fill_rtf, |
.qc_new = sata_oxnas_qc_new, |
.qc_free = sata_oxnas_qc_free, |
|
.scr_read = sata_oxnas_scr_read, |
.scr_write = sata_oxnas_scr_write, |
|
.freeze = sata_oxnas_freeze, |
.thaw = sata_oxnas_thaw, |
.softreset = sata_oxnas_softreset, |
/* .hardreset = sata_oxnas_hardreset, */ |
.postreset = sata_oxnas_postreset, |
.error_handler = sata_oxnas_error_handler, |
.post_internal_cmd = sata_oxnas_post_internal_cmd, |
|
.port_start = sata_oxnas_port_start, |
.port_stop = sata_oxnas_port_stop, |
|
.host_stop = sata_oxnas_host_stop, |
/* .pmp_attach = sata_oxnas_pmp_attach, */ |
/* .pmp_detach = sata_oxnas_pmp_detach, */ |
.sff_check_status = sata_oxnas_check_status, |
.acquire_hw = sata_oxnas_acquire_hw, |
}; |
|
static const struct ata_port_info sata_oxnas_port_info = { |
.flags = SATA_OXNAS_HOST_FLAGS, |
.pio_mask = ATA_PIO4, |
.udma_mask = ATA_UDMA6, |
.port_ops = &sata_oxnas_ops, |
}; |
|
static int sata_oxnas_probe(struct platform_device *ofdev) |
{ |
int retval = -ENXIO; |
int n_ports = 0; |
void __iomem *port_base = NULL; |
void __iomem *dmactl_base = NULL; |
void __iomem *sgdma_base = NULL; |
void __iomem *core_base = NULL; |
void __iomem *phy_base = NULL; |
struct reset_control *rstc; |
|
struct resource res = {}; |
struct sata_oxnas_host_priv *host_priv = NULL; |
int irq = 0; |
struct ata_host *host = NULL; |
struct clk *clk = NULL; |
|
const struct ata_port_info *ppi[] = { &sata_oxnas_port_info, NULL }; |
|
of_property_read_u32(ofdev->dev.of_node, "nr-ports", &n_ports); |
if (n_ports < 1 || n_ports > SATA_OXNAS_MAX_PORTS) |
goto error_exit_with_cleanup; |
|
port_base = of_iomap(ofdev->dev.of_node, 0); |
if (!port_base) |
goto error_exit_with_cleanup; |
|
dmactl_base = of_iomap(ofdev->dev.of_node, 1); |
if (!dmactl_base) |
goto error_exit_with_cleanup; |
|
sgdma_base = of_iomap(ofdev->dev.of_node, 2); |
if (!sgdma_base) |
goto error_exit_with_cleanup; |
|
core_base = of_iomap(ofdev->dev.of_node, 3); |
if (!core_base) |
goto error_exit_with_cleanup; |
|
phy_base = of_iomap(ofdev->dev.of_node, 4); |
if (!phy_base) |
goto error_exit_with_cleanup; |
|
host_priv = devm_kzalloc(&ofdev->dev, |
sizeof(struct sata_oxnas_host_priv), |
GFP_KERNEL); |
if (!host_priv) |
goto error_exit_with_cleanup; |
|
host_priv->port_base = port_base; |
host_priv->dmactl_base = dmactl_base; |
host_priv->sgdma_base = sgdma_base; |
host_priv->core_base = core_base; |
host_priv->phy_base = phy_base; |
host_priv->n_ports = n_ports; |
host_priv->current_ucode = UNKNOWN_MODE; |
|
if (!of_address_to_resource(ofdev->dev.of_node, 5, &res)) { |
host_priv->dma_base = res.start; |
host_priv->dma_size = resource_size(&res); |
} |
|
irq = irq_of_parse_and_map(ofdev->dev.of_node, 0); |
if (!irq) { |
dev_err(&ofdev->dev, "invalid irq from platform\n"); |
goto error_exit_with_cleanup; |
} |
host_priv->irq = irq; |
|
clk = of_clk_get(ofdev->dev.of_node, 0); |
if (IS_ERR(clk)) { |
retval = PTR_ERR(clk); |
clk = NULL; |
goto error_exit_with_cleanup; |
} |
host_priv->clk = clk; |
|
rstc = devm_reset_control_get(&ofdev->dev, "sata"); |
if (IS_ERR(rstc)) { |
retval = PTR_ERR(rstc); |
goto error_exit_with_cleanup; |
} |
host_priv->rst_sata = rstc; |
|
rstc = devm_reset_control_get(&ofdev->dev, "link"); |
if (IS_ERR(rstc)) { |
retval = PTR_ERR(rstc); |
goto error_exit_with_cleanup; |
} |
host_priv->rst_link = rstc; |
|
rstc = devm_reset_control_get(&ofdev->dev, "phy"); |
if (IS_ERR(rstc)) { |
retval = PTR_ERR(rstc); |
goto error_exit_with_cleanup; |
} |
host_priv->rst_phy = rstc; |
|
/* allocate host structure */ |
host = ata_host_alloc_pinfo(&ofdev->dev, ppi, n_ports); |
|
if (!host) { |
retval = -ENOMEM; |
goto error_exit_with_cleanup; |
} |
host->private_data = host_priv; |
host->iomap = port_base; |
|
/* initialize core locking and queues */ |
init_waitqueue_head(&host_priv->fast_wait_queue); |
init_waitqueue_head(&host_priv->scsi_wait_queue); |
spin_lock_init(&host_priv->phy_lock); |
spin_lock_init(&host_priv->core_lock); |
host_priv->core_locked = 0; |
host_priv->reentrant_port_no = -1; |
host_priv->hw_lock_count = 0; |
host_priv->direct_lock_count = 0; |
host_priv->locker_uid = 0; |
host_priv->current_locker_type = SATA_UNLOCKED; |
host_priv->isr_arg = NULL; |
host_priv->isr_callback = NULL; |
|
/* initialize host controller */ |
retval = sata_oxnas_init_controller(host); |
if (retval) |
goto error_exit_with_cleanup; |
|
/* |
* Now, register with libATA core, this will also initiate the |
* device discovery process, invoking our port_start() handler & |
* error_handler() to execute a dummy softreset EH session |
*/ |
ata_host_activate(host, irq, sata_oxnas_interrupt, SATA_OXNAS_IRQ_FLAG, |
&sata_oxnas_sht); |
|
return 0; |
|
error_exit_with_cleanup: |
if (irq) |
irq_dispose_mapping(host_priv->irq); |
if (clk) |
clk_put(clk); |
if (host) |
ata_host_detach(host); |
if (port_base) |
iounmap(port_base); |
if (sgdma_base) |
iounmap(sgdma_base); |
if (core_base) |
iounmap(core_base); |
if (phy_base) |
iounmap(phy_base); |
return retval; |
} |
|
|
static int sata_oxnas_remove(struct platform_device *ofdev) |
{ |
struct ata_host *host = dev_get_drvdata(&ofdev->dev); |
struct sata_oxnas_host_priv *host_priv = host->private_data; |
|
ata_host_detach(host); |
|
irq_dispose_mapping(host_priv->irq); |
iounmap(host_priv->port_base); |
iounmap(host_priv->sgdma_base); |
iounmap(host_priv->core_base); |
|
/* reset Controller, Link and PHY */ |
reset_control_assert(host_priv->rst_sata); |
reset_control_assert(host_priv->rst_link); |
reset_control_assert(host_priv->rst_phy); |
|
/* Disable the clock to the SATA block */ |
clk_disable_unprepare(host_priv->clk); |
clk_put(host_priv->clk); |
|
return 0; |
} |
|
#ifdef CONFIG_PM |
static int sata_oxnas_suspend(struct platform_device *op, pm_message_t state) |
{ |
struct ata_host *host = dev_get_drvdata(&op->dev); |
|
return ata_host_suspend(host, state); |
} |
|
static int sata_oxnas_resume(struct platform_device *op) |
{ |
struct ata_host *host = dev_get_drvdata(&op->dev); |
int ret; |
|
ret = sata_oxnas_init_controller(host); |
if (ret) { |
dev_err(&op->dev, "Error initializing hardware\n"); |
return ret; |
} |
ata_host_resume(host); |
return 0; |
} |
#endif |
|
|
|
static struct of_device_id oxnas_sata_match[] = { |
{ |
.compatible = "plxtech,nas782x-sata", |
}, |
{}, |
}; |
|
MODULE_DEVICE_TABLE(of, oxnas_sata_match); |
|
static struct platform_driver oxnas_sata_driver = { |
.driver = { |
.name = "oxnas-sata", |
.owner = THIS_MODULE, |
.of_match_table = oxnas_sata_match, |
}, |
.probe = sata_oxnas_probe, |
.remove = sata_oxnas_remove, |
#ifdef CONFIG_PM |
.suspend = sata_oxnas_suspend, |
.resume = sata_oxnas_resume, |
#endif |
}; |
|
module_platform_driver(oxnas_sata_driver); |
|
MODULE_LICENSE("GPL"); |
MODULE_VERSION("1.0"); |
MODULE_AUTHOR("Oxford Semiconductor Ltd."); |
MODULE_DESCRIPTION("low-level driver for Oxford 934 SATA core"); |