Skip to contentSkip to conversation

ata: Add APM X-Gene SoC SATA host controller support

PATCH Superseded Loc Ho 2 participants 10 messages Nov 21, 2013 – Nov 25, 2013 2 messages not in the archive

Subsystems (3)

v4 of 15 Superseded Latest is v15 →

lore
Share & tools
lore

Loc Ho

author
#0 lore raw

v4 cover ata: Add APM X-Gene SoC SATA host controller support

ata: Add APM X-Gene SoC SATA host controller support

This patch adds support for the APM X-Gene SoC SATA host controller. In order for the host controller to work, the corresponding SATA PHY driver musts also be available.

v4:
* Remove the ID property in DT
* Remove the temporary PHY direct function call and use PHY function
* Change printk to pr_debug
* Move the IOB flush addresses into the DT
* Remove the parameters retrieval function as no longer needed
* Remove the header file as no longer needed
* Require v2 patch of the SATA PHY driver. Require slightly modification in the Kconfig as it is moved to folder driver/phy and use Kconfig PHY_XGENE_SATA instead SATA_XGENE_PHY.

v3:
* Move out the SATA PHY to another driver
* Remove the clock-cells entry from DTS
* Remove debug wrapper
* Remove delay functions wrapper
* Clean up resource and IRQ query
* Remove query clock name
* Switch to use dma_set_mask/dma_coherent_mask
* Remove un-necessary devm_kfree
* Update GPL license header to v2
* Spilt up function xgene_ahci_hardreset
* Spilt up function xgene_ahci_probe
* Remove all reference of CONFIG_ARCH_MSLIM
* Clean up chip revision code

v2:
* Clean up file sata_xgene.c with Lindent and etc
* Clean up file sata_xgene_serdes.c with Lindent and etc
* Add description to each patch

v1:
* inital version

---
Loc Ho (4):
ata: Export required functions by APM X-Gene SATA driver Documentation:: Add documentation for APM X-Gene SoC SATA host
controller DTS binding
ata: Add APM X-Gene SoC SATA host controller driver
arm64: Add APM X-Gene SoC SATA host controller DTS entries

 .../devicetree/bindings/ata/apm-xgene.txt          |   56 +
 arch/arm64/boot/dts/apm-storm.dtsi                 |   33 +
 drivers/ata/Kconfig                                |    8 +
 drivers/ata/Makefile                               |    1 +
 drivers/ata/ahci.h                                 |    6 +
 drivers/ata/libahci.c                              |   13 +-
 drivers/ata/sata_xgene.c                           | 1128 ++++++++++++++++++++
 7 files changed, 1240 insertions(+), 5 deletions(-)
 create mode 100644 Documentation/devicetree/bindings/ata/apm-xgene.txt
 create mode 100644 drivers/ata/sata_xgene.c
author
#1 lore raw

PATCH v4 1/4 ata: Export required functions by APM X-Gene SATA driver

ata: Export required functions by APM X-Gene SATA driver

This patch exports functions required by APM X-Gene SoC SATA host controller driver to avoid duplication of code.

---
 drivers/ata/ahci.h    |    6 ++++++
 drivers/ata/libahci.c |   13 ++++++++-----
 2 files changed, 14 insertions(+), 5 deletions(-)
drivers/ata/ahci.h +6 -0
index 2289efd..89003f6 100644
@@ -370,6 +370,12 @@ irqreturn_t ahci_thread_fn(int irq, void *dev_instance);
 void ahci_print_info(struct ata_host *host, const char *scc_s);
 int ahci_host_activate(struct ata_host *host, int irq, unsigned int n_msis);
 void ahci_error_handler(struct ata_port *ap);
+void ahci_sw_activity(struct ata_link *link);
+int ahci_scr_write(struct ata_link *link, unsigned int sc_reg, u32 val);
+void ahci_error_intr(struct ata_port *ap, u32 irq_stat);
+int ahci_exec_polled_cmd(struct ata_port *ap, int pmp,
+			 struct ata_taskfile *tf, int is_cmd, u16 flags,
+			 unsigned long timeout_msec);
 
 static inline void __iomem *__ahci_port_base(struct ata_host *host,
 					     unsigned int port_no)
drivers/ata/libahci.c +8 -5
index c482f8c..b5844b9 100644
@@ -68,7 +68,6 @@ static ssize_t ahci_transmit_led_message(struct ata_port *ap, u32 state,
 
 
 static int ahci_scr_read(struct ata_link *link, unsigned int sc_reg, u32 *val);
-static int ahci_scr_write(struct ata_link *link, unsigned int sc_reg, u32 val);
 static unsigned int ahci_qc_issue(struct ata_queued_cmd *qc);
 static bool ahci_qc_fill_rtf(struct ata_queued_cmd *qc);
 static int ahci_port_start(struct ata_port *ap);
@@ -553,7 +552,7 @@ static int ahci_scr_read(struct ata_link *link, unsigned int sc_reg, u32 *val)
 	return -EINVAL;
 }
 
-static int ahci_scr_write(struct ata_link *link, unsigned int sc_reg, u32 val)
+int ahci_scr_write(struct ata_link *link, unsigned int sc_reg, u32 val)
 {
 	void __iomem *port_mmio = ahci_port_base(link->ap);
 	int offset = ahci_scr_offset(link->ap, sc_reg);
@@ -564,6 +563,7 @@ static int ahci_scr_write(struct ata_link *link, unsigned int sc_reg, u32 val)
 	}
 	return -EINVAL;
 }
