Skip to contentSkip to conversation

No subject

PATCH New Hans de Goede 5 participants 29 messages Feb 22, 2014 – Mar 4, 2014

Subsystems (5)
lore
Share & tools
lore
#0 lore raw

Hi all,

Here is v7 of my patchset for adding ahci-sunxi support. This is hopefully the final final version of this set :)

Note I'm going on vacation for a week starting Monday, so if I'm not responding that is why. Tejun if you feel some small cleanups are still necessary and you don't want to wait for me to get back feel free to squash in any cleanups you deem necessary.

This has been tested with Allwinner A10, Allwinner A20 and Freeware imx6x SoCs, including suspend / resume. Note that the ahci_imx driver now also has imx53 sata support, it would be good if someone could test that with this series.

History:

v1, by Olliver Schinagl:
This was using the approach of having a platform device which probe method creates a new child platform device which gets driven by ahci_platform.c, as done by ahci_imx.c .

v2, by Hans de Goede:
Stand-alone platform driver based on Olliver's work

v3, by Hans de Goede:
patch-series, with 4 different parts
a) Make ahci_platform.c more generic, handle more then 1 clk, target pwr regulator
b) New ahci-sunxi code only populating ahci_platform_data, passed to ahci_platform.c to of_device_id matching.
c) Refactor ahci-imx code to work the same as the new ahci-sunxi code, this is the reason why v3 is an RFC, I'm waiting for the wandboard I ordered to arrive so that I can actually test this.
d) dts bindings for the sunxi ahci parts

v4, by Hans de Goede:
patch-series, with 5 different parts:
a) Make ahci_platform.c more generic, handle more then 1 clk, target pwr regulator
b) Turn parts of ahci_platform.c into a library for use by other drivers
c) New ahci-sunxi driver using the ahci_platform.c library functionality
d) Refactor ahci-imx code to work the same as the new ahci-sunxi code
e) dts bindings for the sunxi ahci parts

v5:
v4 + the following changes:
1) fsl,imx6q driver is now tested
2) fixed suspend / resume on fsl,imx6q
3) Modifed devicetree node naming to match dt spec
4) Reworked the busy waiting code in the sunxi-phy handling as suggested by Russell King

v6:
v5 rebased on top of 3.14-rc3 + the following changes
1) Added Roger Quadros' generic phy support series
2) Added a "ARM: sun4i: dt: Remove grouping + simple-bus for regulators" dts patch

v7:
v6 + the following changes:
1) Addressed all Tejun's review remarks:
* Added function header comments to all exported ahci_platform functions
* Added comments in some other places
* Removed use of 2 empty lines to separate functions in some cases
* Use devres to automatically call ahci_platform_put_resources on
get_resource failure, probe failure and regular device remove
2) Dropped patches to move ahci_host_priv struct declaration to include/linux,
this was a left-over from v3 and is no longer necessary
3) Updated Roger's "ata: ahci_platform: Manage SATA PHY" patch:
* Update function header comments for the changes this makes
* Drop the Kconfig PHY requires hack, my patch for the phy-core to always be built-in has been queued in Greg KH's tree, so this is no longer necessary.
4) Dropped Roger's "ata: ahci_platform: Add 'struct device' argument to ahci_platform_put_resources()" patch, ahci_platform_put_resources already has a device argument as result
of it being changed into a devres release function

Tejun, can you please add patches 1-12 to your ata tree for 3.15 ?

Maxime, can you please add patch 13-15 to your dts tree for 3.15 ?

Thanks & Regards,

Hans

#1 lore raw

PATCH v7 1/15 libahci: Allow drivers to override start_engine

Allwinner A10 and A20 ARM SoCs have an AHCI sata controller which needs a special register to be poked before starting the DMA engine.

This register gets reset on an ahci_stop_engine call, so there is no other place then ahci_start_engine where this poking can be done.

This commit allows drivers to override ahci_start_engine behavior for use by the Allwinner AHCI driver (and potentially other drivers in the future).

---
 drivers/ata/ahci.c          |  6 ++++--
 drivers/ata/ahci.h          |  6 ++++++
 drivers/ata/libahci.c       | 26 +++++++++++++++++++-------
 drivers/ata/sata_highbank.c |  3 ++-
 4 files changed, 31 insertions(+), 10 deletions(-)
