diff options
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. |