+EXPORT_SYMBOL_GPL(ahci_scr_write);
 
 void ahci_start_engine(struct ata_port *ap)
 {
@@ -869,7 +869,7 @@ int ahci_reset_controller(struct ata_host *host)
 }
 EXPORT_SYMBOL_GPL(ahci_reset_controller);
 
-static void ahci_sw_activity(struct ata_link *link)
+void ahci_sw_activity(struct ata_link *link)
 {
 	struct ata_port *ap = link->ap;
 	struct ahci_port_priv *pp = ap->private_data;
@@ -882,6 +882,7 @@ static void ahci_sw_activity(struct ata_link *link)
 	if (!timer_pending(&emp->timer))
 		mod_timer(&emp->timer, jiffies + msecs_to_jiffies(10));
 }
+EXPORT_SYMBOL_GPL(ahci_sw_activity);
 
 static void ahci_sw_activity_blink(unsigned long arg)
 {
@@ -1239,7 +1240,7 @@ int ahci_kick_engine(struct ata_port *ap)
 }
 EXPORT_SYMBOL_GPL(ahci_kick_engine);
 
-static int ahci_exec_polled_cmd(struct ata_port *ap, int pmp,
+int ahci_exec_polled_cmd(struct ata_port *ap, int pmp,
 				struct ata_taskfile *tf, int is_cmd, u16 flags,
 				unsigned long timeout_msec)
 {
@@ -1268,6 +1269,7 @@ static int ahci_exec_polled_cmd(struct ata_port *ap, int pmp,
 
 	return 0;
 }
+EXPORT_SYMBOL_GPL(ahci_exec_polled_cmd);
 
 int ahci_do_softreset(struct ata_link *link, unsigned int *class,
 		      int pmp, unsigned long deadline,
@@ -1568,7 +1570,7 @@ static void ahci_fbs_dec_intr(struct ata_port *ap)
 		dev_err(ap->host->dev, "failed to clear device error\n");
 }
 
-static void ahci_error_intr(struct ata_port *ap, u32 irq_stat)
+void ahci_error_intr(struct ata_port *ap, u32 irq_stat)
 {
 	struct ahci_host_priv *hpriv = ap->host->private_data;
 	struct ahci_port_priv *pp = ap->private_data;
@@ -1678,6 +1680,7 @@ static void ahci_error_intr(struct ata_port *ap, u32 irq_stat)
 	} else
 		ata_port_abort(ap);
 }
+EXPORT_SYMBOL_GPL(ahci_error_intr);
 
 static void ahci_handle_port_interrupt(struct ata_port *ap,
 				       void __iomem *port_mmio, u32 status)
Signature · 1 line
1.5.5
author
#2 lore raw

PATCH v4 2/4 Documentation:: Add documentation for APM X-Gene SoC SATA host controller DTS binding

Documentation:: Add documentation for APM X-Gene SoC SATA host controller DTS binding

---
 .../devicetree/bindings/ata/apm-xgene.txt          |   56 ++++++++++++++++++++
 1 files changed, 56 insertions(+), 0 deletions(-)
 create mode 100644 Documentation/devicetree/bindings/ata/apm-xgene.txt
Documentation/devicetree/bindings/ata/apm-xgene.txt new file +56 -0
new file mode 100644
index 0000000..ec470b8
@@ -0,0 +1,56 @@
+* APM X-Gene 6.0 Gb/s SATA host controller nodes
+
+SATA host controller nodes are defined to describe on-chip Serial ATA
+controllers. Each SATA controller (pair of ports) have its own node.
+
+Required properties:
+- compatible		: Shall be "apm,xgene-ahci"
+- reg			: First memory resource shall be the AHCI memory
+			  resource.
+			  Second memory resource shall be the host controller
+			  memory resource.
+- interrupt-parent	: Interrupt controller
+- interrupts		: Interrupt mapping for SATA host controller IRQ
+- clocks		: Reference to the clock entry
+- phys			: PHY reference
+- phy-names		: Name of the PHY reference. The name should be
+			  formed by "sataphy" plus lower case hex for the
+			  lower 32-bit PHY CSR address (2nd memory resource)
+
+Optional properties:
+- status		: Shall be "ok" if enabled or "na" if disabled.
+			  Default is "ok".
+
+Example:
+		sata0: sata at 1a000000 {
+			compatible = "apm,xgene-ahci";
+			reg = <0x0 0x1a000000 0x0 0x100000>,
+			      <0x0 0x1f210000 0x0 0x10000>;
+			interrupt-parent = <&gic>;
+			interrupts = <0x0 0x86 0x4>;
+			status = "disabled";
+			phys = <&sataphy0>;
+			phy-names = "sataphy1f210000";
+		};
+
+		sata1: sata at 1a400000 {
+			compatible = "apm,xgene-ahci";
+			reg = <0x0 0x1a400000 0x0 0x100000>,
+			      <0x0 0x1f220000 0x0 0x10000>;
+			interrupt-parent = <&gic>;
+			interrupts = <0x0 0x87 0x4>;
+			status = "ok";
+			phys = <&sataphy1>;
+			phy-names = "sataphy1f220000";
+		};
+
+		sata2: sata at 1a800000 {
+			compatible = "apm,xgene-ahci";
+			reg = <0x0 0x1a800000 0x0 0x100000>,
+			      <0x0 0x1f230000 0x0 0x10000>;
+			interrupt-parent = <&gic>;
+			interrupts = <0x0 0x88 0x4>;
+			status = "ok";
+			phys = <&sataphy2>;
+			phy-names = "sataphy1f230000";
+		};
Signature · 1 line
1.5.5
author
#3 lore raw

PATCH v4 3/4 ata: Add APM X-Gene SoC SATA host controller driver

ata: Add APM X-Gene SoC SATA host controller driver

This patch adds support for the APM X-Gene SoC SATA host controller driver. It requires the corresponding APM X-Gene SoC SATA PHY driver.

---
 drivers/ata/Kconfig      |    8 +
 drivers/ata/Makefile     |    1 +
 drivers/ata/sata_xgene.c | 1128 ++++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 1137 insertions(+), 0 deletions(-)
 create mode 100644 drivers/ata/sata_xgene.c
drivers/ata/Kconfig +8 -0
index 4e73772..318d113 100644
@@ -106,6 +106,14 @@ config AHCI_IMX
 
 	  If unsure, say N.
 
+config SATA_XGENE
+	tristate "APM X-Gene 6.0Gbps SATA host controller support"
+	depends on ARM64 || COMPILE_TEST
+	select SATA_AHCI_PLATFORM
+	select PHY_XGENE_SATA
+	help
+	  This option enables support for APM X-Gene SoC SATA host controller.
+
 config SATA_FSL
 	tristate "Freescale 3.0Gbps SATA support"
 	depends on FSL_SOC
drivers/ata/Makefile +1 -0
index 46518c6..1c0fabe 100644
@@ -11,6 +11,7 @@ obj-$(CONFIG_SATA_SIL24)	+= sata_sil24.o
 obj-$(CONFIG_SATA_DWC)		+= sata_dwc_460ex.o
 obj-$(CONFIG_SATA_HIGHBANK)	+= sata_highbank.o libahci.o
 obj-$(CONFIG_AHCI_IMX)		+= ahci_imx.o
+obj-$(CONFIG_SATA_XGENE)	+= sata_xgene.o
 
 # SFF w/ custom DMA
 obj-$(CONFIG_PDC_ADMA)		+= pdc_adma.o
drivers/ata/sata_xgene.c new file +1128 -0

1,128 lines not on this page · raw message

Signature · 1 line
1.5.5
author
#4 lore raw

PATCH 1/1 arm64: Add APM X-Gene SoC SATA host controller DTS entries

arm64: Add APM X-Gene SoC SATA host controller DTS entries

---
 arch/arm64/boot/dts/apm-storm.dtsi |   33 +++++++++++++++++++++++++++++++++
 1 files changed, 33 insertions(+), 0 deletions(-)
arch/arm64/boot/dts/apm-storm.dtsi +33 -0
index 359d7b6..592485c 100644
@@ -193,5 +193,38 @@
 			reg = <0x0 0x17000014 0x0 0x100>;
 			mask = <0x1>;
 		};