drivers/ata/ahci.c +4 -2
index dc2756f..eda68b4 100644
@@ -564,6 +564,7 @@ static int ahci_vt8251_hardreset(struct ata_link *link, unsigned int *class,
 				 unsigned long deadline)
 {
 	struct ata_port *ap = link->ap;
+	struct ahci_host_priv *hpriv = ap->host->private_data;
 	bool online;
 	int rc;
 
@@ -574,7 +575,7 @@ static int ahci_vt8251_hardreset(struct ata_link *link, unsigned int *class,
 	rc = sata_link_hardreset(link, sata_ehc_deb_timing(&link->eh_context),
 				 deadline, &online, NULL);
 
-	ahci_start_engine(ap);
+	hpriv->start_engine(ap);
 
 	DPRINTK("EXIT, rc=%d, class=%u\n", rc, *class);
 
@@ -589,6 +590,7 @@ static int ahci_p5wdh_hardreset(struct ata_link *link, unsigned int *class,
 {
 	struct ata_port *ap = link->ap;
 	struct ahci_port_priv *pp = ap->private_data;
+	struct ahci_host_priv *hpriv = ap->host->private_data;
 	u8 *d2h_fis = pp->rx_fis + RX_FIS_D2H_REG;
 	struct ata_taskfile tf;
 	bool online;
@@ -604,7 +606,7 @@ static int ahci_p5wdh_hardreset(struct ata_link *link, unsigned int *class,
 	rc = sata_link_hardreset(link, sata_ehc_deb_timing(&link->eh_context),
 				 deadline, &online, NULL);
 
-	ahci_start_engine(ap);
+	hpriv->start_engine(ap);
 
 	/* The pseudo configuration device on SIMG4726 attached to
 	 * ASUS P5W-DH Deluxe doesn't send signature FIS after
drivers/ata/ahci.h +6 -0
index 2289efd..64d1a99 100644
@@ -323,6 +323,12 @@ struct ahci_host_priv {
 	u32			em_msg_type;	/* EM message type */
 	struct clk		*clk;		/* Only for platforms supporting clk */
 	void			*plat_data;	/* Other platform data */
+	/*
+	 * Optional ahci_start_engine override, if not set this gets set to the
+	 * default ahci_start_engine during ahci_save_initial_config, this can
+	 * be overridden anytime before the host is activated.
+	 */
+	void			(*start_engine)(struct ata_port *ap);
 };
 
 extern int ahci_ignore_sss;
drivers/ata/libahci.c +19 -7
index 36605ab..f839bb3 100644
@@ -394,6 +394,9 @@ static ssize_t ahci_show_em_supported(struct device *dev,
  *
  *	If inconsistent, config values are fixed up by this function.
  *
+ *	If it is not set already this function sets hpriv->start_engine to
+ *	ahci_start_engine.
+ *
  *	LOCKING:
  *	None.
  */
@@ -500,6 +503,9 @@ void ahci_save_initial_config(struct device *dev,
 	hpriv->cap = cap;
 	hpriv->cap2 = cap2;
 	hpriv->port_map = port_map;
+
+	if (!hpriv->start_engine)
+		hpriv->start_engine = ahci_start_engine;
 }
 EXPORT_SYMBOL_GPL(ahci_save_initial_config);
 
@@ -766,7 +772,7 @@ static void ahci_start_port(struct ata_port *ap)
 
 	/* enable DMA */
 	if (!(hpriv->flags & AHCI_HFLAG_DELAY_ENGINE))
-		ahci_start_engine(ap);
+		hpriv->start_engine(ap);
 
 	/* turn on LEDs */
 	if (ap->flags & ATA_FLAG_EM) {
@@ -1234,7 +1240,7 @@ int ahci_kick_engine(struct ata_port *ap)
 
 	/* restart engine */
  out_restart:
-	ahci_start_engine(ap);
+	hpriv->start_engine(ap);
 	return rc;
 }
 EXPORT_SYMBOL_GPL(ahci_kick_engine);
@@ -1426,6 +1432,7 @@ static int ahci_hardreset(struct ata_link *link, unsigned int *class,
 	const unsigned long *timing = sata_ehc_deb_timing(&link->eh_context);
 	struct ata_port *ap = link->ap;
 	struct ahci_port_priv *pp = ap->private_data;
+	struct ahci_host_priv *hpriv = ap->host->private_data;
 	u8 *d2h_fis = pp->rx_fis + RX_FIS_D2H_REG;
 	struct ata_taskfile tf;
 	bool online;
@@ -1443,7 +1450,7 @@ static int ahci_hardreset(struct ata_link *link, unsigned int *class,
 	rc = sata_link_hardreset(link, timing, deadline, &online,
 				 ahci_check_ready);
 
-	ahci_start_engine(ap);
+	hpriv->start_engine(ap);
 
 	if (online)
 		*class = ahci_dev_classify(ap);
@@ -2007,10 +2014,12 @@ static void ahci_thaw(struct ata_port *ap)
 
 void ahci_error_handler(struct ata_port *ap)
 {
+	struct ahci_host_priv *hpriv = ap->host->private_data;
+
 	if (!(ap->pflags & ATA_PFLAG_FROZEN)) {
 		/* restart engine */
 		ahci_stop_engine(ap);
-		ahci_start_engine(ap);
+		hpriv->start_engine(ap);
 	}
 
 	sata_pmp_error_handler(ap);
@@ -2031,6 +2040,7 @@ static void ahci_post_internal_cmd(struct ata_queued_cmd *qc)
 
 static void ahci_set_aggressive_devslp(struct ata_port *ap, bool sleep)
 {
+	struct ahci_host_priv *hpriv = ap->host->private_data;
 	void __iomem *port_mmio = ahci_port_base(ap);
 	struct ata_device *dev = ap->link.device;
 	u32 devslp, dm, dito, mdat, deto;
@@ -2094,7 +2104,7 @@ static void ahci_set_aggressive_devslp(struct ata_port *ap, bool sleep)
 		   PORT_DEVSLP_ADSE);
 	writel(devslp, port_mmio + PORT_DEVSLP);
 
-	ahci_start_engine(ap);
+	hpriv->start_engine(ap);
 
 	/* enable device sleep feature for the drive */
 	err_mask = ata_dev_set_feature(dev,
@@ -2106,6 +2116,7 @@ static void ahci_set_aggressive_devslp(struct ata_port *ap, bool sleep)
 
 static void ahci_enable_fbs(struct ata_port *ap)
 {
+	struct ahci_host_priv *hpriv = ap->host->private_data;
 	struct ahci_port_priv *pp = ap->private_data;
 	void __iomem *port_mmio = ahci_port_base(ap);
 	u32 fbs;
@@ -2134,11 +2145,12 @@ static void ahci_enable_fbs(struct ata_port *ap)
 	} else
 		dev_err(ap->host->dev, "Failed to enable FBS\n");
 
-	ahci_start_engine(ap);
+	hpriv->start_engine(ap);
 }
 
 static void ahci_disable_fbs(struct ata_port *ap)
 {
+	struct ahci_host_priv *hpriv = ap->host->private_data;
 	struct ahci_port_priv *pp = ap->private_data;
 	void __iomem *port_mmio = ahci_port_base(ap);
 	u32 fbs;
@@ -2166,7 +2178,7 @@ static void ahci_disable_fbs(struct ata_port *ap)
 		pp->fbs_enabled = false;
 	}
 
-	ahci_start_engine(ap);
+	hpriv->start_engine(ap);
 }
 
 static void ahci_pmp_attach(struct ata_port *ap)
drivers/ata/sata_highbank.c +2 -1
index 870b11e..b3b18d1 100644
@@ -403,6 +403,7 @@ static int ahci_highbank_hardreset(struct ata_link *link, unsigned int *class,
 	static const unsigned long timing[] = { 5, 100, 500};
 	struct ata_port *ap = link->ap;
 	struct ahci_port_priv *pp = ap->private_data;
+	struct ahci_host_priv *hpriv = ap->host->private_data;
 	u8 *d2h_fis = pp->rx_fis + RX_FIS_D2H_REG;
 	struct ata_taskfile tf;
 	bool online;
@@ -431,7 +432,7 @@ static int ahci_highbank_hardreset(struct ata_link *link, unsigned int *class,
 			break;
 	} while (!online && retry--);
 
-	ahci_start_engine(ap);
+	hpriv->start_engine(ap);
 
 	if (online)
 		*class = ahci_dev_classify(ap);
Signature · 1 line
1.9.0
#2 lore raw

PATCH v7 2/15 ahci-platform: Add support for devices with more then 1 clock

In reply to Hans de Goede

The allwinner-sun4i AHCI controller needs 2 clocks to be enabled and the imx AHCI controller needs 3 clocks to be enabled.

---
 .../devicetree/bindings/ata/ahci-platform.txt      |   1 +
 drivers/ata/ahci.h                                 |   3 +-
 drivers/ata/ahci_platform.c                        | 119 ++++++++++++++++-----
 include/linux/ahci_platform.h                      |   4 +
 4 files changed, 99 insertions(+), 28 deletions(-)
Documentation/devicetree/bindings/ata/ahci-platform.txt +1 -0
index 89de156..3ced07d 100644
@@ -10,6 +10,7 @@ Required properties:
 
 Optional properties:
 - dma-coherent      : Present if dma operations are coherent
+- clocks            : a list of phandle + clock specifier pairs
 
 Example:
         sata at ffe08000 {
drivers/ata/ahci.h +2 -1
index 64d1a99..c12862b 100644
@@ -51,6 +51,7 @@
 
 enum {
 	AHCI_MAX_PORTS		= 32,
+	AHCI_MAX_CLKS		= 3,
 	AHCI_MAX_SG		= 168, /* hardware max is 64K */
 	AHCI_DMA_BOUNDARY	= 0xffffffff,
 	AHCI_MAX_CMDS		= 32,
@@ -321,7 +322,7 @@ struct ahci_host_priv {
 	u32 			em_loc; /* enclosure management location */
 	u32			em_buf_sz;	/* EM buffer size in byte */
 	u32			em_msg_type;	/* EM message type */
-	struct clk		*clk;		/* Only for platforms supporting clk */
+	struct clk		*clks[AHCI_MAX_CLKS]; /* Optional */
 	void			*plat_data;	/* Other platform data */
 	/*
 	 * Optional ahci_start_engine override, if not set this gets set to the
drivers/ata/ahci_platform.c +92 -27

171 lines not on this page · raw message

include/linux/ahci_platform.h +4 -0
index 73a2500..769d065 100644
@@ -19,6 +19,7 @@
 
 struct device;
 struct ata_port_info;
+struct ahci_host_priv;
 
 struct ahci_platform_data {
 	int (*init)(struct device *dev, void __iomem *addr);
@@ -30,4 +31,7 @@ struct ahci_platform_data {
 	unsigned int mask_port_map;
 };
 
+int ahci_platform_enable_clks(struct ahci_host_priv *hpriv);
+void ahci_platform_disable_clks(struct ahci_host_priv *hpriv);
+
 #endif /* _AHCI_PLATFORM_H */
Signature · 1 line
1.9.0
#3 lore raw

PATCH v7 2/15 ahci-platform: Add support for devices with more then 1 clock

Hi,

Quoted 147 lines · Hans de Goede

The allwinner-sun4i AHCI controller needs 2 clocks to be enabled and the imx AHCI controller needs 3 clocks to be enabled.

---
 .../devicetree/bindings/ata/ahci-platform.txt      |   1 +
 drivers/ata/ahci.h                                 |   3 +-
 drivers/ata/ahci_platform.c                        | 119 ++++++++++++++++-----
 include/linux/ahci_platform.h                      |   4 +
 4 files changed, 99 insertions(+), 28 deletions(-)
Documentation/devicetree/bindings/ata/ahci-platform.txt +1 -0
index 89de156..3ced07d 100644
@@ -10,6 +10,7 @@ Required properties:
 
 Optional properties:
 - dma-coherent      : Present if dma operations are coherent
+- clocks            : a list of phandle + clock specifier pairs
 
 Example:
         sata at ffe08000 {
drivers/ata/ahci.h +2 -1
index 64d1a99..c12862b 100644
@@ -51,6 +51,7 @@
 
 enum {
 	AHCI_MAX_PORTS		= 32,
+	AHCI_MAX_CLKS		= 3,
 	AHCI_MAX_SG		= 168, /* hardware max is 64K */
 	AHCI_DMA_BOUNDARY	= 0xffffffff,
 	AHCI_MAX_CMDS		= 32,
@@ -321,7 +322,7 @@ struct ahci_host_priv {
 	u32 			em_loc; /* enclosure management location */
 	u32			em_buf_sz;	/* EM buffer size in byte */
 	u32			em_msg_type;	/* EM message type */
-	struct clk		*clk;		/* Only for platforms supporting clk */
+	struct clk		*clks[AHCI_MAX_CLKS]; /* Optional */
 	void			*plat_data;	/* Other platform data */
 	/*
 	 * Optional ahci_start_engine override, if not set this gets set to the
drivers/ata/ahci_platform.c +73 -8
index 4b231ba..609975d 100644
@@ -87,6 +87,66 @@ static struct scsi_host_template ahci_platform_sht = {
 	AHCI_SHT("ahci_platform"),
 };
 
+/**
+ *	ahci_platform_enable_clks - Enable platform clocks
+ *	@hpriv: host private area to store config values
+ *
+ *	This function enables all the clks found in hpriv->clks, starting
+ *	at index 0. If any clk fails to enable it disables all the clks
+ *	already enabled in reverse order, and then returns an error.
+ *
+ *	LOCKING:
+ *	None.
+ *
+ *	RETURNS:
+ *	0 on success otherwise a negative error code
+ */
+int ahci_platform_enable_clks(struct ahci_host_priv *hpriv)
+{
+	int c, rc;
+
+	for (c = 0; c < AHCI_MAX_CLKS && hpriv->clks[c]; c++) {
+		rc = clk_prepare_enable(hpriv->clks[c]);
+		if (rc)
+			goto disable_unprepare_clk;
+	}
+	return 0;
+
+disable_unprepare_clk:
+	while (--c >= 0)
+		clk_disable_unprepare(hpriv->clks[c]);
+	return rc;
+}
+EXPORT_SYMBOL_GPL(ahci_platform_enable_clks);
+
+/**
+ *	ahci_platform_disable_clks - Disable platform clocks
+ *	@hpriv: host private area to store config values
+ *
+ *	This function disables all the clks found in hpriv->clks, in reverse
+ *	order of ahci_platform_enable_clks (starting at the end of the array).
+ *
+ *	LOCKING:
+ *	None.
+ */
+void ahci_platform_disable_clks(struct ahci_host_priv *hpriv)
+{
+	int c;
+
+	for (c = AHCI_MAX_CLKS - 1; c >= 0; c--)
+		if (hpriv->clks[c])
+			clk_disable_unprepare(hpriv->clks[c]);
+}
+EXPORT_SYMBOL_GPL(ahci_platform_disable_clks);
+
+static void ahci_put_clks(struct ahci_host_priv *hpriv)
+{
+	int c;
+
+	for (c = 0; c < AHCI_MAX_CLKS && hpriv->clks[c]; c++)
+		clk_put(hpriv->clks[c]);
+}
+
 static int ahci_probe(struct platform_device *pdev)
 {
 	struct device *dev = &pdev->dev;
@@ -97,6 +157,7 @@ static int ahci_probe(struct platform_device *pdev)
 	struct ahci_host_priv *hpriv;
 	struct ata_host *host;
 	struct resource *mem;
+	struct clk *clk;
 	int irq;
 	int n_ports;
 	int i;
@@ -131,17 +192,31 @@ static int ahci_probe(struct platform_device *pdev)
 		return -ENOMEM;
 	}
 
-	hpriv->clk = clk_get(dev, NULL);
-	if (IS_ERR(hpriv->clk)) {
-		dev_err(dev, "can't get clock\n");
-	} else {
-		rc = clk_prepare_enable(hpriv->clk);
-		if (rc) {
-			dev_err(dev, "clock prepare enable failed");
-			goto free_clk;
+	for (i = 0; i < AHCI_MAX_CLKS; i++) {
+		/*
+		 * For now we must use clk_get(dev, NULL) for the first clock,
+		 * because some platforms (da850, spear13xx) are not yet
+		 * converted to use devicetree for clocks.  For new platforms
+		 * this is equivalent to of_clk_get(dev->of_node, 0).
+		 */
+		if (i == 0)
+			clk = clk_get(dev, NULL);
+		else
+			clk = of_clk_get(dev->of_node, i);
+

drivers/ata/ahci_platform.c static int ahci_probe(struct platform_device *pdev) · line 216

Quoted 10 lines
drivers/ata/ahci_platform.c +7 -0
+		if (IS_ERR(clk)) {
+			rc = PTR_ERR(clk);
+			if (rc == -EPROBE_DEFER)
+				goto free_clk;
+			break;
 		}
+		hpriv->clks[i] = clk;
 	}
 
+	rc = ahci_enable_clks(dev, hpriv);

This should be ahci_platform_enable_clks().

drivers/ata/ahci_platform.c pdata_exit: · line 300

Quoted 13 lines
drivers/ata/ahci_platform.c +4 -2
+	if (rc)
+		goto free_clk;
+
 	/*
 	 * Some platforms might need to prepare for mmio region access,
 	 * which could be done in the following init call. So, the mmio
@@ -222,11 +297,9 @@ pdata_exit:
 	if (pdata && pdata->exit)
 		pdata->exit(dev);
 disable_unprepare_clk:
-	if (!IS_ERR(hpriv->clk))
-		clk_disable_unprepare(hpriv->clk);
+	ahci_disable_clks(hpriv);

ahci_platform_disable_clks()

Quoted 8 lines
drivers/ata/ahci_platform.c +1 -2
 free_clk:
-	if (!IS_ERR(hpriv->clk))
-		clk_put(hpriv->clk);
+	ahci_put_clks(hpriv);
 	return rc;
 }
 

drivers/ata/ahci_platform.c static void ahci_host_stop(struct ata_host *host) · line 315

Quoted 8 lines
drivers/ata/ahci_platform.c +1 -4
@@ -239,10 +312,8 @@ static void ahci_host_stop(struct ata_host *host)
 	if (pdata && pdata->exit)
 		pdata->exit(dev);
 
-	if (!IS_ERR(hpriv->clk)) {
-		clk_disable_unprepare(hpriv->clk);
-		clk_put(hpriv->clk);
-	}
+	ahci_disable_clks(hpriv);

ahci_platform_disable_clks()

drivers/ata/ahci_platform.c static int ahci_suspend(struct device *dev) · line 351

Quoted 11 lines
drivers/ata/ahci_platform.c +2 -2
+	ahci_put_clks(hpriv);
 }
 
 #ifdef CONFIG_PM_SLEEP
@@ -277,8 +348,7 @@ static int ahci_suspend(struct device *dev)
 	if (pdata && pdata->suspend)
 		return pdata->suspend(dev);
 
-	if (!IS_ERR(hpriv->clk))
-		clk_disable_unprepare(hpriv->clk);
+	ahci_disable_clks(hpriv);

ahci_platform_disable_clks()

Quoted 4 lines
drivers/ata/ahci_platform.c +0 -0
 	return 0;
 }
@@ -290,13 +360,9 @@ static int ahci_resume(struct device *dev)
 	struct ahci_host_priv *hpriv = host->private_data;

drivers/ata/ahci_platform.c static int ahci_resume(struct device *dev) · line 363

Quoted 10 lines
drivers/ata/ahci_platform.c +1 -7
 	int rc;
 
-	if (!IS_ERR(hpriv->clk)) {
-		rc = clk_prepare_enable(hpriv->clk);
-		if (rc) {
-			dev_err(dev, "clock prepare enable failed");
-			return rc;
-		}
-	}
+	rc = ahci_enable_clks(dev, hpriv);

ahci_platform_enable_clks()

drivers/ata/ahci_platform.c static int ahci_resume(struct device *dev) · line 386

Quoted 12 lines
drivers/ata/ahci_platform.c +3 -2
+	if (rc)
+		return rc;
 
 	if (pdata && pdata->resume) {
 		rc = pdata->resume(dev);
@@ -317,8 +383,7 @@ static int ahci_resume(struct device *dev)
 	return 0;
 
 disable_unprepare_clk:
-	if (!IS_ERR(hpriv->clk))
-		clk_disable_unprepare(hpriv->clk);
+	ahci_disable_clks(hpriv);

ahci_platform_disable_clks()

[...]

All code in question gets rewritten in patch #4/15 so the actual problem is the broken bisectability between patch #2 and #4.

It seems too late to fix it now but in the future please remember to build-test each patch separately (in addition to testing the whole patchset).

Best regards,
--
Bartlomiej Zolnierkiewicz
Samsung R&D Institute Poland
Samsung Electronics

#4 lore raw

PATCH v7 3/15 ahci-platform: Add support for an optional regulator for sata-target power

In reply to Hans de Goede
---
 .../devicetree/bindings/ata/ahci-platform.txt      |  1 +
 drivers/ata/ahci.h                                 |  2 ++
 drivers/ata/ahci_platform.c                        | 36 ++++++++++++++++++++--
 3 files changed, 37 insertions(+), 2 deletions(-)
Documentation/devicetree/bindings/ata/ahci-platform.txt +1 -0
index 3ced07d..1ac807f 100644
@@ -11,6 +11,7 @@ Required properties:
 Optional properties:
 - dma-coherent      : Present if dma operations are coherent
 - clocks            : a list of phandle + clock specifier pairs
+- target-supply     : regulator for SATA target power
 
 Example:
         sata at ffe08000 {
drivers/ata/ahci.h +2 -0
index c12862b..bf8100c 100644
@@ -37,6 +37,7 @@
 
 #include <linux/clk.h>
 #include <linux/libata.h>
+#include <linux/regulator/consumer.h>
 
 /* Enclosure Management Control */
 #define EM_CTRL_MSG_TYPE              0x000f0000
@@ -323,6 +324,7 @@ struct ahci_host_priv {
 	u32			em_buf_sz;	/* EM buffer size in byte */
 	u32			em_msg_type;	/* EM message type */
 	struct clk		*clks[AHCI_MAX_CLKS]; /* Optional */
+	struct regulator	*target_pwr;	/* Optional */
 	void			*plat_data;	/* Other platform data */
 	/*
 	 * Optional ahci_start_engine override, if not set this gets set to the
drivers/ata/ahci_platform.c +34 -2
index 609975d..907c076 100644
@@ -192,6 +192,14 @@ static int ahci_probe(struct platform_device *pdev)
 		return -ENOMEM;
 	}
 
+	hpriv->target_pwr = devm_regulator_get_optional(dev, "target");
+	if (IS_ERR(hpriv->target_pwr)) {
+		rc = PTR_ERR(hpriv->target_pwr);
+		if (rc == -EPROBE_DEFER)
+			return -EPROBE_DEFER;
+		hpriv->target_pwr = NULL;
+	}
+
 	for (i = 0; i < AHCI_MAX_CLKS; i++) {
 		/*
 		 * For now we must use clk_get(dev, NULL) for the first clock,
@@ -213,9 +221,15 @@ static int ahci_probe(struct platform_device *pdev)
 		hpriv->clks[i] = clk;
 	}
 
+	if (hpriv->target_pwr) {
+		rc = regulator_enable(hpriv->target_pwr);
+		if (rc)
+			goto free_clk;
+	}
+
 	rc = ahci_enable_clks(dev, hpriv);
 	if (rc)
-		goto free_clk;
+		goto disable_regulator;
 
 	/*
 	 * Some platforms might need to prepare for mmio region access,
@@ -298,6 +312,9 @@ pdata_exit:
 		pdata->exit(dev);
 disable_unprepare_clk:
 	ahci_disable_clks(hpriv);
+disable_regulator:
+	if (hpriv->target_pwr)
+		regulator_disable(hpriv->target_pwr);
 free_clk:
 	ahci_put_clks(hpriv);
 	return rc;
@@ -314,6 +331,9 @@ static void ahci_host_stop(struct ata_host *host)
 
 	ahci_disable_clks(hpriv);
 	ahci_put_clks(hpriv);
+
+	if (hpriv->target_pwr)
+		regulator_disable(hpriv->target_pwr);
 }
 
 #ifdef CONFIG_PM_SLEEP
@@ -350,6 +370,9 @@ static int ahci_suspend(struct device *dev)
 
 	ahci_disable_clks(hpriv);
 
+	if (hpriv->target_pwr)
+		regulator_disable(hpriv->target_pwr);
+
 	return 0;
 }
 
@@ -360,9 +383,15 @@ static int ahci_resume(struct device *dev)
 	struct ahci_host_priv *hpriv = host->private_data;
 	int rc;
 
+	if (hpriv->target_pwr) {
+		rc = regulator_enable(hpriv->target_pwr);
+		if (rc)
+			return rc;
+	}
+
 	rc = ahci_enable_clks(dev, hpriv);
 	if (rc)
-		return rc;
+		goto disable_regulator;
 
 	if (pdata && pdata->resume) {
 		rc = pdata->resume(dev);
@@ -384,6 +413,9 @@ static int ahci_resume(struct device *dev)
 
 disable_unprepare_clk:
 	ahci_disable_clks(hpriv);
+disable_regulator:
+	if (hpriv->target_pwr)
+		regulator_disable(hpriv->target_pwr);
 
 	return rc;
 }
Signature · 1 line
1.9.0
#5 lore raw

PATCH v7 4/15 ahci-platform: Add enable_ / disable_resources helper functions

In reply to Hans de Goede
---
 drivers/ata/ahci_platform.c   | 112 ++++++++++++++++++++++++++++--------------
 include/linux/ahci_platform.h |   2 +
 2 files changed, 77 insertions(+), 37 deletions(-)
drivers/ata/ahci_platform.c +75 -37
index 907c076..6ebbc17 100644
@@ -139,6 +139,68 @@ void ahci_platform_disable_clks(struct ahci_host_priv *hpriv)
 }
 EXPORT_SYMBOL_GPL(ahci_platform_disable_clks);
 
+/**
+ *	ahci_platform_enable_resources - Enable platform resources
+ *	@hpriv: host private area to store config values
+ *
+ *	This function enables all ahci_platform managed resources in
+ *	the following order:
+ *	1) Regulator
+ *	2) Clocks (through ahci_platform_enable_clks)
+ *
+ *	If resource enabling fails at any point the previous enabled
+ *	resources are disabled in reverse order.
+ *
+ *	LOCKING:
+ *	None.
+ *
+ *	RETURNS:
+ *	0 on success otherwise a negative error code
+ */
+int ahci_platform_enable_resources(struct ahci_host_priv *hpriv)
+{
+	int rc;
+
+	if (hpriv->target_pwr) {
+		rc = regulator_enable(hpriv->target_pwr);
+		if (rc)
+			return rc;
+	}
+
+	rc = ahci_platform_enable_clks(hpriv);
+	if (rc)
+		goto disable_regulator;
+
+	return 0;
+
+disable_regulator:
+	if (hpriv->target_pwr)
+		regulator_disable(hpriv->target_pwr);
+	return rc;
+}
+EXPORT_SYMBOL_GPL(ahci_platform_enable_resources);
+
+/**
+ *	ahci_platform_disable_resources - Disable platform resources
+ *	@hpriv: host private area to store config values
+ *
+ *	This function disables all ahci_platform managed resources in
+ *	the following order:
+ *	1) Clocks (through ahci_platform_disable_clks)
+ *	2) Regulator
+ *
+ *	LOCKING:
+ *	None.
+ */
+void ahci_platform_disable_resources(struct ahci_host_priv *hpriv)
+{
+	ahci_platform_disable_clks(hpriv);
+
+	if (hpriv->target_pwr)
+		regulator_disable(hpriv->target_pwr);
+}
+EXPORT_SYMBOL_GPL(ahci_platform_disable_resources);
+
 static void ahci_put_clks(struct ahci_host_priv *hpriv)
 {
 	int c;
@@ -221,15 +283,9 @@ static int ahci_probe(struct platform_device *pdev)
 		hpriv->clks[i] = clk;
 	}
 
-	if (hpriv->target_pwr) {
-		rc = regulator_enable(hpriv->target_pwr);
-		if (rc)
-			goto free_clk;
-	}
-
-	rc = ahci_enable_clks(dev, hpriv);
+	rc = ahci_platform_enable_resources(hpriv);
 	if (rc)
-		goto disable_regulator;
+		goto free_clk;
 
 	/*
 	 * Some platforms might need to prepare for mmio region access,
@@ -240,7 +296,7 @@ static int ahci_probe(struct platform_device *pdev)
 	if (pdata && pdata->init) {
 		rc = pdata->init(dev, hpriv->mmio);
 		if (rc)
-			goto disable_unprepare_clk;
+			goto disable_resources;
 	}
 
 	ahci_save_initial_config(dev, hpriv,
@@ -310,11 +366,8 @@ static int ahci_probe(struct platform_device *pdev)
 pdata_exit:
 	if (pdata && pdata->exit)
 		pdata->exit(dev);
-disable_unprepare_clk:
-	ahci_disable_clks(hpriv);
-disable_regulator:
-	if (hpriv->target_pwr)
-		regulator_disable(hpriv->target_pwr);
+disable_resources:
+	ahci_platform_disable_resources(hpriv);
 free_clk:
 	ahci_put_clks(hpriv);
 	return rc;
@@ -329,11 +382,8 @@ static void ahci_host_stop(struct ata_host *host)
 	if (pdata && pdata->exit)
 		pdata->exit(dev);
 
-	ahci_disable_clks(hpriv);
+	ahci_platform_disable_resources(hpriv);
 	ahci_put_clks(hpriv);
-
-	if (hpriv->target_pwr)
-		regulator_disable(hpriv->target_pwr);
 }
 
 #ifdef CONFIG_PM_SLEEP
@@ -368,10 +418,7 @@ static int ahci_suspend(struct device *dev)
 	if (pdata && pdata->suspend)
 		return pdata->suspend(dev);
 
-	ahci_disable_clks(hpriv);
-
-	if (hpriv->target_pwr)
-		regulator_disable(hpriv->target_pwr);
+	ahci_platform_disable_resources(hpriv);
 
 	return 0;
 }
@@ -383,26 +430,20 @@ static int ahci_resume(struct device *dev)
 	struct ahci_host_priv *hpriv = host->private_data;
 	int rc;
 
-	if (hpriv->target_pwr) {
-		rc = regulator_enable(hpriv->target_pwr);
-		if (rc)
-			return rc;
-	}
-
-	rc = ahci_enable_clks(dev, hpriv);
+	rc = ahci_platform_enable_resources(hpriv);
 	if (rc)
-		goto disable_regulator;
+		return rc;
 
 	if (pdata && pdata->resume) {
 		rc = pdata->resume(dev);
 		if (rc)
-			goto disable_unprepare_clk;
+			goto disable_resources;
 	}
 
 	if (dev->power.power_state.event == PM_EVENT_SUSPEND) {
 		rc = ahci_reset_controller(host);
 		if (rc)
-			goto disable_unprepare_clk;
+			goto disable_resources;
 
 		ahci_init_controller(host);
 	}
@@ -411,11 +452,8 @@ static int ahci_resume(struct device *dev)
 
 	return 0;
 
-disable_unprepare_clk:
-	ahci_disable_clks(hpriv);
-disable_regulator:
-	if (hpriv->target_pwr)
-		regulator_disable(hpriv->target_pwr);
+disable_resources:
+	ahci_platform_disable_resources(hpriv);
 
 	return rc;
 }
include/linux/ahci_platform.h +2 -0
index 769d065..b674b01 100644
@@ -33,5 +33,7 @@ struct ahci_platform_data {
 
 int ahci_platform_enable_clks(struct ahci_host_priv *hpriv);
 void ahci_platform_disable_clks(struct ahci_host_priv *hpriv);
+int ahci_platform_enable_resources(struct ahci_host_priv *hpriv);
+void ahci_platform_disable_resources(struct ahci_host_priv *hpriv);
 
 #endif /* _AHCI_PLATFORM_H */
Signature · 1 line
1.9.0
#6 lore raw

PATCH v7 5/15 ahci-platform: "Library-ise" ahci_probe functionality

In reply to Hans de Goede

ahci_probe consists of 3 steps:
1) Get resources (get mmio, clks, regulator)
2) Enable resources, handled by ahci_platform_enable_resouces
3) The more or less standard ahci-host controller init sequence

This commit refactors step 1 and 3 into separate functions, so the platform drivers for AHCI implementations which need a specific order in step 2, and / or need to do some custom register poking at some time, can re-use ahci-platform.c code without needing to copy and paste it.

Note that ahci_platform_init_host's prototype takes the 3 non function members of ahci_platform_data as arguments, the idea is that drivers using the new exported utility functions will not use ahci_platform_data at all, and hopefully in the future ahci_platform_data can go away entirely.

---
 drivers/ata/ahci_platform.c   | 195 ++++++++++++++++++++++++++++--------------
 include/linux/ahci_platform.h |  14 +++
 2 files changed, 144 insertions(+), 65 deletions(-)
drivers/ata/ahci_platform.c +130 -65
index 6ebbc17..7f3f2ac 100644
@@ -201,64 +201,64 @@ void ahci_platform_disable_resources(struct ahci_host_priv *hpriv)
 }
 EXPORT_SYMBOL_GPL(ahci_platform_disable_resources);
 
-static void ahci_put_clks(struct ahci_host_priv *hpriv)
+static void ahci_platform_put_resources(struct device *dev, void *res)
 {
+	struct ahci_host_priv *hpriv = res;
 	int c;
 
 	for (c = 0; c < AHCI_MAX_CLKS && hpriv->clks[c]; c++)
 		clk_put(hpriv->clks[c]);
 }
 
-static int ahci_probe(struct platform_device *pdev)
+/**
+ *	ahci_platform_get_resources - Get platform resources
+ *	@pdev: platform device to get resources for
+ *
+ *	This function allocates an ahci_host_priv struct, and gets the
+ *	following resources, storing a reference to them inside the returned
+ *	struct:
+ *
+ *	1) mmio registers (IORESOURCE_MEM 0, mandatory)
+ *	2) regulator for controlling the targets power (optional)
+ *	3) 0 - AHCI_MAX_CLKS clocks, as specified in the devs devicetree node,
+ *	   or for non devicetree enabled platforms a single clock
+ *
+ *	LOCKING:
+ *	None.
+ *
+ *	RETURNS:
+ *	The allocated ahci_host_priv on success, otherwise an ERR_PTR value
+ */
+struct ahci_host_priv *ahci_platform_get_resources(
+	struct platform_device *pdev)
 {
 	struct device *dev = &pdev->dev;
-	struct ahci_platform_data *pdata = dev_get_platdata(dev);
-	const struct platform_device_id *id = platform_get_device_id(pdev);
-	struct ata_port_info pi = ahci_port_info[id ? id->driver_data : 0];
-	const struct ata_port_info *ppi[] = { &pi, NULL };
 	struct ahci_host_priv *hpriv;
-	struct ata_host *host;
-	struct resource *mem;
 	struct clk *clk;
-	int irq;
-	int n_ports;
-	int i;
-	int rc;
+	int i, rc = -ENOMEM;
 
-	mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
-	if (!mem) {
-		dev_err(dev, "no mmio space\n");
-		return -EINVAL;
-	}
+	if (!devres_open_group(dev, NULL, GFP_KERNEL))
+		return ERR_PTR(-ENOMEM);
 
-	irq = platform_get_irq(pdev, 0);
-	if (irq <= 0) {
-		dev_err(dev, "no irq\n");
-		return -EINVAL;
-	}
+	hpriv = devres_alloc(ahci_platform_put_resources, sizeof(*hpriv),
+			     GFP_KERNEL);
+	if (!hpriv)
+		goto err_out;
 
-	if (pdata && pdata->ata_port_info)
-		pi = *pdata->ata_port_info;
+	devres_add(dev, hpriv);
 
-	hpriv = devm_kzalloc(dev, sizeof(*hpriv), GFP_KERNEL);
-	if (!hpriv) {
-		dev_err(dev, "can't alloc ahci_host_priv\n");
-		return -ENOMEM;
-	}
-
-	hpriv->flags |= (unsigned long)pi.private_data;
-
-	hpriv->mmio = devm_ioremap(dev, mem->start, resource_size(mem));
+	hpriv->mmio = devm_ioremap_resource(dev,
+			      platform_get_resource(pdev, IORESOURCE_MEM, 0));
 	if (!hpriv->mmio) {
-		dev_err(dev, "can't map %pR\n", mem);
-		return -ENOMEM;
+		dev_err(dev, "no mmio space\n");
+		goto err_out;
 	}
 
 	hpriv->target_pwr = devm_regulator_get_optional(dev, "target");
 	if (IS_ERR(hpriv->target_pwr)) {
 		rc = PTR_ERR(hpriv->target_pwr);
 		if (rc == -EPROBE_DEFER)
-			return -EPROBE_DEFER;
+			goto err_out;
 		hpriv->target_pwr = NULL;
 	}
 
@@ -277,33 +277,62 @@ static int ahci_probe(struct platform_device *pdev)
 		if (IS_ERR(clk)) {
 			rc = PTR_ERR(clk);
 			if (rc == -EPROBE_DEFER)
-				goto free_clk;
+				goto err_out;
 			break;
 		}
 		hpriv->clks[i] = clk;
 	}
 
-	rc = ahci_platform_enable_resources(hpriv);
-	if (rc)
-		goto free_clk;
+	devres_remove_group(dev, NULL);
+	return hpriv;
 
-	/*
-	 * Some platforms might need to prepare for mmio region access,
-	 * which could be done in the following init call. So, the mmio
-	 * region shouldn't be accessed before init (if provided) has
-	 * returned successfully.
-	 */
-	if (pdata && pdata->init) {
-		rc = pdata->init(dev, hpriv->mmio);
-		if (rc)
-			goto disable_resources;
-	}
+err_out:
+	devres_release_group(dev, NULL);
+	return ERR_PTR(rc);
+}
+EXPORT_SYMBOL_GPL(ahci_platform_get_resources);
+
+/**
+ *	ahci_platform_init_host - Bring up an ahci-platform host
+ *	@pdev: platform device pointer for the host
+ *	@hpriv: ahci-host private data for the host
+ *	@pi_template: template for the ata_port_info to use
+ *	@force_port_map: param passed to ahci_save_initial_config
+ *	@mask_port_map: param passed to ahci_save_initial_config
+ *
+ *	This function does all the usual steps needed to bring up an
+ *	ahci-platform host, note any necessary resources (ie clks, phy, etc.)
+ *	must be initialized / enabled before calling this.
+ *
+ *	LOCKING:
+ *	None.
+ *
+ *	RETURNS:
+ *	0 on success otherwise a negative error code
+ */
+int ahci_platform_init_host(struct platform_device *pdev,
+			    struct ahci_host_priv *hpriv,
+			    const struct ata_port_info *pi_template,
+			    unsigned int force_port_map,
+			    unsigned int mask_port_map)
+{
+	struct device *dev = &pdev->dev;
+	struct ata_port_info pi = *pi_template;
+	const struct ata_port_info *ppi[] = { &pi, NULL };
+	struct ata_host *host;
+	int i, irq, n_ports, rc;
 
-	ahci_save_initial_config(dev, hpriv,
-		pdata ? pdata->force_port_map : 0,
-		pdata ? pdata->mask_port_map  : 0);
+	irq = platform_get_irq(pdev, 0);
+	if (irq <= 0) {
+		dev_err(dev, "no irq\n");
+		return -EINVAL;
+	}
 
 	/* prepare host */
+	hpriv->flags |= (unsigned long)pi.private_data;
+
+	ahci_save_initial_config(dev, hpriv, force_port_map, mask_port_map);
+
 	if (hpriv->cap & HOST_CAP_NCQ)
 		pi.flags |= ATA_FLAG_NCQ;
 
@@ -320,10 +349,8 @@ static int ahci_probe(struct platform_device *pdev)
 	n_ports = max(ahci_nr_ports(hpriv->cap), fls(hpriv->port_map));
 
 	host = ata_host_alloc_pinfo(dev, ppi, n_ports);
-	if (!host) {
-		rc = -ENOMEM;
-		goto pdata_exit;
-	}
+	if (!host)
+		return -ENOMEM;
 
 	host->private_data = hpriv;
 
@@ -338,7 +365,8 @@ static int ahci_probe(struct platform_device *pdev)
 	for (i = 0; i < host->n_ports; i++) {
 		struct ata_port *ap = host->ports[i];
 
-		ata_port_desc(ap, "mmio %pR", mem);
+		ata_port_desc(ap, "mmio %pR",
+			      platform_get_resource(pdev, IORESOURCE_MEM, 0));
 		ata_port_desc(ap, "port 0x%x", 0x100 + ap->port_no * 0x80);
 
 		/* set enclosure management message type */
@@ -352,13 +380,53 @@ static int ahci_probe(struct platform_device *pdev)
 
 	rc = ahci_reset_controller(host);
 	if (rc)
-		goto pdata_exit;
+		return rc;
 
 	ahci_init_controller(host);
 	ahci_print_info(host, "platform");
 
-	rc = ata_host_activate(host, irq, ahci_interrupt, IRQF_SHARED,
-			       &ahci_platform_sht);
+	return ata_host_activate(host, irq, ahci_interrupt, IRQF_SHARED,
+				 &ahci_platform_sht);
+}
+EXPORT_SYMBOL_GPL(ahci_platform_init_host);
+
+static int ahci_probe(struct platform_device *pdev)
+{
+	struct device *dev = &pdev->dev;
+	struct ahci_platform_data *pdata = dev_get_platdata(dev);
+	const struct platform_device_id *id = platform_get_device_id(pdev);
+	const struct ata_port_info *pi_template;
+	struct ahci_host_priv *hpriv;
+	int rc;
+
+	hpriv = ahci_platform_get_resources(pdev);
+	if (IS_ERR(hpriv))
+		return PTR_ERR(hpriv);
+
+	rc = ahci_platform_enable_resources(hpriv);
+	if (rc)
+		return rc;
+
+	/*
+	 * Some platforms might need to prepare for mmio region access,
+	 * which could be done in the following init call. So, the mmio
+	 * region shouldn't be accessed before init (if provided) has
+	 * returned successfully.
+	 */
+	if (pdata && pdata->init) {
+		rc = pdata->init(dev, hpriv->mmio);
+		if (rc)
+			goto disable_resources;
+	}
+
+	if (pdata && pdata->ata_port_info)
+		pi_template = pdata->ata_port_info;
+	else
+		pi_template = &ahci_port_info[id ? id->driver_data : 0];
+
+	rc = ahci_platform_init_host(pdev, hpriv, pi_template,
+				     pdata ? pdata->force_port_map : 0,
+				     pdata ? pdata->mask_port_map  : 0);
 	if (rc)
 		goto pdata_exit;
 
@@ -368,8 +436,6 @@ pdata_exit:
 		pdata->exit(dev);
 disable_resources:
 	ahci_platform_disable_resources(hpriv);
-free_clk:
-	ahci_put_clks(hpriv);
 	return rc;
 }
 
@@ -383,7 +449,6 @@ static void ahci_host_stop(struct ata_host *host)
 		pdata->exit(dev);
 
 	ahci_platform_disable_resources(hpriv);
-	ahci_put_clks(hpriv);
 }
 
 #ifdef CONFIG_PM_SLEEP
include/linux/ahci_platform.h +14 -0
index b674b01..b80c51c 100644
@@ -20,7 +20,14 @@
 struct device;
 struct ata_port_info;
 struct ahci_host_priv;
+struct platform_device;
 
+/*
+ * Note ahci_platform_data is deprecated, it is only kept around for use
+ * by the old da850 and spear13xx ahci code.
+ * New drivers should instead declare their own platform_driver struct, and
+ * use ahci_platform* functions in their own probe, suspend and resume methods.
+ */
 struct ahci_platform_data {
 	int (*init)(struct device *dev, void __iomem *addr);
 	void (*exit)(struct device *dev);
@@ -35,5 +42,12 @@ int ahci_platform_enable_clks(struct ahci_host_priv *hpriv);
 void ahci_platform_disable_clks(struct ahci_host_priv *hpriv);
 int ahci_platform_enable_resources(struct ahci_host_priv *hpriv);
 void ahci_platform_disable_resources(struct ahci_host_priv *hpriv);
+struct ahci_host_priv *ahci_platform_get_resources(
+	struct platform_device *pdev);
+int ahci_platform_init_host(struct platform_device *pdev,
+			    struct ahci_host_priv *hpriv,
+			    const struct ata_port_info *pi_template,
+			    unsigned int force_port_map,
+			    unsigned int mask_port_map);
 
 #endif /* _AHCI_PLATFORM_H */
Signature · 1 line
1.9.0
#7 lore raw

PATCH v7 5/15 ahci-platform: "Library-ise" ahci_probe functionality

Hi,

Quoted 51 lines · Hans de Goede

ahci_probe consists of 3 steps:
1) Get resources (get mmio, clks, regulator)
2) Enable resources, handled by ahci_platform_enable_resouces
3) The more or less standard ahci-host controller init sequence

This commit refactors step 1 and 3 into separate functions, so the platform drivers for AHCI implementations which need a specific order in step 2, and / or need to do some custom register poking at some time, can re-use ahci-platform.c code without needing to copy and paste it.

Note that ahci_platform_init_host's prototype takes the 3 non function members of ahci_platform_data as arguments, the idea is that drivers using the new exported utility functions will not use ahci_platform_data at all, and hopefully in the future ahci_platform_data can go away entirely.

---
 drivers/ata/ahci_platform.c   | 195 ++++++++++++++++++++++++++++--------------
 include/linux/ahci_platform.h |  14 +++
 2 files changed, 144 insertions(+), 65 deletions(-)
drivers/ata/ahci_platform.c +13 -2
index 6ebbc17..7f3f2ac 100644
@@ -201,64 +201,64 @@ void ahci_platform_disable_resources(struct ahci_host_priv *hpriv)
 }
 EXPORT_SYMBOL_GPL(ahci_platform_disable_resources);
 
-static void ahci_put_clks(struct ahci_host_priv *hpriv)
+static void ahci_platform_put_resources(struct device *dev, void *res)
 {
+	struct ahci_host_priv *hpriv = res;
 	int c;
 
 	for (c = 0; c < AHCI_MAX_CLKS && hpriv->clks[c]; c++)
 		clk_put(hpriv->clks[c]);
 }
 
-static int ahci_probe(struct platform_device *pdev)
+/**
+ *	ahci_platform_get_resources - Get platform resources
+ *	@pdev: platform device to get resources for
+ *
+ *	This function allocates an ahci_host_priv struct, and gets the
+ *	following resources, storing a reference to them inside the returned
+ *	struct:
+ *
+ *	1) mmio registers (IORESOURCE_MEM 0, mandatory)
+ *	2) regulator for controlling the targets power (optional)
+ *	3) 0 - AHCI_MAX_CLKS clocks, as specified in the devs devicetree node,

drivers/ata/ahci_platform.c void ahci_platform_disable_resources(struct ahci_host_priv *hpriv) · line 233

Quoted 10 lines
drivers/ata/ahci_platform.c +10 -0
+ *	   or for non devicetree enabled platforms a single clock
+ *
+ *	LOCKING:
+ *	None.
+ *
+ *	RETURNS:
+ *	The allocated ahci_host_priv on success, otherwise an ERR_PTR value
+ */
+struct ahci_host_priv *ahci_platform_get_resources(
+	struct platform_device *pdev)

It would be better if these two lines were merged:

struct ahci_host_priv *ahci_platform_get_resources(struct platform_device *pdev)

Quoted 40 lines
drivers/ata/ahci_platform.c +8 -24
 {
 	struct device *dev = &pdev->dev;
-	struct ahci_platform_data *pdata = dev_get_platdata(dev);
-	const struct platform_device_id *id = platform_get_device_id(pdev);
-	struct ata_port_info pi = ahci_port_info[id ? id->driver_data : 0];
-	const struct ata_port_info *ppi[] = { &pi, NULL };
 	struct ahci_host_priv *hpriv;
-	struct ata_host *host;
-	struct resource *mem;
 	struct clk *clk;
-	int irq;
-	int n_ports;
-	int i;
-	int rc;
+	int i, rc = -ENOMEM;
 
-	mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
-	if (!mem) {
-		dev_err(dev, "no mmio space\n");
-		return -EINVAL;
-	}
+	if (!devres_open_group(dev, NULL, GFP_KERNEL))
+		return ERR_PTR(-ENOMEM);
 
-	irq = platform_get_irq(pdev, 0);
-	if (irq <= 0) {
-		dev_err(dev, "no irq\n");
-		return -EINVAL;
-	}
+	hpriv = devres_alloc(ahci_platform_put_resources, sizeof(*hpriv),
+			     GFP_KERNEL);
+	if (!hpriv)
+		goto err_out;
 
-	if (pdata && pdata->ata_port_info)
-		pi = *pdata->ata_port_info;
+	devres_add(dev, hpriv);
 
-	hpriv = devm_kzalloc(dev, sizeof(*hpriv), GFP_KERNEL);
-	if (!hpriv) {

drivers/ata/ahci_platform.c void ahci_platform_disable_resources(struct ahci_host_priv *hpriv) · line 252

Quoted 10 lines
drivers/ata/ahci_platform.c +2 -7
-		dev_err(dev, "can't alloc ahci_host_priv\n");
-		return -ENOMEM;
-	}
-
-	hpriv->flags |= (unsigned long)pi.private_data;
-
-	hpriv->mmio = devm_ioremap(dev, mem->start, resource_size(mem));
+	hpriv->mmio = devm_ioremap_resource(dev,
+			      platform_get_resource(pdev, IORESOURCE_MEM, 0));
 	if (!hpriv->mmio) {

This should use IS_ERR() as devm_ioremap_resource() returns a pointer to the remapped memory or an ERR_PTR() encoded error code on failure.

drivers/ata/ahci_platform.c void ahci_platform_disable_resources(struct ahci_host_priv *hpriv) · line 253

Quoted 3 lines
drivers/ata/ahci_platform.c +1 -2
-		dev_err(dev, "can't map %pR\n", mem);
-		return -ENOMEM;
+		dev_err(dev, "no mmio space\n");

Not needed, devm_ioremap_resource() prints an error message on error.

drivers/ata/ahci_platform.c void ahci_platform_disable_resources(struct ahci_host_priv *hpriv) · line 254

Quoted 1 line
drivers/ata/ahci_platform.c +1 -0
+		goto err_out;

This should set rc to an error value from devm_ioremap_resource() instead of returning -ENOMEM.

Quoted 34 lines
drivers/ata/ahci_platform.c +4 -11
 	}
 
 	hpriv->target_pwr = devm_regulator_get_optional(dev, "target");
 	if (IS_ERR(hpriv->target_pwr)) {
 		rc = PTR_ERR(hpriv->target_pwr);
 		if (rc == -EPROBE_DEFER)
-			return -EPROBE_DEFER;
+			goto err_out;
 		hpriv->target_pwr = NULL;
 	}
 
@@ -277,33 +277,62 @@ static int ahci_probe(struct platform_device *pdev)
 		if (IS_ERR(clk)) {
 			rc = PTR_ERR(clk);
 			if (rc == -EPROBE_DEFER)
-				goto free_clk;
+				goto err_out;
 			break;
 		}
 		hpriv->clks[i] = clk;
 	}
 
-	rc = ahci_platform_enable_resources(hpriv);
-	if (rc)
-		goto free_clk;
+	devres_remove_group(dev, NULL);
+	return hpriv;
 
-	/*
-	 * Some platforms might need to prepare for mmio region access,
-	 * which could be done in the following init call. So, the mmio
-	 * region shouldn't be accessed before init (if provided) has
-	 * returned successfully.
-	 */

drivers/ata/ahci_platform.c static int ahci_probe(struct platform_device *pdev) · line 293

Quoted 10 lines
drivers/ata/ahci_platform.c +5 -5
-	if (pdata && pdata->init) {
-		rc = pdata->init(dev, hpriv->mmio);
-		if (rc)
-			goto disable_resources;
-	}
+err_out:
+	devres_release_group(dev, NULL);
+	return ERR_PTR(rc);
+}
+EXPORT_SYMBOL_GPL(ahci_platform_get_resources);

[...]

The rest of the patch looks OK.

Best regards,
--
Bartlomiej Zolnierkiewicz
Samsung R&D Institute Poland
Samsung Electronics

#8 lore raw

PATCH v7 6/15 ahci-platform: "Library-ise" suspend / resume functionality

In reply to Hans de Goede

Split suspend / resume code into host suspend / resume functionality and resource enable / disabling phases, and export the new suspend_ / resume_host functions.

---
 drivers/ata/ahci_platform.c   | 109 ++++++++++++++++++++++++++++++++++++------
 include/linux/ahci_platform.h |   5 ++
 2 files changed, 99 insertions(+), 15 deletions(-)
drivers/ata/ahci_platform.c +94 -15
index 7f3f2ac..bdadec1 100644
@@ -452,14 +452,26 @@ static void ahci_host_stop(struct ata_host *host)
 }
 
 #ifdef CONFIG_PM_SLEEP
-static int ahci_suspend(struct device *dev)
+/**
+ *	ahci_platform_suspend_host - Suspend an ahci-platform host
+ *	@dev: device pointer for the host
+ *
+ *	This function does all the usual steps needed to suspend an
+ *	ahci-platform host, note any necessary resources (ie clks, phy, etc.)
+ *	must be disabled after calling this.
+ *
+ *	LOCKING:
+ *	None.
+ *
+ *	RETURNS:
+ *	0 on success otherwise a negative error code
+ */
+int ahci_platform_suspend_host(struct device *dev)
 {
-	struct ahci_platform_data *pdata = dev_get_platdata(dev);
 	struct ata_host *host = dev_get_drvdata(dev);
 	struct ahci_host_priv *hpriv = host->private_data;
 	void __iomem *mmio = hpriv->mmio;
 	u32 ctl;
-	int rc;
 
 	if (hpriv->flags & AHCI_HFLAG_NO_SUSPEND) {
 		dev_err(dev, "firmware update required for suspend/resume\n");
@@ -476,7 +488,64 @@ static int ahci_suspend(struct device *dev)
 	writel(ctl, mmio + HOST_CTL);
 	readl(mmio + HOST_CTL); /* flush */
 
-	rc = ata_host_suspend(host, PMSG_SUSPEND);
+	return ata_host_suspend(host, PMSG_SUSPEND);
+}
+EXPORT_SYMBOL_GPL(ahci_platform_suspend_host);
+
+/**
+ *	ahci_platform_resume_host - Resume an ahci-platform host
+ *	@dev: device pointer for the host
+ *
+ *	This function does all the usual steps needed to resume an
+ *	ahci-platform host, note any necessary resources (ie clks, phy, etc.)
+ *	must be initialized / enabled before calling this.
+ *
+ *	LOCKING:
+ *	None.
+ *
+ *	RETURNS:
+ *	0 on success otherwise a negative error code
+ */
+int ahci_platform_resume_host(struct device *dev)
+{
+	struct ata_host *host = dev_get_drvdata(dev);
+	int rc;
+
+	if (dev->power.power_state.event == PM_EVENT_SUSPEND) {
+		rc = ahci_reset_controller(host);
+		if (rc)
+			return rc;
+
+		ahci_init_controller(host);
+	}
+
+	ata_host_resume(host);
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(ahci_platform_resume_host);
+
+/**
+ *	ahci_platform_suspend - Suspend an ahci-platform device
+ *	@dev: the platform device to suspend
+ *
+ *	This function suspends the host associated with the device, followed
+ *	by disabling all the resources of the device.
+ *
+ *	LOCKING:
+ *	None.
+ *
+ *	RETURNS:
+ *	0 on success otherwise a negative error code
+ */
+int ahci_platform_suspend(struct device *dev)
+{
+	struct ahci_platform_data *pdata = dev_get_platdata(dev);
+	struct ata_host *host = dev_get_drvdata(dev);
+	struct ahci_host_priv *hpriv = host->private_data;
+	int rc;
+
+	rc = ahci_platform_suspend_host(dev);
 	if (rc)
 		return rc;
 
@@ -487,8 +556,22 @@ static int ahci_suspend(struct device *dev)
 
 	return 0;
 }
+EXPORT_SYMBOL_GPL(ahci_platform_suspend);
 
-static int ahci_resume(struct device *dev)
+/**
+ *	ahci_platform_resume - Resume an ahci-platform device
+ *	@dev: the platform device to resume
+ *
+ *	This function enables all the resources of the device followed by
+ *	resuming the host associated with the device.
+ *
+ *	LOCKING:
+ *	None.
+ *
+ *	RETURNS:
+ *	0 on success otherwise a negative error code
+ */
+int ahci_platform_resume(struct device *dev)
 {
 	struct ahci_platform_data *pdata = dev_get_platdata(dev);
 	struct ata_host *host = dev_get_drvdata(dev);
@@ -505,15 +588,9 @@ static int ahci_resume(struct device *dev)
 			goto disable_resources;
 	}
 
-	if (dev->power.power_state.event == PM_EVENT_SUSPEND) {
-		rc = ahci_reset_controller(host);
-		if (rc)
-			goto disable_resources;
-
-		ahci_init_controller(host);
-	}
-
-	ata_host_resume(host);
+	rc = ahci_platform_resume_host(dev);
+	if (rc)
+		goto disable_resources;
 
 	return 0;
 
@@ -522,9 +599,11 @@ disable_resources:
 
 	return rc;
 }
+EXPORT_SYMBOL_GPL(ahci_platform_resume);
 #endif
 
-static SIMPLE_DEV_PM_OPS(ahci_pm_ops, ahci_suspend, ahci_resume);
+static SIMPLE_DEV_PM_OPS(ahci_pm_ops, ahci_platform_suspend,
+			 ahci_platform_resume);
 
 static const struct of_device_id ahci_of_match[] = {
 	{ .compatible = "snps,spear-ahci", },
include/linux/ahci_platform.h +5 -0
index b80c51c..542f268 100644
@@ -50,4 +50,9 @@ int ahci_platform_init_host(struct platform_device *pdev,
 			    unsigned int force_port_map,
 			    unsigned int mask_port_map);
 
+int ahci_platform_suspend_host(struct device *dev);
+int ahci_platform_resume_host(struct device *dev);
+int ahci_platform_suspend(struct device *dev);
+int ahci_platform_resume(struct device *dev);
+
 #endif /* _AHCI_PLATFORM_H */
Signature · 1 line
1.9.0
#9 lore raw

PATCH v7 7/15 ARM: sunxi: Add support for Allwinner SUNXi SoCs sata to ahci_platform

In reply to Hans de Goede

From: Olliver Schinagl <[email protected]>

This patch adds support for the ahci sata controler found on Allwinner A10 and A20 SoCs to the ahci_platform driver.

Orignally written by Olliver Schinagl using the approach of having a platform device which probe method creates a new child platform device which gets driven by ahci_platform.c, as done by ahci_imx.c .

Refactored by Hans de Goede to add most of the non sunxi specific functionality to ahci_platform.c and use a platform_data pointer from of_device_id for the sunxi specific bits.

---
 .../devicetree/bindings/ata/ahci-platform.txt      |  15 +-
 drivers/ata/Kconfig                                |   9 +
 drivers/ata/Makefile                               |   1 +
 drivers/ata/ahci_sunxi.c                           | 249 +++++++++++++++++++++
 4 files changed, 271 insertions(+), 3 deletions(-)
 create mode 100644 drivers/ata/ahci_sunxi.c
Documentation/devicetree/bindings/ata/ahci-platform.txt +12 -3
index 1ac807f..499bfed 100644
@@ -4,7 +4,9 @@ SATA nodes are defined to describe on-chip Serial ATA controllers.
 Each SATA controller should have its own node.
 
 Required properties:
-- compatible        : compatible list, contains "snps,spear-ahci"
+- compatible        : compatible list, one of "snps,spear-ahci",
+                      "snps,exynos5440-ahci", "ibm,476gtr-ahci", or
+                      "allwinner,sun4i-a10-ahci"
 - interrupts        : <interrupt mapping for SATA IRQ>
 - reg               : <registers mapping>
 
@@ -13,10 +15,17 @@ Optional properties:
 - clocks            : a list of phandle + clock specifier pairs
 - target-supply     : regulator for SATA target power
 
-Example:
+Examples:
         sata at ffe08000 {
 		compatible = "snps,spear-ahci";
 		reg = <0xffe08000 0x1000>;
 		interrupts = <115>;
-
         };
+
+	ahci: sata at 01c18000 {
+		compatible = "allwinner,sun4i-a10-ahci";
+		reg = <0x01c18000 0x1000>;
+		interrupts = <56>;
+		clocks = <&pll6 0>, <&ahb_gates 25>;
+		target-supply = <&reg_ahci_5v>;
+	};
drivers/ata/Kconfig +9 -0
index 4e73772..cc67cc0 100644
@@ -106,6 +106,15 @@ config AHCI_IMX
 
 	  If unsure, say N.
 
+config AHCI_SUNXI
+	tristate "Allwinner sunxi AHCI SATA support"
+	depends on ARCH_SUNXI && SATA_AHCI_PLATFORM
+	help
+	  This option enables support for the Allwinner sunxi SoC's
+	  onboard AHCI SATA.
+
+	  If unsure, say N.
+
 config SATA_FSL
 	tristate "Freescale 3.0Gbps SATA support"
 	depends on FSL_SOC
drivers/ata/Makefile +1 -0
index 46518c6..246050b 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_AHCI_SUNXI)	+= ahci_sunxi.o
 
 # SFF w/ custom DMA
 obj-$(CONFIG_PDC_ADMA)		+= pdc_adma.o
drivers/ata/ahci_sunxi.c new file +249 -0

249 lines not on this page · raw message

Signature · 1 line
1.9.0
#10 lore raw

PATCH v7 7/15 ARM: sunxi: Add support for Allwinner SUNXi SoCs sata to ahci_platform

On Sat, Feb 22, 2014 at 04:53:36PM +0100, Hans de Goede wrote:
...

Quoted 13 lines
drivers/ata/ahci_sunxi.c new file +13 -0
+static int ahci_sunxi_phy_init(struct device *dev, void __iomem *reg_base)
+{
+	u32 reg_val;
+	int timeout;
+
+	/* This magic is from the original code */
+	writel(0, reg_base + AHCI_RWCR);
+	mdelay(5);
+
+	sunxi_setbits(reg_base + AHCI_PHYCS1R, BIT(19));
+	sunxi_clrsetbits(reg_base + AHCI_PHYCS0R,
+			 (0x7 << 24),
+			 (0x5 << 24) | BIT(23) | BIT(18));

drivers/ata/ahci_sunxi.c line 108

Quoted 10 lines
drivers/ata/ahci_sunxi.c new file +10 -0
+	sunxi_clrsetbits(reg_base + AHCI_PHYCS1R,
+			 (0x3 << 16) | (0x1f << 8) | (0x3 << 6),
+			 (0x2 << 16) | (0x6 << 8) | (0x2 << 6));
+	sunxi_setbits(reg_base + AHCI_PHYCS1R, BIT(28) | BIT(15));
+	sunxi_clrbits(reg_base + AHCI_PHYCS1R, BIT(19));
+	sunxi_clrsetbits(reg_base + AHCI_PHYCS0R,
+			 (0x7 << 20), (0x3 << 20));
+	sunxi_clrsetbits(reg_base + AHCI_PHYCS2R,
+			 (0x1f << 5), (0x19 << 5));
+	mdelay(5);

Please use msleep() instead. This is called with full process context. mdelay() is almost always wrong. Even if the hardware is broken enough to require millisec level breather, the better thing to do would be using threaded handler and using msleep(), not mdelay().

Thanks.

Signature · 1 line
tejun
#11 lore raw

PATCH v7 8/15 ahci-imx: Port to library-ised ahci_platform

In reply to Hans de Goede

This avoids the ugliness of creating a nested platform device from probe.

While moving it around anyways, move the mk6q phy init code from probe to imx_sata_enable, as the phy needs to be re-initialized on resume too, otherwise the drive won't be recognized after resume.

Tested on a wandboard i.mx6 quad.

---
 .../devicetree/bindings/ata/ahci-platform.txt      |   9 +-
 drivers/ata/ahci_imx.c                             | 331 ++++++++-------------
 2 files changed, 134 insertions(+), 206 deletions(-)
Documentation/devicetree/bindings/ata/ahci-platform.txt +7 -2
index 499bfed..d86e854 100644
@@ -5,8 +5,9 @@ Each SATA controller should have its own node.
 
 Required properties:
 - compatible        : compatible list, one of "snps,spear-ahci",
-                      "snps,exynos5440-ahci", "ibm,476gtr-ahci", or
-                      "allwinner,sun4i-a10-ahci"
+                      "snps,exynos5440-ahci", "ibm,476gtr-ahci",
+                      "allwinner,sun4i-a10-ahci", "fsl,imx53-ahci" or
+                      "fsl,imx6q-ahci"
 - interrupts        : <interrupt mapping for SATA IRQ>
 - reg               : <registers mapping>
 
@@ -15,6 +16,10 @@ Optional properties:
 - clocks            : a list of phandle + clock specifier pairs
 - target-supply     : regulator for SATA target power
 
+"fsl,imx53-ahci", "fsl,imx6q-ahci" required properties:
+- clocks            : must contain the sata, sata_ref and ahb clocks
+- clock-names       : must contain "ahb" for the ahb clock
+
 Examples:
         sata at ffe08000 {
 		compatible = "snps,spear-ahci";
drivers/ata/ahci_imx.c +127 -204

451 lines not on this page · raw message

Signature · 1 line
1.9.0
#12 lore raw

PATCH v7 8/15 ahci-imx: Port to library-ised ahci_platform

Quoted 15 lines · Hans de Goede
drivers/ata/ahci_imx.c +15 -0
+		/*
+		 * set PHY Paremeters, two steps to configure the GPR13,
+		 * one write for rest of parameters, mask of first write
+		 * is 0x07ffffff, and the other one write for setting
+		 * the mpll_clk_en.
+		 */
+		regmap_update_bits(imxpriv->gpr, IOMUXC_GPR13,
+				   IMX6Q_GPR13_SATA_RX_EQ_VAL_MASK |
+				   IMX6Q_GPR13_SATA_RX_LOS_LVL_MASK |
+				   IMX6Q_GPR13_SATA_RX_DPLL_MODE_MASK |
+				   IMX6Q_GPR13_SATA_SPD_MODE_MASK |
+				   IMX6Q_GPR13_SATA_MPLL_SS_EN |
+				   IMX6Q_GPR13_SATA_TX_ATTEN_MASK |
+				   IMX6Q_GPR13_SATA_TX_BOOST_MASK |
+				   IMX6Q_GPR13_SATA_TX_LVL_MASK |

drivers/ata/ahci_imx.c static int ahci_imx_hotplug; · line 100

Quoted 10 lines
drivers/ata/ahci_imx.c +10 -0
+				   IMX6Q_GPR13_SATA_MPLL_CLK_EN |
+				   IMX6Q_GPR13_SATA_TX_EDGE_RATE,
+				   IMX6Q_GPR13_SATA_RX_EQ_VAL_3_0_DB |
+				   IMX6Q_GPR13_SATA_RX_LOS_LVL_SATA2M |
+				   IMX6Q_GPR13_SATA_RX_DPLL_MODE_2P_4F |
+				   IMX6Q_GPR13_SATA_SPD_MODE_3P0G |
+				   IMX6Q_GPR13_SATA_MPLL_SS_EN |
+				   IMX6Q_GPR13_SATA_TX_ATTEN_9_16 |
+				   IMX6Q_GPR13_SATA_TX_BOOST_3_33_DB |
+				   IMX6Q_GPR13_SATA_TX_LVL_1_025_V);

While I see this, I'll stick an oar in.

It is totally wrong for this to be hard-coded into the driver - many of these should be specified via DT, since they're board specific. These are already different from the reset default in this register.

The side effect of this hard coding is that eSATA just doesn't work at all on some boards - the board I have requires TX_LVL_1_104V and TX_BOOST_0_00_DB.

Hence, I'd ask that while you move stuff like this around, you bear in mind that we do need to add additional properties.

I'm in two minds about this - whether to make the existing binding with its compatible string always use these settings, and invent a new compatible string for one which is fully configurable (as it _should_ have been from the very start) or whether to make this the default if the various properties aren't specified.

Either way, this driver is electrically unusable as it stands here.

Signature · 2 lines
FTTC broadband for 0.8mile line: now at 9.7Mbps down 460kbps up... slowly
improving, and getting towards what was expected from it.
#13 lore raw

PATCH v7 8/15 ahci-imx: Port to library-ised ahci_platform

Hi Russell,

Quoted 29 lines · Russell King - ARM Linux
Quoted 15 lines · Hans de Goede
drivers/ata/ahci_imx.c +15 -0
+		/*
+		 * set PHY Paremeters, two steps to configure the GPR13,
+		 * one write for rest of parameters, mask of first write
+		 * is 0x07ffffff, and the other one write for setting
+		 * the mpll_clk_en.
+		 */
+		regmap_update_bits(imxpriv->gpr, IOMUXC_GPR13,
+				   IMX6Q_GPR13_SATA_RX_EQ_VAL_MASK |
+				   IMX6Q_GPR13_SATA_RX_LOS_LVL_MASK |
+				   IMX6Q_GPR13_SATA_RX_DPLL_MODE_MASK |
+				   IMX6Q_GPR13_SATA_SPD_MODE_MASK |
+				   IMX6Q_GPR13_SATA_MPLL_SS_EN |
+				   IMX6Q_GPR13_SATA_TX_ATTEN_MASK |
+				   IMX6Q_GPR13_SATA_TX_BOOST_MASK |
+				   IMX6Q_GPR13_SATA_TX_LVL_MASK |

drivers/ata/ahci_imx.c static int ahci_imx_hotplug; · line 100

Quoted 10 lines
drivers/ata/ahci_imx.c +10 -0
+				   IMX6Q_GPR13_SATA_MPLL_CLK_EN |
+				   IMX6Q_GPR13_SATA_TX_EDGE_RATE,
+				   IMX6Q_GPR13_SATA_RX_EQ_VAL_3_0_DB |
+				   IMX6Q_GPR13_SATA_RX_LOS_LVL_SATA2M |
+				   IMX6Q_GPR13_SATA_RX_DPLL_MODE_2P_4F |
+				   IMX6Q_GPR13_SATA_SPD_MODE_3P0G |
+				   IMX6Q_GPR13_SATA_MPLL_SS_EN |
+				   IMX6Q_GPR13_SATA_TX_ATTEN_9_16 |
+				   IMX6Q_GPR13_SATA_TX_BOOST_3_33_DB |
+				   IMX6Q_GPR13_SATA_TX_LVL_1_025_V);

While I see this, I'll stick an oar in.

Quoted 7 lines

It is totally wrong for this to be hard-coded into the driver - many of these should be specified via DT, since they're board specific. These are already different from the reset default in this register.

The side effect of this hard coding is that eSATA just doesn't work at all on some boards - the board I have requires TX_LVL_1_104V and TX_BOOST_0_00_DB.

I completely agree that if this is board specific it should be settable (override-able since we've existing dt files without a setting for it).

Quoted 2 lines

Hence, I'd ask that while you move stuff like this around, you bear in mind that we do need to add additional properties.

I understand, but my interest in the imx code only goes as far as not making it regress. I already went above and beyond duty by buying an imx board with sata and cleaning up the horific platform device driver instantiating a platform device hack there was.

My interest in platform-ahci was to clean it up so that it could be used as a proper basis to build other ARM ahci drivers on top of, such as a driver for the allwinner A10 and A20 ahci controller.

While at this I ended up having to clean up the imx driver too, so as to not break at. As a bonus I fixed it to not loose the harddisk after a suspend / resume cycle, and that is really as far as my interest in the imx driver goes.

I'm sure Tejun would welcome patches to add a dts property for setting the board-specific phy parameters, but I won't be writing it.

Quoted 5 lines

I'm in two minds about this - whether to make the existing binding with its compatible string always use these settings, and invent a new compatible string for one which is fully configurable (as it _should_ have been from the very start) or whether to make this the default if the various properties aren't specified.

IMHO this does not warrant doing a new compatibole-string. Simply default to the current hardcoded phy settings if no settings are specified through devicetree.

Quoted 1 line

Either way, this driver is electrically unusable as it stands here.

That is only true on some boards.

Regards,

Hans

#14 lore raw

PATCH v7 8/15 ahci-imx: Port to library-ised ahci_platform

I've moved the devicetree mailing list to the To: header in case anyone there wants to comment on this.

Quoted 3 lines · Hans de Goede

I'm sure Tejun would welcome patches to add a dts property for setting the board-specific phy parameters, but I won't be writing it.

Don't worry, I already have something :)

Quoted 9 lines
Quoted 5 lines

I'm in two minds about this - whether to make the existing binding with its compatible string always use these settings, and invent a new compatible string for one which is fully configurable (as it _should_ have been from the very start) or whether to make this the default if the various properties aren't specified.

IMHO this does not warrant doing a new compatibole-string. Simply default to the current hardcoded phy settings if no settings are specified through devicetree.

The problem is that we need to keep existing setups working - which means when there's no properties specified, we need to default to the settings hard-coded into the driver.

That means introducing properties like:

fsl,no-spread-spectrum

so that the hard-coded defaults can be turned off, and also deal with a whole load of defaults for the other properties. That's not particularly nice. Doing it this way, what I currently have is this:

struct reg_value {
        u32 of_value;
        u32 reg_value;
};
struct reg_property {
        const char *name;
        const struct reg_value *values;
        size_t num_values;
        u32 def_value;
        u32 set_value;
};
static const struct reg_value gpr13_tx_level[] = {
        {  937, IMX6Q_GPR13_SATA_TX_LVL_0_937_V },
        {  947, IMX6Q_GPR13_SATA_TX_LVL_0_947_V },
...
        { 1230, IMX6Q_GPR13_SATA_TX_LVL_1_230_V },
        { 1240, IMX6Q_GPR13_SATA_TX_LVL_1_240_V }
};
static const struct reg_value gpr13_tx_boost[] = {
        {    0, IMX6Q_GPR13_SATA_TX_BOOST_0_00_DB },
        {  370, IMX6Q_GPR13_SATA_TX_BOOST_0_37_DB },
...
        {  528, IMX6Q_GPR13_SATA_TX_BOOST_5_28_DB },
        {  575, IMX6Q_GPR13_SATA_TX_BOOST_5_75_DB }
};
static const struct reg_value gpr13_tx_atten[] = {
        {  8, IMX6Q_GPR13_SATA_TX_ATTEN_8_16 },
        {  9, IMX6Q_GPR13_SATA_TX_ATTEN_9_16 },
...
        { 14, IMX6Q_GPR13_SATA_TX_ATTEN_14_16 },
        { 16, IMX6Q_GPR13_SATA_TX_ATTEN_16_16 },
};
static const struct reg_value gpr13_rx_eq[] = {
        {  500, IMX6Q_GPR13_SATA_RX_EQ_VAL_0_5_DB },
        { 1000, IMX6Q_GPR13_SATA_RX_EQ_VAL_1_0_DB },
...
        { 3500, IMX6Q_GPR13_SATA_RX_EQ_VAL_3_5_DB },
        { 4000, IMX6Q_GPR13_SATA_RX_EQ_VAL_4_0_DB },
};
static const struct reg_property gpr13_props[] = {
        {
                .name = "fsl,transmit-level-mV",
                .values = gpr13_tx_level,
                .num_values = ARRAY_SIZE(gpr13_tx_level),
                .def_value = IMX6Q_GPR13_SATA_TX_LVL_1_025_V,
        }, {
                .name = "fsl,transmit-boost-mdB",
                .values = gpr13_tx_boost,
                .num_values = ARRAY_SIZE(gpr13_tx_boost),
                .def_value = IMX6Q_GPR13_SATA_TX_BOOST_3_33_DB,
        }, {
                .name = "fsl,transmit-atten-16ths",
                .values = gpr13_tx_atten,
                .num_values = ARRAY_SIZE(gpr13_tx_atten),
                .def_value = IMX6Q_GPR13_SATA_TX_ATTEN_9_16,
        }, {
                .name = "fsl,receive-eq-mdB",
                .values = gpr13_rx_eq,
                .num_values = ARRAY_SIZE(gpr13_rx_eq),
                .def_value = IMX6Q_GPR13_SATA_RX_EQ_VAL_3_0_DB,
        }, {
                .name = "fsl,no-spread-spectrum",
                .def_value = IMX6Q_GPR13_SATA_MPLL_SS_EN,
                .set_value = 0,
        },
};

and then a bunch of code to read through these tables and work out the GPR13 register value from it - it doesn't handle everything yet because I've not worked out a good way to do the last remaining three - I'm thinking that they want to be strings:

                regmap_update_bits(imxpriv->gpr, IOMUXC_GPR13,
                                   IMX6Q_GPR13_SATA_RX_EQ_VAL_MASK |
...
                                   IMX6Q_GPR13_SATA_TX_EDGE_RATE,
                                   IMX6Q_GPR13_SATA_RX_LOS_LVL_SATA2M |
                                   IMX6Q_GPR13_SATA_RX_DPLL_MODE_2P_4F |
                                   IMX6Q_GPR13_SATA_SPD_MODE_3P0G |
                                   reg_value);

	RX_LOS_LVL: SATA1I / SATA1M / SATA1X / SATA2I / SATA2M / SATA2X
	RX_DPLL_MODE: 1P_1F / 2P_2F / 1P_4F / 2P_4F
	SPD_MODE: 3P0G / 1P5G (I do wonder whether this should be changed
		when the Linux wants to slow the link speed.)

With a new compatible string, we can use the hard-coded version for fsl,imx6q-ahci, but require all properties (with values) to be specified for a different compatible string, thereby eliminating all the defaults, and making things like "no-spread-spectrum" be a positive property instead of negative, and this simplifies the parsing code.

There's also the obvious question about which of these properties should be generic ones for AHCI/SATA interfaces... The only one I see with any kind of electrical properties specified is sata_highbank:

- calxeda,tx-atten : a u32 array that contains TX attenuation override codes, one per port. The upper 3 bytes are always 0 and thus ignored.

and that seems to be a register-coded value rather than an actual attenuation figure.

A simpler solution to this would be to just provide an imx6-specific property which encodes the GPR13 value directly, and not bother with trying to provide individual properties.

Signature · 2 lines
FTTC broadband for 0.8mile line: now at 9.7Mbps down 460kbps up... slowly
improving, and getting towards what was expected from it.
#15 lore raw

PATCH v7 8/15 ahci-imx: Port to library-ised ahci_platform

Hi,

Quoted 9 lines · Russell King - ARM Linux

I've moved the devicetree mailing list to the To: header in case anyone there wants to comment on this.

Quoted 3 lines · Hans de Goede

I'm sure Tejun would welcome patches to add a dts property for setting the board-specific phy parameters, but I won't be writing it.

Don't worry, I already have something :)

Great.

Quoted 114 lines
Quoted 9 lines
Quoted 5 lines

I'm in two minds about this - whether to make the existing binding with its compatible string always use these settings, and invent a new compatible string for one which is fully configurable (as it _should_ have been from the very start) or whether to make this the default if the various properties aren't specified.

IMHO this does not warrant doing a new compatibole-string. Simply default to the current hardcoded phy settings if no settings are specified through devicetree.

The problem is that we need to keep existing setups working - which means when there's no properties specified, we need to default to the settings hard-coded into the driver.

That means introducing properties like:

fsl,no-spread-spectrum

so that the hard-coded defaults can be turned off, and also deal with a whole load of defaults for the other properties. That's not particularly nice. Doing it this way, what I currently have is this:

struct reg_value {
        u32 of_value;
        u32 reg_value;
};
struct reg_property {
        const char *name;
        const struct reg_value *values;
        size_t num_values;
        u32 def_value;
        u32 set_value;
};
static const struct reg_value gpr13_tx_level[] = {
        {  937, IMX6Q_GPR13_SATA_TX_LVL_0_937_V },
        {  947, IMX6Q_GPR13_SATA_TX_LVL_0_947_V },
...
        { 1230, IMX6Q_GPR13_SATA_TX_LVL_1_230_V },
        { 1240, IMX6Q_GPR13_SATA_TX_LVL_1_240_V }
};
static const struct reg_value gpr13_tx_boost[] = {
        {    0, IMX6Q_GPR13_SATA_TX_BOOST_0_00_DB },
        {  370, IMX6Q_GPR13_SATA_TX_BOOST_0_37_DB },
...
        {  528, IMX6Q_GPR13_SATA_TX_BOOST_5_28_DB },
        {  575, IMX6Q_GPR13_SATA_TX_BOOST_5_75_DB }
};
static const struct reg_value gpr13_tx_atten[] = {
        {  8, IMX6Q_GPR13_SATA_TX_ATTEN_8_16 },
        {  9, IMX6Q_GPR13_SATA_TX_ATTEN_9_16 },
...
        { 14, IMX6Q_GPR13_SATA_TX_ATTEN_14_16 },
        { 16, IMX6Q_GPR13_SATA_TX_ATTEN_16_16 },
};
static const struct reg_value gpr13_rx_eq[] = {
        {  500, IMX6Q_GPR13_SATA_RX_EQ_VAL_0_5_DB },
        { 1000, IMX6Q_GPR13_SATA_RX_EQ_VAL_1_0_DB },
...
        { 3500, IMX6Q_GPR13_SATA_RX_EQ_VAL_3_5_DB },
        { 4000, IMX6Q_GPR13_SATA_RX_EQ_VAL_4_0_DB },
};
static const struct reg_property gpr13_props[] = {
        {
                .name = "fsl,transmit-level-mV",
                .values = gpr13_tx_level,
                .num_values = ARRAY_SIZE(gpr13_tx_level),
                .def_value = IMX6Q_GPR13_SATA_TX_LVL_1_025_V,
        }, {
                .name = "fsl,transmit-boost-mdB",
                .values = gpr13_tx_boost,
                .num_values = ARRAY_SIZE(gpr13_tx_boost),
                .def_value = IMX6Q_GPR13_SATA_TX_BOOST_3_33_DB,
        }, {
                .name = "fsl,transmit-atten-16ths",
                .values = gpr13_tx_atten,
                .num_values = ARRAY_SIZE(gpr13_tx_atten),
                .def_value = IMX6Q_GPR13_SATA_TX_ATTEN_9_16,
        }, {
                .name = "fsl,receive-eq-mdB",
                .values = gpr13_rx_eq,
                .num_values = ARRAY_SIZE(gpr13_rx_eq),
                .def_value = IMX6Q_GPR13_SATA_RX_EQ_VAL_3_0_DB,
        }, {
                .name = "fsl,no-spread-spectrum",
                .def_value = IMX6Q_GPR13_SATA_MPLL_SS_EN,
                .set_value = 0,
        },
};

and then a bunch of code to read through these tables and work out the GPR13 register value from it - it doesn't handle everything yet because I've not worked out a good way to do the last remaining three - I'm thinking that they want to be strings:

                regmap_update_bits(imxpriv->gpr, IOMUXC_GPR13,
                                   IMX6Q_GPR13_SATA_RX_EQ_VAL_MASK |
...
                                   IMX6Q_GPR13_SATA_TX_EDGE_RATE,
                                   IMX6Q_GPR13_SATA_RX_LOS_LVL_SATA2M |
                                   IMX6Q_GPR13_SATA_RX_DPLL_MODE_2P_4F |
                                   IMX6Q_GPR13_SATA_SPD_MODE_3P0G |
                                   reg_value);

	RX_LOS_LVL: SATA1I / SATA1M / SATA1X / SATA2I / SATA2M / SATA2X
	RX_DPLL_MODE: 1P_1F / 2P_2F / 1P_4F / 2P_4F
	SPD_MODE: 3P0G / 1P5G (I do wonder whether this should be changed
		when the Linux wants to slow the link speed.)
Quoted 5 lines

With a new compatible string, we can use the hard-coded version for fsl,imx6q-ahci, but require all properties (with values) to be specified for a different compatible string, thereby eliminating all the defaults, and making things like "no-spread-spectrum" be a positive property instead of negative, and this simplifies the parsing code.

I see, that does make sense. So consider my +1 for keeping the same compatible string changed to a 0

Quoted 8 lines

There's also the obvious question about which of these properties should be generic ones for AHCI/SATA interfaces... The only one I see with any kind of electrical properties specified is sata_highbank:

- calxeda,tx-atten : a u32 array that contains TX attenuation override codes, one per port. The upper 3 bytes are always 0 and thus ignored.

Quoted 6 lines

and that seems to be a register-coded value rather than an actual attenuation figure.

A simpler solution to this would be to just provide an imx6-specific property which encodes the GPR13 value directly, and not bother with trying to provide individual properties.

Yes, that is actually the direction I was thinking in. I think it is great we know what all the bits do in so much detail in the freescale case, but for many other phys we don't have such extensive documentation.

Still I can see how in the freescale case you do want to use that documentation to do something better then coding a register value inside the devicetree.

OTOH I do like the KISS value of jut specifying a register value.

So in the end it is all up to you I'm afraid.

Regards,

Hans

#16 lore raw

PATCH v7 8/15 ahci-imx: Port to library-ised ahci_platform

In reply to Hans de Goede

Hi,

Quoted 45 lines · Hans de Goede

This avoids the ugliness of creating a nested platform device from probe.

While moving it around anyways, move the mk6q phy init code from probe to imx_sata_enable, as the phy needs to be re-initialized on resume too, otherwise the drive won't be recognized after resume.

Tested on a wandboard i.mx6 quad.

---
 .../devicetree/bindings/ata/ahci-platform.txt      |   9 +-
 drivers/ata/ahci_imx.c                             | 331 ++++++++-------------
 2 files changed, 134 insertions(+), 206 deletions(-)
Documentation/devicetree/bindings/ata/ahci-platform.txt +7 -2
index 499bfed..d86e854 100644
@@ -5,8 +5,9 @@ Each SATA controller should have its own node.
 
 Required properties:
 - compatible        : compatible list, one of "snps,spear-ahci",
-                      "snps,exynos5440-ahci", "ibm,476gtr-ahci", or
-                      "allwinner,sun4i-a10-ahci"
+                      "snps,exynos5440-ahci", "ibm,476gtr-ahci",
+                      "allwinner,sun4i-a10-ahci", "fsl,imx53-ahci" or
+                      "fsl,imx6q-ahci"
 - interrupts        : <interrupt mapping for SATA IRQ>
 - reg               : <registers mapping>
 
@@ -15,6 +16,10 @@ Optional properties:
 - clocks            : a list of phandle + clock specifier pairs
 - target-supply     : regulator for SATA target power
 
+"fsl,imx53-ahci", "fsl,imx6q-ahci" required properties:
+- clocks            : must contain the sata, sata_ref and ahb clocks
+- clock-names       : must contain "ahb" for the ahb clock
+
 Examples:
         sata at ffe08000 {
 		compatible = "snps,spear-ahci";
drivers/ata/ahci_imx.c +0 -0
index dd4d6f7..3cb5d69 100644

[...]

Quoted 40 lines
drivers/ata/ahci_imx.c +17 -14
+static void ahci_imx_host_stop(struct ata_host *host)
+{
+	struct ahci_host_priv *hpriv = host->private_data;
 
-	ret = platform_device_add_resources(ahci_pdev, res, 2);
-	if (ret)
-		goto err_out;
+	imx_sata_disable(hpriv);
+}
 
-	ret = platform_device_add_data(ahci_pdev, pdata, sizeof(*pdata));
-	if (ret)
-		goto err_out;
+static int imx_ahci_suspend(struct device *dev)
+{
+	struct ata_host *host = dev_get_drvdata(dev);
+	struct ahci_host_priv *hpriv = host->private_data;
+	int ret;
 
-	ret = platform_device_add(ahci_pdev);
-	if (ret) {
-err_out:
-		platform_device_put(ahci_pdev);
+	ret = ahci_platform_suspend_host(dev);
+	if (ret)
 		return ret;
-	}
+
+	imx_sata_disable(hpriv);
 
 	return 0;
 }
 
-static int imx_ahci_remove(struct platform_device *pdev)
+static int imx_ahci_resume(struct device *dev)
 {
-	struct imx_ahci_priv *imxpriv = platform_get_drvdata(pdev);
-	struct platform_device *ahci_pdev = imxpriv->ahci_pdev;
+	struct ata_host *host = dev_get_drvdata(dev);
+	struct ahci_host_priv *hpriv = host->private_data;

drivers/ata/ahci_imx.c MODULE_DEVICE_TABLE(of, imx_ahci_of_match); · line 310

Quoted 10 lines
drivers/ata/ahci_imx.c +6 -2
+	int ret;
 
-	platform_device_unregister(ahci_pdev);
-	return 0;
+	ret = imx_sata_enable(hpriv);
+	if (ret)
+		return ret;
+
+	return ahci_platform_resume_host(dev);
 }

The code above introduces two new warnings for CONFIG_PM_SLEEP=n:

drivers/ata/ahci_imx.c:284:12: warning: ?imx_ahci_suspend? defined but not used [-Wunused-function] drivers/ata/ahci_imx.c:299:12: warning: ?imx_ahci_resume? defined but not used [-Wunused-function]

[ There needs to be CONFIG_PM_SLEEP ifdef around imh_ahci_suspend()
and imx_ahci_resume(). ]

All the rest looks good.

Quoted 4 lines
drivers/ata/ahci_imx.c +2 -0
+static SIMPLE_DEV_PM_OPS(ahci_imx_pm_ops, imx_ahci_suspend, imx_ahci_resume);
+
 static struct platform_driver imx_ahci_driver = {
 	.probe = imx_ahci_probe,

drivers/ata/ahci_imx.c MODULE_DEVICE_TABLE(of, imx_ahci_of_match); · line 324

Quoted 10 lines
drivers/ata/ahci_imx.c +2 -1
-	.remove = imx_ahci_remove,
+	.remove = ata_platform_remove_one,
 	.driver = {
 		.name = "ahci-imx",
 		.owner = THIS_MODULE,
 		.of_match_table = imx_ahci_of_match,
+		.pm = &ahci_imx_pm_ops,
 	},
 };
 module_platform_driver(imx_ahci_driver);

Best regards,
--
Bartlomiej Zolnierkiewicz
Samsung R&D Institute Poland
Samsung Electronics

#17 lore raw

PATCH v7 9/15 ata: ahci_platform: Add DT compatible for Synopsis DWC AHCI controller

In reply to Hans de Goede

From: Roger Quadros <[email protected]>

Add compatible string "snps,dwc-ahci", which should be used for Synopsis Designware SATA cores. e.g. on TI OMAP5 and DRA7 platforms.

---
 drivers/ata/ahci_platform.c | 1 +
 1 file changed, 1 insertion(+)
drivers/ata/ahci_platform.c +1 -0
index bdadec1..d7e55ba 100644
@@ -609,6 +609,7 @@ static const struct of_device_id ahci_of_match[] = {
 	{ .compatible = "snps,spear-ahci", },
 	{ .compatible = "snps,exynos5440-ahci", },
 	{ .compatible = "ibm,476gtr-ahci", },
+	{ .compatible = "snps,dwc-ahci", },
 	{},
 };
 MODULE_DEVICE_TABLE(of, ahci_of_match);
Signature · 1 line
1.9.0
#18 lore raw

PATCH v7 10/15 ata: ahci_platform: Update DT compatible list

In reply to Hans de Goede

From: Roger Quadros <[email protected]>

The ahci_platform driver supports "snps,dwc-ahci". Add this to the DT binding information.

---
 Documentation/devicetree/bindings/ata/ahci-platform.txt | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)
Documentation/devicetree/bindings/ata/ahci-platform.txt +2 -2
index d86e854..48b285f 100644
@@ -6,8 +6,8 @@ Each SATA controller should have its own node.
 Required properties:
 - compatible        : compatible list, one of "snps,spear-ahci",
                       "snps,exynos5440-ahci", "ibm,476gtr-ahci",
-                      "allwinner,sun4i-a10-ahci", "fsl,imx53-ahci" or
-                      "fsl,imx6q-ahci"
+                      "allwinner,sun4i-a10-ahci", "fsl,imx53-ahci"
+                      "fsl,imx6q-ahci" or "snps,dwc-ahci"
 - interrupts        : <interrupt mapping for SATA IRQ>
 - reg               : <registers mapping>
 
Signature · 1 line
1.9.0
#19 lore raw

PATCH v7 11/15 ata: ahci_platform: Manage SATA PHY

In reply to Hans de Goede

From: Roger Quadros <[email protected]>

Some platforms have a PHY hooked up to the SATA controller. The PHY needs to be initialized and powered up for SATA to work. We do that using the PHY framework.

---
 drivers/ata/ahci.h          |  2 ++
 drivers/ata/ahci_platform.c | 47 +++++++++++++++++++++++++++++++++++++++++++--
 2 files changed, 47 insertions(+), 2 deletions(-)
drivers/ata/ahci.h +2 -0
index bf8100c..3ab7ac9 100644
@@ -37,6 +37,7 @@
 
 #include <linux/clk.h>
 #include <linux/libata.h>
+#include <linux/phy/phy.h>
 #include <linux/regulator/consumer.h>
 
 /* Enclosure Management Control */
@@ -325,6 +326,7 @@ struct ahci_host_priv {
 	u32			em_msg_type;	/* EM message type */
 	struct clk		*clks[AHCI_MAX_CLKS]; /* Optional */
 	struct regulator	*target_pwr;	/* Optional */
+	struct phy		*phy;		/* If platform uses phy */
 	void			*plat_data;	/* Other platform data */
 	/*
 	 * Optional ahci_start_engine override, if not set this gets set to the
drivers/ata/ahci_platform.c +45 -2

91 lines not on this page · raw message

Signature · 1 line
1.9.0
#20 lore raw

PATCH v7 12/15 ata: ahci_platform: runtime resume the device before use

In reply to Hans de Goede

From: Roger Quadros <[email protected]>

On OMAP platforms the device needs to be runtime resumed before it can be accessed. The OMAP HWMOD framework takes care of enabling the module and its resources based on the device's runtime PM state.

In this patch we runtime resume during .probe() and runtime suspend after .remove().

We also update the runtime PM state during .resume().

---
 drivers/ata/ahci.h          |  1 +
 drivers/ata/ahci_platform.c | 15 +++++++++++++++
 2 files changed, 16 insertions(+)
drivers/ata/ahci.h +1 -0
index 3ab7ac9..51af275 100644
@@ -324,6 +324,7 @@ struct ahci_host_priv {
 	u32 			em_loc; /* enclosure management location */
 	u32			em_buf_sz;	/* EM buffer size in byte */
 	u32			em_msg_type;	/* EM message type */
+	bool			got_runtime_pm; /* Did we do pm_runtime_get? */
 	struct clk		*clks[AHCI_MAX_CLKS]; /* Optional */
 	struct regulator	*target_pwr;	/* Optional */
 	struct phy		*phy;		/* If platform uses phy */
drivers/ata/ahci_platform.c +15 -0
index 99d38c1..75698a4 100644
@@ -24,6 +24,7 @@
 #include <linux/libata.h>
 #include <linux/ahci_platform.h>
 #include <linux/phy/phy.h>
+#include <linux/pm_runtime.h>
 #include "ahci.h"
 
 static void ahci_host_stop(struct ata_host *host);
@@ -229,6 +230,11 @@ static void ahci_platform_put_resources(struct device *dev, void *res)
 	struct ahci_host_priv *hpriv = res;
 	int c;
 
+	if (hpriv->got_runtime_pm) {
+		pm_runtime_put_sync(dev);
+		pm_runtime_disable(dev);
+	}
+
 	for (c = 0; c < AHCI_MAX_CLKS && hpriv->clks[c]; c++)
 		clk_put(hpriv->clks[c]);
 }
@@ -326,6 +332,10 @@ struct ahci_host_priv *ahci_platform_get_resources(
 		}
 	}
 
+	pm_runtime_enable(dev);
+	pm_runtime_get_sync(dev);
+	hpriv->got_runtime_pm = true;
+
 	devres_remove_group(dev, NULL);
 	return hpriv;
 
@@ -635,6 +645,11 @@ int ahci_platform_resume(struct device *dev)
 	if (rc)
 		goto disable_resources;
 
+	/* We resumed so update PM runtime state */
+	pm_runtime_disable(dev);
+	pm_runtime_set_active(dev);
+	pm_runtime_enable(dev);
+
 	return 0;
 
 disable_resources:
Signature · 1 line
1.9.0
#21 lore raw

PATCH v7 13/15 ARM: sun4i: dt: Remove grouping + simple-bus compatible for regulators

In reply to Hans de Goede

According to Documentation/devicetree/bindings/regulator/regulator.txt regulator nodes should not be placed under 'simple-bus'.

Mark Rutland also explains about it at:
http://www.spinics.net/lists/linux-usb/msg101497.html

---
 arch/arm/boot/dts/sun4i-a10-a1000.dts     | 22 +++++++++-------------
 arch/arm/boot/dts/sun4i-a10-hackberry.dts | 18 +++++++-----------
 2 files changed, 16 insertions(+), 24 deletions(-)
arch/arm/boot/dts/sun4i-a10-a1000.dts +9 -13
index d4b081d..cbd2e13 100644
@@ -80,18 +80,14 @@
 		};
 	};
 
-	regulators {
-		compatible = "simple-bus";
-
-		reg_emac_3v3: emac-3v3 {
-			compatible = "regulator-fixed";
-			pinctrl-names = "default";
-			pinctrl-0 = <&emac_power_pin_a1000>;
-			regulator-name = "emac-3v3";
-			regulator-min-microvolt = <3300000>;
-			regulator-max-microvolt = <3300000>;
-			enable-active-high;
-			gpio = <&pio 7 15 0>;
-		};
+	reg_emac_3v3: emac-3v3 {
+		compatible = "regulator-fixed";
+		pinctrl-names = "default";
+		pinctrl-0 = <&emac_power_pin_a1000>;
+		regulator-name = "emac-3v3";
+		regulator-min-microvolt = <3300000>;
+		regulator-max-microvolt = <3300000>;
+		enable-active-high;
+		gpio = <&pio 7 15 0>;
 	};
 };
arch/arm/boot/dts/sun4i-a10-hackberry.dts +7 -11
index 3a1595f..6692d336 100644
@@ -54,16 +54,12 @@
 		};
 	};
 
-	regulators {
-		compatible = "simple-bus";
-
-		reg_emac_3v3: emac-3v3 {
-			compatible = "regulator-fixed";
-			regulator-name = "emac-3v3";
-			regulator-min-microvolt = <3300000>;
-			regulator-max-microvolt = <3300000>;
-			enable-active-high;
-			gpio = <&pio 7 19 0>;
-		};
+	reg_emac_3v3: emac-3v3 {
+		compatible = "regulator-fixed";
+		regulator-name = "emac-3v3";
+		regulator-min-microvolt = <3300000>;
+		regulator-max-microvolt = <3300000>;
+		enable-active-high;
+		gpio = <&pio 7 19 0>;
 	};
 };
Signature · 1 line
1.9.0
#22 lore raw

PATCH v7 13/15 ARM: sun4i: dt: Remove grouping + simple-bus compatible for regulators

Hi,

Quoted 7 lines · Hans de Goede

According to Documentation/devicetree/bindings/regulator/regulator.txt regulator nodes should not be placed under 'simple-bus'.

Mark Rutland also explains about it at:
http://www.spinics.net/lists/linux-usb/msg101497.html

Applied in my sunxi/dt-for-3.15 branch.

Thanks!
Maxime

Signature · 10 lines
Maxime Ripard, Free Electrons
Embedded Linux, Kernel and Android engineering
http://free-electrons.com
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 836 bytes
Desc: Digital signature
URL: <http://lists.infradead.org/pipermail/linux-arm-kernel/attachments/20140222/6179fd74/attachment.sig>
#23 lore raw

PATCH v7 13/15 ARM: sun4i: dt: Remove grouping + simple-bus compatible for regulators

-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1

Hi,

Quoted 10 lines · Maxime Ripard

Hi,

Quoted 5 lines · Hans de Goede

According to Documentation/devicetree/bindings/regulator/regulator.txt regulator nodes should not be placed under 'simple-bus'.

Mark Rutland also explains about it at: http://www.spinics.net/lists/linux-usb/msg101497.html

Applied in my sunxi/dt-for-3.15 branch.

Thanks, I assume this only applies to this patch, since we were still discussing the common regulator bits of the ahci enablement dts patches ?

I'm not seeing any of your recent merges here:
https://github.com/mripard/linux/commits/sunxi/dt-for-3.15

Did you not push them, or am I looking at the wrong tree ?

Regards,

Hans

-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1
Comment: Using GnuPG with Thunderbird - http://www.enigmail.net/

iEYEARECAAYFAlMJq18ACgkQF3VEtJrzE/vngACfbYd5K2ZixBm45Y+e3cMFSq9M v68Anjze6R6meI1Z5R0l4nFCNS8txl9p
=0XGo
-----END PGP SIGNATURE-----

#24 lore raw

PATCH v7 13/15 ARM: sun4i: dt: Remove grouping + simple-bus compatible for regulators

Quoted 2 lines · Hans de Goede

Thanks, I assume this only applies to this patch, since we were still discussing the common regulator bits of the ahci enablement dts patches ?

Yes.

Quoted 4 lines

I'm not seeing any of your recent merges here:
https://github.com/mripard/linux/commits/sunxi/dt-for-3.15

Did you not push them, or am I looking at the wrong tree ?

I forgot to push it. It's fixed now.

Thanks!
Maxime

Signature · 10 lines
Maxime Ripard, Free Electrons
Embedded Linux, Kernel and Android engineering
http://free-electrons.com
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 836 bytes
Desc: Digital signature
URL: <http://lists.infradead.org/pipermail/linux-arm-kernel/attachments/20140224/4941aa27/attachment.sig>
#25 lore raw

PATCH v7 14/15 ARM: sun4i: dt: Add ahci / sata support

In reply to Hans de Goede

From: Oliver Schinagl <[email protected]>

This patch adds sunxi sata support to A10 boards that have such a connector. Some boards also feature a regulator via a GPIO and support for this is also added.

---
 arch/arm/boot/dts/sun4i-a10-a1000.dts      |  4 ++++
 arch/arm/boot/dts/sun4i-a10-cubieboard.dts |  6 +++++
 arch/arm/boot/dts/sun4i-a10.dtsi           |  8 +++++++
 arch/arm/boot/dts/sunxi-ahci-reg.dtsi      | 36 ++++++++++++++++++++++++++++++
 4 files changed, 54 insertions(+)
 create mode 100644 arch/arm/boot/dts/sunxi-ahci-reg.dtsi
arch/arm/boot/dts/sun4i-a10-a1000.dts +4 -0
index cbd2e13..d6ec839 100644
@@ -35,6 +35,10 @@
 			};
 		};
 
+		ahci: sata at 01c18000 {
+			status = "okay";
+		};
+
 		pinctrl at 01c20800 {
 			emac_power_pin_a1000: emac_power_pin at 0 {
 				allwinner,pins = "PH15";
arch/arm/boot/dts/sun4i-a10-cubieboard.dts +6 -0
index b139ee6..6df237d8 100644
@@ -12,6 +12,7 @@
 
 /dts-v1/;
 /include/ "sun4i-a10.dtsi"
+/include/ "sunxi-ahci-reg.dtsi"
 
 / {
 	model = "Cubietech Cubieboard";
@@ -33,6 +34,11 @@
 			};
 		};
 
+		ahci: sata at 01c18000 {
+			target-supply = <&reg_ahci_5v>;
+			status = "okay";
+		};
+
 		pinctrl at 01c20800 {
 			led_pins_cubieboard: led_pins at 0 {
 				allwinner,pins = "PH20", "PH21";
arch/arm/boot/dts/sun4i-a10.dtsi +8 -0
index 336dbec..454077a 100644
@@ -338,6 +338,14 @@
 			#size-cells = <0>;
 		};
 
+		ahci: sata at 01c18000 {
+			compatible = "allwinner,sun4i-a10-ahci";
+			reg = <0x01c18000 0x1000>;
+			interrupts = <56>;
+			clocks = <&pll6 0>, <&ahb_gates 25>;
+			status = "disabled";
+		};
+
 		intc: interrupt-controller at 01c20400 {
 			compatible = "allwinner,sun4i-ic";
 			reg = <0x01c20400 0x400>;
arch/arm/boot/dts/sunxi-ahci-reg.dtsi new file +36 -0
new file mode 100644
index 0000000..7072af1
@@ -0,0 +1,36 @@
+/*
+ * sunxi boards sata target power supply common code
+ *
+ * Copyright 2014 - Hans de Goede <[email protected]>
+ *
+ * The code contained herein is licensed under the GNU General Public
+ * License. You may obtain a copy of the GNU General Public License
+ * Version 2 or later at the following locations:
+ *
+ * http://www.opensource.org/licenses/gpl-license.html
+ * http://www.gnu.org/copyleft/gpl.html
+ */
+
+/ {
+	soc at 01c00000 {
+		pio: pinctrl at 01c20800 {
+			ahci_pwr_pin_a: ahci_pwr_pin at 0 {
+				allwinner,pins = "PB8";
+				allwinner,function = "gpio_out";
+				allwinner,drive = <0>;
+				allwinner,pull = <0>;
+			};
+		};
+	};
+
+	reg_ahci_5v: ahci-5v {
+		compatible = "regulator-fixed";
+		pinctrl-names = "default";
+		pinctrl-0 = <&ahci_pwr_pin_a>;
+		regulator-name = "ahci-5v";
+		regulator-min-microvolt = <5000000>;
+		regulator-max-microvolt = <5000000>;
+		enable-active-high;
+		gpio = <&pio 1 8 0>;
+	};
+};
Signature · 1 line
1.9.0
#26 lore raw

PATCH v7 15/15 ARM: sun7i: dt: Add ahci / sata support

In reply to Hans de Goede

This patch adds sunxi sata support to A20 boards that have such a connector. Some boards also feature a regulator via a GPIO and support for this is also added.

---
 arch/arm/boot/dts/sun7i-a20-cubieboard2.dts     |  6 ++++++
 arch/arm/boot/dts/sun7i-a20-cubietruck.dts      | 18 ++++++++++++++++++
 arch/arm/boot/dts/sun7i-a20-olinuxino-micro.dts |  6 ++++++
 arch/arm/boot/dts/sun7i-a20.dtsi                |  8 ++++++++
 4 files changed, 38 insertions(+)
arch/arm/boot/dts/sun7i-a20-cubieboard2.dts +6 -0
index 7bf4935..07823c2 100644
@@ -13,12 +13,18 @@
 
 /dts-v1/;
 /include/ "sun7i-a20.dtsi"
+/include/ "sunxi-ahci-reg.dtsi"
 
 / {
 	model = "Cubietech Cubieboard2";
 	compatible = "cubietech,cubieboard2", "allwinner,sun7i-a20";
 
 	soc at 01c00000 {
+		ahci: sata at 01c18000 {
+			target-supply = <&reg_ahci_5v>;
+			status = "okay";
+		};
+
 		pinctrl at 01c20800 {
 			led_pins_cubieboard2: led_pins at 0 {
 				allwinner,pins = "PH20", "PH21";
arch/arm/boot/dts/sun7i-a20-cubietruck.dts +18 -0
index 025ce52..403bd2e 100644
@@ -13,13 +13,26 @@
 
 /dts-v1/;
 /include/ "sun7i-a20.dtsi"
+/include/ "sunxi-ahci-reg.dtsi"
 
 / {
 	model = "Cubietech Cubietruck";
 	compatible = "cubietech,cubietruck", "allwinner,sun7i-a20";
 
 	soc at 01c00000 {
+		ahci: sata at 01c18000 {
+			target-supply = <&reg_ahci_5v>;
+			status = "okay";
+		};
+
 		pinctrl at 01c20800 {
+			ahci_pwr_pin_cubietruck: ahci_pwr_pin at 1 {
+				allwinner,pins = "PH12";
+				allwinner,function = "gpio_out";
+				allwinner,drive = <0>;
+				allwinner,pull = <0>;
+			};
+
 			led_pins_cubietruck: led_pins at 0 {
 				allwinner,pins = "PH7", "PH11", "PH20", "PH21";
 				allwinner,function = "gpio_out";
@@ -90,4 +103,9 @@
 			gpios = <&pio 7 7 0>;
 		};
 	};
+
+	reg_ahci_5v: ahci-5v {
+		pinctrl-0 = <&ahci_pwr_pin_cubietruck>;
+		gpio = <&pio 7 12 0>;
+	};
 };
arch/arm/boot/dts/sun7i-a20-olinuxino-micro.dts +6 -0
index b02a796..d5c6799 100644
@@ -13,12 +13,18 @@
 
 /dts-v1/;
 /include/ "sun7i-a20.dtsi"
+/include/ "sunxi-ahci-reg.dtsi"
 
 / {
 	model = "Olimex A20-Olinuxino Micro";
 	compatible = "olimex,a20-olinuxino-micro", "allwinner,sun7i-a20";
 
 	soc at 01c00000 {
+		ahci: sata at 01c18000 {
+			target-supply = <&reg_ahci_5v>;
+			status = "okay";
+		};
+
 		pinctrl at 01c20800 {
 			led_pins_olinuxino: led_pins at 0 {
 				allwinner,pins = "PH2";
arch/arm/boot/dts/sun7i-a20.dtsi +8 -0
index daaafd0..3385994 100644
@@ -392,6 +392,14 @@
 			#size-cells = <0>;
 		};
 
+		ahci: sata at 01c18000 {
+			compatible = "allwinner,sun4i-a10-ahci";
+			reg = <0x01c18000 0x1000>;
+			interrupts = <0 56 4>;
+			clocks = <&pll6 0>, <&ahb_gates 25>;
+			status = "disabled";
+		};
+
 		pio: pinctrl at 01c20800 {
 			compatible = "allwinner,sun7i-a20-pinctrl";
 			reg = <0x01c20800 0x400>;
Signature · 1 line
1.9.0
#27 lore raw

v7 cover ahci: library-ise ahci_platform, add sunxi driver and cleanup imx driver

In reply to Hans de Goede

<resend of cover-letter with a proper subject, sorry about that>

Hi all,

Here is v7 of my patchset for adding ahci-sunxi support. This is hopefully the final final version of this set :)

Note I'm going on vacation for a week starting Monday, so if I'm not responding that is why. Tejun if you feel some small cleanups are still necessary and you don't want to wait for me to get back feel free to squash in any cleanups you deem necessary.

This has been tested with Allwinner A10, Allwinner A20 and Freeware imx6x SoCs, including suspend / resume. Note that the ahci_imx driver now also has imx53 sata support, it would be good if someone could test that with this series.

History:

v1, by Olliver Schinagl:
This was using the approach of having a platform device which probe method creates a new child platform device which gets driven by ahci_platform.c, as done by ahci_imx.c .

v2, by Hans de Goede:
Stand-alone platform driver based on Olliver's work

v3, by Hans de Goede:
patch-series, with 4 different parts
a) Make ahci_platform.c more generic, handle more then 1 clk, target pwr regulator
b) New ahci-sunxi code only populating ahci_platform_data, passed to ahci_platform.c to of_device_id matching.
c) Refactor ahci-imx code to work the same as the new ahci-sunxi code, this is the reason why v3 is an RFC, I'm waiting for the wandboard I ordered to arrive so that I can actually test this.
d) dts bindings for the sunxi ahci parts

v4, by Hans de Goede:
patch-series, with 5 different parts:
a) Make ahci_platform.c more generic, handle more then 1 clk, target pwr regulator
b) Turn parts of ahci_platform.c into a library for use by other drivers
c) New ahci-sunxi driver using the ahci_platform.c library functionality
d) Refactor ahci-imx code to work the same as the new ahci-sunxi code
e) dts bindings for the sunxi ahci parts

v5:
v4 + the following changes:
1) fsl,imx6q driver is now tested
2) fixed suspend / resume on fsl,imx6q
3) Modifed devicetree node naming to match dt spec
4) Reworked the busy waiting code in the sunxi-phy handling as suggested by Russell King

v6:
v5 rebased on top of 3.14-rc3 + the following changes
1) Added Roger Quadros' generic phy support series
2) Added a "ARM: sun4i: dt: Remove grouping + simple-bus for regulators" dts patch

