All of lore.kernel.org
 help / color / mirror / Atom feed
From: Jean-Jacques Hiblot <jjhiblot@ti.com>
To: u-boot@lists.denx.de
Subject: [U-Boot] [PATCH 05/11] drivers: phy: add PIPE3 phy driver
Date: Fri, 14 Apr 2017 13:08:03 +0200	[thread overview]
Message-ID: <1492168089-15437-6-git-send-email-jjhiblot@ti.com> (raw)
In-Reply-To: <1492168089-15437-1-git-send-email-jjhiblot@ti.com>

This phy is found on omap platforms with sata capabilities.
Except for the part related to the DM and the PHY framework, the code is
basically a copy paste from arch/arm/mach-omap2/pipe3-phy.c

Signed-off-by: Jean-Jacques Hiblot <jjhiblot@ti.com>
Reviewed-by: Tom Rini <trini@konsulko.com>
Reviewed-by: Simon Glass <sjg@chromium.org>
---
 drivers/phy/Kconfig        |  18 +++
 drivers/phy/Makefile       |   1 +
 drivers/phy/ti-pipe3-phy.c | 365 +++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 384 insertions(+)
 create mode 100644 drivers/phy/ti-pipe3-phy.c

diff --git a/drivers/phy/Kconfig b/drivers/phy/Kconfig
index 032b932..b9a8093 100644
--- a/drivers/phy/Kconfig
+++ b/drivers/phy/Kconfig
@@ -38,4 +38,22 @@ config PHY_SANDBOX
 	  unit tests for the generic phy framework
 
 
+config PIPE3_PHY
+	bool "Support omap's PIPE3 PHY"
+	depends on GENERIC_PHY && ARCH_OMAP2
+	help
+	  Support for the omap PIPE3 phy for sata
+
+	  This PHY is found on omap devices supporting SATA such as dra7, am57x
+	  and omap5
+
+config SPL_PIPE3_PHY
+	bool "Support omap's PIPE3 PHY in SPL"
+	depends on SPL_GENERIC_PHY && ARCH_OMAP2
+	help
+	  Support for the omap PIPE3 phy for sata in SPL
+
+	  This PHY is found on omap devices supporting SATA such as dra7, am57x
+	  and omap5
+
 endmenu
diff --git a/drivers/phy/Makefile b/drivers/phy/Makefile
index 0125844..701f7c8 100644
--- a/drivers/phy/Makefile
+++ b/drivers/phy/Makefile
@@ -1,3 +1,4 @@
 obj-$(CONFIG_$(SPL_)GENERIC_PHY) += phy-uclass.o
 obj-$(CONFIG_PHY_SANDBOX) += sandbox-phy.o
+obj-$(CONFIG_$(SPL_)PIPE3_PHY) += ti-pipe3-phy.o
 
