aboutsummaryrefslogtreecommitdiffstats
path: root/target
diff options
context:
space:
mode:
authorJohn Crispin <john@openwrt.org>2014-12-10 15:51:07 +0000
committerJohn Crispin <john@openwrt.org>2014-12-10 15:51:07 +0000
commit9432cbd577561f047ff13da7519a0ba3a93a252e (patch)
treeb8e2e7ed5430f9616b04e71f75d001b321afcf76 /target
parent1fa7c8587dda724dcd19294dc8932d790ec5acb3 (diff)
downloadupstream-9432cbd577561f047ff13da7519a0ba3a93a252e.tar.gz
upstream-9432cbd577561f047ff13da7519a0ba3a93a252e.tar.bz2
upstream-9432cbd577561f047ff13da7519a0ba3a93a252e.zip
oxnas: add support for 2nd S-ATA port to sata_oxnas driver
similar to mv_sata, use nr-ports attribute from device tree. import and adapt locking code from vendor GPL sources. add dma controller handling, it may be used in future to avoid full core resets similar to the vendor SDK's "progressive cleanup" function. this is still very dirty and aimed to first of all do things quite exactly like the reference code. and it somehow works. obviously there is lots of room for improvement :) Signed-off-by: Daniel Golle <daniel@makrotopia.org> SVN-Revision: 43598
Diffstat (limited to 'target')
-rw-r--r--target/linux/oxnas/files/arch/arm/boot/dts/ox820-kd20.dts1
-rw-r--r--target/linux/oxnas/files/arch/arm/boot/dts/ox820.dtsi9
-rw-r--r--target/linux/oxnas/files/drivers/ata/sata_oxnas.c643
-rw-r--r--target/linux/oxnas/patches-3.14/999-libata-hacks.patch60
-rw-r--r--target/linux/oxnas/patches-3.18/999-libata-hacks.patch60
5 files changed, 686 insertions, 87 deletions
diff --git a/target/linux/oxnas/files/arch/arm/boot/dts/ox820-kd20.dts b/target/linux/oxnas/files/arch/arm/boot/dts/ox820-kd20.dts
index 9f52f43f80..2db15ea5dc 100644
--- a/target/linux/oxnas/files/arch/arm/boot/dts/ox820-kd20.dts
+++ b/target/linux/oxnas/files/arch/arm/boot/dts/ox820-kd20.dts
@@ -26,6 +26,7 @@
sata@45900000 {
status = "okay";
+ nr-ports = <2>;
};
nand@41000000 {
diff --git a/target/linux/oxnas/files/arch/arm/boot/dts/ox820.dtsi b/target/linux/oxnas/files/arch/arm/boot/dts/ox820.dtsi
index 77efd43157..3e1d9f38b4 100644
--- a/target/linux/oxnas/files/arch/arm/boot/dts/ox820.dtsi
+++ b/target/linux/oxnas/files/arch/arm/boot/dts/ox820.dtsi
@@ -286,14 +286,15 @@
sata@45900000 {
compatible = "plxtech,nas782x-sata";
- /* port sgdma core */
- reg = <0x45900000 0x100>, <0x459B0000 0x10>, <0x459E0000 0x2000>,
- /* phy descriptors (optional) */
- <0x44900000 0x0C>, <0x50000000 0x1000>;
+ /* ports dmactl sgdma */
+ reg = <0x45900000 0x20000>, <0x459A0000 0x40>, <0x459B0000 0x20>,
+ /* core phy descriptors (optional) */
+ <0x459E0000 0x2000>, <0x44900000 0x0C>, <0x50000000 0x1000>;
interrupts = <0 18 0x304>;
clocks = <&stdclk 4>;
resets = <&rst 11>, <&rst 12>, <&rst 13>;
reset-names = "sata", "link", "phy";
+ nr-ports = <1>;
status = "disabled";
};
diff --git a/target/linux/oxnas/files/drivers/ata/sata_oxnas.c b/target/linux/oxnas/files/drivers/ata/sata_oxnas.c
index 743d907cff..f8b53f1ef6 100644
--- a/target/linux/oxnas/files/drivers/ata/sata_oxnas.c
+++ b/target/linux/oxnas/files/drivers/ata/sata_oxnas.c
@@ -23,6 +23,7 @@
#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>
@@ -45,7 +46,7 @@ enum {
SATA_OXNAS_DMA_SIZE = SATA_OXNAS_MAX_PRD *
sizeof(struct ata_bmdma_prd) +
sizeof(struct sgdma_request),
- SATA_OXNAS_MAX_PORTS = 1,
+ SATA_OXNAS_MAX_PORTS = 2,
/** The different Oxsemi SATA core version numbers */
SATA_OXNAS_CORE_VERSION = 0x1f3,
SATA_OXNAS_IRQ_FLAG = IRQF_SHARED,
@@ -92,7 +93,6 @@ enum {
WIN2LO = 0x88,
WIN2HI = 0x8C,
WIN0_CONTROL = 0x90,
-
};
/** sata port register bits */
@@ -160,6 +160,14 @@ enum {
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) */
@@ -218,6 +226,7 @@ enum {
RAID_WP_TOP_HIGH = 0x1FFC,
DATA_MUX_RAM0 = 0x8000,
DATA_MUX_RAM1 = 0xA000,
+ PORT_SIZE = 0x10000,
};
enum {
@@ -271,6 +280,8 @@ enum {
/** OX820 specific HW-RAID register values */
RAID_TWODISKS = 3,
UNKNOWN_MODE = ~0,
+
+ CONFIG_IN_RESUME = 2,
};
/* SATA PHY Registers */
@@ -309,21 +320,34 @@ enum {
};
enum {
- OXNAS_SATA_UCODE_RAID0,
- OXNAS_SATA_UCODE_RAID1,
- OXNAS_SATA_UCODE_JBOD,
- OXNAS_SATA_UCODE_NONE,
+ 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
};
struct sata_oxnas_host_priv {
- void __iomem *port_base[SATA_OXNAS_MAX_PORTS];
- void __iomem *sgdma_base[SATA_OXNAS_MAX_PORTS];
+ 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;
@@ -331,9 +355,13 @@ struct sata_oxnas_host_priv {
struct reset_control *rst_phy;
};
+typedef irqreturn_t (*ox820sata_isr_callback_t)(int, unsigned long, int);
+static DEFINE_SPINLOCK(oxsphy_lock);
+static DEFINE_SPINLOCK(oxsacs_lock);
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;
@@ -347,7 +375,26 @@ static void sata_oxnas_tf_load(struct ata_port *ap,
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(int port_no, int may_sleep,
+ int timeout_jiffies);
+static void sata_oxnas_release_hw(unsigned int port_no);
+
+static int core_locked = 0;
+static int reentrant_port_no = -1;
+static int hw_lock_count = 0;
+static int direct_lock_count = 0;
+static void *locker_uid = 0;
+static int current_locker_type = SATA_UNLOCKED;
+static const void *HW_LOCKER_UID = (void*)0xdeadbeef;
+static DECLARE_WAIT_QUEUE_HEAD(fast_wait_queue);
+static DECLARE_WAIT_QUEUE_HEAD(scsi_wait_queue);
+static ox820sata_isr_callback_t ox820sata_isr_callback = NULL;
+static unsigned long ox820sata_isr_arg = 0;
+static int scsi_nonblocking_attempts = 0;
+
+/***************************************************************************
+* ASIC access
+***************************************************************************/
static void wait_cr_ack(void __iomem *phy_base)
{
while ((ioread32(phy_base + PHY_STAT) >> 16) & 0x1f)
@@ -407,9 +454,11 @@ void sata_oxnas_link_write(struct ata_port *ap, unsigned int link_reg, u32 val)
struct sata_oxnas_port_priv *port_priv = ap->private_data;
void __iomem *port_base = port_priv->port_base;
u32 patience;
+ unsigned long flags;
- DPRINTK("[0x%02x]->0x%08x\n", link_reg, val);
+ DPRINTK("P%d [0x%02x]->0x%08x\n", ap->port_no, link_reg, val);
+ spin_lock_irqsave(&oxsphy_lock, flags);
iowrite32(val, port_base + LINK_DATA);
/* accessed twice as a work around for a bug in the SATA abp bridge
@@ -421,6 +470,7 @@ void sata_oxnas_link_write(struct ata_port *ap, unsigned int link_reg, u32 val)
if (ioread32(port_base + LINK_CONTROL) & 0x00000001)
break;
}
+ spin_unlock_irqrestore(&oxsphy_lock, flags);
}
static int sata_oxnas_scr_write_port(struct ata_port *ap, unsigned int sc_reg,
@@ -436,14 +486,15 @@ static int sata_oxnas_scr_write(struct ata_link *link, unsigned int sc_reg,
return sata_oxnas_scr_write_port(link->ap, sc_reg, val);
}
-/* FIXME lock */
u32 sata_oxnas_link_read(struct ata_port *ap, unsigned int link_reg)
{
struct sata_oxnas_port_priv *pd = ap->private_data;
void __iomem *port_base = pd->port_base;
u32 result;
u32 patience;
+ unsigned long flags;
+ spin_lock_irqsave(&oxsphy_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);
@@ -454,9 +505,10 @@ u32 sata_oxnas_link_read(struct ata_port *ap, unsigned int link_reg)
break;
}
if (patience == 0)
- DPRINTK("link read timed out\n");
+ DPRINTK("link read timed out for port %d\n", ap->port_no);
- result = readl(port_base + LINK_DATA);
+ result = ioread32(port_base + LINK_DATA);
+ spin_unlock_irqrestore(&oxsphy_lock, flags);
return result;
}
@@ -471,7 +523,6 @@ static int sata_oxnas_scr_read_port(struct ata_port *ap, unsigned int sc_reg,
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);
}
@@ -502,9 +553,12 @@ static void sata_oxnas_irq_clear(struct ata_port *ap)
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 */
@@ -525,8 +579,10 @@ static unsigned int sata_oxnas_qc_issue(struct ata_queued_cmd *qc)
}
/* enable passing of error signals to DMA sub-core by clearing the
- * appropriate bit (all transfers are on dma channel 0) */
+ * 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);
@@ -575,19 +631,295 @@ void sata_oxnas_checkforhotplug(struct ata_port *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(
+ int port_no,
+ ox820sata_isr_callback_t callback,
+ unsigned long 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;
+ DEFINE_WAIT(wait);
+
+ spin_lock_irqsave(&oxsacs_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, ox820sata_isr_callback %p\n",
+ uid, port_no, hw_lock_count, direct_lock_count, callback, hw_access,
+ core_locked, reentrant_port_no, ox820sata_isr_callback);
+
+ while (!timed_out) {
+ if (core_locked || (!hw_access && 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 == reentrant_port_no)) {
+ BUG_ON(!hw_lock_count);
+ ++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) && (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, locker_uid,
+ port_no, hw_lock_count, 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 == 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, current_locker_type,
+ locker_uid, port_no, hw_lock_count, direct_lock_count, hw_access);
+ }
+ } else {
+ WARN(hw_lock_count || 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, locker_uid, port_no,
+ hw_lock_count, direct_lock_count, hw_access);
+
+ BUG_ON(current_locker_type != SATA_UNLOCKED);
+
+ WARN(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, locker_uid, port_no, hw_lock_count, direct_lock_count,
+ hw_access);
+
+ if (!hw_access) {
+ /* Direct access attempting to acquire non-contented lock */
+ BUG_ON(!callback); // Must have callback for direct access
+ BUG_ON(reentrant_port_no != -1); // Sanity check lock state
+
+ ox820sata_isr_callback = callback;
+ ox820sata_isr_arg = arg;
+ ++direct_lock_count;
+
+ current_locker_type = locker_type;
+ } else {
+ /* SCSI/SATA attempting to acquire non-contented lock */
+ BUG_ON(callback); // No callbacks for SCSI/SATA access
+ BUG_ON(arg); // No callback args for SCSI/SATA access
+
+ BUG_ON(ox820sata_isr_callback); // Sanity check lock state
+ BUG_ON(ox820sata_isr_arg); // Sanity check lock state
+
+ ++hw_lock_count;
+ reentrant_port_no = port_no;
+
+ current_locker_type = SATA_SCSI_STACK;
+ }
+
+ core_locked = 1;
+ acquired = 1;
+ locker_uid = uid;
+ 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, current_locker_type);
+
+ if (hw_access) {
+ ++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 ? &scsi_wait_queue : &fast_wait_queue,
+ &wait, TASK_UNINTERRUPTIBLE);
+ if (!core_locked && !(!hw_access && 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("__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, ox820sata_isr_callback %p, "
+ "ox820sata_isr_arg %p\n", uid, port_no, locker_uid,
+ hw_lock_count, direct_lock_count, callback, hw_access,
+ core_locked, reentrant_port_no, ox820sata_isr_callback,
+ (void*)ox820sata_isr_arg);
+ timed_out = 1;
+ break;
+ }
+ spin_unlock_irqrestore(&oxsacs_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, locker_uid);
+ }
+ spin_lock_irqsave(&oxsacs_lock, flags);
+ }
+ finish_wait(hw_access ? &scsi_wait_queue : &fast_wait_queue, &wait);
+ }
+
+ if (hw_access && acquired) {
+ if (scsi_nonblocking_attempts) {
+ 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(&scsi_wait_queue.task_list)) {
+ wake_up(&scsi_wait_queue);
+ }
+ }
+
+ DPRINTK("Leaving uid %p with acquired = %d, port %d, callback %p\n", uid, acquired, port_no, callback);
+
+ spin_unlock_irqrestore(&oxsacs_lock, flags);
+
+ return acquired;
+}
+
+int sata_core_has_fast_waiters(void)
+{
+ int has_waiters;
+ unsigned long flags;
+
+ spin_lock_irqsave(&oxsacs_lock, flags);
+ has_waiters = !list_empty(&fast_wait_queue.task_list);
+ spin_unlock_irqrestore(&oxsacs_lock, flags);
+
+ return has_waiters;
+}
+EXPORT_SYMBOL(sata_core_has_fast_waiters);
+
+int sata_core_has_scsi_waiters(void)
+{
+ int has_waiters;
+ unsigned long flags;
+
+ spin_lock_irqsave(&oxsacs_lock, flags);
+ has_waiters = scsi_nonblocking_attempts || !list_empty(&scsi_wait_queue.task_list);
+ spin_unlock_irqrestore(&oxsacs_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(
+ int port_no,
+ int may_sleep,
+ int timeout_jiffies)
+{
+ return __acquire_sata_core(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(unsigned int port_no)
+{
+ unsigned long flags;
+ int released = 0;
+
+ spin_lock_irqsave(&oxsacs_lock, flags);
+
+ DPRINTK("Entered port_no = %d, h/w count %d, d count %d, core locked = %d, "
+ "reentrant_port_no = %d, ox820sata_isr_callback %p\n", port_no,
+ hw_lock_count, direct_lock_count, core_locked, reentrant_port_no, ox820sata_isr_callback);
+
+ if (!core_locked) {
+ /* Nobody holds the SATA lock */
+ printk(KERN_WARNING "Nobody holds SATA lock, port_no %d\n", port_no);
+ released = 1;
+ } else if (!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", port_no);
+ } else {
+ /* Trap incorrect usage */
+ BUG_ON(reentrant_port_no == -1);
+ BUG_ON(port_no != reentrant_port_no);
+ BUG_ON(direct_lock_count);
+ BUG_ON(current_locker_type != SATA_SCSI_STACK);
+
+ WARN(!locker_uid || (locker_uid != HW_LOCKER_UID), "Invalid locker "
+ "uid %p, h/w count %d, d count %d, reentrant_port_no %d, "
+ "core_locked %d, ox820sata_isr_callback %p\n", locker_uid,
+ hw_lock_count, direct_lock_count, reentrant_port_no, core_locked,
+ ox820sata_isr_callback);
+
+ if (--hw_lock_count) {
+ DPRINTK("Still nested port_no %d\n", port_no);
+ } else {
+ DPRINTK("Release port_no %d\n", port_no);
+ reentrant_port_no = -1;
+ ox820sata_isr_callback = NULL;
+ current_locker_type = SATA_UNLOCKED;
+ locker_uid = 0;
+ core_locked = 0;
+ released = 1;
+ wake_up(!list_empty(&scsi_wait_queue.task_list) ? &scsi_wait_queue : &fast_wait_queue);
+ }
+ }
+
+ DPRINTK("Leaving, port_no %d, count %d\n", port_no, hw_lock_count);
+
+ spin_unlock_irqrestore(&oxsacs_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;
+ return hd->port_in_eh || hd->port_frozen;
}
+
static inline u32 sata_oxnas_hostportbusy(struct ata_port *ap)
{
- struct sata_oxnas_port_priv *pd = ap->private_data;
+ struct sata_oxnas_host_priv *hd = ap->host->private_data;
- return ioread32(pd->port_base + SATA_COMMAND) & CMD_CORE_BUSY;
+ 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)
@@ -604,6 +936,7 @@ static inline u32 sata_oxnas_hostdmabusy(struct ata_port *ap)
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);
@@ -626,8 +959,10 @@ static void sata_oxnas_reset_core(struct ata_host *ah)
/* tune for sata compatability */
sata_oxnas_link_write(ah->ports[0], 0x60, 0x2988);
- /* each port in turn */
- sata_oxnas_link_write(ah->ports[0], 0x70, 0x55629);
+ for (n=0; n < host_priv->n_ports; n++) {
+ /* each port in turn */
+ sata_oxnas_link_write(ah->ports[n], 0x70, 0x55629);
+ }
udelay(50);
}
@@ -739,16 +1074,17 @@ static void sata_oxnas_tf_load(struct ata_port *ap,
}
-void sata_oxnas_set_mode(struct ata_port *ap, u32 mode, u32 force)
+void sata_oxnas_set_mode(struct ata_host *ah, u32 mode, u32 force)
{
- struct sata_oxnas_port_priv *port_priv = ap->private_data;
- void __iomem *core_base = port_priv->core_base;
+ 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;
- static u32 previous_mode = UNKNOWN_MODE;
+
+ u32 previous_mode;
/* these micro-code programs _should_ include the version word */
@@ -861,12 +1197,18 @@ void sata_oxnas_set_mode(struct ata_port *ap, u32 mode, u32 force)
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:
@@ -892,6 +1234,14 @@ void sata_oxnas_set_mode(struct ata_port *ap, u32 mode, u32 force)
}
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;
@@ -972,7 +1322,7 @@ void sata_oxnas_set_mode(struct ata_port *ap, u32 mode, u32 force)
/* enable jbod mode */
reg = ioread32(core_base + DATA_PLANE_CTRL);
reg &= ~DPC_JBOD_UCODE;
- reg |= DPC_FIS_SWCH;
+ reg &= ~DPC_FIS_SWCH;
iowrite32(reg, core_base + DATA_PLANE_CTRL);
wmb();
@@ -980,11 +1330,14 @@ void sata_oxnas_set_mode(struct ata_port *ap, u32 mode, u32 force)
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;
}
}
-
- previous_mode = mode;
}
/**
@@ -1006,8 +1359,10 @@ static inline void sata_oxnas_send_sync_escape(struct ata_port *ap)
}
/* clears errors */
-static inline void sata_oxnas_clear_CS_error(u32 *base)
+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);
@@ -1015,35 +1370,74 @@ static inline void sata_oxnas_clear_CS_error(u32 *base)
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(u32 *base)
+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) {
- printk(KERN_INFO "clearing register access error\n");
+ printk(KERN_INFO "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)
printk(KERN_INFO "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;
-
- printk(KERN_INFO "ox820sata: reseting SATA core\n");
-
+ int n;
+ printk(KERN_INFO "sata_oxnas: reseting SATA core\n");
/* core not recovering, reset it */
mdelay(5);
sata_oxnas_reset_core(ah);
@@ -1052,12 +1446,56 @@ static int sata_oxnas_cleanup(struct ata_host *ah)
/* 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]);
- sata_oxnas_post_reset_init(ah->ports[0]);
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->port_no, 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->port_no);
+}
+
+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;
@@ -1131,6 +1569,7 @@ int sata_oxnas_check_link(struct ata_port *ap)
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;
@@ -1138,7 +1577,9 @@ static void sata_oxnas_postreset(struct ata_link *link, unsigned int *classes)
ata_std_postreset(link, classes);
/* turn on phy error detection by removing the masks */
- sata_oxnas_link_write(ap , 0x0c, 0x30003);
+ 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) {
@@ -1233,6 +1674,26 @@ static u8 sata_oxnas_check_status(struct ata_port *ap)
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.
@@ -1243,16 +1704,18 @@ static void sata_oxnas_qc_prep(struct ata_queued_cmd *qc)
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)) {
printk(KERN_ERR "port %d not connected completing with error\n",
- qc->ap->port_no);
+ 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);
@@ -1296,16 +1759,20 @@ static int sata_oxnas_port_start(struct ata_port *ap)
if (!pp)
return -ENOMEM;
- pp->port_base = host_priv->port_base[ap->port_no];
- pp->sgdma_base = host_priv->sgdma_base[ap->port_no];
+ 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 * SATA_OXNAS_MAX_PORTS) {
+ 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;
+ (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);
@@ -1315,8 +1782,9 @@ static int sata_oxnas_port_start(struct ata_port *ap)
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->bmdma_prd = mem + sizeof(struct sgdma_request);
ap->private_data = pp;
@@ -1348,26 +1816,14 @@ static void sata_oxnas_port_stop(struct ata_port *ap)
kfree(pp);
}
+
static void sata_oxnas_post_reset_init(struct ata_port *ap)
{
- struct sata_oxnas_port_priv *pd = ap->private_data;
uint dev;
- int no_microcode = 0;
- DPRINTK("ENTER\n");
- if (no_microcode) {
- u32 reg;
- sata_oxnas_set_mode(ap, UNKNOWN_MODE, 1);
- reg = readl(pd->core_base + DEVICE_CONTROL);
- reg |= DEVICE_CONTROL_ATA_ERR_OVERRIDE;
- writel(reg, pd->core_base + DEVICE_CONTROL);
- } else {
- /* JBOD uCode */
- sata_oxnas_set_mode(ap, OXNASSATA_NOTRAID, 1);
- /* Turn the work around off as it may have been left on by any
- * HW-RAID code that we've been working with */
- writel(0x0, pd->core_base + PORT_ERROR_MASK);
- }
+ /* 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);
@@ -1440,6 +1896,9 @@ static int sata_oxnas_softreset(struct ata_link *link, unsigned int *class,
}
/* 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 */
@@ -1505,7 +1964,7 @@ int sata_oxnas_init_controller(struct ata_host *host)
*
* @param port SATA port to check and if necessary, correct.
*/
-static int ox820sata_bug_6320_workaround(struct ata_port *ap)
+static int sata_oxnas_bug_6320_workaround(struct ata_port *ap)
{
struct sata_oxnas_port_priv *pd = ap->private_data;
void __iomem *core_base = pd->core_base;
@@ -1516,14 +1975,14 @@ static int ox820sata_bug_6320_workaround(struct ata_port *ap)
int bug_present = 0;
/* Only want to apply fix to reads */
- is_read = !(readl(core_base + DM_DBG1) & (ap->port_no ?
+ 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 =
- readl(core_base + (ap->port_no ?
+ ioread32(core_base + (ap->port_no ?
DATACOUNT_PORT1 : DATACOUNT_PORT0));
remainder = quads_transferred & 0x7f;
@@ -1556,8 +2015,8 @@ static void sata_oxnas_port_irq(struct ata_port *ap, int force_error)
u32 int_status;
unsigned long flags = 0;
- /* DPRINTK("ENTER irqstatus %x\n", ioread32(port_base + INT_STATUS)); */
-/*
+ 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");
@@ -1567,7 +2026,6 @@ static void sata_oxnas_port_irq(struct ata_port *ap, int force_error)
}
return;
}
-*/
qc = ata_qc_from_tag(ap, ap->link.active_tag);
@@ -1625,26 +2083,26 @@ static irqreturn_t sata_oxnas_interrupt(int irq, void *dev_instance)
int bug_present;
/* loop until there are no more interrupts */
- while ((int_status = ioread32(core_base + CORE_INT_STATUS)) &
- COREINT_END) {
+ 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 this workaround for single disk systems as dual
+ /* Only need workaround_bug_6320 for single disk systems as dual
* disk will use uCode which prevents this read underrun problem
* from occuring.
* All single disk systems will use port 0 */
-
- for (port_no = 0; port_no < SATA_OXNAS_MAX_PORTS; ++port_no) {
+ 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 = (CORERAW_HOST << port_no);
+ mask = (COREINT_END << port_no);
if (int_status & mask) {
/* this port had an interrupt, clear it */
iowrite32(mask, core_base + CORE_INT_CLEAR);
- bug_present = ox820sata_bug_6320_workaround(
- ah->ports[port_no]);
+ bug_present = ( hd->current_ucode == UNKNOWN_MODE ) ?
+ sata_oxnas_bug_6320_workaround(
+ ah->ports[port_no]) : 0;
sata_oxnas_port_irq(ah->ports[port_no],
bug_present);
ret = IRQ_HANDLED;
@@ -1672,12 +2130,14 @@ static struct ata_port_operations sata_oxnas_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, */
+ .freeze = sata_oxnas_freeze,
+ .thaw = sata_oxnas_thaw,
.softreset = sata_oxnas_softreset,
/* .hardreset = sata_oxnas_hardreset, */
.postreset = sata_oxnas_postreset,
@@ -1691,6 +2151,7 @@ static struct ata_port_operations sata_oxnas_ops = {
/* .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 = {
@@ -1703,7 +2164,9 @@ static const struct ata_port_info sata_oxnas_port_info = {
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;
@@ -1716,20 +2179,29 @@ static int sata_oxnas_probe(struct platform_device *ofdev)
struct clk *clk = NULL;
const struct ata_port_info *ppi[] = { &sata_oxnas_port_info, NULL };
+ const struct ata_port_info *dppi[] = { &sata_oxnas_port_info, &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;
- sgdma_base = of_iomap(ofdev->dev.of_node, 1);
+ 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, 2);
+ 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, 3);
+ phy_base = of_iomap(ofdev->dev.of_node, 4);
if (!phy_base)
goto error_exit_with_cleanup;
@@ -1739,12 +2211,15 @@ static int sata_oxnas_probe(struct platform_device *ofdev)
if (!host_priv)
goto error_exit_with_cleanup;
- host_priv->port_base[0] = port_base;
- host_priv->sgdma_base[0] = sgdma_base;
+ 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, 4, &res)) {
+ if (!of_address_to_resource(ofdev->dev.of_node, 5, &res)) {
host_priv->dma_base = res.start;
host_priv->dma_size = resource_size(&res);
}
@@ -1786,7 +2261,9 @@ static int sata_oxnas_probe(struct platform_device *ofdev)
host_priv->rst_phy = rstc;
/* allocate host structure */
- host = ata_host_alloc_pinfo(&ofdev->dev, ppi, SATA_OXNAS_MAX_PORTS);
+ host = ata_host_alloc_pinfo(&ofdev->dev, n_ports>1 ? dppi : ppi,
+ n_ports);
+
if (!host) {
retval = -ENOMEM;
goto error_exit_with_cleanup;
diff --git a/target/linux/oxnas/patches-3.14/999-libata-hacks.patch b/target/linux/oxnas/patches-3.14/999-libata-hacks.patch
new file mode 100644
index 0000000000..39d9e51cf0
--- /dev/null
+++ b/target/linux/oxnas/patches-3.14/999-libata-hacks.patch
@@ -0,0 +1,60 @@
+Index: linux-3.18-rc7/drivers/ata/libata-core.c
+===================================================================
+--- linux-3.18-rc7.orig/drivers/ata/libata-core.c
++++ linux-3.18-rc7/drivers/ata/libata-core.c
+@@ -1568,6 +1568,14 @@ unsigned ata_exec_internal_sg(struct ata
+ return AC_ERR_SYSTEM;
+ }
+
++ if (ap->ops->acquire_hw && !ap->ops->acquire_hw(ap->port_no, 0, 0)) {
++ spin_unlock_irqrestore(ap->lock, flags);
++ if (!ap->ops->acquire_hw(ap->port_no, 1, (2*HZ))) {
++ return AC_ERR_TIMEOUT;
++ }
++ spin_lock_irqsave(ap->lock, flags);
++ }
++
+ /* initialize internal qc */
+
+ /* XXX: Tag 0 is used for drivers with legacy EH as some
+@@ -4739,6 +4747,9 @@ static struct ata_queued_cmd *ata_qc_new
+ if (unlikely(ap->pflags & ATA_PFLAG_FROZEN))
+ return NULL;
+
++ if (ap->ops->qc_new && ap->ops->qc_new(ap))
++ return NULL;
++
+ for (i = 0, tag = ap->last_tag + 1; i < max_queue; i++, tag++) {
+ tag = tag < max_queue ? tag : 0;
+
+@@ -4805,6 +4816,8 @@ void ata_qc_free(struct ata_queued_cmd *
+ if (likely(ata_tag_valid(tag))) {
+ qc->tag = ATA_TAG_POISON;
+ clear_bit(tag, &ap->qc_allocated);
++ if (ap->ops->qc_free)
++ ap->ops->qc_free(qc);
+ }
+ }
+
+Index: linux-3.18-rc7/include/linux/libata.h
+===================================================================
+--- linux-3.18-rc7.orig/include/linux/libata.h
++++ linux-3.18-rc7/include/linux/libata.h
+@@ -884,6 +884,8 @@ struct ata_port_operations {
+ void (*qc_prep)(struct ata_queued_cmd *qc);
+ unsigned int (*qc_issue)(struct ata_queued_cmd *qc);
+ bool (*qc_fill_rtf)(struct ata_queued_cmd *qc);
++ int (*qc_new)(struct ata_port *ap);
++ void (*qc_free)(struct ata_queued_cmd *qc);
+
+ /*
+ * Configuration and exception handling
+@@ -974,6 +976,8 @@ struct ata_port_operations {
+ void (*phy_reset)(struct ata_port *ap);
+ void (*eng_timeout)(struct ata_port *ap);
+
++ int (*acquire_hw)(int port_no, int may_sleep, int timeout_jiffies);
++
+ /*
+ * ->inherits must be the last field and all the preceding
+ * fields must be pointers.
diff --git a/target/linux/oxnas/patches-3.18/999-libata-hacks.patch b/target/linux/oxnas/patches-3.18/999-libata-hacks.patch
new file mode 100644
index 0000000000..39d9e51cf0
--- /dev/null
+++ b/target/linux/oxnas/patches-3.18/999-libata-hacks.patch
@@ -0,0 +1,60 @@
+Index: linux-3.18-rc7/drivers/ata/libata-core.c
+===================================================================
+--- linux-3.18-rc7.orig/drivers/ata/libata-core.c
++++ linux-3.18-rc7/drivers/ata/libata-core.c
+@@ -1568,6 +1568,14 @@ unsigned ata_exec_internal_sg(struct ata
+ return AC_ERR_SYSTEM;
+ }
+
++ if (ap->ops->acquire_hw && !ap->ops->acquire_hw(ap->port_no, 0, 0)) {
++ spin_unlock_irqrestore(ap->lock, flags);
++ if (!ap->ops->acquire_hw(ap->port_no, 1, (2*HZ))) {
++ return AC_ERR_TIMEOUT;
++ }
++ spin_lock_irqsave(ap->lock, flags);
++ }
++
+ /* initialize internal qc */
+
+ /* XXX: Tag 0 is used for drivers with legacy EH as some
+@@ -4739,6 +4747,9 @@ static struct ata_queued_cmd *ata_qc_new
+ if (unlikely(ap->pflags & ATA_PFLAG_FROZEN))
+ return NULL;
+
++ if (ap->ops->qc_new && ap->ops->qc_new(ap))
++ return NULL;
++
+ for (i = 0, tag = ap->last_tag + 1; i < max_queue; i++, tag++) {
+ tag = tag < max_queue ? tag : 0;
+
+@@ -4805,6 +4816,8 @@ void ata_qc_free(struct ata_queued_cmd *
+ if (likely(ata_tag_valid(tag))) {
+ qc->tag = ATA_TAG_POISON;
+ clear_bit(tag, &ap->qc_allocated);
++ if (ap->ops->qc_free)
++ ap->ops->qc_free(qc);
+ }
+ }
+
+Index: linux-3.18-rc7/include/linux/libata.h
+===================================================================
+--- linux-3.18-rc7.orig/include/linux/libata.h
++++ linux-3.18-rc7/include/linux/libata.h
+@@ -884,6 +884,8 @@ struct ata_port_operations {
+ void (*qc_prep)(struct ata_queued_cmd *qc);
+ unsigned int (*qc_issue)(struct ata_queued_cmd *qc);
+ bool (*qc_fill_rtf)(struct ata_queued_cmd *qc);
++ int (*qc_new)(struct ata_port *ap);
++ void (*qc_free)(struct ata_queued_cmd *qc);
+
+ /*
+ * Configuration and exception handling
+@@ -974,6 +976,8 @@ struct ata_port_operations {
+ void (*phy_reset)(struct ata_port *ap);
+ void (*eng_timeout)(struct ata_port *ap);
+
++ int (*acquire_hw)(int port_no, int may_sleep, int timeout_jiffies);
++
+ /*
+ * ->inherits must be the last field and all the preceding
+ * fields must be pointers.