+
+		sata0: sata at 1a000000 {
+			compatible = "apm,xgene-ahci";
+			reg = <0x0 0x1a000000 0x0 0x100000>,
+			      <0x0 0x1f210000 0x0 0x10000>;
+			interrupt-parent = <&gic>;
+			interrupts = <0x0 0x86 0x4>;
+			status = "disabled";
+			phys = <&sataphy0>;
+			phy-names = "sataphy1f210000";
+		};
+
+		sata1: sata at 1a400000 {
+			compatible = "apm,xgene-ahci";
+			reg = <0x0 0x1a400000 0x0 0x100000>,
+			      <0x0 0x1f220000 0x0 0x10000>;
+			interrupt-parent = <&gic>;
+			interrupts = <0x0 0x87 0x4>;
+			status = "ok";
+			phys = <&sataphy1>;
+			phy-names = "sataphy1f220000";
+		};
+
+		sata2: sata at 1a800000 {
+			compatible = "apm,xgene-ahci";
+			reg = <0x0 0x1a800000 0x0 0x100000>,
+			      <0x0 0x1f230000 0x0 0x10000>;
+			interrupt-parent = <&gic>;
+			interrupts = <0x0 0x88 0x4>;
+			status = "ok";
+			phys = <&sataphy2>;
+			phy-names = "sataphy1f230000";
+		};
 	};
 };
Signature · 1 line
1.5.5
#5 lore raw

PATCH v4 3/4 ata: Add APM X-Gene SoC SATA host controller driver

In reply to Loc Ho

drivers/ata/sata_xgene.c