diff --git a/drivers/phy/ti-pipe3-phy.c b/drivers/phy/ti-pipe3-phy.c
new file mode 100644
index 0000000..3964823
--- /dev/null
+++ b/drivers/phy/ti-pipe3-phy.c
@@ -0,0 +1,365 @@
+/*
+ * Copyright (C) 2017 Texas Instruments Incorporated - http://www.ti.com/
+ * Written by Jean-Jacques Hiblot  <jjhiblot@ti.com>
+ *
+ * SPDX-License-Identifier:	GPL-2.0+
+ */
+
+#include <common.h>
+#include <dm.h>
+#include <dm/device.h>
+#include <generic-phy.h>
+#include <asm/io.h>
+#include <asm/arch/sys_proto.h>
+#include <syscon.h>
+#include <regmap.h>
+
+/* PLLCTRL Registers */
+#define PLL_STATUS              0x00000004
+#define PLL_GO                  0x00000008
+#define PLL_CONFIGURATION1      0x0000000C
+#define PLL_CONFIGURATION2      0x00000010
+#define PLL_CONFIGURATION3      0x00000014
+#define PLL_CONFIGURATION4      0x00000020
+
+#define PLL_REGM_MASK           0x001FFE00
+#define PLL_REGM_SHIFT          9
+#define PLL_REGM_F_MASK         0x0003FFFF
+#define PLL_REGM_F_SHIFT        0
+#define PLL_REGN_MASK           0x000001FE
+#define PLL_REGN_SHIFT          1
+#define PLL_SELFREQDCO_MASK     0x0000000E
+#define PLL_SELFREQDCO_SHIFT    1
+#define PLL_SD_MASK             0x0003FC00
+#define PLL_SD_SHIFT            10
+#define SET_PLL_GO              0x1
+#define PLL_TICOPWDN            BIT(16)
+#define PLL_LDOPWDN             BIT(15)
+#define PLL_LOCK                0x2
+#define PLL_IDLE                0x1
+
+/* Software rest for the SATA PLL (in CTRL_CORE_SMA_SW_0 register)*/
+#define SATA_PLL_SOFT_RESET (1<<18)
+
+/* PHY POWER CONTROL Register */
+#define OMAP_CTRL_PIPE3_PHY_PWRCTL_CLK_CMD_MASK         0x003FC000
+#define OMAP_CTRL_PIPE3_PHY_PWRCTL_CLK_CMD_SHIFT        0xE
+
+#define OMAP_CTRL_PIPE3_PHY_PWRCTL_CLK_FREQ_MASK        0xFFC00000
+#define OMAP_CTRL_PIPE3_PHY_PWRCTL_CLK_FREQ_SHIFT       0x16
+
+#define OMAP_CTRL_PIPE3_PHY_TX_RX_POWERON       0x3
+#define OMAP_CTRL_PIPE3_PHY_TX_RX_POWEROFF      0x0
+
+
+#define PLL_IDLE_TIME   100     /* in milliseconds */
+#define PLL_LOCK_TIME   100     /* in milliseconds */
+
+struct omap_pipe3 {
+	void __iomem		*pll_ctrl_base;
+	void __iomem		*power_reg;
+	void __iomem		*pll_reset_reg;
+	struct pipe3_dpll_map	*dpll_map;
+};
+
+
+struct pipe3_dpll_params {
+	u16     m;
+	u8      n;
+	u8      freq:3;
+	u8      sd;
+	u32     mf;
+};
+
+struct pipe3_dpll_map {
+	unsigned long rate;
+	struct pipe3_dpll_params params;
+};
+
+static inline u32 omap_pipe3_readl(void __iomem *addr, unsigned offset)
+{
+	return readl(addr + offset);
+}
+
+static inline void omap_pipe3_writel(void __iomem *addr, unsigned offset,
+		u32 data)
+{
+	writel(data, addr + offset);
+}
+
+static struct pipe3_dpll_params *omap_pipe3_get_dpll_params(struct omap_pipe3
+									*pipe3)
+{
+	u32 rate;
+	struct pipe3_dpll_map *dpll_map = pipe3->dpll_map;
+
+	rate = get_sys_clk_freq();
+
+	for (; dpll_map->rate; dpll_map++) {
+		if (rate == dpll_map->rate)
+			return &dpll_map->params;
+	}
+
+	printf("%s: No DPLL configuration for %u Hz SYS CLK\n",
+	       __func__, rate);
+	return NULL;
+}
+
+static int omap_pipe3_wait_lock(struct omap_pipe3 *pipe3)
+{
+	u32 val;
+	int timeout = PLL_LOCK_TIME;
+
+	do {
+		mdelay(1);
+		val = omap_pipe3_readl(pipe3->pll_ctrl_base, PLL_STATUS);
+		if (val & PLL_LOCK)
+			break;
+	} while (--timeout);
+
+	if (!(val & PLL_LOCK)) {
+		printf("%s: DPLL failed to lock\n", __func__);
+		return -EBUSY;
+	}
+
+	return 0;
+}
+
+static int omap_pipe3_dpll_program(struct omap_pipe3 *pipe3)
+{
+	u32                     val;
+	struct pipe3_dpll_params *dpll_params;
+
+	dpll_params = omap_pipe3_get_dpll_params(pipe3);
+	if (!dpll_params) {
+		printf("%s: Invalid DPLL parameters\n", __func__);
+		return -EINVAL;
+	}
+
+	val = omap_pipe3_readl(pipe3->pll_ctrl_base, PLL_CONFIGURATION1);
+	val &= ~PLL_REGN_MASK;
+	val |= dpll_params->n << PLL_REGN_SHIFT;
+	omap_pipe3_writel(pipe3->pll_ctrl_base, PLL_CONFIGURATION1, val);
+
+	val = omap_pipe3_readl(pipe3->pll_ctrl_base, PLL_CONFIGURATION2);
+	val &= ~PLL_SELFREQDCO_MASK;
+	val |= dpll_params->freq << PLL_SELFREQDCO_SHIFT;
+	omap_pipe3_writel(pipe3->pll_ctrl_base, PLL_CONFIGURATION2, val);
+
+	val = omap_pipe3_readl(pipe3->pll_ctrl_base, PLL_CONFIGURATION1);
+	val &= ~PLL_REGM_MASK;
+	val |= dpll_params->m << PLL_REGM_SHIFT;
+	omap_pipe3_writel(pipe3->pll_ctrl_base, PLL_CONFIGURATION1, val);
+
+	val = omap_pipe3_readl(pipe3->pll_ctrl_base, PLL_CONFIGURATION4);
+	val &= ~PLL_REGM_F_MASK;
+	val |= dpll_params->mf << PLL_REGM_F_SHIFT;
+	omap_pipe3_writel(pipe3->pll_ctrl_base, PLL_CONFIGURATION4, val);
+
+	val = omap_pipe3_readl(pipe3->pll_ctrl_base, PLL_CONFIGURATION3);
+	val &= ~PLL_SD_MASK;
+	val |= dpll_params->sd << PLL_SD_SHIFT;
+	omap_pipe3_writel(pipe3->pll_ctrl_base, PLL_CONFIGURATION3, val);
+
+	omap_pipe3_writel(pipe3->pll_ctrl_base, PLL_GO, SET_PLL_GO);
+
+	return omap_pipe3_wait_lock(pipe3);
+}
+
+static void omap_control_pipe3_power(struct omap_pipe3 *pipe3, int on)
+{
+	u32 val, rate;
+
+	val = readl(pipe3->power_reg);
+
+	rate = get_sys_clk_freq();
+	rate = rate/1000000;
+
+	if (on) {
+		val &= ~(OMAP_CTRL_PIPE3_PHY_PWRCTL_CLK_CMD_MASK |
+				OMAP_CTRL_PIPE3_PHY_PWRCTL_CLK_FREQ_MASK);
+		val |= OMAP_CTRL_PIPE3_PHY_TX_RX_POWERON <<
+			OMAP_CTRL_PIPE3_PHY_PWRCTL_CLK_CMD_SHIFT;
+		val |= rate <<
+			OMAP_CTRL_PIPE3_PHY_PWRCTL_CLK_FREQ_SHIFT;
+	} else {
+		val &= ~OMAP_CTRL_PIPE3_PHY_PWRCTL_CLK_CMD_MASK;
+		val |= OMAP_CTRL_PIPE3_PHY_TX_RX_POWEROFF <<
+			OMAP_CTRL_PIPE3_PHY_PWRCTL_CLK_CMD_SHIFT;
+	}
+
+	writel(val, pipe3->power_reg);
+}
+
+static int pipe3_power_on(struct udevice *dev)
+{
+	int ret;
+	u32 val;
+	struct omap_pipe3 *pipe3 = dev_get_priv(dev);
+
+	/* Program the DPLL only if not locked */
+	val = omap_pipe3_readl(pipe3->pll_ctrl_base, PLL_STATUS);
+	if (!(val & PLL_LOCK)) {
+		ret = omap_pipe3_dpll_program(pipe3);
+		if (ret)
+			return ret;
+	} else {
+		/* else just bring it out of IDLE mode */
+		val = omap_pipe3_readl(pipe3->pll_ctrl_base,
+				       PLL_CONFIGURATION2);
+		if (val & PLL_IDLE) {
+			val &= ~PLL_IDLE;
+			omap_pipe3_writel(pipe3->pll_ctrl_base,
+					  PLL_CONFIGURATION2, val);
+			ret = omap_pipe3_wait_lock(pipe3);
+			if (ret)
+				return ret;
+		}
+	}
+
+	/* Power up the PHY */
+	omap_control_pipe3_power(pipe3, 1);
+
+	return 0;
+}
+
+static int pipe3_power_off(struct udevice *dev)
+{
+	u32 val;
+	int timeout = PLL_IDLE_TIME;
+	struct omap_pipe3 *pipe3 = dev_get_priv(dev);
+
+	/* Power down the PHY */
+	omap_control_pipe3_power(pipe3, 0);
+
+	/* Put DPLL in IDLE mode */
+	val = omap_pipe3_readl(pipe3->pll_ctrl_base, PLL_CONFIGURATION2);
+	val |= PLL_IDLE;
+	omap_pipe3_writel(pipe3->pll_ctrl_base, PLL_CONFIGURATION2, val);
+
+	/* wait for LDO and Oscillator to power down */
+	do {
+		mdelay(1);
+		val = omap_pipe3_readl(pipe3->pll_ctrl_base, PLL_STATUS);
+		if ((val & PLL_TICOPWDN) && (val & PLL_LDOPWDN))
+			break;
+	} while (--timeout);
+
+	if (!(val & PLL_TICOPWDN) || !(val & PLL_LDOPWDN)) {
+		printf("%s: Failed to power down DPLL: PLL_STATUS 0x%x\n",
+		       __func__, val);
+		return -EBUSY;
+	}
+
+	return 0;
+}
+
+static int pipe3_reset(struct udevice *dev)
+{
+	u32 val;
+	struct omap_pipe3 *pipe3 = dev_get_priv(dev);
+
+	val = readl(pipe3->pll_reset_reg);
+	writel(val | SATA_PLL_SOFT_RESET, pipe3->pll_reset_reg);
+	mdelay(1);
+	writel(val & ~SATA_PLL_SOFT_RESET, pipe3->pll_reset_reg);
+	return 0;
+}
+
+static void *get_reg(struct udevice *dev, const char *name)
+{
+	struct udevice *syscon;
+	struct regmap *regmap;
+	const fdt32_t *cell;
+	int len, err;
+	void *base;
+
+	err = uclass_get_device_by_phandle(UCLASS_SYSCON, dev,
+					   name, &syscon);
+	if (err) {
+		error("unable to find syscon device for %s (%d)\n",
+		      name, err);
+		return NULL;
+	}
+
+	regmap = syscon_get_regmap(syscon);
+	if (IS_ERR(regmap)) {
+		error("unable to find regmap for %s (%ld)\n",
+		      name, PTR_ERR(regmap));
+		return NULL;
+	}
+
+	cell = fdt_getprop(gd->fdt_blob, dev->of_offset, name,
+			   &len);
+	if (len < 2*sizeof(fdt32_t)) {
+		error("offset not available for %s\n", name);
+		return NULL;
+	}
+
+	base = regmap_get_range(regmap, 0);
+	if (!base)
+		return NULL;
+
+	return fdtdec_get_number(cell + 1, 1) + base;
+}
+
+static int pipe3_phy_probe(struct udevice *dev)
+{
+	fdt_addr_t addr;
+	fdt_size_t sz;
+	struct omap_pipe3 *pipe3 = dev_get_priv(dev);
+
+	addr = dev_get_addr_size_index(dev, 2, &sz);
+	if (addr == FDT_ADDR_T_NONE) {
+		error("missing pll ctrl address\n");
+		return -EINVAL;
+	}
+
+	pipe3->pll_ctrl_base = map_physmem(addr, sz, MAP_NOCACHE);
+	if (!pipe3->pll_ctrl_base) {
+		error("unable to remap pll ctrl\n");
+		return -EINVAL;
+	}
+
+	pipe3->power_reg = get_reg(dev, "syscon-phy-power");
+	if (!pipe3->power_reg)
+		return -EINVAL;
+
+	pipe3->pll_reset_reg = get_reg(dev, "syscon-pllreset");
+	if (!pipe3->pll_reset_reg)
+		return -EINVAL;
+
+	pipe3->dpll_map = (struct pipe3_dpll_map *)dev_get_driver_data(dev);
+
+	return 0;
+}
+
+static struct pipe3_dpll_map dpll_map_sata[] = {
+	{12000000, {1000, 7, 4, 6, 0} },        /* 12 MHz */
+	{16800000, {714, 7, 4, 6, 0} },         /* 16.8 MHz */
+	{19200000, {625, 7, 4, 6, 0} },         /* 19.2 MHz */
+	{20000000, {600, 7, 4, 6, 0} },         /* 20 MHz */
+	{26000000, {461, 7, 4, 6, 0} },         /* 26 MHz */
+	{38400000, {312, 7, 4, 6, 0} },         /* 38.4 MHz */
+	{ },                                    /* Terminator */
+};
+
+static const struct udevice_id pipe3_phy_ids[] = {
+	{ .compatible = "ti,phy-pipe3-sata", .data = (ulong)&dpll_map_sata },
+	{ }
+};
+
+static struct generic_phy_ops pipe3_phy_ops = {
+	.power_on = pipe3_power_on,
+	.power_off = pipe3_power_off,
+	.reset = pipe3_reset
+};
+
+U_BOOT_DRIVER(pipe3_phy) = {
+	.name	= "pipe3_phy",
+	.id	= UCLASS_PHY,
+	.of_match = pipe3_phy_ids,
+	.ops = &pipe3_phy_ops,
+	.probe = pipe3_phy_probe,
+	.priv_auto_alloc_size = sizeof(struct omap_pipe3),
+};
-- 
1.9.1

  parent reply	other threads:[~2017-04-14 11:08 UTC|newest]

