diff --git a/target/linux/apm821xx/patches-4.4/020-sata-dwc.patch b/target/linux/apm821xx/patches-4.4/020-sata-dwc.patch new file mode 100644 index 0000000000..772c0dcf9b --- /dev/null +++ b/target/linux/apm821xx/patches-4.4/020-sata-dwc.patch @@ -0,0 +1,1088 @@ +From: Andy Shevchenko +Date: Sat, 21 May 2016 22:46:32 +0200 +Subject: [PATCH v2 00/23] ata: sata_dwc_460ex: make it working again + +The last approach in the commit 8b3444852a2b ("sata_dwc_460ex: move to generic +DMA driver") to switch to generic DMA engine API wasn't tested on bare metal. +Besides that we expecting new board support coming with the same SATA IP but +with different DMA. + +This series is targetting the following things: +- a few bug fixes to the original driver +- a part to fix the DMA engine usage and in particularly dw_dmac driver +- move driver to use generic PHY and "dmas" property which leads to update in DTS + +The driver has been tested myself on Sam460ex and WD MyBookLive (apollo3g) +boards. In any case I ask Christian, Måns, and Julian to independently test and +provide Tested-by tag or an error report. + +Series depends on previously published but not yet fully applied series [1]. + +The patches are also available via public branch [2]. + +[1] http://www.spinics.net/lists/dmaengine/msg09250.html +[2] https://bitbucket.org/andy-shev/linux/branch/topic%2Fdw%2Fsata + +Since v1: +- simplify patch 8 (David Laight) +- add Tested-by and Acked-by tags + +Andy Shevchenko (11): + ata: sata_dwc_460ex: set dma_boundary to 0x1fff + ata: sata_dwc_460ex: burst size must be in items not bytes + ata: sata_dwc_460ex: DMA is always a flow controller + ata: sata_dwc_460ex: select only core part of DMA driver + ata: sata_dwc_460ex: don't call ata_sff_qc_issue() on DMA commands + ata: sata_dwc_460ex: correct HOSTDEV{P}_FROM_*() macros + ata: sata_dwc_460ex: supply physical address of FIFO to DMA + ata: sata_dwc_460ex: switch to new dmaengine_terminate_* API + ata: sata_dwc_460ex: use devm_ioremap + ata: sata_dwc_460ex: make debug messages neat + powerpc/4xx: Device tree update for the 460ex DWC SATA + +Christian Lamparter (1): + ata: sata_dwc_460ex: fix crash on offline links without an attached + drive + +Mans Rullgard (11): + ata: sata_dwc_460ex: remove incorrect locking + ata: sata_dwc_460ex: skip dma setup for non-dma commands + ata: sata_dwc_460ex: use "dmas" DT property to find dma channel + ata: sata_dwc_460ex: add phy support + ata: sata_dwc_460ex: get rid of global data + ata: sata_dwc_460ex: remove empty libata callback + ata: sata_dwc_460ex: get rid of some pointless casts + ata: sata_dwc_460ex: get rid of incorrect cast + ata: sata_dwc_460ex: add __iomem to register base pointer + ata: sata_dwc_460ex: use readl/writel_relaxed() + ata: sata_dwc_460ex: tidy up sata_dwc_clear_dmacr() + + arch/powerpc/boot/dts/canyonlands.dts | 15 +- + drivers/ata/Kconfig | 12 +- + drivers/ata/sata_dwc_460ex.c | 552 +++++++++++++++++----------------- + 3 files changed, 305 insertions(+), 274 deletions(-) + +--- + drivers/ata/sata_dwc_460ex.c | 552 ++++++++++++++++++++++--------------------- + 1 file changed, 283 insertions(+), 269 deletions(-) + +diff --git a/drivers/ata/sata_dwc_460ex.c b/drivers/ata/sata_dwc_460ex.c +index 9020349..00c2af1 100644 +--- a/drivers/ata/sata_dwc_460ex.c ++++ b/drivers/ata/sata_dwc_460ex.c +@@ -30,10 +30,12 @@ + #include + #include + #include ++#include + #include + #include + #include + #include ++#include + #include + #include + +@@ -42,10 +44,6 @@ + #include + #include + +-/* Supported DMA engine drivers */ +-#include +-#include +- + /* These two are defined in "libata.h" */ + #undef DRV_NAME + #undef DRV_VERSION +@@ -53,19 +51,14 @@ + #define DRV_NAME "sata-dwc" + #define DRV_VERSION "1.3" + +-#ifndef out_le32 +-#define out_le32(a, v) __raw_writel(__cpu_to_le32(v), (void __iomem *)(a)) +-#endif +- +-#ifndef in_le32 +-#define in_le32(a) __le32_to_cpu(__raw_readl((void __iomem *)(a))) +-#endif ++#define sata_dwc_writel(a, v) writel_relaxed(v, a) ++#define sata_dwc_readl(a) readl_relaxed(a) + + #ifndef NO_IRQ + #define NO_IRQ 0 + #endif + +-#define AHB_DMA_BRST_DFLT 64 /* 16 data items burst length*/ ++#define AHB_DMA_BRST_DFLT 64 /* 16 data items burst length */ + + enum { + SATA_DWC_MAX_PORTS = 1, +@@ -102,7 +95,7 @@ struct sata_dwc_regs { + u32 versionr; /* Version Register */ + u32 idr; /* ID Register */ + u32 unimpl[192]; /* Unimplemented */ +- u32 dmadr[256]; /* FIFO Locations in DMA Mode */ ++ u32 dmadr[256]; /* FIFO Locations in DMA Mode */ + }; + + enum { +@@ -146,9 +139,14 @@ struct sata_dwc_device { + struct device *dev; /* generic device struct */ + struct ata_probe_ent *pe; /* ptr to probe-ent */ + struct ata_host *host; +- u8 __iomem *reg_base; +- struct sata_dwc_regs *sata_dwc_regs; /* DW Synopsys SATA specific */ ++ struct sata_dwc_regs __iomem *sata_dwc_regs; /* DW SATA specific */ ++ u32 sactive_issued; ++ u32 sactive_queued; ++ struct phy *phy; ++ phys_addr_t dmadr; ++#ifdef CONFIG_SATA_DWC_OLD_DMA + struct dw_dma_chip *dma; ++#endif + }; + + #define SATA_DWC_QCMD_MAX 32 +@@ -159,25 +157,19 @@ struct sata_dwc_device_port { + int dma_pending[SATA_DWC_QCMD_MAX]; + + /* DMA info */ +- struct dw_dma_slave *dws; + struct dma_chan *chan; + struct dma_async_tx_descriptor *desc[SATA_DWC_QCMD_MAX]; + u32 dma_interrupt_count; + }; + + /* +- * Commonly used DWC SATA driver Macros ++ * Commonly used DWC SATA driver macros + */ +-#define HSDEV_FROM_HOST(host) ((struct sata_dwc_device *)\ +- (host)->private_data) +-#define HSDEV_FROM_AP(ap) ((struct sata_dwc_device *)\ +- (ap)->host->private_data) +-#define HSDEVP_FROM_AP(ap) ((struct sata_dwc_device_port *)\ +- (ap)->private_data) +-#define HSDEV_FROM_QC(qc) ((struct sata_dwc_device *)\ +- (qc)->ap->host->private_data) +-#define HSDEV_FROM_HSDEVP(p) ((struct sata_dwc_device *)\ +- (hsdevp)->hsdev) ++#define HSDEV_FROM_HOST(host) ((struct sata_dwc_device *)(host)->private_data) ++#define HSDEV_FROM_AP(ap) ((struct sata_dwc_device *)(ap)->host->private_data) ++#define HSDEVP_FROM_AP(ap) ((struct sata_dwc_device_port *)(ap)->private_data) ++#define HSDEV_FROM_QC(qc) ((struct sata_dwc_device *)(qc)->ap->host->private_data) ++#define HSDEV_FROM_HSDEVP(p) ((struct sata_dwc_device *)(p)->hsdev) + + enum { + SATA_DWC_CMD_ISSUED_NOT = 0, +@@ -190,21 +182,6 @@ enum { + SATA_DWC_DMA_PENDING_RX = 2, + }; + +-struct sata_dwc_host_priv { +- void __iomem *scr_addr_sstatus; +- u32 sata_dwc_sactive_issued ; +- u32 sata_dwc_sactive_queued ; +-}; +- +-static struct sata_dwc_host_priv host_pvt; +- +-static struct dw_dma_slave sata_dwc_dma_dws = { +- .src_id = 0, +- .dst_id = 0, +- .src_master = 0, +- .dst_master = 1, +-}; +- + /* + * Prototypes + */ +@@ -215,6 +192,93 @@ static void sata_dwc_dma_xfer_complete(struct ata_port *ap, u32 check_status); + static void sata_dwc_port_stop(struct ata_port *ap); + static void sata_dwc_clear_dmacr(struct sata_dwc_device_port *hsdevp, u8 tag); + ++#ifdef CONFIG_SATA_DWC_OLD_DMA ++ ++#include ++#include ++ ++static struct dw_dma_slave sata_dwc_dma_dws = { ++ .src_id = 0, ++ .dst_id = 0, ++ .m_master = 1, ++ .p_master = 0, ++}; ++ ++static bool sata_dwc_dma_filter(struct dma_chan *chan, void *param) ++{ ++ struct dw_dma_slave *dws = &sata_dwc_dma_dws; ++ ++ if (dws->dma_dev != chan->device->dev) ++ return false; ++ ++ chan->private = dws; ++ return true; ++} ++ ++static int sata_dwc_dma_get_channel_old(struct sata_dwc_device_port *hsdevp) ++{ ++ struct sata_dwc_device *hsdev = hsdevp->hsdev; ++ struct dw_dma_slave *dws = &sata_dwc_dma_dws; ++ dma_cap_mask_t mask; ++ ++ dws->dma_dev = hsdev->dev; ++ ++ dma_cap_zero(mask); ++ dma_cap_set(DMA_SLAVE, mask); ++ ++ /* Acquire DMA channel */ ++ hsdevp->chan = dma_request_channel(mask, sata_dwc_dma_filter, hsdevp); ++ if (!hsdevp->chan) { ++ dev_err(hsdev->dev, "%s: dma channel unavailable\n", ++ __func__); ++ return -EAGAIN; ++ } ++ ++ return 0; ++} ++ ++static int sata_dwc_dma_init_old(struct platform_device *pdev, ++ struct sata_dwc_device *hsdev) ++{ ++ struct device_node *np = pdev->dev.of_node; ++ struct resource *res; ++ ++ hsdev->dma = devm_kzalloc(&pdev->dev, sizeof(*hsdev->dma), GFP_KERNEL); ++ if (!hsdev->dma) ++ return -ENOMEM; ++ ++ hsdev->dma->dev = &pdev->dev; ++ ++ /* Get SATA DMA interrupt number */ ++ hsdev->dma->irq = irq_of_parse_and_map(np, 1); ++ if (hsdev->dma->irq == NO_IRQ) { ++ dev_err(&pdev->dev, "no SATA DMA irq\n"); ++ return -ENODEV; ++ } ++ ++ /* Get physical SATA DMA register base address */ ++ res = platform_get_resource(pdev, IORESOURCE_MEM, 1); ++ hsdev->dma->regs = devm_ioremap_resource(&pdev->dev, res); ++ if (IS_ERR(hsdev->dma->regs)) { ++ dev_err(&pdev->dev, ++ "ioremap failed for AHBDMA register address\n"); ++ return PTR_ERR(hsdev->dma->regs); ++ } ++ ++ /* Initialize AHB DMAC */ ++ return dw_dma_probe(hsdev->dma); ++} ++ ++static void sata_dwc_dma_exit_old(struct sata_dwc_device *hsdev) ++{ ++ if (!hsdev->dma) ++ return; ++ ++ dw_dma_remove(hsdev->dma); ++} ++ ++#endif ++ + static const char *get_prot_descript(u8 protocol) + { + switch ((enum ata_tf_protocols)protocol) { +@@ -305,21 +369,20 @@ static struct dma_async_tx_descriptor *dma_dwc_xfer_setup(struct ata_queued_cmd + struct ata_port *ap = qc->ap; + struct sata_dwc_device_port *hsdevp = HSDEVP_FROM_AP(ap); + struct sata_dwc_device *hsdev = HSDEV_FROM_AP(ap); +- dma_addr_t addr = (dma_addr_t)&hsdev->sata_dwc_regs->dmadr; + struct dma_slave_config sconf; + struct dma_async_tx_descriptor *desc; + + if (qc->dma_dir == DMA_DEV_TO_MEM) { +- sconf.src_addr = addr; +- sconf.device_fc = true; ++ sconf.src_addr = hsdev->dmadr; ++ sconf.device_fc = false; + } else { /* DMA_MEM_TO_DEV */ +- sconf.dst_addr = addr; ++ sconf.dst_addr = hsdev->dmadr; + sconf.device_fc = false; + } + + sconf.direction = qc->dma_dir; +- sconf.src_maxburst = AHB_DMA_BRST_DFLT; +- sconf.dst_maxburst = AHB_DMA_BRST_DFLT; ++ sconf.src_maxburst = AHB_DMA_BRST_DFLT / 4; /* in items */ ++ sconf.dst_maxburst = AHB_DMA_BRST_DFLT / 4; /* in items */ + sconf.src_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; + sconf.dst_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; + +@@ -336,8 +399,8 @@ static struct dma_async_tx_descriptor *dma_dwc_xfer_setup(struct ata_queued_cmd + desc->callback = dma_dwc_xfer_done; + desc->callback_param = hsdev; + +- dev_dbg(hsdev->dev, "%s sg: 0x%p, count: %d addr: %pad\n", +- __func__, qc->sg, qc->n_elem, &addr); ++ dev_dbg(hsdev->dev, "%s sg: 0x%p, count: %d addr: %pa\n", __func__, ++ qc->sg, qc->n_elem, &hsdev->dmadr); + + return desc; + } +@@ -350,48 +413,38 @@ static int sata_dwc_scr_read(struct ata_link *link, unsigned int scr, u32 *val) + return -EINVAL; + } + +- *val = in_le32(link->ap->ioaddr.scr_addr + (scr * 4)); +- dev_dbg(link->ap->dev, "%s: id=%d reg=%d val=val=0x%08x\n", +- __func__, link->ap->print_id, scr, *val); ++ *val = sata_dwc_readl(link->ap->ioaddr.scr_addr + (scr * 4)); ++ dev_dbg(link->ap->dev, "%s: id=%d reg=%d val=0x%08x\n", __func__, ++ link->ap->print_id, scr, *val); + + return 0; + } + + static int sata_dwc_scr_write(struct ata_link *link, unsigned int scr, u32 val) + { +- dev_dbg(link->ap->dev, "%s: id=%d reg=%d val=val=0x%08x\n", +- __func__, link->ap->print_id, scr, val); ++ dev_dbg(link->ap->dev, "%s: id=%d reg=%d val=0x%08x\n", __func__, ++ link->ap->print_id, scr, val); + if (scr > SCR_NOTIFICATION) { + dev_err(link->ap->dev, "%s: Incorrect SCR offset 0x%02x\n", + __func__, scr); + return -EINVAL; + } +- out_le32(link->ap->ioaddr.scr_addr + (scr * 4), val); ++ sata_dwc_writel(link->ap->ioaddr.scr_addr + (scr * 4), val); + + return 0; + } + +-static u32 core_scr_read(unsigned int scr) +-{ +- return in_le32(host_pvt.scr_addr_sstatus + (scr * 4)); +-} +- +-static void core_scr_write(unsigned int scr, u32 val) +-{ +- out_le32(host_pvt.scr_addr_sstatus + (scr * 4), val); +-} +- +-static void clear_serror(void) ++static void clear_serror(struct ata_port *ap) + { + u32 val; +- val = core_scr_read(SCR_ERROR); +- core_scr_write(SCR_ERROR, val); ++ sata_dwc_scr_read(&ap->link, SCR_ERROR, &val); ++ sata_dwc_scr_write(&ap->link, SCR_ERROR, val); + } + + static void clear_interrupt_bit(struct sata_dwc_device *hsdev, u32 bit) + { +- out_le32(&hsdev->sata_dwc_regs->intpr, +- in_le32(&hsdev->sata_dwc_regs->intpr)); ++ sata_dwc_writel(&hsdev->sata_dwc_regs->intpr, ++ sata_dwc_readl(&hsdev->sata_dwc_regs->intpr)); + } + + static u32 qcmd_tag_to_mask(u8 tag) +@@ -412,7 +465,7 @@ static void sata_dwc_error_intr(struct ata_port *ap, + + ata_ehi_clear_desc(ehi); + +- serror = core_scr_read(SCR_ERROR); ++ sata_dwc_scr_read(&ap->link, SCR_ERROR, &serror); + status = ap->ops->sff_check_status(ap); + + tag = ap->link.active_tag; +@@ -423,7 +476,7 @@ static void sata_dwc_error_intr(struct ata_port *ap, + hsdevp->dma_pending[tag], hsdevp->cmd_issued[tag]); + + /* Clear error register and interrupt bit */ +- clear_serror(); ++ clear_serror(ap); + clear_interrupt_bit(hsdev, SATA_DWC_INTPR_ERR); + + /* This is the only error happening now. TODO check for exact error */ +@@ -462,12 +515,12 @@ static irqreturn_t sata_dwc_isr(int irq, void *dev_instance) + int handled, num_processed, port = 0; + uint intpr, sactive, sactive2, tag_mask; + struct sata_dwc_device_port *hsdevp; +- host_pvt.sata_dwc_sactive_issued = 0; ++ hsdev->sactive_issued = 0; + + spin_lock_irqsave(&host->lock, flags); + + /* Read the interrupt register */ +- intpr = in_le32(&hsdev->sata_dwc_regs->intpr); ++ intpr = sata_dwc_readl(&hsdev->sata_dwc_regs->intpr); + + ap = host->ports[port]; + hsdevp = HSDEVP_FROM_AP(ap); +@@ -486,12 +539,12 @@ static irqreturn_t sata_dwc_isr(int irq, void *dev_instance) + if (intpr & SATA_DWC_INTPR_NEWFP) { + clear_interrupt_bit(hsdev, SATA_DWC_INTPR_NEWFP); + +- tag = (u8)(in_le32(&hsdev->sata_dwc_regs->fptagr)); ++ tag = (u8)(sata_dwc_readl(&hsdev->sata_dwc_regs->fptagr)); + dev_dbg(ap->dev, "%s: NEWFP tag=%d\n", __func__, tag); + if (hsdevp->cmd_issued[tag] != SATA_DWC_CMD_ISSUED_PEND) + dev_warn(ap->dev, "CMD tag=%d not pending?\n", tag); + +- host_pvt.sata_dwc_sactive_issued |= qcmd_tag_to_mask(tag); ++ hsdev->sactive_issued |= qcmd_tag_to_mask(tag); + + qc = ata_qc_from_tag(ap, tag); + /* +@@ -505,11 +558,11 @@ static irqreturn_t sata_dwc_isr(int irq, void *dev_instance) + handled = 1; + goto DONE; + } +- sactive = core_scr_read(SCR_ACTIVE); +- tag_mask = (host_pvt.sata_dwc_sactive_issued | sactive) ^ sactive; ++ sata_dwc_scr_read(&ap->link, SCR_ACTIVE, &sactive); ++ tag_mask = (hsdev->sactive_issued | sactive) ^ sactive; + + /* If no sactive issued and tag_mask is zero then this is not NCQ */ +- if (host_pvt.sata_dwc_sactive_issued == 0 && tag_mask == 0) { ++ if (hsdev->sactive_issued == 0 && tag_mask == 0) { + if (ap->link.active_tag == ATA_TAG_POISON) + tag = 0; + else +@@ -579,22 +632,19 @@ DRVSTILLBUSY: + */ + + /* process completed commands */ +- sactive = core_scr_read(SCR_ACTIVE); +- tag_mask = (host_pvt.sata_dwc_sactive_issued | sactive) ^ sactive; ++ sata_dwc_scr_read(&ap->link, SCR_ACTIVE, &sactive); ++ tag_mask = (hsdev->sactive_issued | sactive) ^ sactive; + +- if (sactive != 0 || (host_pvt.sata_dwc_sactive_issued) > 1 || \ +- tag_mask > 1) { ++ if (sactive != 0 || hsdev->sactive_issued > 1 || tag_mask > 1) { + dev_dbg(ap->dev, + "%s NCQ:sactive=0x%08x sactive_issued=0x%08x tag_mask=0x%08x\n", +- __func__, sactive, host_pvt.sata_dwc_sactive_issued, +- tag_mask); ++ __func__, sactive, hsdev->sactive_issued, tag_mask); + } + +- if ((tag_mask | (host_pvt.sata_dwc_sactive_issued)) != \ +- (host_pvt.sata_dwc_sactive_issued)) { ++ if ((tag_mask | hsdev->sactive_issued) != hsdev->sactive_issued) { + dev_warn(ap->dev, +- "Bad tag mask? sactive=0x%08x (host_pvt.sata_dwc_sactive_issued)=0x%08x tag_mask=0x%08x\n", +- sactive, host_pvt.sata_dwc_sactive_issued, tag_mask); ++ "Bad tag mask? sactive=0x%08x sactive_issued=0x%08x tag_mask=0x%08x\n", ++ sactive, hsdev->sactive_issued, tag_mask); + } + + /* read just to clear ... not bad if currently still busy */ +@@ -656,7 +706,7 @@ STILLBUSY: + * we were processing --we read status as part of processing a completed + * command). + */ +- sactive2 = core_scr_read(SCR_ACTIVE); ++ sata_dwc_scr_read(&ap->link, SCR_ACTIVE, &sactive2); + if (sactive2 != sactive) { + dev_dbg(ap->dev, + "More completed - sactive=0x%x sactive2=0x%x\n", +@@ -672,15 +722,14 @@ DONE: + static void sata_dwc_clear_dmacr(struct sata_dwc_device_port *hsdevp, u8 tag) + { + struct sata_dwc_device *hsdev = HSDEV_FROM_HSDEVP(hsdevp); ++ u32 dmacr = sata_dwc_readl(&hsdev->sata_dwc_regs->dmacr); + + if (hsdevp->dma_pending[tag] == SATA_DWC_DMA_PENDING_RX) { +- out_le32(&(hsdev->sata_dwc_regs->dmacr), +- SATA_DWC_DMACR_RX_CLEAR( +- in_le32(&(hsdev->sata_dwc_regs->dmacr)))); ++ dmacr = SATA_DWC_DMACR_RX_CLEAR(dmacr); ++ sata_dwc_writel(&hsdev->sata_dwc_regs->dmacr, dmacr); + } else if (hsdevp->dma_pending[tag] == SATA_DWC_DMA_PENDING_TX) { +- out_le32(&(hsdev->sata_dwc_regs->dmacr), +- SATA_DWC_DMACR_TX_CLEAR( +- in_le32(&(hsdev->sata_dwc_regs->dmacr)))); ++ dmacr = SATA_DWC_DMACR_TX_CLEAR(dmacr); ++ sata_dwc_writel(&hsdev->sata_dwc_regs->dmacr, dmacr); + } else { + /* + * This should not happen, it indicates the driver is out of +@@ -688,10 +737,9 @@ static void sata_dwc_clear_dmacr(struct sata_dwc_device_port *hsdevp, u8 tag) + */ + dev_err(hsdev->dev, + "%s DMA protocol RX and TX DMA not pending tag=0x%02x pending=%d dmacr: 0x%08x\n", +- __func__, tag, hsdevp->dma_pending[tag], +- in_le32(&hsdev->sata_dwc_regs->dmacr)); +- out_le32(&(hsdev->sata_dwc_regs->dmacr), +- SATA_DWC_DMACR_TXRXCH_CLEAR); ++ __func__, tag, hsdevp->dma_pending[tag], dmacr); ++ sata_dwc_writel(&hsdev->sata_dwc_regs->dmacr, ++ SATA_DWC_DMACR_TXRXCH_CLEAR); + } + } + +@@ -716,7 +764,7 @@ static void sata_dwc_dma_xfer_complete(struct ata_port *ap, u32 check_status) + __func__, qc->tag, qc->tf.command, + get_dma_dir_descript(qc->dma_dir), + get_prot_descript(qc->tf.protocol), +- in_le32(&(hsdev->sata_dwc_regs->dmacr))); ++ sata_dwc_readl(&hsdev->sata_dwc_regs->dmacr)); + } + #endif + +@@ -725,7 +773,7 @@ static void sata_dwc_dma_xfer_complete(struct ata_port *ap, u32 check_status) + dev_err(ap->dev, + "%s DMA protocol RX and TX DMA not pending dmacr: 0x%08x\n", + __func__, +- in_le32(&(hsdev->sata_dwc_regs->dmacr))); ++ sata_dwc_readl(&hsdev->sata_dwc_regs->dmacr)); + } + + hsdevp->dma_pending[tag] = SATA_DWC_DMA_PENDING_NONE; +@@ -742,8 +790,9 @@ static int sata_dwc_qc_complete(struct ata_port *ap, struct ata_queued_cmd *qc, + u8 status = 0; + u32 mask = 0x0; + u8 tag = qc->tag; ++ struct sata_dwc_device *hsdev = HSDEV_FROM_AP(ap); + struct sata_dwc_device_port *hsdevp = HSDEVP_FROM_AP(ap); +- host_pvt.sata_dwc_sactive_queued = 0; ++ hsdev->sactive_queued = 0; + dev_dbg(ap->dev, "%s checkstatus? %x\n", __func__, check_status); + + if (hsdevp->dma_pending[tag] == SATA_DWC_DMA_PENDING_TX) +@@ -756,10 +805,8 @@ static int sata_dwc_qc_complete(struct ata_port *ap, struct ata_queued_cmd *qc, + + /* clear active bit */ + mask = (~(qcmd_tag_to_mask(tag))); +- host_pvt.sata_dwc_sactive_queued = (host_pvt.sata_dwc_sactive_queued) \ +- & mask; +- host_pvt.sata_dwc_sactive_issued = (host_pvt.sata_dwc_sactive_issued) \ +- & mask; ++ hsdev->sactive_queued = hsdev->sactive_queued & mask; ++ hsdev->sactive_issued = hsdev->sactive_issued & mask; + ata_qc_complete(qc); + return 0; + } +@@ -767,54 +814,62 @@ static int sata_dwc_qc_complete(struct ata_port *ap, struct ata_queued_cmd *qc, + static void sata_dwc_enable_interrupts(struct sata_dwc_device *hsdev) + { + /* Enable selective interrupts by setting the interrupt maskregister*/ +- out_le32(&hsdev->sata_dwc_regs->intmr, +- SATA_DWC_INTMR_ERRM | +- SATA_DWC_INTMR_NEWFPM | +- SATA_DWC_INTMR_PMABRTM | +- SATA_DWC_INTMR_DMATM); ++ sata_dwc_writel(&hsdev->sata_dwc_regs->intmr, ++ SATA_DWC_INTMR_ERRM | ++ SATA_DWC_INTMR_NEWFPM | ++ SATA_DWC_INTMR_PMABRTM | ++ SATA_DWC_INTMR_DMATM); + /* + * Unmask the error bits that should trigger an error interrupt by + * setting the error mask register. + */ +- out_le32(&hsdev->sata_dwc_regs->errmr, SATA_DWC_SERROR_ERR_BITS); ++ sata_dwc_writel(&hsdev->sata_dwc_regs->errmr, SATA_DWC_SERROR_ERR_BITS); + + dev_dbg(hsdev->dev, "%s: INTMR = 0x%08x, ERRMR = 0x%08x\n", +- __func__, in_le32(&hsdev->sata_dwc_regs->intmr), +- in_le32(&hsdev->sata_dwc_regs->errmr)); ++ __func__, sata_dwc_readl(&hsdev->sata_dwc_regs->intmr), ++ sata_dwc_readl(&hsdev->sata_dwc_regs->errmr)); + } + +-static bool sata_dwc_dma_filter(struct dma_chan *chan, void *param) ++static void sata_dwc_setup_port(struct ata_ioports *port, void __iomem *base) + { +- struct sata_dwc_device_port *hsdevp = param; +- struct dw_dma_slave *dws = hsdevp->dws; ++ port->cmd_addr = base + 0x00; ++ port->data_addr = base + 0x00; + +- if (dws->dma_dev != chan->device->dev) +- return false; ++ port->error_addr = base + 0x04; ++ port->feature_addr = base + 0x04; + +- chan->private = dws; +- return true; +-} ++ port->nsect_addr = base + 0x08; + +-static void sata_dwc_setup_port(struct ata_ioports *port, unsigned long base) +-{ +- port->cmd_addr = (void __iomem *)base + 0x00; +- port->data_addr = (void __iomem *)base + 0x00; ++ port->lbal_addr = base + 0x0c; ++ port->lbam_addr = base + 0x10; ++ port->lbah_addr = base + 0x14; + +- port->error_addr = (void __iomem *)base + 0x04; +- port->feature_addr = (void __iomem *)base + 0x04; ++ port->device_addr = base + 0x18; ++ port->command_addr = base + 0x1c; ++ port->status_addr = base + 0x1c; + +- port->nsect_addr = (void __iomem *)base + 0x08; ++ port->altstatus_addr = base + 0x20; ++ port->ctl_addr = base + 0x20; ++} ++ ++static int sata_dwc_dma_get_channel(struct sata_dwc_device_port *hsdevp) ++{ ++ struct sata_dwc_device *hsdev = hsdevp->hsdev; ++ struct device *dev = hsdev->dev; + +- port->lbal_addr = (void __iomem *)base + 0x0c; +- port->lbam_addr = (void __iomem *)base + 0x10; +- port->lbah_addr = (void __iomem *)base + 0x14; ++#ifdef CONFIG_SATA_DWC_OLD_DMA ++ if (!of_find_property(dev->of_node, "dmas", NULL)) ++ return sata_dwc_dma_get_channel_old(hsdevp); ++#endif + +- port->device_addr = (void __iomem *)base + 0x18; +- port->command_addr = (void __iomem *)base + 0x1c; +- port->status_addr = (void __iomem *)base + 0x1c; ++ hsdevp->chan = dma_request_chan(dev, "sata-dma"); ++ if (IS_ERR(hsdevp->chan)) { ++ dev_err(dev, "failed to allocate dma channel: %ld\n", ++ PTR_ERR(hsdevp->chan)); ++ return PTR_ERR(hsdevp->chan); ++ } + +- port->altstatus_addr = (void __iomem *)base + 0x20; +- port->ctl_addr = (void __iomem *)base + 0x20; ++ return 0; + } + + /* +@@ -829,7 +884,6 @@ static int sata_dwc_port_start(struct ata_port *ap) + struct sata_dwc_device *hsdev; + struct sata_dwc_device_port *hsdevp = NULL; + struct device *pdev; +- dma_cap_mask_t mask; + int i; + + hsdev = HSDEV_FROM_AP(ap); +@@ -853,20 +907,13 @@ static int sata_dwc_port_start(struct ata_port *ap) + } + hsdevp->hsdev = hsdev; + +- hsdevp->dws = &sata_dwc_dma_dws; +- hsdevp->dws->dma_dev = hsdev->dev; +- +- dma_cap_zero(mask); +- dma_cap_set(DMA_SLAVE, mask); ++ err = sata_dwc_dma_get_channel(hsdevp); ++ if (err) ++ goto CLEANUP_ALLOC; + +- /* Acquire DMA channel */ +- hsdevp->chan = dma_request_channel(mask, sata_dwc_dma_filter, hsdevp); +- if (!hsdevp->chan) { +- dev_err(hsdev->dev, "%s: dma channel unavailable\n", +- __func__); +- err = -EAGAIN; ++ err = phy_power_on(hsdev->phy); ++ if (err) + goto CLEANUP_ALLOC; +- } + + for (i = 0; i < SATA_DWC_QCMD_MAX; i++) + hsdevp->cmd_issued[i] = SATA_DWC_CMD_ISSUED_NOT; +@@ -877,18 +924,18 @@ static int sata_dwc_port_start(struct ata_port *ap) + if (ap->port_no == 0) { + dev_dbg(ap->dev, "%s: clearing TXCHEN, RXCHEN in DMAC\n", + __func__); +- out_le32(&hsdev->sata_dwc_regs->dmacr, +- SATA_DWC_DMACR_TXRXCH_CLEAR); ++ sata_dwc_writel(&hsdev->sata_dwc_regs->dmacr, ++ SATA_DWC_DMACR_TXRXCH_CLEAR); + + dev_dbg(ap->dev, "%s: setting burst size in DBTSR\n", + __func__); +- out_le32(&hsdev->sata_dwc_regs->dbtsr, +- (SATA_DWC_DBTSR_MWR(AHB_DMA_BRST_DFLT) | +- SATA_DWC_DBTSR_MRD(AHB_DMA_BRST_DFLT))); ++ sata_dwc_writel(&hsdev->sata_dwc_regs->dbtsr, ++ (SATA_DWC_DBTSR_MWR(AHB_DMA_BRST_DFLT) | ++ SATA_DWC_DBTSR_MRD(AHB_DMA_BRST_DFLT))); + } + + /* Clear any error bits before libata starts issuing commands */ +- clear_serror(); ++ clear_serror(ap); + ap->private_data = hsdevp; + dev_dbg(ap->dev, "%s: done\n", __func__); + return 0; +@@ -903,11 +950,13 @@ CLEANUP: + static void sata_dwc_port_stop(struct ata_port *ap) + { + struct sata_dwc_device_port *hsdevp = HSDEVP_FROM_AP(ap); ++ struct sata_dwc_device *hsdev = HSDEV_FROM_AP(ap); + + dev_dbg(ap->dev, "%s: ap->id = %d\n", __func__, ap->print_id); + +- dmaengine_terminate_all(hsdevp->chan); ++ dmaengine_terminate_sync(hsdevp->chan); + dma_release_channel(hsdevp->chan); ++ phy_power_off(hsdev->phy); + + kfree(hsdevp); + ap->private_data = NULL; +@@ -924,22 +973,20 @@ static void sata_dwc_exec_command_by_tag(struct ata_port *ap, + struct ata_taskfile *tf, + u8 tag, u32 cmd_issued) + { +- unsigned long flags; + struct sata_dwc_device_port *hsdevp = HSDEVP_FROM_AP(ap); + + dev_dbg(ap->dev, "%s cmd(0x%02x): %s tag=%d\n", __func__, tf->command, + ata_get_cmd_descript(tf->command), tag); + +- spin_lock_irqsave(&ap->host->lock, flags); + hsdevp->cmd_issued[tag] = cmd_issued; +- spin_unlock_irqrestore(&ap->host->lock, flags); ++ + /* + * Clear SError before executing a new command. + * sata_dwc_scr_write and read can not be used here. Clearing the PM + * managed SError register for the disk needs to be done before the + * task file is loaded. + */ +- clear_serror(); ++ clear_serror(ap); + ata_sff_exec_command(ap, tf); + } + +@@ -992,18 +1039,18 @@ static void sata_dwc_bmdma_start_by_tag(struct ata_queued_cmd *qc, u8 tag) + sata_dwc_tf_dump(ap, &qc->tf); + + if (start_dma) { +- reg = core_scr_read(SCR_ERROR); ++ sata_dwc_scr_read(&ap->link, SCR_ERROR, ®); + if (reg & SATA_DWC_SERROR_ERR_BITS) { + dev_err(ap->dev, "%s: ****** SError=0x%08x ******\n", + __func__, reg); + } + + if (dir == DMA_TO_DEVICE) +- out_le32(&hsdev->sata_dwc_regs->dmacr, +- SATA_DWC_DMACR_TXCHEN); ++ sata_dwc_writel(&hsdev->sata_dwc_regs->dmacr, ++ SATA_DWC_DMACR_TXCHEN); + else +- out_le32(&hsdev->sata_dwc_regs->dmacr, +- SATA_DWC_DMACR_RXCHEN); ++ sata_dwc_writel(&hsdev->sata_dwc_regs->dmacr, ++ SATA_DWC_DMACR_RXCHEN); + + /* Enable AHB DMA transfer on the specified channel */ + dmaengine_submit(desc); +@@ -1025,36 +1072,12 @@ static void sata_dwc_bmdma_start(struct ata_queued_cmd *qc) + sata_dwc_bmdma_start_by_tag(qc, tag); + } + +-/* +- * Function : sata_dwc_qc_prep_by_tag +- * arguments : ata_queued_cmd *qc, u8 tag +- * Return value : None +- * qc_prep for a particular queued command based on tag +- */ +-static void sata_dwc_qc_prep_by_tag(struct ata_queued_cmd *qc, u8 tag) +-{ +- struct dma_async_tx_descriptor *desc; +- struct ata_port *ap = qc->ap; +- struct sata_dwc_device_port *hsdevp = HSDEVP_FROM_AP(ap); +- +- dev_dbg(ap->dev, "%s: port=%d dma dir=%s n_elem=%d\n", +- __func__, ap->port_no, get_dma_dir_descript(qc->dma_dir), +- qc->n_elem); +- +- desc = dma_dwc_xfer_setup(qc); +- if (!desc) { +- dev_err(ap->dev, "%s: dma_dwc_xfer_setup returns NULL\n", +- __func__); +- return; +- } +- hsdevp->desc[tag] = desc; +-} +- + static unsigned int sata_dwc_qc_issue(struct ata_queued_cmd *qc) + { + u32 sactive; + u8 tag = qc->tag; + struct ata_port *ap = qc->ap; ++ struct sata_dwc_device_port *hsdevp = HSDEVP_FROM_AP(ap); + + #ifdef DEBUG_NCQ + if (qc->tag > 0 || ap->link.sactive > 1) +@@ -1068,47 +1091,33 @@ static unsigned int sata_dwc_qc_issue(struct ata_queued_cmd *qc) + + if (!ata_is_ncq(qc->tf.protocol)) + tag = 0; +- sata_dwc_qc_prep_by_tag(qc, tag); ++ ++ if (ata_is_dma(qc->tf.protocol)) { ++ hsdevp->desc[tag] = dma_dwc_xfer_setup(qc); ++ if (!hsdevp->desc[tag]) ++ return AC_ERR_SYSTEM; ++ } else { ++ hsdevp->desc[tag] = NULL; ++ } + + if (ata_is_ncq(qc->tf.protocol)) { +- sactive = core_scr_read(SCR_ACTIVE); ++ sata_dwc_scr_read(&ap->link, SCR_ACTIVE, &sactive); + sactive |= (0x00000001 << tag); +- core_scr_write(SCR_ACTIVE, sactive); ++ sata_dwc_scr_write(&ap->link, SCR_ACTIVE, sactive); + + dev_dbg(qc->ap->dev, + "%s: tag=%d ap->link.sactive = 0x%08x sactive=0x%08x\n", + __func__, tag, qc->ap->link.sactive, sactive); + + ap->ops->sff_tf_load(ap, &qc->tf); +- sata_dwc_exec_command_by_tag(ap, &qc->tf, qc->tag, ++ sata_dwc_exec_command_by_tag(ap, &qc->tf, tag, + SATA_DWC_CMD_ISSUED_PEND); + } else { +- ata_sff_qc_issue(qc); ++ return ata_bmdma_qc_issue(qc); + } + return 0; + } + +-/* +- * Function : sata_dwc_qc_prep +- * arguments : ata_queued_cmd *qc +- * Return value : None +- * qc_prep for a particular queued command +- */ +- +-static void sata_dwc_qc_prep(struct ata_queued_cmd *qc) +-{ +- if ((qc->dma_dir == DMA_NONE) || (qc->tf.protocol == ATA_PROT_PIO)) +- return; +- +-#ifdef DEBUG_NCQ +- if (qc->tag > 0) +- dev_info(qc->ap->dev, "%s: qc->tag=%d ap->active_tag=0x%08x\n", +- __func__, qc->tag, qc->ap->link.active_tag); +- +- return ; +-#endif +-} +- + static void sata_dwc_error_handler(struct ata_port *ap) + { + ata_sff_error_handler(ap); +@@ -1125,17 +1134,22 @@ static int sata_dwc_hardreset(struct ata_link *link, unsigned int *class, + sata_dwc_enable_interrupts(hsdev); + + /* Reconfigure the DMA control register */ +- out_le32(&hsdev->sata_dwc_regs->dmacr, +- SATA_DWC_DMACR_TXRXCH_CLEAR); ++ sata_dwc_writel(&hsdev->sata_dwc_regs->dmacr, ++ SATA_DWC_DMACR_TXRXCH_CLEAR); + + /* Reconfigure the DMA Burst Transaction Size register */ +- out_le32(&hsdev->sata_dwc_regs->dbtsr, +- SATA_DWC_DBTSR_MWR(AHB_DMA_BRST_DFLT) | +- SATA_DWC_DBTSR_MRD(AHB_DMA_BRST_DFLT)); ++ sata_dwc_writel(&hsdev->sata_dwc_regs->dbtsr, ++ SATA_DWC_DBTSR_MWR(AHB_DMA_BRST_DFLT) | ++ SATA_DWC_DBTSR_MRD(AHB_DMA_BRST_DFLT)); + + return ret; + } + ++static void sata_dwc_dev_select(struct ata_port *ap, unsigned int device) ++{ ++ /* SATA DWC is master only */ ++} ++ + /* + * scsi mid-layer and libata interface structures + */ +@@ -1148,7 +1162,13 @@ static struct scsi_host_template sata_dwc_sht = { + */ + .sg_tablesize = LIBATA_MAX_PRD, + /* .can_queue = ATA_MAX_QUEUE, */ +- .dma_boundary = ATA_DMA_BOUNDARY, ++ /* ++ * Make sure a LLI block is not created that will span 8K max FIS ++ * boundary. If the block spans such a FIS boundary, there is a chance ++ * that a DMA burst will cross that boundary -- this results in an ++ * error in the host controller. ++ */ ++ .dma_boundary = 0x1fff /* ATA_DMA_BOUNDARY */, + }; + + static struct ata_port_operations sata_dwc_ops = { +@@ -1157,7 +1177,6 @@ static struct ata_port_operations sata_dwc_ops = { + .error_handler = sata_dwc_error_handler, + .hardreset = sata_dwc_hardreset, + +- .qc_prep = sata_dwc_qc_prep, + .qc_issue = sata_dwc_qc_issue, + + .scr_read = sata_dwc_scr_read, +@@ -1166,6 +1185,8 @@ static struct ata_port_operations sata_dwc_ops = { + .port_start = sata_dwc_port_start, + .port_stop = sata_dwc_port_stop, + ++ .sff_dev_select = sata_dwc_dev_select, ++ + .bmdma_setup = sata_dwc_bmdma_setup, + .bmdma_start = sata_dwc_bmdma_start, + }; +@@ -1184,13 +1205,14 @@ static int sata_dwc_probe(struct platform_device *ofdev) + struct sata_dwc_device *hsdev; + u32 idr, versionr; + char *ver = (char *)&versionr; +- u8 __iomem *base; ++ void __iomem *base; + int err = 0; + int irq; + struct ata_host *host; + struct ata_port_info pi = sata_dwc_port_info[0]; + const struct ata_port_info *ppi[] = { &pi, NULL }; + struct device_node *np = ofdev->dev.of_node; ++ struct resource *res; + + /* Allocate DWC SATA device */ + host = ata_host_alloc_pinfo(&ofdev->dev, ppi, SATA_DWC_MAX_PORTS); +@@ -1201,57 +1223,33 @@ static int sata_dwc_probe(struct platform_device *ofdev) + host->private_data = hsdev; + + /* Ioremap SATA registers */ +- base = of_iomap(np, 0); +- if (!base) { ++ res = platform_get_resource(ofdev, IORESOURCE_MEM, 0); ++ base = devm_ioremap_resource(&ofdev->dev, res); ++ if (IS_ERR(base)) { + dev_err(&ofdev->dev, + "ioremap failed for SATA register address\n"); +- return -ENODEV; ++ return PTR_ERR(base); + } +- hsdev->reg_base = base; + dev_dbg(&ofdev->dev, "ioremap done for SATA register address\n"); + + /* Synopsys DWC SATA specific Registers */ +- hsdev->sata_dwc_regs = (void *__iomem)(base + SATA_DWC_REG_OFFSET); ++ hsdev->sata_dwc_regs = base + SATA_DWC_REG_OFFSET; ++ hsdev->dmadr = res->start + SATA_DWC_REG_OFFSET + offsetof(struct sata_dwc_regs, dmadr); + + /* Setup port */ + host->ports[0]->ioaddr.cmd_addr = base; + host->ports[0]->ioaddr.scr_addr = base + SATA_DWC_SCR_OFFSET; +- host_pvt.scr_addr_sstatus = base + SATA_DWC_SCR_OFFSET; +- sata_dwc_setup_port(&host->ports[0]->ioaddr, (unsigned long)base); ++ sata_dwc_setup_port(&host->ports[0]->ioaddr, base); + + /* Read the ID and Version Registers */ +- idr = in_le32(&hsdev->sata_dwc_regs->idr); +- versionr = in_le32(&hsdev->sata_dwc_regs->versionr); ++ idr = sata_dwc_readl(&hsdev->sata_dwc_regs->idr); ++ versionr = sata_dwc_readl(&hsdev->sata_dwc_regs->versionr); + dev_notice(&ofdev->dev, "id %d, controller version %c.%c%c\n", + idr, ver[0], ver[1], ver[2]); + +- /* Get SATA DMA interrupt number */ +- hsdev->dma->irq = irq_of_parse_and_map(np, 1); +- if (hsdev->dma->irq == NO_IRQ) { +- dev_err(&ofdev->dev, "no SATA DMA irq\n"); +- err = -ENODEV; +- goto error_iomap; +- } +- +- /* Get physical SATA DMA register base address */ +- hsdev->dma->regs = of_iomap(np, 1); +- if (!hsdev->dma->regs) { +- dev_err(&ofdev->dev, +- "ioremap failed for AHBDMA register address\n"); +- err = -ENODEV; +- goto error_iomap; +- } +- + /* Save dev for later use in dev_xxx() routines */ + hsdev->dev = &ofdev->dev; + +- hsdev->dma->dev = &ofdev->dev; +- +- /* Initialize AHB DMAC */ +- err = dw_dma_probe(hsdev->dma, NULL); +- if (err) +- goto error_dma_iomap; +- + /* Enable SATA Interrupts */ + sata_dwc_enable_interrupts(hsdev); + +@@ -1263,6 +1261,25 @@ static int sata_dwc_probe(struct platform_device *ofdev) + goto error_out; + } + ++#ifdef CONFIG_SATA_DWC_OLD_DMA ++ if (!of_find_property(np, "dmas", NULL)) { ++ err = sata_dwc_dma_init_old(ofdev, hsdev); ++ if (err) ++ goto error_out; ++ } ++#endif ++ ++ hsdev->phy = devm_phy_optional_get(hsdev->dev, "sata-phy"); ++ if (IS_ERR(hsdev->phy)) { ++ err = PTR_ERR(hsdev->phy); ++ hsdev->phy = NULL; ++ goto error_out; ++ } ++ ++ err = phy_init(hsdev->phy); ++ if (err) ++ goto error_out; ++ + /* + * Now, register with libATA core, this will also initiate the + * device discovery process, invoking our port_start() handler & +@@ -1276,12 +1293,7 @@ static int sata_dwc_probe(struct platform_device *ofdev) + return 0; + + error_out: +- /* Free SATA DMA resources */ +- dw_dma_remove(hsdev->dma); +-error_dma_iomap: +- iounmap(hsdev->dma->regs); +-error_iomap: +- iounmap(base); ++ phy_exit(hsdev->phy); + return err; + } + +@@ -1293,11 +1305,13 @@ static int sata_dwc_remove(struct platform_device *ofdev) + + ata_host_detach(host); + ++ phy_exit(hsdev->phy); ++ ++#ifdef CONFIG_SATA_DWC_OLD_DMA + /* Free SATA DMA resources */ +- dw_dma_remove(hsdev->dma); ++ sata_dwc_dma_exit_old(hsdev); ++#endif + +- iounmap(hsdev->dma->regs); +- iounmap(hsdev->reg_base); + dev_dbg(&ofdev->dev, "done\n"); + return 0; + } +-- +2.8.1 +