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
next prev 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.