v7:
v6 + the following changes:
1) Addressed all Tejun's review remarks:
* Added function header comments to all exported ahci_platform functions
* Added comments in some other places
* Removed use of 2 empty lines to separate functions in some cases
* Use devres to automatically call ahci_platform_put_resources on
get_resource failure, probe failure and regular device remove
2) Dropped patches to move ahci_host_priv struct declaration to include/linux,
this was a left-over from v3 and is no longer necessary
3) Updated Roger's "ata: ahci_platform: Manage SATA PHY" patch:
* Update function header comments for the changes this makes
* Drop the Kconfig PHY requires hack, my patch for the phy-core to always be built-in has been queued in Greg KH's tree, so this is no longer necessary.
4) Dropped Roger's "ata: ahci_platform: Add 'struct device' argument to ahci_platform_put_resources()" patch, ahci_platform_put_resources already has a device argument as result
of it being changed into a devres release function

Tejun, can you please add patches 1-12 to your ata tree for 3.15 ?

Maxime, can you please add patch 13-15 to your dts tree for 3.15 ?

Thanks & Regards,

Hans

Signature · 3 lines
You received this message because you are subscribed to the Google Groups "linux-sunxi" group.
To unsubscribe from this group and stop receiving emails from it, send an email to linux-sunxi+unsubscribe at googlegroups.com.
For more options, visit https://groups.google.com/groups/opt_out.
#28 lore raw

v7 cover ahci: library-ise ahci_platform, add sunxi driver and cleanup imx driver

Quoted 1 line · Hans de Goede

Tejun, can you please add patches 1-12 to your ata tree for 3.15 ?

Applied 1-12 to libata/for-3.15 with comment format slightly updated.

Thanks.

Signature · 1 line
tejun