Quoted 10 lines · Loc Ho
drivers/ata/sata_xgene.c new file +10 -0
+struct xgene_ahci_context {
+	struct ahci_host_priv  hpriv;
+	struct device *dev;
+	int irq;		/* IRQ */
+	void __iomem *csr_base;	/* CSR base address of IP */
+	u64 csr_phys;		/* Physical address of CSR base address */
+	void __iomem *mmio_base; /* AHCI I/O base address */
+	u64 mmio_phys;		/* Physical address of MMIO base address */
+	void __iomem *ahbc_csr_base; /* Used for IOB flushing if non-zero */
+	void __iomem *ahbc_io_base;  /* Used for IOB flushing if non-zero *

I still think the flushing should be split out into a separate subsystem of some sort, or into common code. Can you explain why it is needed in this driver, and what other parts of your SoC also need to do the same thing?

Maybe we can have custom dma_map_ops for your platform so the flushing automatically happens in dma_unmap_sg().

drivers/ata/sata_xgene.c line 110

Quoted 6 lines
drivers/ata/sata_xgene.c new file +6 -0
+/* These wrapper existed for future expansion to support running on MSLIM
+   cores. */
+#define xgene_ahci_fill_cmd_slot	ahci_fill_cmd_slot
+#define xgene_ahci_exec_polled_cmd	ahci_exec_polled_cmd
+#define xgene_ahci_to_axi(x)		(x)
+#define xgene_ahci_dflush(x, ...)

In general, I recommend against adding code "for future expansion", because you never actually know what that future expansion is going to be like until the patches have been reviewed.

drivers/ata/sata_xgene.c line 129

Quoted 6 lines
drivers/ata/sata_xgene.c new file +6 -0
+static void xgene_wr_flush(void *addr, u32 val)
+{
+	writel(val, addr);
+	pr_debug("X-Gene SATA CSR WR: 0x%p value: 0x%08x\n", addr, val);
+	val = readl(addr);
+}

This seems to be a rather expensive operation. Can you explain what the extra flush is trying to achieve here? Normally you'd want to eliminate any readl() from high-performance device drivers if you can. Is this used in any fast path?

Quoted 10 lines
drivers/ata/sata_xgene.c new file +10 -0
+static int xgene_ahci_init_memram(struct xgene_ahci_context *ctx)
+{
+	void *diagcsr = ctx->csr_base + SATA_DIAG_OFFSET;
+	int timeout;
+	u32 val;
+
+	xgene_rd(diagcsr + REGSPEC_CFG_MEM_RAM_SHUTDOWN_ADDR, &val);
+	if (val == 0) {
+		dev_dbg(ctx->dev, "memory already released from shutdown\n");
+		return 0;

drivers/ata/sata_xgene.c line 159

Quoted 10 lines
drivers/ata/sata_xgene.c new file +10 -0
+	}
+	dev_dbg(ctx->dev, "Release memory from shutdown\n");
+	/* SATA controller memory in shutdown. Remove from shutdown. */
+	xgene_wr_flush(diagcsr + REGSPEC_CFG_MEM_RAM_SHUTDOWN_ADDR, 0x00);
+	timeout = SATA_RESET_MEM_RAM_TO;
+	do {
+		xgene_rd(diagcsr + REGSPEC_BLOCK_MEM_RDY_ADDR, &val);
+		if (val != 0xFFFFFFFF)
+			udelay(1);
+	} while (val != 0xFFFFFFFF && timeout-- > 0);

With SATA_RESET_MEM_RAM_TO == 100000, this is an awfully long busy-loop. Can you replace the udelay with usleep_range() providing a reasonable wide range, and the replace the retry count with a time_before(jiffies(), timeout) comparision?

drivers/ata/sata_xgene.c line 430

Quoted 11 lines
drivers/ata/sata_xgene.c new file +11 -0
+/* Flush the IOB to ensure all SATA controller writes completed before
+   servicing the completed command. This is needed due to the possibility
+   that interrupt serviced before the data actually written to the cache/DDR.
+   Writes from the IP to the CPU domain is not synchronized with the IRQ
+   line or the IP core toggled the CI bits before the data write completed. */
+static int xgene_ahci_iob_flush(struct xgene_ahci_context *ctx)
+{
+	if (ctx->ahbc_io_base)
+		readl(ctx->ahbc_io_base);
+	return 0;
+}

Normally the synchronization should be guaranteed by using MSI interrupts rather than edge or level interrupt pins. Do you have MSI support in your hardware? Using that would get rid of this ugly hack and likely improve performance significantly.

If you don't have MSI, why isn't the PORT_IRQ_STAT read sufficient to do the flush? Like the MSI message, that should serialize any DMA from this device.

Quoted 8 lines
drivers/ata/sata_xgene.c new file +8 -0
+static unsigned int xgene_ahci_fill_sg(struct ata_queued_cmd *qc,
+				       void *cmd_tbl)
+{
+	struct scatterlist *sg;
+	struct ahci_sg *ahci_sg = cmd_tbl + AHCI_CMD_TBL_HDR_SZ;
+	unsigned int si;
+
+	/*

drivers/ata/sata_xgene.c line 449

Quoted 10 lines
drivers/ata/sata_xgene.c new file +10 -0
+	 * Next, the S/G list.
+	 */
+	for_each_sg(qc->sg, sg, qc->n_elem, si) {
+		dma_addr_t addr = sg_dma_address(sg);
+		u64 dma_addr = xgene_ahci_to_axi(addr);
+		u32 sg_len = sg_dma_len(sg);
+		ahci_sg[si].addr = cpu_to_le32(dma_addr & 0xffffffff);
+		ahci_sg[si].addr_hi = cpu_to_le32((dma_addr >> 16) >> 16);
+		ahci_sg[si].flags_size = cpu_to_le32(sg_len - 1);
+		xgene_ahci_dflush((void *) __va(addr), sg_len);

(void *) __va(addr) is /not/ generally speaking a valid pointer, you cannot rely on this to work, e.g. when you have an IOMMU, or if someone tries to reuse the driver for a 32-bit system where the memory is in highmem.

Also, I suspect the xgene_ahci_to_axi() call is what you incorrectly do instead of using the dma_map_ops provide the correct dma_addr_t.

Note the sg_list is populated using dma_map_sg(), which is supposed to fill the dma_addr_t field in the address space of the current device, and also do all the necessary flushing.

I suspect a significant portion of your ahci driver (and by extension, all other dma master drivers on the same bus) can be removed after you implement the dma_map_ops for your hardware.

drivers/ata/sata_xgene.c line 561

Quoted 10 lines
drivers/ata/sata_xgene.c new file +10 -0
+	tf.ctl |= ATA_SRST;
+	/* Must call X-Gene version in case it needs to flush the cache for
+	   MSLIM as well as AXI address translation */
+	if (xgene_ahci_exec_polled_cmd(ap, pmp, &tf, 0,
+				       AHCI_CMD_RESET | AHCI_CMD_CLR_BUSY,
+				       msecs)) {
+		rc = -EIO;
+		reason = "1st FIS failed";
+		goto fail;
+	}

This part seems to fall in the same category, according to the comment.

drivers/ata/sata_xgene.c line 856

Quoted 10 lines
drivers/ata/sata_xgene.c new file +10 -0
+	/* When both ACPi and DTS are enabled, custom ACPI built-in ACPI
+	   table, and booting via DTS, we need to skip the probe of the
+	   built-in ACPI table probe. */
+	if (!efi_enabled(EFI_BOOT) && pdev->dev.of_node == NULL)
+		return -ENODEV;
+
+	/* Check if the entry is disabled for OF only */
+	if (!efi_enabled(EFI_BOOT) &&
+	    !of_device_is_available(pdev->dev.of_node))
+		return -ENODEV;

You seem to do the same check twice, with the first one being incorrect in case of_node is present but disabled.

drivers/ata/sata_xgene.c line 867

Quoted 11 lines
drivers/ata/sata_xgene.c new file +11 -0
+#if defined(CONFIG_ACPI)
+	if (efi_enabled(EFI_BOOT)) {
+		struct acpi_device *device;
+
+		if (acpi_bus_get_device(ACPI_HANDLE(&pdev->dev), &device))
+			return -ENODEV;
+
+		if (acpi_bus_get_status(device) || !device->status.present)
+			return -ENODEV;
+	}
+#endif

This will fail when ACPI is enabled and you boot on a UEFI system and DT probing. I think that would be the common case for a while.

I also suspect this driver won't work with ACPI as-is, as it currently relies on the phy driver and won't use that with ACPI. IMHO it would be easier to defer the ACPI enablement for now. It was good that you had it in here in the initial version as Jon recommended, but for the purpose of merging the driver it seems like more of a distraction.

Quoted 5 lines
drivers/ata/sata_xgene.c new file +5 -0
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	if (!res) {
+		dev_err(&pdev->dev, "no AHCI MMIO resource address\n");
+		goto error;
+	}

drivers/ata/sata_xgene.c line 890

Quoted 10 lines
drivers/ata/sata_xgene.c new file +10 -0
+	hpriv->mmio_phys = res->start;
+	hpriv->mmio_base = devm_ioremap(&pdev->dev, res->start,
+					resource_size(res));
+	if (!hpriv->mmio_base) {
+		dev_err(&pdev->dev, "can't map AHCI MMIO resource\n");
+		rc = -ENOMEM;
+		goto error;
+	}
+	hpriv->hpriv.mmio = hpriv->mmio_base;
+

You can use devm_ioremap_resource() here now to replace the longer construct.
I think you can also remove the hpriv->mmio_phys field as that is used only
for informational purposes (use the dev_name instead).

drivers/ata/sata_xgene.c line 903

Quoted 13 lines
drivers/ata/sata_xgene.c new file +13 -0
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
+	if (!res) {
+		dev_err(&pdev->dev, "no host resource address\n");
+		goto error;
+	}
+	hpriv->csr_phys = res->start;
+	hpriv->csr_base = devm_ioremap(&pdev->dev, res->start,
+				       resource_size(res));
+	if (!hpriv->csr_base) {
+		dev_err(&pdev->dev, "can't map host resource\n");
+		rc = -ENOMEM;
+		goto error;
+	}

same here and for the other resources.

drivers/ata/sata_xgene.c line 1024

Quoted 3 lines
drivers/ata/sata_xgene.c new file +3 -0
+	/* Setup DMA mask */
+	dma_set_mask(&pdev->dev, DMA_BIT_MASK(64));
+	dma_set_coherent_mask(&pdev->dev, DMA_BIT_MASK(64));

You need to check the errors here: this will fail if the bus underneath is not 64-bit. I would also recommend using the new dma_set_mask_and_coherent() helper that was introduced in this merge window.

Quoted 17 lines
drivers/ata/sata_xgene.c new file +17 -0
+#ifdef CONFIG_PM
+static int xgene_ahci_suspend(struct platform_device *pdev, pm_message_t state)
+{
+	struct ata_host *host = dev_get_drvdata(&pdev->dev);
+	struct xgene_ahci_context *hpriv = host->private_data;
+	void __iomem *mmio = hpriv->mmio_base;
+	u32 ctl;
+	int rc;
+
+	dev_dbg(&pdev->dev, "suspend\n");
+
+	/*
+	 * AHCI spec rev1.1 section 8.3.3:
+	 * Software must disable interrupts prior to requesting a
+	 * transition of the HBA to D3 state.
+	 */
+	ctl = readl(mmio + HOST_CTL);

drivers/ata/sata_xgene.c line 1071

Quoted 10 lines
drivers/ata/sata_xgene.c new file +10 -0
+	ctl &= ~HOST_IRQ_EN;
+	writel(ctl, mmio + HOST_CTL);
+	readl(mmio + HOST_CTL);	/* flush */
+
+	rc = ata_host_suspend(host, state);
+	if (rc)
+		return rc;
+
+	return 0;
+}

Nothing in this function seems to be xgene specific, and you don't use the hpriv pointer. Can you use a generic libata function instead, or add this one to libata if it can be reused for other drivers?

Quoted 1 line

+static int xgene_ahci_resume(struct platform_device *pdev)

Same here.

Arnd

A message that is not in the archive (sent off-list or not yet received): CAPw-ZTnt0QnXxb_dkDpkXbz4xFOcMM2Z-oOVJdH9o9u3S+DuPg@mail.gmail.com

#7 lore raw

PATCH v4 3/4 ata: Add APM X-Gene SoC SATA host controller driver

Quoted 34 lines · Loc Ho
Quoted 10 lines

drivers/ata/sata_xgene.c

Quoted 10 lines
drivers/ata/sata_xgene.c new file +9 -0
+struct xgene_ahci_context {
+     struct ahci_host_priv  hpriv;
+     struct device *dev;
+     int irq;                /* IRQ */
+     void __iomem *csr_base; /* CSR base address of IP */
+     u64 csr_phys;           /* Physical address of CSR base address */
+     void __iomem *mmio_base; /* AHCI I/O base address */
+     u64 mmio_phys;          /* Physical address of MMIO base address */
+     void __iomem *ahbc_csr_base; /* Used for IOB flushing if non-zero
Quoted 9 lines

*/

Quoted 1 line

+ void __iomem *ahbc_io_base; /* Used for IOB flushing if non-zero *

I still think the flushing should be split out into a separate subsystem of some
sort, or into common code. Can you explain why it is needed in this driver, and
what other parts of your SoC also need to do the same thing?

[Loc Ho]
Due to HW bug, the read to flush does not function properly with the SATA and SD IP's. For this reason, to ensure that data has reached the DDR after reading the CI register of the AHCI, we need to flush the bus. This will ensure data for the corresponding CI bit are available before the SATA upper layer consumed the data. The chance of this happen is extremely lower but this will ensure 100% data consistency. As the SD IP is single operation at a time, it will never need this. Also, if it does, by enable the feature itself, any read on the SD register is enough to handle the bus flush. So, SD don't needs this as SATA will enable it. This will not be fix in the first generation of the chip and this explicit flush is required for SATA.

Quoted 1 line
Quoted 7 lines
Quoted 4 lines

Maybe we can have custom dma_map_ops for your platform so the flushing automatically
happens in dma_unmap_sg().

[Loc Ho]
We don't want this for other IP as it will affact performance for the other IP's.

The dma_map_ops can be set per device, so it would still be possible to do, but if the AHCI is the only one that needs it, I guess it makes sense to do it the currect way.

Quoted 14 lines
Quoted 5 lines
Quoted 5 lines
drivers/ata/sata_xgene.c new file +5 -0
+static void xgene_wr_flush(void *addr, u32 val)
+{
+     writel(val, addr);
+     pr_debug("X-Gene SATA CSR WR: 0x%p value: 0x%08x\n", addr, val);
+     val = readl(addr);
Quoted 9 lines
Quoted 1 line

+}

This seems to be a rather expensive operation. Can you explain what the extra
flush is trying to achieve here? Normally you'd want to eliminate any readl()
from high-performance device drivers if you can. Is this used in any fast path?

Quoted 2 lines

[Loc Ho]
This is only for configuration barrier. Will not be used for IO.

Ok.

Quoted 22 lines
Quoted 22 lines

drivers/ata/sata_xgene.c

Quoted 9 lines
drivers/ata/sata_xgene.c new file +9 -0
+static int xgene_ahci_init_memram(struct xgene_ahci_context *ctx)
+{
+     void *diagcsr = ctx->csr_base + SATA_DIAG_OFFSET;
+     int timeout;
+     u32 val;
+
+     xgene_rd(diagcsr + REGSPEC_CFG_MEM_RAM_SHUTDOWN_ADDR, &val);
+     if (val == 0) {
+             dev_dbg(ctx->dev, "memory already released from

shutdown\n");

Quoted 11 lines
drivers/ata/sata_xgene.c new file +11 -0
+             return 0;
+     }
+     dev_dbg(ctx->dev, "Release memory from shutdown\n");
+     /* SATA controller memory in shutdown. Remove from shutdown. */
+     xgene_wr_flush(diagcsr + REGSPEC_CFG_MEM_RAM_SHUTDOWN_ADDR, 0x00);
+     timeout = SATA_RESET_MEM_RAM_TO;
+     do {
+             xgene_rd(diagcsr + REGSPEC_BLOCK_MEM_RDY_ADDR, &val);
+             if (val != 0xFFFFFFFF)
+                     udelay(1);
+     } while (val != 0xFFFFFFFF && timeout-- > 0);
Quoted 7 lines
Quoted 5 lines

With SATA_RESET_MEM_RAM_TO == 100000, this is an awfully long busy-loop. Can you replace the udelay with usleep_range() providing a reasonable wide range, and the replace the retry count with a time_before(jiffies(), timeout) comparision?

[Loc Ho]
I reduce to 1000. It only takes about one or two reads.

It's still over two miliseconds for the (unlikely) timeout in that case, plus the time it takes to do the 1000 reads. The important part of my comment was about using usleep_range(), which lets another process run in the meantime rather than blocking the CPU.

If you want to keep using a retry count rather than a timeout, don't call it "timeout".

Quoted 20 lines
Quoted 8 lines

Review

Quoted 2 lines
(quoted diff) +2 -0
+/* Flush the IOB to ensure all SATA controller writes completed before
+   servicing the completed command. This is needed due to the

possibility

Quoted 1 line

+ that interrupt serviced before the data actually written to the

cache/DDR.

Review

Quoted 2 lines
(quoted diff) +2 -0
+   Writes from the IP to the CPU domain is not synchronized with the IRQ
+   line or the IP core toggled the CI bits before the data write

completed. */

Quoted 12 lines

drivers/ata/sata_xgene.c line 430

Quoted 6 lines
drivers/ata/sata_xgene.c new file +6 -0
+static int xgene_ahci_iob_flush(struct xgene_ahci_context *ctx)
+{
+     if (ctx->ahbc_io_base)
+             readl(ctx->ahbc_io_base);
+     return 0;
+}

Normally the synchronization should be guaranteed by using MSI interrupts rather than edge or level interrupt pins. Do you have MSI support in your hardware? Using that would get rid of this ugly hack and likely improve performance significantly.

Quoted 3 lines

[Loc Ho]
The SATA IP is built into the chip and NOT on the PCIe bus. The need for this is already mentioned above.

I don't understand the comment about PCIe. MSI should work regardless of where the device resides in the system, unless you have the MSI controller integrated into the PCIe host, which would be a bug because it causes the same problem that you have on this device for any PCIe add-on device (the MSI would no longer flush the a PCIe bus master DMA transfer all the way to memory if the message is consumed by the PCIe host).

Arnd

A message that is not in the archive (sent off-list or not yet received): CAPw-ZTkBovLrQEk_CQzuw03t5P2N2ZecQhK8tkZQM0grGwPxSA@mail.gmail.com

author
#9 lore raw

PATCH v4 3/4 ata: Add APM X-Gene SoC SATA host controller driver

Hi,

Resend in plain text as mailing list reject HTML format.

Quoted 30 lines
Quoted 22 lines
Quoted 22 lines

drivers/ata/sata_xgene.c

Quoted 9 lines
drivers/ata/sata_xgene.c new file +9 -0
+static int xgene_ahci_init_memram(struct xgene_ahci_context *ctx)
+{
+     void *diagcsr = ctx->csr_base + SATA_DIAG_OFFSET;
+     int timeout;
+     u32 val;
+
+     xgene_rd(diagcsr + REGSPEC_CFG_MEM_RAM_SHUTDOWN_ADDR, &val);
+     if (val == 0) {
+             dev_dbg(ctx->dev, "memory already released from

shutdown\n");

Quoted 11 lines
drivers/ata/sata_xgene.c new file +11 -0
+             return 0;
+     }
+     dev_dbg(ctx->dev, "Release memory from shutdown\n");
+     /* SATA controller memory in shutdown. Remove from shutdown. */
+     xgene_wr_flush(diagcsr + REGSPEC_CFG_MEM_RAM_SHUTDOWN_ADDR, 0x00);
+     timeout = SATA_RESET_MEM_RAM_TO;
+     do {
+             xgene_rd(diagcsr + REGSPEC_BLOCK_MEM_RDY_ADDR, &val);
+             if (val != 0xFFFFFFFF)
+                     udelay(1);
+     } while (val != 0xFFFFFFFF && timeout-- > 0);
Quoted 7 lines
Quoted 5 lines

With SATA_RESET_MEM_RAM_TO == 100000, this is an awfully long busy-loop. Can you replace the udelay with usleep_range() providing a reasonable wide range, and the replace the retry count with a time_before(jiffies(), timeout) comparision?

[Loc Ho]
I reduce to 1000. It only takes about one or two reads.

Quoted 8 lines

It's still over two miliseconds for the (unlikely) timeout in that case, plus the time it takes to do the 1000 reads. The important part of my comment was about using usleep_range(), which lets another process run in the meantime rather than blocking the CPU.

If you want to keep using a retry count rather than a timeout, don't call it "timeout".

[Loc Ho]
I will give usleep_range a try.

Quoted 25 lines
Quoted 25 lines
Quoted 24 lines
Quoted 9 lines

Review

Quoted 3 lines
(quoted diff) +2 -0
+/* Flush the IOB to ensure all SATA controller writes completed before
+   servicing the completed command. This is needed due to the

possibility

Quoted 1 line

+ that interrupt serviced before the data actually written to the

cache/DDR.

Review

Quoted 2 lines
(quoted diff) +2 -0
+   Writes from the IP to the CPU domain is not synchronized with the IRQ
+   line or the IP core toggled the CI bits before the data write

completed. */

Quoted 12 lines

drivers/ata/sata_xgene.c line 430

Quoted 6 lines
drivers/ata/sata_xgene.c new file +6 -0
+static int xgene_ahci_iob_flush(struct xgene_ahci_context *ctx)
+{
+     if (ctx->ahbc_io_base)
+             readl(ctx->ahbc_io_base);
+     return 0;
+}

Normally the synchronization should be guaranteed by using MSI interrupts rather than edge or level interrupt pins. Do you have MSI support in your hardware? Using that would get rid of this ugly hack and likely improve performance significantly.

[Loc Ho]
The SATA IP is built into the chip and NOT on the PCIe bus. The need for this is already mentioned above.

Quoted 7 lines
Quoted 6 lines

I don't understand the comment about PCIe. MSI should work regardless of where the device resides in the system, unless you have the MSI controller integrated into the PCIe host, which would be a bug because it causes the same problem that you have on this device for any PCIe add-on device (the MSI would no longer flush the a PCIe bus master DMA transfer all the way to memory if the message is consumed by the PCIe host).

[Loc Ho]
The SATA IP does not support MSI as it is NOT an PCIe interface. The interrupt line is just a direct line into the GIC similar to the UART.

-Loc

#10 lore raw

PATCH v4 2/4 Documentation:: Add documentation for APM X-Gene SoC SATA host controller DTS binding

In reply to Loc Ho
Quoted 6 lines · Loc Ho
Documentation/devicetree/bindings/ata/apm-xgene.txt new file +6 -0
+SATA host controller nodes are defined to describe on-chip Serial ATA
+controllers. Each SATA controller (pair of ports) have its own node.
+
+Required properties:
+- compatible		: Shall be "apm,xgene-ahci"
+- reg			: First memory resource shall be the AHCI memory

Documentation/devicetree/bindings/ata/apm-xgene.txt line 18

Quoted 10 lines
Documentation/devicetree/bindings/ata/apm-xgene.txt new file +10 -0
+			  resource.
+			  Second memory resource shall be the host controller
+			  memory resource.
+- interrupt-parent	: Interrupt controller
+- interrupts		: Interrupt mapping for SATA host controller IRQ
+- clocks		: Reference to the clock entry
+- phys			: PHY reference
+- phy-names		: Name of the PHY reference. The name should be
+			  formed by "sataphy" plus lower case hex for the
+			  lower 32-bit PHY CSR address (2nd memory resource)

I've tried to explain this before, but apparently you missed the point:

Any "*-names" property is supposed to be the identifier inside the device you are describing, *not* an identifier for what it points to, as that already has a name. The idea is that the driver can ask for phy (or clock, irq, memory, etc) "foo" here, with foo identifying the functionality in the device. If you only have one phy, you probably don't want to name it at all. If you imagine a device that was connected to two different phys depending on the mode it's used in (e.g. SATA-II vs SATA-6G), you would list this as

- phy-names : one name per references in the "phys" property.
valid names are "sata-ii" and "sata-6g".

If the generic phy subsystem requires you to name this, I'd suggest either changing the phy subsystem to make the name optional, or to just call it "serdes", as that is the kind of phy you are connected to.

Does this make more sense to you now?

Arnd

author
#11 lore raw

PATCH v4 2/4 Documentation:: Add documentation for APM X-Gene SoC SATA host controller DTS binding

Hi,

Quoted 28 lines
Quoted 6 lines
Documentation/devicetree/bindings/ata/apm-xgene.txt new file +6 -0
+SATA host controller nodes are defined to describe on-chip Serial ATA
+controllers. Each SATA controller (pair of ports) have its own node.
+
+Required properties:
+- compatible         : Shall be "apm,xgene-ahci"
+- reg                        : First memory resource shall be the AHCI memory

Documentation/devicetree/bindings/ata/apm-xgene.txt line 18

Quoted 10 lines
Documentation/devicetree/bindings/ata/apm-xgene.txt new file +10 -0
+                       resource.
+                       Second memory resource shall be the host controller
+                       memory resource.
+- interrupt-parent   : Interrupt controller
+- interrupts         : Interrupt mapping for SATA host controller IRQ
+- clocks             : Reference to the clock entry
+- phys                       : PHY reference
+- phy-names          : Name of the PHY reference. The name should be
+                       formed by "sataphy" plus lower case hex for the
+                       lower 32-bit PHY CSR address (2nd memory resource)

I've tried to explain this before, but apparently you missed the point:

Any "*-names" property is supposed to be the identifier inside the device you are describing, *not* an identifier for what it points to, as that already has a name. The idea is that the driver can ask for phy (or clock, irq, memory, etc) "foo" here, with foo identifying the functionality in the device. If you only have one phy, you probably don't want to name it at all. If you imagine a device that was connected to two different phys depending on the mode it's used in (e.g. SATA-II vs SATA-6G), you would list this as

Quoted 9 lines

- phy-names : one name per references in the "phys" property.
valid names are "sata-ii" and "sata-6g".

If the generic phy subsystem requires you to name this, I'd suggest either changing the phy subsystem to make the name optional, or to just call it "serdes", as that is the kind of phy you are connected to.

Does this make more sense to you now?

[Loc Ho]
Okay... Got it. I will name it as 'sata-6g'

-Loc