Thread overview: 18+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2017-04-14 11:07 [U-Boot] [PATCH 00/11] OMAP: Move SATA to use block driver model Jean-Jacques Hiblot
2017-04-14 11:07 ` [U-Boot] [PATCH 01/11] arm: omap: sata: move enable sata clocks to enable_basic_clocks() Jean-Jacques Hiblot
2017-04-14 11:08 ` [U-Boot] [PATCH 02/11] arm: omap: sata: compile out board-level sata code when CONFIG_DM_SCSI is defined Jean-Jacques Hiblot
2017-04-14 11:08 ` [U-Boot] [PATCH 03/11] drivers: phy: add generic PHY framework Jean-Jacques Hiblot
2017-04-15 17:10   ` Simon Glass
2017-04-18 13:32     ` Jean-Jacques Hiblot
2017-04-19  0:12       ` Simon Glass
2017-04-14 11:08 ` [U-Boot] [PATCH 04/11] dm: test: Add tests for the generic PHY uclass Jean-Jacques Hiblot
2017-04-15 17:10   ` Simon Glass
2017-04-14 11:08 ` Jean-Jacques Hiblot [this message]
2017-04-14 11:08 ` [U-Boot] [PATCH 06/11] dra7: dtsi: mark ocp2scp bus compatible with "simple-bus" Jean-Jacques Hiblot
2017-04-14 20:06   ` Tom Rini
2017-04-14 11:08 ` [U-Boot] [PATCH 07/11] drivers: block: dwc_ahci: Implement a driver for Synopsys DWC sata device Jean-Jacques Hiblot
2017-04-14 11:08 ` [U-Boot] [PATCH 08/11] scsi: make the LUN a parameter of scsi_detect_dev() Jean-Jacques Hiblot
2017-04-14 11:08 ` [U-Boot] [PATCH 09/11] scsi: move the partition initialization out of the scsi detection Jean-Jacques Hiblot
2017-04-14 11:08 ` [U-Boot] [PATCH 10/11] dm: scsi: fix divide-by-0 error in scsi_scan() Jean-Jacques Hiblot
2017-04-14 11:08 ` [U-Boot] [PATCH 11/11] defconfig: dra7xx_evm: enable CONFIG_BLK and disk driver model for SCSI Jean-Jacques Hiblot
2017-04-14 11:13 ` [U-Boot] [PATCH 00/11] OMAP: Move SATA to use block driver model Jean-Jacques Hiblot

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=1492168089-15437-6-git-send-email-jjhiblot@ti.com \
    --to=jjhiblot@ti.com \
    --cc=u-boot@lists.denx.de \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.