From d821bfa4ca7d2699ef82557bceb05192fffcc51a Mon Sep 17 00:00:00 2001 From: Lee Jones Date: Wed, 9 Jul 2014 12:41:11 +0100 Subject: [PATCH 01/28] phy: miphy365x: Add MiPHY365x header file for DT x Driver defines This provides the shared header file which will be reference from both the MiPHY365x driver and its associated Device Tree node(s). Cc: Kishon Vijay Abraham I Acked-by: Mark Rutland Acked-by: Alexandre Torgue Signed-off-by: Lee Jones Signed-off-by: Kishon Vijay Abraham I --- include/dt-bindings/phy/phy-miphy365x.h | 14 ++++++++++++++ 1 file changed, 14 insertions(+) create mode 100644 include/dt-bindings/phy/phy-miphy365x.h diff --git a/include/dt-bindings/phy/phy-miphy365x.h b/include/dt-bindings/phy/phy-miphy365x.h new file mode 100644 index 000000000000..8ef8aba6edd6 --- /dev/null +++ b/include/dt-bindings/phy/phy-miphy365x.h @@ -0,0 +1,14 @@ +/* + * This header provides constants for the phy framework + * based on the STMicroelectronics MiPHY365x. + * + * Author: Lee Jones + */ +#ifndef _DT_BINDINGS_PHY_MIPHY +#define _DT_BINDINGS_PHY_MIPHY + +#define MIPHY_TYPE_SATA 1 +#define MIPHY_TYPE_PCIE 2 +#define MIPHY_TYPE_USB 3 + +#endif /* _DT_BINDINGS_PHY_MIPHY */ From bbe21b2a9bb807957baf36e659bfe2aedc51e5ea Mon Sep 17 00:00:00 2001 From: Sachin Kamat Date: Thu, 29 May 2014 12:00:47 +0530 Subject: [PATCH 02/28] phy: exynos-dp-video: Use PTR_ERR_OR_ZERO PTR_ERR_OR_ZERO simplifies the code. Signed-off-by: Sachin Kamat Cc: Jingoo Han Acked-by: Hans de Goede Signed-off-by: Kishon Vijay Abraham I --- drivers/phy/phy-exynos-dp-video.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/drivers/phy/phy-exynos-dp-video.c b/drivers/phy/phy-exynos-dp-video.c index 0786fef842e7..098f822a2fa4 100644 --- a/drivers/phy/phy-exynos-dp-video.c +++ b/drivers/phy/phy-exynos-dp-video.c @@ -9,6 +9,7 @@ * published by the Free Software Foundation. */ +#include #include #include #include @@ -84,10 +85,8 @@ static int exynos_dp_video_phy_probe(struct platform_device *pdev) phy_set_drvdata(phy, state); phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate); - if (IS_ERR(phy_provider)) - return PTR_ERR(phy_provider); - return 0; + return PTR_ERR_OR_ZERO(phy_provider); } static const struct of_device_id exynos_dp_video_phy_of_match[] = { From 22fda307ca53f18a179c38ac10a0bc4293dd9f21 Mon Sep 17 00:00:00 2001 From: Sachin Kamat Date: Thu, 29 May 2014 12:00:48 +0530 Subject: [PATCH 03/28] phy: exynos-mipi-video: Use PTR_ERR_OR_ZERO PTR_ERR_OR_ZERO simplifies the code. Signed-off-by: Sachin Kamat Cc: Sylwester Nawrocki Acked-by: Hans de Goede Signed-off-by: Kishon Vijay Abraham I --- drivers/phy/phy-exynos-mipi-video.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/drivers/phy/phy-exynos-mipi-video.c b/drivers/phy/phy-exynos-mipi-video.c index ff026689358c..6d6bcf52a10e 100644 --- a/drivers/phy/phy-exynos-mipi-video.c +++ b/drivers/phy/phy-exynos-mipi-video.c @@ -9,6 +9,7 @@ * published by the Free Software Foundation. */ +#include #include #include #include @@ -149,10 +150,8 @@ static int exynos_mipi_video_phy_probe(struct platform_device *pdev) phy_provider = devm_of_phy_provider_register(dev, exynos_mipi_video_phy_xlate); - if (IS_ERR(phy_provider)) - return PTR_ERR(phy_provider); - return 0; + return PTR_ERR_OR_ZERO(phy_provider); } static const struct of_device_id exynos_mipi_video_phy_of_match[] = { From 2d84aff9c395d770bfe4d3fb6d1749882be7e4cc Mon Sep 17 00:00:00 2001 From: Sachin Kamat Date: Thu, 29 May 2014 12:00:49 +0530 Subject: [PATCH 04/28] phy: sun4i-usb: Use PTR_ERR_OR_ZERO PTR_ERR_OR_ZERO simplifies the code. Signed-off-by: Sachin Kamat Cc: Maxime Ripard Acked-by: Hans de Goede Signed-off-by: Kishon Vijay Abraham I --- drivers/phy/phy-sun4i-usb.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/drivers/phy/phy-sun4i-usb.c b/drivers/phy/phy-sun4i-usb.c index 115d8d5190d5..7a4ea552f621 100644 --- a/drivers/phy/phy-sun4i-usb.c +++ b/drivers/phy/phy-sun4i-usb.c @@ -22,6 +22,7 @@ */ #include +#include #include #include #include @@ -306,10 +307,8 @@ static int sun4i_usb_phy_probe(struct platform_device *pdev) dev_set_drvdata(dev, data); phy_provider = devm_of_phy_provider_register(dev, sun4i_usb_phy_xlate); - if (IS_ERR(phy_provider)) - return PTR_ERR(phy_provider); - return 0; + return PTR_ERR_OR_ZERO(phy_provider); } static const struct of_device_id sun4i_usb_phy_of_match[] = { From ad6202b4d941c69e9154419bd3002fbcacccbb67 Mon Sep 17 00:00:00 2001 From: Jingoo Han Date: Thu, 22 May 2014 13:00:28 +0900 Subject: [PATCH 05/28] phy: exynos5-usbdrd: Make local functions static Make local functions static, because these are used only in this file. Signed-off-by: Jingoo Han Signed-off-by: Kishon Vijay Abraham I --- drivers/phy/phy-exynos5-usbdrd.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/phy/phy-exynos5-usbdrd.c b/drivers/phy/phy-exynos5-usbdrd.c index 76d862b2202f..205159db37a3 100644 --- a/drivers/phy/phy-exynos5-usbdrd.c +++ b/drivers/phy/phy-exynos5-usbdrd.c @@ -506,7 +506,7 @@ static struct phy_ops exynos5_usbdrd_phy_ops = { .owner = THIS_MODULE, }; -const struct exynos5_usbdrd_phy_config phy_cfg_exynos5[] = { +static const struct exynos5_usbdrd_phy_config phy_cfg_exynos5[] = { { .id = EXYNOS5_DRDPHY_UTMI, .phy_isol = exynos5_usbdrd_phy_isol, @@ -521,13 +521,13 @@ const struct exynos5_usbdrd_phy_config phy_cfg_exynos5[] = { }, }; -const struct exynos5_usbdrd_phy_drvdata exynos5420_usbdrd_phy = { +static const struct exynos5_usbdrd_phy_drvdata exynos5420_usbdrd_phy = { .phy_cfg = phy_cfg_exynos5, .pmu_offset_usbdrd0_phy = EXYNOS5_USBDRD_PHY_CONTROL, .pmu_offset_usbdrd1_phy = EXYNOS5420_USBDRD1_PHY_CONTROL, }; -const struct exynos5_usbdrd_phy_drvdata exynos5250_usbdrd_phy = { +static const struct exynos5_usbdrd_phy_drvdata exynos5250_usbdrd_phy = { .phy_cfg = phy_cfg_exynos5, .pmu_offset_usbdrd0_phy = EXYNOS5_USBDRD_PHY_CONTROL, }; From 57416c23e3ab57e65ba6e7c2e30fb40ba85035a6 Mon Sep 17 00:00:00 2001 From: Kamil Debski Date: Tue, 1 Jul 2014 17:15:54 +0200 Subject: [PATCH 06/28] phy: phy-samsung-usb2: Change phy power on/power off sequence The Exynos4412 USB 2.0 PHY hardware differs from the description provided in the documentation. Some register bits have different function. This patch fixes the defines of register bits and changes the way how phys are powered on and off. Signed-off-by: Kamil Debski Tested-by: Daniel Drake Tested-by: Marek Szyprowski Signed-off-by: Kishon Vijay Abraham I --- drivers/phy/phy-exynos4x12-usb2.c | 112 ++++++++++++++++++++---------- drivers/phy/phy-exynos5250-usb2.c | 2 - drivers/phy/phy-samsung-usb2.h | 3 +- 3 files changed, 77 insertions(+), 40 deletions(-) diff --git a/drivers/phy/phy-exynos4x12-usb2.c b/drivers/phy/phy-exynos4x12-usb2.c index d92a7cc5698a..63134d8bda08 100644 --- a/drivers/phy/phy-exynos4x12-usb2.c +++ b/drivers/phy/phy-exynos4x12-usb2.c @@ -86,13 +86,23 @@ #define EXYNOS_4x12_URSTCON_OTG_HLINK BIT(1) #define EXYNOS_4x12_URSTCON_OTG_PHYLINK BIT(2) #define EXYNOS_4x12_URSTCON_HOST_PHY BIT(3) +/* The following bit defines are presented in the + * order taken from the Exynos4412 reference manual. + * + * During experiments with the hardware and debugging + * it was determined that the hardware behaves contrary + * to the manual. + * + * The following bit values were chaned accordingly to the + * results of real hardware experiments. + */ #define EXYNOS_4x12_URSTCON_PHY1 BIT(4) -#define EXYNOS_4x12_URSTCON_HSIC0 BIT(5) -#define EXYNOS_4x12_URSTCON_HSIC1 BIT(6) +#define EXYNOS_4x12_URSTCON_HSIC0 BIT(6) +#define EXYNOS_4x12_URSTCON_HSIC1 BIT(5) #define EXYNOS_4x12_URSTCON_HOST_LINK_ALL BIT(7) -#define EXYNOS_4x12_URSTCON_HOST_LINK_P0 BIT(8) +#define EXYNOS_4x12_URSTCON_HOST_LINK_P0 BIT(10) #define EXYNOS_4x12_URSTCON_HOST_LINK_P1 BIT(9) -#define EXYNOS_4x12_URSTCON_HOST_LINK_P2 BIT(10) +#define EXYNOS_4x12_URSTCON_HOST_LINK_P2 BIT(8) /* Isolation, configured in the power management unit */ #define EXYNOS_4x12_USB_ISOL_OFFSET 0x704 @@ -188,6 +198,7 @@ static void exynos4x12_setup_clk(struct samsung_usb2_phy_instance *inst) clk = readl(drv->reg_phy + EXYNOS_4x12_UPHYCLK); clk &= ~EXYNOS_4x12_UPHYCLK_PHYFSEL_MASK; clk |= drv->ref_reg_val << EXYNOS_4x12_UPHYCLK_PHYFSEL_OFFSET; + clk |= EXYNOS_4x12_UPHYCLK_PHY1_COMMON_ON; writel(clk, drv->reg_phy + EXYNOS_4x12_UPHYCLK); } @@ -198,27 +209,22 @@ static void exynos4x12_phy_pwr(struct samsung_usb2_phy_instance *inst, bool on) u32 phypwr = 0; u32 rst; u32 pwr; - u32 mode = 0; - u32 switch_mode = 0; switch (inst->cfg->id) { case EXYNOS4x12_DEVICE: phypwr = EXYNOS_4x12_UPHYPWR_PHY0; rstbits = EXYNOS_4x12_URSTCON_PHY0; - mode = EXYNOS_4x12_MODE_SWITCH_DEVICE; - switch_mode = 1; break; case EXYNOS4x12_HOST: phypwr = EXYNOS_4x12_UPHYPWR_PHY1; - rstbits = EXYNOS_4x12_URSTCON_HOST_PHY; - mode = EXYNOS_4x12_MODE_SWITCH_HOST; - switch_mode = 1; + rstbits = EXYNOS_4x12_URSTCON_HOST_PHY | + EXYNOS_4x12_URSTCON_PHY1 | + EXYNOS_4x12_URSTCON_HOST_LINK_P0; break; case EXYNOS4x12_HSIC0: phypwr = EXYNOS_4x12_UPHYPWR_HSIC0; - rstbits = EXYNOS_4x12_URSTCON_HSIC1 | - EXYNOS_4x12_URSTCON_HOST_LINK_P0 | - EXYNOS_4x12_URSTCON_HOST_PHY; + rstbits = EXYNOS_4x12_URSTCON_HSIC0 | + EXYNOS_4x12_URSTCON_HOST_LINK_P1; break; case EXYNOS4x12_HSIC1: phypwr = EXYNOS_4x12_UPHYPWR_HSIC1; @@ -228,11 +234,6 @@ static void exynos4x12_phy_pwr(struct samsung_usb2_phy_instance *inst, bool on) }; if (on) { - if (switch_mode) - regmap_update_bits(drv->reg_sys, - EXYNOS_4x12_MODE_SWITCH_OFFSET, - EXYNOS_4x12_MODE_SWITCH_MASK, mode); - pwr = readl(drv->reg_phy + EXYNOS_4x12_UPHYPWR); pwr &= ~phypwr; writel(pwr, drv->reg_phy + EXYNOS_4x12_UPHYPWR); @@ -253,41 +254,78 @@ static void exynos4x12_phy_pwr(struct samsung_usb2_phy_instance *inst, bool on) } } +static void exynos4x12_power_on_int(struct samsung_usb2_phy_instance *inst) +{ + if (inst->int_cnt++ > 0) + return; + + exynos4x12_setup_clk(inst); + exynos4x12_isol(inst, 0); + exynos4x12_phy_pwr(inst, 1); +} + static int exynos4x12_power_on(struct samsung_usb2_phy_instance *inst) { struct samsung_usb2_phy_driver *drv = inst->drv; - inst->enabled = 1; - exynos4x12_setup_clk(inst); - exynos4x12_phy_pwr(inst, 1); - exynos4x12_isol(inst, 0); + if (inst->ext_cnt++ > 0) + return 0; - /* Power on the device, as it is necessary for HSIC to work */ - if (inst->cfg->id == EXYNOS4x12_HSIC0) { - struct samsung_usb2_phy_instance *device = - &drv->instances[EXYNOS4x12_DEVICE]; - exynos4x12_phy_pwr(device, 1); - exynos4x12_isol(device, 0); + if (inst->cfg->id == EXYNOS4x12_HOST) { + regmap_update_bits(drv->reg_sys, EXYNOS_4x12_MODE_SWITCH_OFFSET, + EXYNOS_4x12_MODE_SWITCH_MASK, + EXYNOS_4x12_MODE_SWITCH_HOST); + exynos4x12_power_on_int(&drv->instances[EXYNOS4x12_DEVICE]); } + if (inst->cfg->id == EXYNOS4x12_DEVICE) + regmap_update_bits(drv->reg_sys, EXYNOS_4x12_MODE_SWITCH_OFFSET, + EXYNOS_4x12_MODE_SWITCH_MASK, + EXYNOS_4x12_MODE_SWITCH_DEVICE); + + if (inst->cfg->id == EXYNOS4x12_HSIC0 || + inst->cfg->id == EXYNOS4x12_HSIC1) { + exynos4x12_power_on_int(&drv->instances[EXYNOS4x12_DEVICE]); + exynos4x12_power_on_int(&drv->instances[EXYNOS4x12_HOST]); + } + + exynos4x12_power_on_int(inst); + return 0; } +static void exynos4x12_power_off_int(struct samsung_usb2_phy_instance *inst) +{ + if (inst->int_cnt-- > 1) + return; + + exynos4x12_isol(inst, 1); + exynos4x12_phy_pwr(inst, 0); +} + static int exynos4x12_power_off(struct samsung_usb2_phy_instance *inst) { struct samsung_usb2_phy_driver *drv = inst->drv; - struct samsung_usb2_phy_instance *device = - &drv->instances[EXYNOS4x12_DEVICE]; - inst->enabled = 0; - exynos4x12_isol(inst, 1); - exynos4x12_phy_pwr(inst, 0); + if (inst->ext_cnt-- > 1) + return 0; - if (inst->cfg->id == EXYNOS4x12_HSIC0 && !device->enabled) { - exynos4x12_isol(device, 1); - exynos4x12_phy_pwr(device, 0); + if (inst->cfg->id == EXYNOS4x12_DEVICE) + regmap_update_bits(drv->reg_sys, EXYNOS_4x12_MODE_SWITCH_OFFSET, + EXYNOS_4x12_MODE_SWITCH_MASK, + EXYNOS_4x12_MODE_SWITCH_HOST); + + if (inst->cfg->id == EXYNOS4x12_HOST) + exynos4x12_power_off_int(&drv->instances[EXYNOS4x12_DEVICE]); + + if (inst->cfg->id == EXYNOS4x12_HSIC0 || + inst->cfg->id == EXYNOS4x12_HSIC1) { + exynos4x12_power_off_int(&drv->instances[EXYNOS4x12_DEVICE]); + exynos4x12_power_off_int(&drv->instances[EXYNOS4x12_HOST]); } + exynos4x12_power_off_int(inst); + return 0; } diff --git a/drivers/phy/phy-exynos5250-usb2.c b/drivers/phy/phy-exynos5250-usb2.c index 94179afda951..1c139aa0d074 100644 --- a/drivers/phy/phy-exynos5250-usb2.c +++ b/drivers/phy/phy-exynos5250-usb2.c @@ -318,7 +318,6 @@ static int exynos5250_power_on(struct samsung_usb2_phy_instance *inst) break; } - inst->enabled = 1; exynos5250_isol(inst, 0); return 0; @@ -331,7 +330,6 @@ static int exynos5250_power_off(struct samsung_usb2_phy_instance *inst) u32 otg; u32 hsic; - inst->enabled = 0; exynos5250_isol(inst, 1); switch (inst->cfg->id) { diff --git a/drivers/phy/phy-samsung-usb2.h b/drivers/phy/phy-samsung-usb2.h index 45b3170652bd..918847843a95 100644 --- a/drivers/phy/phy-samsung-usb2.h +++ b/drivers/phy/phy-samsung-usb2.h @@ -29,7 +29,8 @@ struct samsung_usb2_phy_instance { const struct samsung_usb2_common_phy *cfg; struct phy *phy; struct samsung_usb2_phy_driver *drv; - bool enabled; + int int_cnt; + int ext_cnt; }; struct samsung_usb2_phy_driver { From 99bbd48c2065552fd2d224c9f065dcac9b7e25ce Mon Sep 17 00:00:00 2001 From: Kishon Vijay Abraham I Date: Wed, 25 Jun 2014 23:22:56 +0530 Subject: [PATCH 07/28] phy: phy-omap-pipe3: Add support for PCIe PHY PCIe PHY uses an external pll instead of the internal pll used by SATA and USB3. So added support in pipe3 PHY to use external pll. Signed-off-by: Kishon Vijay Abraham I Reviewed-by: Roger Quadros --- .../devicetree/bindings/phy/ti-phy.txt | 11 +- drivers/phy/phy-ti-pipe3.c | 107 ++++++++++++++---- 2 files changed, 93 insertions(+), 25 deletions(-) diff --git a/Documentation/devicetree/bindings/phy/ti-phy.txt b/Documentation/devicetree/bindings/phy/ti-phy.txt index 9ce458f32945..b50e1c10a05d 100644 --- a/Documentation/devicetree/bindings/phy/ti-phy.txt +++ b/Documentation/devicetree/bindings/phy/ti-phy.txt @@ -56,8 +56,8 @@ usb2phy@4a0ad080 { TI PIPE3 PHY Required properties: - - compatible: Should be "ti,phy-usb3" or "ti,phy-pipe3-sata". - "ti,omap-usb3" is deprecated. + - compatible: Should be "ti,phy-usb3", "ti,phy-pipe3-sata" or + "ti,phy-pipe3-pcie. "ti,omap-usb3" is deprecated. - reg : Address and length of the register set for the device. - reg-names: The names of the register addresses corresponding to the registers filled in "reg". @@ -69,10 +69,17 @@ Required properties: * "wkupclk" - wakeup clock. * "sysclk" - system clock. * "refclk" - reference clock. + * "dpll_ref" - external dpll ref clk + * "dpll_ref_m2" - external dpll ref clk + * "phy-div" - divider for apll + * "div-clk" - apll clock Optional properties: - ctrl-module : phandle of the control module used by PHY driver to power on the PHY. + - id: If there are multiple instance of the same type, in order to + differentiate between each instance "id" can be used (e.g., multi-lane PCIe + PHY). If "id" is not provided, it is set to default value of '1'. This is usually a subnode of ocp2scp to which it is connected. diff --git a/drivers/phy/phy-ti-pipe3.c b/drivers/phy/phy-ti-pipe3.c index 591367654613..6174f4b1a5de 100644 --- a/drivers/phy/phy-ti-pipe3.c +++ b/drivers/phy/phy-ti-pipe3.c @@ -80,7 +80,9 @@ struct ti_pipe3 { struct clk *wkupclk; struct clk *sys_clk; struct clk *refclk; + struct clk *div_clk; struct pipe3_dpll_map *dpll_map; + u8 id; }; static struct pipe3_dpll_map dpll_map_usb[] = { @@ -215,6 +217,9 @@ static int ti_pipe3_init(struct phy *x) u32 val; int ret = 0; + if (of_device_is_compatible(phy->dev->of_node, "ti,phy-pipe3-pcie")) + return 0; + /* Bring it out of IDLE if it is IDLE */ val = ti_pipe3_readl(phy->pll_ctrl_base, PLL_CONFIGURATION2); if (val & PLL_IDLE) { @@ -238,8 +243,11 @@ static int ti_pipe3_exit(struct phy *x) u32 val; unsigned long timeout; - /* SATA DPLL can't be powered down due to Errata i783 */ - if (of_device_is_compatible(phy->dev->of_node, "ti,phy-pipe3-sata")) + /* SATA DPLL can't be powered down due to Errata i783 and PCIe + * does not have internal DPLL + */ + if (of_device_is_compatible(phy->dev->of_node, "ti,phy-pipe3-sata") || + of_device_is_compatible(phy->dev->of_node, "ti,phy-pipe3-pcie")) return 0; /* Put DPLL in IDLE mode */ @@ -286,32 +294,41 @@ static int ti_pipe3_probe(struct platform_device *pdev) struct device_node *control_node; struct platform_device *control_pdev; const struct of_device_id *match; - - match = of_match_device(of_match_ptr(ti_pipe3_id_table), &pdev->dev); - if (!match) - return -EINVAL; + struct clk *clk; phy = devm_kzalloc(&pdev->dev, sizeof(*phy), GFP_KERNEL); if (!phy) { dev_err(&pdev->dev, "unable to alloc mem for TI PIPE3 PHY\n"); return -ENOMEM; } - - phy->dpll_map = (struct pipe3_dpll_map *)match->data; - if (!phy->dpll_map) { - dev_err(&pdev->dev, "no DPLL data\n"); - return -EINVAL; - } - - res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "pll_ctrl"); - phy->pll_ctrl_base = devm_ioremap_resource(&pdev->dev, res); - if (IS_ERR(phy->pll_ctrl_base)) - return PTR_ERR(phy->pll_ctrl_base); - phy->dev = &pdev->dev; - if (!of_device_is_compatible(node, "ti,phy-pipe3-sata")) { + if (!of_device_is_compatible(node, "ti,phy-pipe3-pcie")) { + match = of_match_device(of_match_ptr(ti_pipe3_id_table), + &pdev->dev); + if (!match) + return -EINVAL; + phy->dpll_map = (struct pipe3_dpll_map *)match->data; + if (!phy->dpll_map) { + dev_err(&pdev->dev, "no DPLL data\n"); + return -EINVAL; + } + + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, + "pll_ctrl"); + phy->pll_ctrl_base = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(phy->pll_ctrl_base)) + return PTR_ERR(phy->pll_ctrl_base); + + phy->sys_clk = devm_clk_get(phy->dev, "sysclk"); + if (IS_ERR(phy->sys_clk)) { + dev_err(&pdev->dev, "unable to get sysclk\n"); + return -EINVAL; + } + } + + if (!of_device_is_compatible(node, "ti,phy-pipe3-sata")) { phy->wkupclk = devm_clk_get(phy->dev, "wkupclk"); if (IS_ERR(phy->wkupclk)) { dev_err(&pdev->dev, "unable to get wkupclk\n"); @@ -328,10 +345,38 @@ static int ti_pipe3_probe(struct platform_device *pdev) phy->refclk = ERR_PTR(-ENODEV); } - phy->sys_clk = devm_clk_get(phy->dev, "sysclk"); - if (IS_ERR(phy->sys_clk)) { - dev_err(&pdev->dev, "unable to get sysclk\n"); - return -EINVAL; + if (of_device_is_compatible(node, "ti,phy-pipe3-pcie")) { + if (of_property_read_u8(node, "id", &phy->id) < 0) + phy->id = 1; + + clk = devm_clk_get(phy->dev, "dpll_ref"); + if (IS_ERR(clk)) { + dev_err(&pdev->dev, "unable to get dpll ref clk\n"); + return PTR_ERR(clk); + } + clk_set_rate(clk, 1500000000); + + clk = devm_clk_get(phy->dev, "dpll_ref_m2"); + if (IS_ERR(clk)) { + dev_err(&pdev->dev, "unable to get dpll ref m2 clk\n"); + return PTR_ERR(clk); + } + clk_set_rate(clk, 100000000); + + clk = devm_clk_get(phy->dev, "phy-div"); + if (IS_ERR(clk)) { + dev_err(&pdev->dev, "unable to get phy-div clk\n"); + return PTR_ERR(clk); + } + clk_set_rate(clk, 100000000); + + phy->div_clk = devm_clk_get(phy->dev, "div-clk"); + if (IS_ERR(phy->div_clk)) { + dev_err(&pdev->dev, "unable to get div-clk\n"); + return PTR_ERR(phy->div_clk); + } + } else { + phy->div_clk = ERR_PTR(-ENODEV); } control_node = of_parse_phandle(node, "ctrl-module", 0); @@ -387,6 +432,8 @@ static int ti_pipe3_runtime_suspend(struct device *dev) clk_disable_unprepare(phy->wkupclk); if (!IS_ERR(phy->refclk)) clk_disable_unprepare(phy->refclk); + if (!IS_ERR(phy->div_clk)) + clk_disable_unprepare(phy->div_clk); return 0; } @@ -412,8 +459,19 @@ static int ti_pipe3_runtime_resume(struct device *dev) } } + if (!IS_ERR(phy->div_clk)) { + ret = clk_prepare_enable(phy->div_clk); + if (ret) { + dev_err(phy->dev, "Failed to enable div_clk %d\n", ret); + goto err3; + } + } return 0; +err3: + if (!IS_ERR(phy->wkupclk)) + clk_disable_unprepare(phy->wkupclk); + err2: if (!IS_ERR(phy->refclk)) clk_disable_unprepare(phy->refclk); @@ -446,6 +504,9 @@ static const struct of_device_id ti_pipe3_id_table[] = { .compatible = "ti,phy-pipe3-sata", .data = dpll_map_sata, }, + { + .compatible = "ti,phy-pipe3-pcie", + }, {} }; MODULE_DEVICE_TABLE(of, ti_pipe3_id_table); From f0e2cf7b912522c9c7146d9d6e99d1b0ea5c97c6 Mon Sep 17 00:00:00 2001 From: Kishon Vijay Abraham I Date: Wed, 25 Jun 2014 23:22:57 +0530 Subject: [PATCH 08/28] phy: pipe3: insert delay to enumerate in GEN2 mode 8-bit delay value (0xF1) is required for GEN2 devices to be enumerated consistently. Added an API to be called from PHY drivers to set this delay value and called it from PIPE3 driver to set the delay value. Signed-off-by: Kishon Vijay Abraham I Reviewed-by: Roger Quadros --- .../devicetree/bindings/phy/ti-phy.txt | 12 +++-- drivers/phy/phy-omap-control.c | 52 ++++++++++++++++++- drivers/phy/phy-ti-pipe3.c | 4 +- include/linux/phy/omap_control_phy.h | 10 ++++ 4 files changed, 71 insertions(+), 7 deletions(-) diff --git a/Documentation/devicetree/bindings/phy/ti-phy.txt b/Documentation/devicetree/bindings/phy/ti-phy.txt index b50e1c10a05d..305e3df3d9b1 100644 --- a/Documentation/devicetree/bindings/phy/ti-phy.txt +++ b/Documentation/devicetree/bindings/phy/ti-phy.txt @@ -9,15 +9,17 @@ Required properties: e.g. USB2_PHY on OMAP5. "ti,control-phy-pipe3" - if it has DPLL and individual Rx & Tx power control e.g. USB3 PHY and SATA PHY on OMAP5. + "ti,control-phy-pcie" - for pcie to support external clock for pcie and to + set PCS delay value. + e.g. PCIE PHY in DRA7x "ti,control-phy-usb2-dra7" - if it has power down register like USB2 PHY on DRA7 platform. "ti,control-phy-usb2-am437" - if it has power down register like USB2 PHY on AM437 platform. - - reg : Address and length of the register set for the device. It contains - the address of "otghs_control" for control-phy-otghs or "power" register - for other types. - - reg-names: should be "otghs_control" control-phy-otghs and "power" for - other types. + - reg : register ranges as listed in the reg-names property + - reg-names: "otghs_control" for control-phy-otghs + "power", "pcie_pcs" and "control_sma" for control-phy-pcie + "power" for all other types omap_control_usb: omap-control-usb@4a002300 { compatible = "ti,control-phy-otghs"; diff --git a/drivers/phy/phy-omap-control.c b/drivers/phy/phy-omap-control.c index 311b4f9a5132..9487bf112267 100644 --- a/drivers/phy/phy-omap-control.c +++ b/drivers/phy/phy-omap-control.c @@ -26,6 +26,41 @@ #include #include +/** + * omap_control_pcie_pcs - set the PCS delay count + * @dev: the control module device + * @id: index of the pcie PHY (should be 1 or 2) + * @delay: 8 bit delay value + */ +void omap_control_pcie_pcs(struct device *dev, u8 id, u8 delay) +{ + u32 val; + struct omap_control_phy *control_phy; + + if (IS_ERR(dev) || !dev) { + pr_err("%s: invalid device\n", __func__); + return; + } + + control_phy = dev_get_drvdata(dev); + if (!control_phy) { + dev_err(dev, "%s: invalid control phy device\n", __func__); + return; + } + + if (control_phy->type != OMAP_CTRL_TYPE_PCIE) { + dev_err(dev, "%s: unsupported operation\n", __func__); + return; + } + + val = readl(control_phy->pcie_pcs); + val &= ~(OMAP_CTRL_PCIE_PCS_MASK << + (id * OMAP_CTRL_PCIE_PCS_DELAY_COUNT_SHIFT)); + val |= delay << (id * OMAP_CTRL_PCIE_PCS_DELAY_COUNT_SHIFT); + writel(val, control_phy->pcie_pcs); +} +EXPORT_SYMBOL_GPL(omap_control_pcie_pcs); + /** * omap_control_phy_power - power on/off the phy using control module reg * @dev: the control module device @@ -61,6 +96,7 @@ void omap_control_phy_power(struct device *dev, int on) val |= OMAP_CTRL_DEV_PHY_PD; break; + case OMAP_CTRL_TYPE_PCIE: case OMAP_CTRL_TYPE_PIPE3: rate = clk_get_rate(control_phy->sys_clk); rate = rate/1000000; @@ -211,6 +247,7 @@ EXPORT_SYMBOL_GPL(omap_control_usb_set_mode); static const enum omap_control_phy_type otghs_data = OMAP_CTRL_TYPE_OTGHS; static const enum omap_control_phy_type usb2_data = OMAP_CTRL_TYPE_USB2; static const enum omap_control_phy_type pipe3_data = OMAP_CTRL_TYPE_PIPE3; +static const enum omap_control_phy_type pcie_data = OMAP_CTRL_TYPE_PCIE; static const enum omap_control_phy_type dra7usb2_data = OMAP_CTRL_TYPE_DRA7USB2; static const enum omap_control_phy_type am437usb2_data = OMAP_CTRL_TYPE_AM437USB2; @@ -227,6 +264,10 @@ static const struct of_device_id omap_control_phy_id_table[] = { .compatible = "ti,control-phy-pipe3", .data = &pipe3_data, }, + { + .compatible = "ti,control-phy-pcie", + .data = &pcie_data, + }, { .compatible = "ti,control-phy-usb2-dra7", .data = &dra7usb2_data, @@ -279,7 +320,8 @@ static int omap_control_phy_probe(struct platform_device *pdev) } } - if (control_phy->type == OMAP_CTRL_TYPE_PIPE3) { + if (control_phy->type == OMAP_CTRL_TYPE_PIPE3 || + control_phy->type == OMAP_CTRL_TYPE_PCIE) { control_phy->sys_clk = devm_clk_get(control_phy->dev, "sys_clkin"); if (IS_ERR(control_phy->sys_clk)) { @@ -288,6 +330,14 @@ static int omap_control_phy_probe(struct platform_device *pdev) } } + if (control_phy->type == OMAP_CTRL_TYPE_PCIE) { + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, + "pcie_pcs"); + control_phy->pcie_pcs = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(control_phy->pcie_pcs)) + return PTR_ERR(control_phy->pcie_pcs); + } + dev_set_drvdata(control_phy->dev, control_phy); return 0; diff --git a/drivers/phy/phy-ti-pipe3.c b/drivers/phy/phy-ti-pipe3.c index 6174f4b1a5de..93bcd67f1b22 100644 --- a/drivers/phy/phy-ti-pipe3.c +++ b/drivers/phy/phy-ti-pipe3.c @@ -217,8 +217,10 @@ static int ti_pipe3_init(struct phy *x) u32 val; int ret = 0; - if (of_device_is_compatible(phy->dev->of_node, "ti,phy-pipe3-pcie")) + if (of_device_is_compatible(phy->dev->of_node, "ti,phy-pipe3-pcie")) { + omap_control_pcie_pcs(phy->control_dev, phy->id, 0xF1); return 0; + } /* Bring it out of IDLE if it is IDLE */ val = ti_pipe3_readl(phy->pll_ctrl_base, PLL_CONFIGURATION2); diff --git a/include/linux/phy/omap_control_phy.h b/include/linux/phy/omap_control_phy.h index 5450403c7546..e9e6cfbfbb58 100644 --- a/include/linux/phy/omap_control_phy.h +++ b/include/linux/phy/omap_control_phy.h @@ -23,6 +23,7 @@ enum omap_control_phy_type { OMAP_CTRL_TYPE_OTGHS = 1, /* Mailbox OTGHS_CONTROL */ OMAP_CTRL_TYPE_USB2, /* USB2_PHY, power down in CONTROL_DEV_CONF */ OMAP_CTRL_TYPE_PIPE3, /* PIPE3 PHY, DPLL & seperate Rx/Tx power */ + OMAP_CTRL_TYPE_PCIE, /* RX TX control of ACSPCIE */ OMAP_CTRL_TYPE_DRA7USB2, /* USB2 PHY, power and power_aux e.g. DRA7 */ OMAP_CTRL_TYPE_AM437USB2, /* USB2 PHY, power e.g. AM437x */ }; @@ -33,6 +34,7 @@ struct omap_control_phy { u32 __iomem *otghs_control; u32 __iomem *power; u32 __iomem *power_aux; + u32 __iomem *pcie_pcs; struct clk *sys_clk; @@ -63,6 +65,9 @@ enum omap_control_usb_mode { #define OMAP_CTRL_PIPE3_PHY_TX_RX_POWERON 0x3 #define OMAP_CTRL_PIPE3_PHY_TX_RX_POWEROFF 0x0 +#define OMAP_CTRL_PCIE_PCS_MASK 0xff +#define OMAP_CTRL_PCIE_PCS_DELAY_COUNT_SHIFT 0x8 + #define OMAP_CTRL_USB2_PHY_PD BIT(28) #define AM437X_CTRL_USB2_PHY_PD BIT(0) @@ -74,6 +79,7 @@ enum omap_control_usb_mode { void omap_control_phy_power(struct device *dev, int on); void omap_control_usb_set_mode(struct device *dev, enum omap_control_usb_mode mode); +void omap_control_pcie_pcs(struct device *dev, u8 id, u8 delay); #else static inline void omap_control_phy_power(struct device *dev, int on) @@ -84,6 +90,10 @@ static inline void omap_control_usb_set_mode(struct device *dev, enum omap_control_usb_mode mode) { } + +static inline void omap_control_pcie_pcs(struct device *dev, u8 id, u8 delay) +{ +} #endif #endif /* __OMAP_CONTROL_PHY_H__ */ From f1876accff7ffb3f3cb91ab86aaa866a5eec3f0a Mon Sep 17 00:00:00 2001 From: Jiancheng Xue Date: Thu, 3 Jul 2014 22:28:37 +0800 Subject: [PATCH 09/28] Documentation: Document Hisilicon hix5hd2 sata PHY Add necessary binding documentation SATA PHY on Hisilicon hix5hd2 soc. Signed-off-by: Jiancheng Xue Signed-off-by: Zhangfei Gao Signed-off-by: Kishon Vijay Abraham I --- .../devicetree/bindings/phy/hix5hd2-phy.txt | 22 +++++++++++++++++++ 1 file changed, 22 insertions(+) create mode 100644 Documentation/devicetree/bindings/phy/hix5hd2-phy.txt diff --git a/Documentation/devicetree/bindings/phy/hix5hd2-phy.txt b/Documentation/devicetree/bindings/phy/hix5hd2-phy.txt new file mode 100644 index 000000000000..296168b74d24 --- /dev/null +++ b/Documentation/devicetree/bindings/phy/hix5hd2-phy.txt @@ -0,0 +1,22 @@ +Hisilicon hix5hd2 SATA PHY +----------------------- + +Required properties: +- compatible: should be "hisilicon,hix5hd2-sata-phy" +- reg: offset and length of the PHY registers +- #phy-cells: must be 0 +Refer to phy/phy-bindings.txt for the generic PHY binding properties + +Optional Properties: +- hisilicon,peripheral-syscon: phandle of syscon used to control peripheral. +- hisilicon,power-reg: offset and bit number within peripheral-syscon, + register of controlling sata power supply. + +Example: + sata_phy: phy@f9900000 { + compatible = "hisilicon,hix5hd2-sata-phy"; + reg = <0xf9900000 0x10000>; + #phy-cells = <0>; + hisilicon,peripheral-syscon = <&peripheral_ctrl>; + hisilicon,power-reg = <0x8 10>; + }; From e379413a346ce5943ab895aa5a702ec752577f13 Mon Sep 17 00:00:00 2001 From: Jiancheng Xue Date: Thu, 3 Jul 2014 22:28:38 +0800 Subject: [PATCH 10/28] phy: add hix5hd2-sata-phy driver Add hix5hd2-sata-phy driver on Hisilicon hix5hd2 soc. Signed-off-by: Jiancheng Xue Signed-off-by: Zhangfei Gao Signed-off-by: Kishon Vijay Abraham I --- drivers/phy/Kconfig | 8 ++ drivers/phy/Makefile | 1 + drivers/phy/phy-hix5hd2-sata.c | 192 +++++++++++++++++++++++++++++++++ 3 files changed, 201 insertions(+) create mode 100644 drivers/phy/phy-hix5hd2-sata.c diff --git a/drivers/phy/Kconfig b/drivers/phy/Kconfig index 64b98d242ea6..30c82fcbb492 100644 --- a/drivers/phy/Kconfig +++ b/drivers/phy/Kconfig @@ -109,6 +109,14 @@ config PHY_EXYNOS5250_SATA SATA 3.0 Gb/s, SATA 6.0 Gb/s speeds. It supports one SATA host port to accept one SATA device. +config PHY_HIX5HD2_SATA + tristate "HIX5HD2 SATA PHY Driver" + depends on ARCH_HIX5HD2 && OF && HAS_IOMEM + select GENERIC_PHY + select MFD_SYSCON + help + Support for SATA PHY on Hisilicon hix5hd2 Soc. + config PHY_SUN4I_USB tristate "Allwinner sunxi SoC USB PHY driver" depends on ARCH_SUNXI && HAS_IOMEM && OF diff --git a/drivers/phy/Makefile b/drivers/phy/Makefile index b4f1d5770601..54f04d04e85d 100644 --- a/drivers/phy/Makefile +++ b/drivers/phy/Makefile @@ -12,6 +12,7 @@ obj-$(CONFIG_OMAP_USB2) += phy-omap-usb2.o obj-$(CONFIG_TI_PIPE3) += phy-ti-pipe3.o obj-$(CONFIG_TWL4030_USB) += phy-twl4030-usb.o obj-$(CONFIG_PHY_EXYNOS5250_SATA) += phy-exynos5250-sata.o +obj-$(CONFIG_PHY_HIX5HD2_SATA) += phy-hix5hd2-sata.o obj-$(CONFIG_PHY_SUN4I_USB) += phy-sun4i-usb.o obj-$(CONFIG_PHY_SAMSUNG_USB2) += phy-exynos-usb2.o phy-exynos-usb2-y += phy-samsung-usb2.o diff --git a/drivers/phy/phy-hix5hd2-sata.c b/drivers/phy/phy-hix5hd2-sata.c new file mode 100644 index 000000000000..d44283453d71 --- /dev/null +++ b/drivers/phy/phy-hix5hd2-sata.c @@ -0,0 +1,192 @@ +/* + * Copyright (c) 2014 Linaro Ltd. + * Copyright (c) 2014 Hisilicon Limited. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ + +#include +#include +#include +#include +#include +#include +#include + +#define SATA_PHY0_CTLL 0xa0 +#define MPLL_MULTIPLIER_SHIFT 1 +#define MPLL_MULTIPLIER_MASK 0xfe +#define MPLL_MULTIPLIER_50M 0x3c +#define MPLL_MULTIPLIER_100M 0x1e +#define PHY_RESET BIT(0) +#define REF_SSP_EN BIT(9) +#define SSC_EN BIT(10) +#define REF_USE_PAD BIT(23) + +#define SATA_PORT_PHYCTL 0x174 +#define SPEED_MODE_MASK 0x6f0000 +#define HALF_RATE_SHIFT 16 +#define PHY_CONFIG_SHIFT 18 +#define GEN2_EN_SHIFT 21 +#define SPEED_CTRL BIT(20) + +#define SATA_PORT_PHYCTL1 0x148 +#define AMPLITUDE_MASK 0x3ffffe +#define AMPLITUDE_GEN3 0x68 +#define AMPLITUDE_GEN3_SHIFT 15 +#define AMPLITUDE_GEN2 0x56 +#define AMPLITUDE_GEN2_SHIFT 8 +#define AMPLITUDE_GEN1 0x56 +#define AMPLITUDE_GEN1_SHIFT 1 + +#define SATA_PORT_PHYCTL2 0x14c +#define PREEMPH_MASK 0x3ffff +#define PREEMPH_GEN3 0x20 +#define PREEMPH_GEN3_SHIFT 12 +#define PREEMPH_GEN2 0x15 +#define PREEMPH_GEN2_SHIFT 6 +#define PREEMPH_GEN1 0x5 +#define PREEMPH_GEN1_SHIFT 0 + +struct hix5hd2_priv { + void __iomem *base; + struct regmap *peri_ctrl; +}; + +enum phy_speed_mode { + SPEED_MODE_GEN1 = 0, + SPEED_MODE_GEN2 = 1, + SPEED_MODE_GEN3 = 2, +}; + +static int hix5hd2_sata_phy_init(struct phy *phy) +{ + struct hix5hd2_priv *priv = phy_get_drvdata(phy); + u32 val, data[2]; + int ret; + + if (priv->peri_ctrl) { + ret = of_property_read_u32_array(phy->dev.of_node, + "hisilicon,power-reg", + &data[0], 2); + if (ret) { + dev_err(&phy->dev, "Fail read hisilicon,power-reg\n"); + return ret; + } + + regmap_update_bits(priv->peri_ctrl, data[0], + BIT(data[1]), BIT(data[1])); + } + + /* reset phy */ + val = readl_relaxed(priv->base + SATA_PHY0_CTLL); + val &= ~(MPLL_MULTIPLIER_MASK | REF_USE_PAD); + val |= MPLL_MULTIPLIER_50M << MPLL_MULTIPLIER_SHIFT | + REF_SSP_EN | PHY_RESET; + writel_relaxed(val, priv->base + SATA_PHY0_CTLL); + msleep(20); + val &= ~PHY_RESET; + writel_relaxed(val, priv->base + SATA_PHY0_CTLL); + + val = readl_relaxed(priv->base + SATA_PORT_PHYCTL1); + val &= ~AMPLITUDE_MASK; + val |= AMPLITUDE_GEN3 << AMPLITUDE_GEN3_SHIFT | + AMPLITUDE_GEN2 << AMPLITUDE_GEN2_SHIFT | + AMPLITUDE_GEN1 << AMPLITUDE_GEN1_SHIFT; + writel_relaxed(val, priv->base + SATA_PORT_PHYCTL1); + + val = readl_relaxed(priv->base + SATA_PORT_PHYCTL2); + val &= ~PREEMPH_MASK; + val |= PREEMPH_GEN3 << PREEMPH_GEN3_SHIFT | + PREEMPH_GEN2 << PREEMPH_GEN2_SHIFT | + PREEMPH_GEN1 << PREEMPH_GEN1_SHIFT; + writel_relaxed(val, priv->base + SATA_PORT_PHYCTL2); + + /* ensure PHYCTRL setting takes effect */ + val = readl_relaxed(priv->base + SATA_PORT_PHYCTL); + val &= ~SPEED_MODE_MASK; + val |= SPEED_MODE_GEN1 << HALF_RATE_SHIFT | + SPEED_MODE_GEN1 << PHY_CONFIG_SHIFT | + SPEED_MODE_GEN1 << GEN2_EN_SHIFT | SPEED_CTRL; + writel_relaxed(val, priv->base + SATA_PORT_PHYCTL); + + msleep(20); + val &= ~SPEED_MODE_MASK; + val |= SPEED_MODE_GEN3 << HALF_RATE_SHIFT | + SPEED_MODE_GEN3 << PHY_CONFIG_SHIFT | + SPEED_MODE_GEN3 << GEN2_EN_SHIFT | SPEED_CTRL; + writel_relaxed(val, priv->base + SATA_PORT_PHYCTL); + + val &= ~(SPEED_MODE_MASK | SPEED_CTRL); + val |= SPEED_MODE_GEN2 << HALF_RATE_SHIFT | + SPEED_MODE_GEN2 << PHY_CONFIG_SHIFT | + SPEED_MODE_GEN2 << GEN2_EN_SHIFT; + writel_relaxed(val, priv->base + SATA_PORT_PHYCTL); + + return 0; +} + +static struct phy_ops hix5hd2_sata_phy_ops = { + .init = hix5hd2_sata_phy_init, + .owner = THIS_MODULE, +}; + +static int hix5hd2_sata_phy_probe(struct platform_device *pdev) +{ + struct phy_provider *phy_provider; + struct device *dev = &pdev->dev; + struct resource *res; + struct phy *phy; + struct hix5hd2_priv *priv; + + priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + priv->base = devm_ioremap(dev, res->start, resource_size(res)); + if (!priv->base) + return -ENOMEM; + + priv->peri_ctrl = syscon_regmap_lookup_by_phandle(dev->of_node, + "hisilicon,peripheral-syscon"); + if (IS_ERR(priv->peri_ctrl)) + priv->peri_ctrl = NULL; + + phy = devm_phy_create(dev, &hix5hd2_sata_phy_ops, NULL); + if (IS_ERR(phy)) { + dev_err(dev, "failed to create PHY\n"); + return PTR_ERR(phy); + } + + phy_set_drvdata(phy, priv); + phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate); + if (IS_ERR(phy_provider)) + return PTR_ERR(phy_provider); + + return 0; +} + +static const struct of_device_id hix5hd2_sata_phy_of_match[] = { + {.compatible = "hisilicon,hix5hd2-sata-phy",}, + { }, +}; +MODULE_DEVICE_TABLE(of, hix5hd2_sata_phy_of_match); + +static struct platform_driver hix5hd2_sata_phy_driver = { + .probe = hix5hd2_sata_phy_probe, + .driver = { + .name = "hix5hd2-sata-phy", + .owner = THIS_MODULE, + .of_match_table = hix5hd2_sata_phy_of_match, + } +}; +module_platform_driver(hix5hd2_sata_phy_driver); + +MODULE_AUTHOR("Jiancheng Xue "); +MODULE_DESCRIPTION("HISILICON HIX5HD2 SATA PHY driver"); +MODULE_ALIAS("platform:hix5hd2-sata-phy"); +MODULE_LICENSE("GPL v2"); From 016e0d3cb72c1433810fd85a7a7c0946e710d3d6 Mon Sep 17 00:00:00 2001 From: Marek Szyprowski Date: Mon, 7 Jul 2014 11:39:26 +0200 Subject: [PATCH 11/28] drivers: phy: exynos-usb2: add support for Exynos 3250 This patch adds support for Exynos3250 SoC to Exynos2USB PHY driver. Although Exynos3250 has only one device phy interface, the register layout and all operations that are required to get it enabled are almost same as on Exynos4x12. The only different is one more register (REFCLKSEL) which need to be set and lack of MODE SWITCH register. Signed-off-by: Marek Szyprowski Reviewed-by: Tomasz Figa Signed-off-by: Kishon Vijay Abraham I --- .../devicetree/bindings/phy/samsung-phy.txt | 2 ++ drivers/phy/Kconfig | 12 ++++++------ drivers/phy/phy-exynos4x12-usb2.c | 17 +++++++++++++++-- drivers/phy/phy-samsung-usb2.c | 6 ++++++ drivers/phy/phy-samsung-usb2.h | 2 ++ 5 files changed, 31 insertions(+), 8 deletions(-) diff --git a/Documentation/devicetree/bindings/phy/samsung-phy.txt b/Documentation/devicetree/bindings/phy/samsung-phy.txt index 2049261d8c31..6099a5c94283 100644 --- a/Documentation/devicetree/bindings/phy/samsung-phy.txt +++ b/Documentation/devicetree/bindings/phy/samsung-phy.txt @@ -26,6 +26,7 @@ Samsung S5P/EXYNOS SoC series USB PHY Required properties: - compatible : should be one of the listed compatibles: + - "samsung,exynos3250-usb2-phy" - "samsung,exynos4210-usb2-phy" - "samsung,exynos4x12-usb2-phy" - "samsung,exynos5250-usb2-phy" @@ -46,6 +47,7 @@ and Exynos 4212) it is as follows: 1 - USB host ("host"), 2 - HSIC0 ("hsic0"), 3 - HSIC1 ("hsic1"), +Exynos3250 has only USB device phy available as phy 0. Exynos 4210 and Exynos 4212 use mode switching and require that mode switch register is supplied. diff --git a/drivers/phy/Kconfig b/drivers/phy/Kconfig index 30c82fcbb492..7c49c4c61727 100644 --- a/drivers/phy/Kconfig +++ b/drivers/phy/Kconfig @@ -151,14 +151,14 @@ config PHY_EXYNOS4210_USB2 phys are available - device, host, HSIC0 and HSIC1. config PHY_EXYNOS4X12_USB2 - bool "Support for Exynos 4x12" + bool "Support for Exynos 3250/4x12" depends on PHY_SAMSUNG_USB2 - depends on (SOC_EXYNOS4212 || SOC_EXYNOS4412) + depends on (SOC_EXYNOS3250 || SOC_EXYNOS4212 || SOC_EXYNOS4412) help - Enable USB PHY support for Exynos 4x12. This option requires that - Samsung USB 2.0 PHY driver is enabled and means that support for this - particular SoC is compiled in the driver. In case of Exynos 4x12 four - phys are available - device, host, HSIC0 and HSIC1. + Enable USB PHY support for Exynos 3250/4x12. This option requires + that Samsung USB 2.0 PHY driver is enabled and means that support for + this particular SoC is compiled in the driver. In case of Exynos 4x12 + four phys are available - device, host, HSIC0 and HSIC1. config PHY_EXYNOS5250_USB2 bool "Support for Exynos 5250" diff --git a/drivers/phy/phy-exynos4x12-usb2.c b/drivers/phy/phy-exynos4x12-usb2.c index 63134d8bda08..0b9de88579b1 100644 --- a/drivers/phy/phy-exynos4x12-usb2.c +++ b/drivers/phy/phy-exynos4x12-usb2.c @@ -67,6 +67,8 @@ #define EXYNOS_4x12_UPHYCLK_PHYFSEL_24MHZ (0x5 << 0) #define EXYNOS_4x12_UPHYCLK_PHYFSEL_50MHZ (0x7 << 0) +#define EXYNOS_3250_UPHYCLK_REFCLKSEL (0x2 << 8) + #define EXYNOS_4x12_UPHYCLK_PHY0_ID_PULLUP BIT(3) #define EXYNOS_4x12_UPHYCLK_PHY0_COMMON_ON BIT(4) #define EXYNOS_4x12_UPHYCLK_PHY1_COMMON_ON BIT(7) @@ -197,6 +199,10 @@ static void exynos4x12_setup_clk(struct samsung_usb2_phy_instance *inst) clk = readl(drv->reg_phy + EXYNOS_4x12_UPHYCLK); clk &= ~EXYNOS_4x12_UPHYCLK_PHYFSEL_MASK; + + if (drv->cfg->has_refclk_sel) + clk = EXYNOS_3250_UPHYCLK_REFCLKSEL; + clk |= drv->ref_reg_val << EXYNOS_4x12_UPHYCLK_PHYFSEL_OFFSET; clk |= EXYNOS_4x12_UPHYCLK_PHY1_COMMON_ON; writel(clk, drv->reg_phy + EXYNOS_4x12_UPHYCLK); @@ -278,7 +284,7 @@ static int exynos4x12_power_on(struct samsung_usb2_phy_instance *inst) exynos4x12_power_on_int(&drv->instances[EXYNOS4x12_DEVICE]); } - if (inst->cfg->id == EXYNOS4x12_DEVICE) + if (inst->cfg->id == EXYNOS4x12_DEVICE && drv->cfg->has_mode_switch) regmap_update_bits(drv->reg_sys, EXYNOS_4x12_MODE_SWITCH_OFFSET, EXYNOS_4x12_MODE_SWITCH_MASK, EXYNOS_4x12_MODE_SWITCH_DEVICE); @@ -310,7 +316,7 @@ static int exynos4x12_power_off(struct samsung_usb2_phy_instance *inst) if (inst->ext_cnt-- > 1) return 0; - if (inst->cfg->id == EXYNOS4x12_DEVICE) + if (inst->cfg->id == EXYNOS4x12_DEVICE && drv->cfg->has_mode_switch) regmap_update_bits(drv->reg_sys, EXYNOS_4x12_MODE_SWITCH_OFFSET, EXYNOS_4x12_MODE_SWITCH_MASK, EXYNOS_4x12_MODE_SWITCH_HOST); @@ -358,6 +364,13 @@ static const struct samsung_usb2_common_phy exynos4x12_phys[] = { {}, }; +const struct samsung_usb2_phy_config exynos3250_usb2_phy_config = { + .has_refclk_sel = 1, + .num_phys = 1, + .phys = exynos4x12_phys, + .rate_to_clk = exynos4x12_rate_to_clk, +}; + const struct samsung_usb2_phy_config exynos4x12_usb2_phy_config = { .has_mode_switch = 1, .num_phys = EXYNOS4x12_NUM_PHYS, diff --git a/drivers/phy/phy-samsung-usb2.c b/drivers/phy/phy-samsung-usb2.c index 1e69a32c221d..16aae7a285f0 100644 --- a/drivers/phy/phy-samsung-usb2.c +++ b/drivers/phy/phy-samsung-usb2.c @@ -87,6 +87,12 @@ static struct phy *samsung_usb2_phy_xlate(struct device *dev, } static const struct of_device_id samsung_usb2_phy_of_match[] = { +#ifdef CONFIG_PHY_EXYNOS4X12_USB2 + { + .compatible = "samsung,exynos3250-usb2-phy", + .data = &exynos3250_usb2_phy_config, + }, +#endif #ifdef CONFIG_PHY_EXYNOS4210_USB2 { .compatible = "samsung,exynos4210-usb2-phy", diff --git a/drivers/phy/phy-samsung-usb2.h b/drivers/phy/phy-samsung-usb2.h index 918847843a95..b03da0ef39ac 100644 --- a/drivers/phy/phy-samsung-usb2.h +++ b/drivers/phy/phy-samsung-usb2.h @@ -60,8 +60,10 @@ struct samsung_usb2_phy_config { int (*rate_to_clk)(unsigned long, u32 *); unsigned int num_phys; bool has_mode_switch; + bool has_refclk_sel; }; +extern const struct samsung_usb2_phy_config exynos3250_usb2_phy_config; extern const struct samsung_usb2_phy_config exynos4210_usb2_phy_config; extern const struct samsung_usb2_phy_config exynos4x12_usb2_phy_config; extern const struct samsung_usb2_phy_config exynos5250_usb2_phy_config; From 3be88125d85df587085b0be0a5c0e9953eb5ed6b Mon Sep 17 00:00:00 2001 From: Roger Quadros Date: Fri, 4 Jul 2014 12:55:45 +0300 Subject: [PATCH 12/28] phy: core: Support regulator supply for PHY power Some PHYs can be powered by an external power regulator. e.g. USB_HS PHY on DRA7 SoC. Make the PHY core support a power regulator. Signed-off-by: Roger Quadros Signed-off-by: Kishon Vijay Abraham I --- drivers/phy/phy-core.c | 26 ++++++++++++++++++++++++++ include/linux/phy/phy.h | 2 ++ 2 files changed, 28 insertions(+) diff --git a/drivers/phy/phy-core.c b/drivers/phy/phy-core.c index 49c446530101..75c97396dbfa 100644 --- a/drivers/phy/phy-core.c +++ b/drivers/phy/phy-core.c @@ -21,6 +21,7 @@ #include #include #include +#include static struct class *phy_class; static DEFINE_MUTEX(phy_provider_mutex); @@ -226,6 +227,12 @@ int phy_power_on(struct phy *phy) if (!phy) return 0; + if (phy->pwr) { + ret = regulator_enable(phy->pwr); + if (ret) + return ret; + } + ret = phy_pm_runtime_get_sync(phy); if (ret < 0 && ret != -ENOTSUPP) return ret; @@ -247,6 +254,8 @@ int phy_power_on(struct phy *phy) out: mutex_unlock(&phy->mutex); phy_pm_runtime_put_sync(phy); + if (phy->pwr) + regulator_disable(phy->pwr); return ret; } @@ -272,6 +281,9 @@ int phy_power_off(struct phy *phy) mutex_unlock(&phy->mutex); phy_pm_runtime_put(phy); + if (phy->pwr) + regulator_disable(phy->pwr); + return 0; } EXPORT_SYMBOL_GPL(phy_power_off); @@ -588,6 +600,16 @@ struct phy *phy_create(struct device *dev, const struct phy_ops *ops, goto free_phy; } + /* phy-supply */ + phy->pwr = regulator_get_optional(dev, "phy"); + if (IS_ERR(phy->pwr)) { + if (PTR_ERR(phy->pwr) == -EPROBE_DEFER) { + ret = -EPROBE_DEFER; + goto free_ida; + } + phy->pwr = NULL; + } + device_initialize(&phy->dev); mutex_init(&phy->mutex); @@ -617,6 +639,9 @@ put_dev: put_device(&phy->dev); /* calls phy_release() which frees resources */ return ERR_PTR(ret); +free_ida: + ida_simple_remove(&phy_ida, phy->id); + free_phy: kfree(phy); return ERR_PTR(ret); @@ -800,6 +825,7 @@ static void phy_release(struct device *dev) phy = to_phy(dev); dev_vdbg(dev, "releasing '%s'\n", dev_name(dev)); + regulator_put(phy->pwr); ida_simple_remove(&phy_ida, phy->id); kfree(phy); } diff --git a/include/linux/phy/phy.h b/include/linux/phy/phy.h index 2760744cb2a7..9a8694524742 100644 --- a/include/linux/phy/phy.h +++ b/include/linux/phy/phy.h @@ -18,6 +18,7 @@ #include #include #include +#include struct phy; @@ -65,6 +66,7 @@ struct phy { int init_count; int power_count; struct phy_attrs attrs; + struct regulator *pwr; }; /** From e9e8cf49f9ebbdc8ffafc9627bfade76c5384845 Mon Sep 17 00:00:00 2001 From: Roger Quadros Date: Fri, 4 Jul 2014 12:55:46 +0300 Subject: [PATCH 13/28] phy: core: Add phy-supply to DT binding documentation phy-supply is a phandle to the regulator that provides power to the PHY. This regulator is managed during the PHY power on/off sequence by the phy core driver. Signed-off-by: Roger Quadros Signed-off-by: Kishon Vijay Abraham I --- Documentation/devicetree/bindings/phy/phy-bindings.txt | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Documentation/devicetree/bindings/phy/phy-bindings.txt b/Documentation/devicetree/bindings/phy/phy-bindings.txt index 8ae844fc0c60..2aa1840200ed 100644 --- a/Documentation/devicetree/bindings/phy/phy-bindings.txt +++ b/Documentation/devicetree/bindings/phy/phy-bindings.txt @@ -10,6 +10,10 @@ Required Properties: provider can use the values in cells to find the appropriate PHY. +Optional Properties: +phy-supply: Phandle to a regulator that provides power to the PHY. This + regulator will be managed during the PHY power on/off sequence. + For example: phys: phy { From d6125af9afd622b6dbb6d7cd97972e2f398b8d35 Mon Sep 17 00:00:00 2001 From: Sachin Kamat Date: Mon, 14 Jul 2014 15:38:41 +0530 Subject: [PATCH 14/28] phy: Kconfig: Re-organize Exynos USB 2.0 PHY configs Since the USB 2.0 PHYs are required with EHCI/OHCI USB drivers and USB gadget controller supported by the DWC2 gadget driver, make it depend on them and default to ARCH_EXYNOS as they are meant for Exynos platforms. Also, make the sub-drivers silent options enabling them based on the SoC platforms that they are meant to work with. This will make life easier for end users who do not have any way knowing the dependencies. Signed-off-by: Sachin Kamat Reviewed-by: Jingoo Han Tested-by: Vivek Gautam Signed-off-by: Kishon Vijay Abraham I --- drivers/phy/Kconfig | 35 +++++++++++------------------------ 1 file changed, 11 insertions(+), 24 deletions(-) diff --git a/drivers/phy/Kconfig b/drivers/phy/Kconfig index 7c49c4c61727..f28b87f1582d 100644 --- a/drivers/phy/Kconfig +++ b/drivers/phy/Kconfig @@ -132,43 +132,30 @@ config PHY_SUN4I_USB config PHY_SAMSUNG_USB2 tristate "Samsung USB 2.0 PHY driver" depends on HAS_IOMEM + depends on USB_EHCI_EXYNOS || USB_OHCI_EXYNOS || USB_DWC2 select GENERIC_PHY select MFD_SYSCON + default ARCH_EXYNOS help Enable this to support the Samsung USB 2.0 PHY driver for Samsung - SoCs. This driver provides the interface for USB 2.0 PHY. Support for - particular SoCs has to be enabled in addition to this driver. Number - and type of supported phys depends on the SoC. + SoCs. This driver provides the interface for USB 2.0 PHY. Support + for particular PHYs will be enabled based on the SoC type in addition + to this driver. config PHY_EXYNOS4210_USB2 - bool "Support for Exynos 4210" + bool depends on PHY_SAMSUNG_USB2 - depends on CPU_EXYNOS4210 - help - Enable USB PHY support for Exynos 4210. This option requires that - Samsung USB 2.0 PHY driver is enabled and means that support for this - particular SoC is compiled in the driver. In case of Exynos 4210 four - phys are available - device, host, HSIC0 and HSIC1. + default CPU_EXYNOS4210 config PHY_EXYNOS4X12_USB2 - bool "Support for Exynos 3250/4x12" + bool depends on PHY_SAMSUNG_USB2 - depends on (SOC_EXYNOS3250 || SOC_EXYNOS4212 || SOC_EXYNOS4412) - help - Enable USB PHY support for Exynos 3250/4x12. This option requires - that Samsung USB 2.0 PHY driver is enabled and means that support for - this particular SoC is compiled in the driver. In case of Exynos 4x12 - four phys are available - device, host, HSIC0 and HSIC1. + default SOC_EXYNOS3250 || SOC_EXYNOS4212 || SOC_EXYNOS4412 config PHY_EXYNOS5250_USB2 - bool "Support for Exynos 5250" + bool depends on PHY_SAMSUNG_USB2 - depends on SOC_EXYNOS5250 - help - Enable USB PHY support for Exynos 5250. This option requires that - Samsung USB 2.0 PHY driver is enabled and means that support for this - particular SoC is compiled in the driver. In case of Exynos 5250 four - phys are available - device, host, HSIC0 and HSIC. + default SOC_EXYNOS5250 || SOC_EXYNOS5420 config PHY_EXYNOS5_USBDRD tristate "Exynos5 SoC series USB DRD PHY driver" From c233f529480398d46d7098316fc7203587f03c0f Mon Sep 17 00:00:00 2001 From: Sachin Kamat Date: Mon, 14 Jul 2014 15:38:42 +0530 Subject: [PATCH 15/28] phy: Kconfig: Update config for Exynos USB DRD USB DWC3 driver on Exynos platform does not work without its corresponding phy driver. Hence make the PHY driver depend on Exynos DWC3 driver and default it to yes to make things easier for the end user. Signed-off-by: Sachin Kamat Reviewed-by: Jingoo Han Tested-by: Vivek Gautam Signed-off-by: Kishon Vijay Abraham I --- drivers/phy/Kconfig | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/phy/Kconfig b/drivers/phy/Kconfig index f28b87f1582d..778e126700bf 100644 --- a/drivers/phy/Kconfig +++ b/drivers/phy/Kconfig @@ -161,8 +161,10 @@ config PHY_EXYNOS5_USBDRD tristate "Exynos5 SoC series USB DRD PHY driver" depends on ARCH_EXYNOS5 && OF depends on HAS_IOMEM + depends on USB_DWC3_EXYNOS select GENERIC_PHY select MFD_SYSCON + default y help Enable USB DRD PHY support for Exynos 5 SoC series. This driver provides PHY interface for USB 3.0 DRD controller From 1de990d8a169de8aa971cea650e5dec6cdf62a09 Mon Sep 17 00:00:00 2001 From: Srinivas Kandagatla Date: Mon, 14 Jul 2014 12:17:59 +0100 Subject: [PATCH 16/28] phy: qcom: Add driver for QCOM APQ8064 SATA PHY Add a PHY driver for uses with AHCI based SATA controller driver on the APQ8064 family of SoCs. This patch is a forward port from Qualcomm's v3.4 andriod kernel. Tested on IFC6410 board. CC: Sujit Reddy Thumma Tested-by: Kiran Padwal Signed-off-by: Srinivas Kandagatla Signed-off-by: Kishon Vijay Abraham I --- drivers/phy/Kconfig | 7 + drivers/phy/Makefile | 1 + drivers/phy/phy-qcom-apq8064-sata.c | 288 ++++++++++++++++++++++++++++ 3 files changed, 296 insertions(+) create mode 100644 drivers/phy/phy-qcom-apq8064-sata.c diff --git a/drivers/phy/Kconfig b/drivers/phy/Kconfig index 778e126700bf..691115292a99 100644 --- a/drivers/phy/Kconfig +++ b/drivers/phy/Kconfig @@ -177,4 +177,11 @@ config PHY_XGENE help This option enables support for APM X-Gene SoC multi-purpose PHY. +config PHY_QCOM_APQ8064_SATA + tristate "Qualcomm APQ8064 SATA SerDes/PHY driver" + depends on ARCH_QCOM + depends on HAS_IOMEM + depends on OF + select GENERIC_PHY + endmenu diff --git a/drivers/phy/Makefile b/drivers/phy/Makefile index 54f04d04e85d..a4819d378619 100644 --- a/drivers/phy/Makefile +++ b/drivers/phy/Makefile @@ -21,3 +21,4 @@ phy-exynos-usb2-$(CONFIG_PHY_EXYNOS4X12_USB2) += phy-exynos4x12-usb2.o phy-exynos-usb2-$(CONFIG_PHY_EXYNOS5250_USB2) += phy-exynos5250-usb2.o obj-$(CONFIG_PHY_EXYNOS5_USBDRD) += phy-exynos5-usbdrd.o obj-$(CONFIG_PHY_XGENE) += phy-xgene.o +obj-$(CONFIG_PHY_QCOM_APQ8064_SATA) += phy-qcom-apq8064-sata.o diff --git a/drivers/phy/phy-qcom-apq8064-sata.c b/drivers/phy/phy-qcom-apq8064-sata.c new file mode 100644 index 000000000000..c9b4dd6becf5 --- /dev/null +++ b/drivers/phy/phy-qcom-apq8064-sata.c @@ -0,0 +1,288 @@ +/* + * Copyright (c) 2014, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* PHY registers */ +#define UNIPHY_PLL_REFCLK_CFG 0x000 +#define UNIPHY_PLL_PWRGEN_CFG 0x014 +#define UNIPHY_PLL_GLB_CFG 0x020 +#define UNIPHY_PLL_SDM_CFG0 0x038 +#define UNIPHY_PLL_SDM_CFG1 0x03C +#define UNIPHY_PLL_SDM_CFG2 0x040 +#define UNIPHY_PLL_SDM_CFG3 0x044 +#define UNIPHY_PLL_SDM_CFG4 0x048 +#define UNIPHY_PLL_SSC_CFG0 0x04C +#define UNIPHY_PLL_SSC_CFG1 0x050 +#define UNIPHY_PLL_SSC_CFG2 0x054 +#define UNIPHY_PLL_SSC_CFG3 0x058 +#define UNIPHY_PLL_LKDET_CFG0 0x05C +#define UNIPHY_PLL_LKDET_CFG1 0x060 +#define UNIPHY_PLL_LKDET_CFG2 0x064 +#define UNIPHY_PLL_CAL_CFG0 0x06C +#define UNIPHY_PLL_CAL_CFG8 0x08C +#define UNIPHY_PLL_CAL_CFG9 0x090 +#define UNIPHY_PLL_CAL_CFG10 0x094 +#define UNIPHY_PLL_CAL_CFG11 0x098 +#define UNIPHY_PLL_STATUS 0x0C0 + +#define SATA_PHY_SER_CTRL 0x100 +#define SATA_PHY_TX_DRIV_CTRL0 0x104 +#define SATA_PHY_TX_DRIV_CTRL1 0x108 +#define SATA_PHY_TX_IMCAL0 0x11C +#define SATA_PHY_TX_IMCAL2 0x124 +#define SATA_PHY_RX_IMCAL0 0x128 +#define SATA_PHY_EQUAL 0x13C +#define SATA_PHY_OOB_TERM 0x144 +#define SATA_PHY_CDR_CTRL0 0x148 +#define SATA_PHY_CDR_CTRL1 0x14C +#define SATA_PHY_CDR_CTRL2 0x150 +#define SATA_PHY_CDR_CTRL3 0x154 +#define SATA_PHY_PI_CTRL0 0x168 +#define SATA_PHY_POW_DWN_CTRL0 0x180 +#define SATA_PHY_POW_DWN_CTRL1 0x184 +#define SATA_PHY_TX_DATA_CTRL 0x188 +#define SATA_PHY_ALIGNP 0x1A4 +#define SATA_PHY_TX_IMCAL_STAT 0x1E4 +#define SATA_PHY_RX_IMCAL_STAT 0x1E8 + +#define UNIPHY_PLL_LOCK BIT(0) +#define SATA_PHY_TX_CAL BIT(0) +#define SATA_PHY_RX_CAL BIT(0) + +/* default timeout set to 1 sec */ +#define TIMEOUT_MS 10000 +#define DELAY_INTERVAL_US 100 + +struct qcom_apq8064_sata_phy { + void __iomem *mmio; + struct clk *cfg_clk; + struct device *dev; +}; + +/* Helper function to do poll and timeout */ +static int read_poll_timeout(void __iomem *addr, u32 mask) +{ + unsigned long timeout = jiffies + msecs_to_jiffies(TIMEOUT_MS); + + do { + if (readl_relaxed(addr) & mask) + return 0; + + usleep_range(DELAY_INTERVAL_US, DELAY_INTERVAL_US + 50); + } while (!time_after(jiffies, timeout)); + + return -ETIMEDOUT; +} + +static int qcom_apq8064_sata_phy_init(struct phy *generic_phy) +{ + struct qcom_apq8064_sata_phy *phy = phy_get_drvdata(generic_phy); + void __iomem *base = phy->mmio; + int ret = 0; + + /* SATA phy initialization */ + writel_relaxed(0x01, base + SATA_PHY_SER_CTRL); + writel_relaxed(0xB1, base + SATA_PHY_POW_DWN_CTRL0); + /* Make sure the power down happens before power up */ + mb(); + usleep_range(10, 60); + + writel_relaxed(0x01, base + SATA_PHY_POW_DWN_CTRL0); + writel_relaxed(0x3E, base + SATA_PHY_POW_DWN_CTRL1); + writel_relaxed(0x01, base + SATA_PHY_RX_IMCAL0); + writel_relaxed(0x01, base + SATA_PHY_TX_IMCAL0); + writel_relaxed(0x02, base + SATA_PHY_TX_IMCAL2); + + /* Write UNIPHYPLL registers to configure PLL */ + writel_relaxed(0x04, base + UNIPHY_PLL_REFCLK_CFG); + writel_relaxed(0x00, base + UNIPHY_PLL_PWRGEN_CFG); + + writel_relaxed(0x0A, base + UNIPHY_PLL_CAL_CFG0); + writel_relaxed(0xF3, base + UNIPHY_PLL_CAL_CFG8); + writel_relaxed(0x01, base + UNIPHY_PLL_CAL_CFG9); + writel_relaxed(0xED, base + UNIPHY_PLL_CAL_CFG10); + writel_relaxed(0x02, base + UNIPHY_PLL_CAL_CFG11); + + writel_relaxed(0x36, base + UNIPHY_PLL_SDM_CFG0); + writel_relaxed(0x0D, base + UNIPHY_PLL_SDM_CFG1); + writel_relaxed(0xA3, base + UNIPHY_PLL_SDM_CFG2); + writel_relaxed(0xF0, base + UNIPHY_PLL_SDM_CFG3); + writel_relaxed(0x00, base + UNIPHY_PLL_SDM_CFG4); + + writel_relaxed(0x19, base + UNIPHY_PLL_SSC_CFG0); + writel_relaxed(0xE1, base + UNIPHY_PLL_SSC_CFG1); + writel_relaxed(0x00, base + UNIPHY_PLL_SSC_CFG2); + writel_relaxed(0x11, base + UNIPHY_PLL_SSC_CFG3); + + writel_relaxed(0x04, base + UNIPHY_PLL_LKDET_CFG0); + writel_relaxed(0xFF, base + UNIPHY_PLL_LKDET_CFG1); + + writel_relaxed(0x02, base + UNIPHY_PLL_GLB_CFG); + /* make sure global config LDO power down happens before power up */ + mb(); + + writel_relaxed(0x03, base + UNIPHY_PLL_GLB_CFG); + writel_relaxed(0x05, base + UNIPHY_PLL_LKDET_CFG2); + + /* PLL Lock wait */ + ret = read_poll_timeout(base + UNIPHY_PLL_STATUS, UNIPHY_PLL_LOCK); + if (ret) { + dev_err(phy->dev, "poll timeout UNIPHY_PLL_STATUS\n"); + return ret; + } + + /* TX Calibration */ + ret = read_poll_timeout(base + SATA_PHY_TX_IMCAL_STAT, SATA_PHY_TX_CAL); + if (ret) { + dev_err(phy->dev, "poll timeout SATA_PHY_TX_IMCAL_STAT\n"); + return ret; + } + + /* RX Calibration */ + ret = read_poll_timeout(base + SATA_PHY_RX_IMCAL_STAT, SATA_PHY_RX_CAL); + if (ret) { + dev_err(phy->dev, "poll timeout SATA_PHY_RX_IMCAL_STAT\n"); + return ret; + } + + /* SATA phy calibrated succesfully, power up to functional mode */ + writel_relaxed(0x3E, base + SATA_PHY_POW_DWN_CTRL1); + writel_relaxed(0x01, base + SATA_PHY_RX_IMCAL0); + writel_relaxed(0x01, base + SATA_PHY_TX_IMCAL0); + + writel_relaxed(0x00, base + SATA_PHY_POW_DWN_CTRL1); + writel_relaxed(0x59, base + SATA_PHY_CDR_CTRL0); + writel_relaxed(0x04, base + SATA_PHY_CDR_CTRL1); + writel_relaxed(0x00, base + SATA_PHY_CDR_CTRL2); + writel_relaxed(0x00, base + SATA_PHY_PI_CTRL0); + writel_relaxed(0x00, base + SATA_PHY_CDR_CTRL3); + writel_relaxed(0x01, base + SATA_PHY_POW_DWN_CTRL0); + + writel_relaxed(0x11, base + SATA_PHY_TX_DATA_CTRL); + writel_relaxed(0x43, base + SATA_PHY_ALIGNP); + writel_relaxed(0x04, base + SATA_PHY_OOB_TERM); + + writel_relaxed(0x01, base + SATA_PHY_EQUAL); + writel_relaxed(0x09, base + SATA_PHY_TX_DRIV_CTRL0); + writel_relaxed(0x09, base + SATA_PHY_TX_DRIV_CTRL1); + + return 0; +} + +static int qcom_apq8064_sata_phy_exit(struct phy *generic_phy) +{ + struct qcom_apq8064_sata_phy *phy = phy_get_drvdata(generic_phy); + void __iomem *base = phy->mmio; + + /* Power down PHY */ + writel_relaxed(0xF8, base + SATA_PHY_POW_DWN_CTRL0); + writel_relaxed(0xFE, base + SATA_PHY_POW_DWN_CTRL1); + + /* Power down PLL block */ + writel_relaxed(0x00, base + UNIPHY_PLL_GLB_CFG); + + return 0; +} + +static struct phy_ops qcom_apq8064_sata_phy_ops = { + .init = qcom_apq8064_sata_phy_init, + .exit = qcom_apq8064_sata_phy_exit, + .owner = THIS_MODULE, +}; + +static int qcom_apq8064_sata_phy_probe(struct platform_device *pdev) +{ + struct qcom_apq8064_sata_phy *phy; + struct device *dev = &pdev->dev; + struct resource *res; + struct phy_provider *phy_provider; + struct phy *generic_phy; + int ret; + + phy = devm_kzalloc(dev, sizeof(*phy), GFP_KERNEL); + if (!phy) + return -ENOMEM; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + phy->mmio = devm_ioremap_resource(dev, res); + if (IS_ERR(phy->mmio)) + return PTR_ERR(phy->mmio); + + generic_phy = devm_phy_create(dev, &qcom_apq8064_sata_phy_ops, NULL); + if (IS_ERR(generic_phy)) { + dev_err(dev, "%s: failed to create phy\n", __func__); + return PTR_ERR(generic_phy); + } + + phy->dev = dev; + phy_set_drvdata(generic_phy, phy); + platform_set_drvdata(pdev, phy); + + phy->cfg_clk = devm_clk_get(dev, "cfg"); + if (IS_ERR(phy->cfg_clk)) { + dev_err(dev, "Failed to get sata cfg clock\n"); + return PTR_ERR(phy->cfg_clk); + } + + ret = clk_prepare_enable(phy->cfg_clk); + if (ret) + return ret; + + phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate); + if (IS_ERR(phy_provider)) { + clk_disable_unprepare(phy->cfg_clk); + dev_err(dev, "%s: failed to register phy\n", __func__); + return PTR_ERR(phy_provider); + } + + return 0; +} + +static int qcom_apq8064_sata_phy_remove(struct platform_device *pdev) +{ + struct qcom_apq8064_sata_phy *phy = platform_get_drvdata(pdev); + + clk_disable_unprepare(phy->cfg_clk); + + return 0; +} + +static const struct of_device_id qcom_apq8064_sata_phy_of_match[] = { + { .compatible = "qcom,apq8064-sata-phy" }, + { }, +}; +MODULE_DEVICE_TABLE(of, qcom_apq8064_sata_phy_of_match); + +static struct platform_driver qcom_apq8064_sata_phy_driver = { + .probe = qcom_apq8064_sata_phy_probe, + .remove = qcom_apq8064_sata_phy_remove, + .driver = { + .name = "qcom-apq8064-sata-phy", + .owner = THIS_MODULE, + .of_match_table = qcom_apq8064_sata_phy_of_match, + } +}; +module_platform_driver(qcom_apq8064_sata_phy_driver); + +MODULE_DESCRIPTION("QCOM apq8064 SATA PHY driver"); +MODULE_LICENSE("GPL v2"); From e299f59a2ea1d1f6ce43ebfc56c75ea266a056de Mon Sep 17 00:00:00 2001 From: Srinivas Kandagatla Date: Mon, 14 Jul 2014 12:18:08 +0100 Subject: [PATCH 17/28] phy: qcom: Add APQ8064 SATA PHY device tree bindings This patch adds binding spec for Qualcomm AP8064 SATA PHY. Signed-off-by: Srinivas Kandagatla Tested-by: Kiran Padwal Signed-off-by: Kishon Vijay Abraham I --- .../bindings/phy/qcom-apq8064-sata-phy.txt | 24 +++++++++++++++++++ 1 file changed, 24 insertions(+) create mode 100644 Documentation/devicetree/bindings/phy/qcom-apq8064-sata-phy.txt diff --git a/Documentation/devicetree/bindings/phy/qcom-apq8064-sata-phy.txt b/Documentation/devicetree/bindings/phy/qcom-apq8064-sata-phy.txt new file mode 100644 index 000000000000..952f6c96bab9 --- /dev/null +++ b/Documentation/devicetree/bindings/phy/qcom-apq8064-sata-phy.txt @@ -0,0 +1,24 @@ +Qualcomm APQ8064 SATA PHY Controller +------------------------------------ + +SATA PHY nodes are defined to describe on-chip SATA Physical layer controllers. +Each SATA PHY controller should have its own node. + +Required properties: +- compatible: compatible list, contains "qcom,apq8064-sata-phy". +- reg: offset and length of the SATA PHY register set; +- #phy-cells: must be zero +- clocks: a list of phandles and clock-specifier pairs, one for each entry in + clock-names. +- clock-names: must be "cfg" for phy config clock. + +Example: + sata_phy: sata-phy@1b400000 { + compatible = "qcom,apq8064-sata-phy"; + reg = <0x1b400000 0x200>; + + clocks = <&gcc SATA_PHY_CFG_CLK>; + clock-names = "cfg"; + + #phy-cells = <0>; + }; From 74d64b59b49b322c8bbc73cdaba37348faf59582 Mon Sep 17 00:00:00 2001 From: Andrew Lunn Date: Thu, 10 Jul 2014 23:36:30 +0200 Subject: [PATCH 18/28] phy: Remove ARCH_KIRKWOOD dependency mach-kirkwood has been removed, now that kirkwood lives in mach-mvebu. Depend on MACH_KIRKWOOD, which will be set when these SoCs are built as part of mach-mvebv. Signed-off-by: Andrew Lunn Cc: Kishon Vijay Abraham I Signed-off-by: Kishon Vijay Abraham I --- drivers/phy/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/phy/Kconfig b/drivers/phy/Kconfig index 691115292a99..261ad18854c1 100644 --- a/drivers/phy/Kconfig +++ b/drivers/phy/Kconfig @@ -27,7 +27,7 @@ config PHY_EXYNOS_MIPI_VIDEO config PHY_MVEBU_SATA def_bool y - depends on ARCH_KIRKWOOD || ARCH_DOVE || MACH_DOVE || MACH_KIRKWOOD + depends on ARCH_DOVE || MACH_DOVE || MACH_KIRKWOOD depends on OF select GENERIC_PHY From 942a31b521911b0f162a37844c29f91022d129f0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Antoine=20T=C3=A9nart?= Date: Mon, 7 Jul 2014 12:16:07 +0200 Subject: [PATCH 19/28] phy: add a driver for the Berlin SATA PHY MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The Berlin SoC has a two SATA ports. Add a PHY driver to handle them. The mode selection can let us think this PHY can be configured to fit other purposes. But there are reasons to think the SATA mode will be the only one usable: the PHY registers are only accessible indirectly through two registers in the SATA range, the PHY seems to be integrated and no information tells us the contrary. For these reasons, make the driver a SATA PHY driver. Signed-off-by: Antoine Ténart Signed-off-by: Kishon Vijay Abraham I --- drivers/phy/Kconfig | 7 + drivers/phy/Makefile | 1 + drivers/phy/phy-berlin-sata.c | 284 ++++++++++++++++++++++++++++++++++ 3 files changed, 292 insertions(+) create mode 100644 drivers/phy/phy-berlin-sata.c diff --git a/drivers/phy/Kconfig b/drivers/phy/Kconfig index 261ad18854c1..1704fd4be99e 100644 --- a/drivers/phy/Kconfig +++ b/drivers/phy/Kconfig @@ -15,6 +15,13 @@ config GENERIC_PHY phy users can obtain reference to the PHY. All the users of this framework should select this config. +config PHY_BERLIN_SATA + tristate "Marvell Berlin SATA PHY driver" + depends on ARCH_BERLIN && HAS_IOMEM && OF + select GENERIC_PHY + help + Enable this to support the SATA PHY on Marvell Berlin SoCs. + config PHY_EXYNOS_MIPI_VIDEO tristate "S5P/EXYNOS SoC series MIPI CSI-2/DSI PHY driver" depends on HAS_IOMEM diff --git a/drivers/phy/Makefile b/drivers/phy/Makefile index a4819d378619..3c2ad5915be2 100644 --- a/drivers/phy/Makefile +++ b/drivers/phy/Makefile @@ -3,6 +3,7 @@ # obj-$(CONFIG_GENERIC_PHY) += phy-core.o +obj-$(CONFIG_PHY_BERLIN_SATA) += phy-berlin-sata.o obj-$(CONFIG_BCM_KONA_USB2_PHY) += phy-bcm-kona-usb2.o obj-$(CONFIG_PHY_EXYNOS_DP_VIDEO) += phy-exynos-dp-video.o obj-$(CONFIG_PHY_EXYNOS_MIPI_VIDEO) += phy-exynos-mipi-video.o diff --git a/drivers/phy/phy-berlin-sata.c b/drivers/phy/phy-berlin-sata.c new file mode 100644 index 000000000000..c5e688b0899f --- /dev/null +++ b/drivers/phy/phy-berlin-sata.c @@ -0,0 +1,284 @@ +/* + * Marvell Berlin SATA PHY driver + * + * Copyright (C) 2014 Marvell Technology Group Ltd. + * + * Antoine Ténart + * + * This file is licensed under the terms of the GNU General Public + * License version 2. This program is licensed "as is" without any + * warranty of any kind, whether express or implied. + */ + +#include +#include +#include +#include +#include + +#define HOST_VSA_ADDR 0x0 +#define HOST_VSA_DATA 0x4 +#define PORT_SCR_CTL 0x2c +#define PORT_VSR_ADDR 0x78 +#define PORT_VSR_DATA 0x7c + +#define CONTROL_REGISTER 0x0 +#define MBUS_SIZE_CONTROL 0x4 + +#define POWER_DOWN_PHY0 BIT(6) +#define POWER_DOWN_PHY1 BIT(14) +#define MBUS_WRITE_REQUEST_SIZE_128 (BIT(2) << 16) +#define MBUS_READ_REQUEST_SIZE_128 (BIT(2) << 19) + +#define PHY_BASE 0x200 + +/* register 0x01 */ +#define REF_FREF_SEL_25 BIT(0) +#define PHY_MODE_SATA (0x0 << 5) + +/* register 0x02 */ +#define USE_MAX_PLL_RATE BIT(12) + +/* register 0x23 */ +#define DATA_BIT_WIDTH_10 (0x0 << 10) +#define DATA_BIT_WIDTH_20 (0x1 << 10) +#define DATA_BIT_WIDTH_40 (0x2 << 10) + +/* register 0x25 */ +#define PHY_GEN_MAX_1_5 (0x0 << 10) +#define PHY_GEN_MAX_3_0 (0x1 << 10) +#define PHY_GEN_MAX_6_0 (0x2 << 10) + +struct phy_berlin_desc { + struct phy *phy; + u32 power_bit; + unsigned index; +}; + +struct phy_berlin_priv { + void __iomem *base; + spinlock_t lock; + struct clk *clk; + struct phy_berlin_desc **phys; + unsigned nphys; +}; + +static inline void phy_berlin_sata_reg_setbits(void __iomem *ctrl_reg, u32 reg, + u32 mask, u32 val) +{ + u32 regval; + + /* select register */ + writel(PHY_BASE + reg, ctrl_reg + PORT_VSR_ADDR); + + /* set bits */ + regval = readl(ctrl_reg + PORT_VSR_DATA); + regval &= ~mask; + regval |= val; + writel(regval, ctrl_reg + PORT_VSR_DATA); +} + +static int phy_berlin_sata_power_on(struct phy *phy) +{ + struct phy_berlin_desc *desc = phy_get_drvdata(phy); + struct phy_berlin_priv *priv = dev_get_drvdata(phy->dev.parent); + void __iomem *ctrl_reg = priv->base + 0x60 + (desc->index * 0x80); + int ret = 0; + u32 regval; + + clk_prepare_enable(priv->clk); + + spin_lock(&priv->lock); + + /* Power on PHY */ + writel(CONTROL_REGISTER, priv->base + HOST_VSA_ADDR); + regval = readl(priv->base + HOST_VSA_DATA); + regval &= ~desc->power_bit; + writel(regval, priv->base + HOST_VSA_DATA); + + /* Configure MBus */ + writel(MBUS_SIZE_CONTROL, priv->base + HOST_VSA_ADDR); + regval = readl(priv->base + HOST_VSA_DATA); + regval |= MBUS_WRITE_REQUEST_SIZE_128 | MBUS_READ_REQUEST_SIZE_128; + writel(regval, priv->base + HOST_VSA_DATA); + + /* set PHY mode and ref freq to 25 MHz */ + phy_berlin_sata_reg_setbits(ctrl_reg, 0x1, 0xff, + REF_FREF_SEL_25 | PHY_MODE_SATA); + + /* set PHY up to 6 Gbps */ + phy_berlin_sata_reg_setbits(ctrl_reg, 0x25, 0xc00, PHY_GEN_MAX_6_0); + + /* set 40 bits width */ + phy_berlin_sata_reg_setbits(ctrl_reg, 0x23, 0xc00, DATA_BIT_WIDTH_40); + + /* use max pll rate */ + phy_berlin_sata_reg_setbits(ctrl_reg, 0x2, 0x0, USE_MAX_PLL_RATE); + + /* set Gen3 controller speed */ + regval = readl(ctrl_reg + PORT_SCR_CTL); + regval &= ~GENMASK(7, 4); + regval |= 0x30; + writel(regval, ctrl_reg + PORT_SCR_CTL); + + spin_unlock(&priv->lock); + + clk_disable_unprepare(priv->clk); + + return ret; +} + +static int phy_berlin_sata_power_off(struct phy *phy) +{ + struct phy_berlin_desc *desc = phy_get_drvdata(phy); + struct phy_berlin_priv *priv = dev_get_drvdata(phy->dev.parent); + u32 regval; + + clk_prepare_enable(priv->clk); + + spin_lock(&priv->lock); + + /* Power down PHY */ + writel(CONTROL_REGISTER, priv->base + HOST_VSA_ADDR); + regval = readl(priv->base + HOST_VSA_DATA); + regval |= desc->power_bit; + writel(regval, priv->base + HOST_VSA_DATA); + + spin_unlock(&priv->lock); + + clk_disable_unprepare(priv->clk); + + return 0; +} + +static struct phy *phy_berlin_sata_phy_xlate(struct device *dev, + struct of_phandle_args *args) +{ + struct phy_berlin_priv *priv = dev_get_drvdata(dev); + int i; + + if (WARN_ON(args->args[0] >= priv->nphys)) + return ERR_PTR(-ENODEV); + + for (i = 0; i < priv->nphys; i++) { + if (priv->phys[i]->index == args->args[0]) + break; + } + + if (i == priv->nphys) + return ERR_PTR(-ENODEV); + + return priv->phys[i]->phy; +} + +static struct phy_ops phy_berlin_sata_ops = { + .power_on = phy_berlin_sata_power_on, + .power_off = phy_berlin_sata_power_off, + .owner = THIS_MODULE, +}; + +static u32 phy_berlin_power_down_bits[] = { + POWER_DOWN_PHY0, + POWER_DOWN_PHY1, +}; + +static int phy_berlin_sata_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct device_node *child; + struct phy *phy; + struct phy_provider *phy_provider; + struct phy_berlin_priv *priv; + struct resource *res; + int i = 0; + u32 phy_id; + + priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) + return -EINVAL; + + priv->base = devm_ioremap(dev, res->start, resource_size(res)); + if (!priv->base) + return -ENOMEM; + + priv->clk = devm_clk_get(dev, NULL); + if (IS_ERR(priv->clk)) + return PTR_ERR(priv->clk); + + priv->nphys = of_get_child_count(dev->of_node); + if (priv->nphys == 0) + return -ENODEV; + + priv->phys = devm_kzalloc(dev, priv->nphys * sizeof(*priv->phys), + GFP_KERNEL); + if (!priv->phys) + return -ENOMEM; + + dev_set_drvdata(dev, priv); + spin_lock_init(&priv->lock); + + for_each_available_child_of_node(dev->of_node, child) { + struct phy_berlin_desc *phy_desc; + + if (of_property_read_u32(child, "reg", &phy_id)) { + dev_err(dev, "missing reg property in node %s\n", + child->name); + return -EINVAL; + } + + if (phy_id >= ARRAY_SIZE(phy_berlin_power_down_bits)) { + dev_err(dev, "invalid reg in node %s\n", child->name); + return -EINVAL; + } + + phy_desc = devm_kzalloc(dev, sizeof(*phy_desc), GFP_KERNEL); + if (!phy_desc) + return -ENOMEM; + + phy = devm_phy_create(dev, &phy_berlin_sata_ops, NULL); + if (IS_ERR(phy)) { + dev_err(dev, "failed to create PHY %d\n", phy_id); + return PTR_ERR(phy); + } + + phy_desc->phy = phy; + phy_desc->power_bit = phy_berlin_power_down_bits[phy_id]; + phy_desc->index = phy_id; + phy_set_drvdata(phy, phy_desc); + + priv->phys[i++] = phy_desc; + + /* Make sure the PHY is off */ + phy_berlin_sata_power_off(phy); + } + + phy_provider = + devm_of_phy_provider_register(dev, phy_berlin_sata_phy_xlate); + if (IS_ERR(phy_provider)) + return PTR_ERR(phy_provider); + + return 0; +} + +static const struct of_device_id phy_berlin_sata_of_match[] = { + { .compatible = "marvell,berlin2q-sata-phy" }, + { }, +}; + +static struct platform_driver phy_berlin_sata_driver = { + .probe = phy_berlin_sata_probe, + .driver = { + .name = "phy-berlin-sata", + .owner = THIS_MODULE, + .of_match_table = phy_berlin_sata_of_match, + }, +}; +module_platform_driver(phy_berlin_sata_driver); + +MODULE_DESCRIPTION("Marvell Berlin SATA PHY driver"); +MODULE_AUTHOR("Antoine Ténart "); +MODULE_LICENSE("GPL v2"); From 6e58240fae556c23150bb0c7cb9fdba17e6c14cf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Antoine=20T=C3=A9nart?= Date: Mon, 7 Jul 2014 12:16:08 +0200 Subject: [PATCH 20/28] Documentation: bindings: add the Berlin SATA PHY MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The Berlin SATA PHY drives the PHY related to the SATA interface. Add the corresponding documentation. Signed-off-by: Antoine Ténart Signed-off-by: Kishon Vijay Abraham I --- .../bindings/phy/berlin-sata-phy.txt | 34 +++++++++++++++++++ 1 file changed, 34 insertions(+) create mode 100644 Documentation/devicetree/bindings/phy/berlin-sata-phy.txt diff --git a/Documentation/devicetree/bindings/phy/berlin-sata-phy.txt b/Documentation/devicetree/bindings/phy/berlin-sata-phy.txt new file mode 100644 index 000000000000..88f8c23384c0 --- /dev/null +++ b/Documentation/devicetree/bindings/phy/berlin-sata-phy.txt @@ -0,0 +1,34 @@ +Berlin SATA PHY +--------------- + +Required properties: +- compatible: should be "marvell,berlin2q-sata-phy" +- address-cells: should be 1 +- size-cells: should be 0 +- phy-cells: from the generic PHY bindings, must be 1 +- reg: address and length of the register +- clocks: reference to the clock entry + +Sub-nodes: +Each PHY should be represented as a sub-node. + +Sub-nodes required properties: +- reg: the PHY number + +Example: + sata_phy: phy@f7e900a0 { + compatible = "marvell,berlin2q-sata-phy"; + reg = <0xf7e900a0 0x200>; + clocks = <&chip CLKID_SATA>; + #address-cells = <1>; + #size-cells = <0>; + #phy-cells = <1>; + + sata-phy@0 { + reg = <0>; + }; + + sata-phy@1 { + reg = <1>; + }; + }; From 2a4c37016ca96e413cd352985d3a0db8cfb7716c Mon Sep 17 00:00:00 2001 From: Kishon Vijay Abraham I Date: Mon, 14 Jul 2014 15:55:01 +0530 Subject: [PATCH 21/28] phy: core: Fix of_phy_provider_lookup to return PHY provider for sub node Fixed of_phy_provider_lookup to return 'phy_provider' if _of_phy_get passes the node pointer of the sub-node of phy provider node. This is needed when phy provider implements multiple PHYs and each PHY is modelled as the sub-node of PHY provider device node. Signed-off-by: Kishon Vijay Abraham I Acked-by: Lee Jones --- drivers/phy/phy-core.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/drivers/phy/phy-core.c b/drivers/phy/phy-core.c index 75c97396dbfa..527e744a3809 100644 --- a/drivers/phy/phy-core.c +++ b/drivers/phy/phy-core.c @@ -87,10 +87,15 @@ static struct phy *phy_lookup(struct device *device, const char *port) static struct phy_provider *of_phy_provider_lookup(struct device_node *node) { struct phy_provider *phy_provider; + struct device_node *child; list_for_each_entry(phy_provider, &phy_provider_list, list) { if (phy_provider->dev->of_node == node) return phy_provider; + + for_each_child_of_node(phy_provider->dev->of_node, child) + if (child == node) + return phy_provider; } return ERR_PTR(-EPROBE_DEFER); From f0ed817638b59aa927f1f7e9564dd8796b18dc4f Mon Sep 17 00:00:00 2001 From: Kishon Vijay Abraham I Date: Mon, 14 Jul 2014 15:55:02 +0530 Subject: [PATCH 22/28] phy: core: Let node ptr of PHY point to PHY and not of PHY provider In case of multi-phy PHY providers, each PHY should be modeled as a sub node of the PHY provider. Then each PHY will have a different node pointer (node pointer of sub node) than that of PHY provider. Added this provision in the PHY core. Also fixed all drivers to use the updated API. Signed-off-by: Kishon Vijay Abraham I Acked-by: Lee Jones --- Documentation/phy.txt | 10 ++++++---- drivers/phy/phy-bcm-kona-usb2.c | 2 +- drivers/phy/phy-berlin-sata.c | 2 +- drivers/phy/phy-core.c | 25 ++++++++++++++++++------- drivers/phy/phy-exynos-dp-video.c | 2 +- drivers/phy/phy-exynos-mipi-video.c | 2 +- drivers/phy/phy-exynos5-usbdrd.c | 3 ++- drivers/phy/phy-exynos5250-sata.c | 2 +- drivers/phy/phy-hix5hd2-sata.c | 2 +- drivers/phy/phy-mvebu-sata.c | 2 +- drivers/phy/phy-omap-usb2.c | 2 +- drivers/phy/phy-qcom-apq8064-sata.c | 3 ++- drivers/phy/phy-samsung-usb2.c | 3 ++- drivers/phy/phy-sun4i-usb.c | 2 +- drivers/phy/phy-ti-pipe3.c | 2 +- drivers/phy/phy-twl4030-usb.c | 2 +- drivers/phy/phy-xgene.c | 2 +- include/linux/phy/phy.h | 15 ++++++++++----- 18 files changed, 52 insertions(+), 31 deletions(-) diff --git a/Documentation/phy.txt b/Documentation/phy.txt index ebff6ee52441..c6594af94d25 100644 --- a/Documentation/phy.txt +++ b/Documentation/phy.txt @@ -53,10 +53,12 @@ unregister the PHY. The PHY driver should create the PHY in order for other peripheral controllers to make use of it. The PHY framework provides 2 APIs to create the PHY. -struct phy *phy_create(struct device *dev, const struct phy_ops *ops, - struct phy_init_data *init_data); -struct phy *devm_phy_create(struct device *dev, const struct phy_ops *ops, - struct phy_init_data *init_data); +struct phy *phy_create(struct device *dev, struct device_node *node, + const struct phy_ops *ops, + struct phy_init_data *init_data); +struct phy *devm_phy_create(struct device *dev, struct device_node *node, + const struct phy_ops *ops, + struct phy_init_data *init_data); The PHY drivers can use one of the above 2 APIs to create the PHY by passing the device pointer, phy ops and init_data. diff --git a/drivers/phy/phy-bcm-kona-usb2.c b/drivers/phy/phy-bcm-kona-usb2.c index e94f5a6a5645..894fe74c1e44 100644 --- a/drivers/phy/phy-bcm-kona-usb2.c +++ b/drivers/phy/phy-bcm-kona-usb2.c @@ -117,7 +117,7 @@ static int bcm_kona_usb2_probe(struct platform_device *pdev) platform_set_drvdata(pdev, phy); - gphy = devm_phy_create(dev, &ops, NULL); + gphy = devm_phy_create(dev, NULL, &ops, NULL); if (IS_ERR(gphy)) return PTR_ERR(gphy); diff --git a/drivers/phy/phy-berlin-sata.c b/drivers/phy/phy-berlin-sata.c index c5e688b0899f..5c3a0424aeb4 100644 --- a/drivers/phy/phy-berlin-sata.c +++ b/drivers/phy/phy-berlin-sata.c @@ -239,7 +239,7 @@ static int phy_berlin_sata_probe(struct platform_device *pdev) if (!phy_desc) return -ENOMEM; - phy = devm_phy_create(dev, &phy_berlin_sata_ops, NULL); + phy = devm_phy_create(dev, NULL, &phy_berlin_sata_ops, NULL); if (IS_ERR(phy)) { dev_err(dev, "failed to create PHY %d\n", phy_id); return PTR_ERR(phy); diff --git a/drivers/phy/phy-core.c b/drivers/phy/phy-core.c index 527e744a3809..ff5eec5af817 100644 --- a/drivers/phy/phy-core.c +++ b/drivers/phy/phy-core.c @@ -415,13 +415,20 @@ struct phy *of_phy_simple_xlate(struct device *dev, struct of_phandle_args struct phy *phy; struct class_dev_iter iter; struct device_node *node = dev->of_node; + struct device_node *child; class_dev_iter_init(&iter, phy_class, NULL, NULL); while ((dev = class_dev_iter_next(&iter))) { phy = to_phy(dev); - if (node != phy->dev.of_node) + if (node != phy->dev.of_node) { + for_each_child_of_node(node, child) { + if (child == phy->dev.of_node) + goto phy_found; + } continue; + } +phy_found: class_dev_iter_exit(&iter); return phy; } @@ -579,13 +586,15 @@ EXPORT_SYMBOL_GPL(devm_of_phy_get); /** * phy_create() - create a new phy * @dev: device that is creating the new phy + * @node: device node of the phy * @ops: function pointers for performing phy operations * @init_data: contains the list of PHY consumers or NULL * * Called to create a phy using phy framework. */ -struct phy *phy_create(struct device *dev, const struct phy_ops *ops, - struct phy_init_data *init_data) +struct phy *phy_create(struct device *dev, struct device_node *node, + const struct phy_ops *ops, + struct phy_init_data *init_data) { int ret; int id; @@ -620,7 +629,7 @@ struct phy *phy_create(struct device *dev, const struct phy_ops *ops, phy->dev.class = phy_class; phy->dev.parent = dev; - phy->dev.of_node = dev->of_node; + phy->dev.of_node = node ?: dev->of_node; phy->id = id; phy->ops = ops; phy->init_data = init_data; @@ -656,6 +665,7 @@ EXPORT_SYMBOL_GPL(phy_create); /** * devm_phy_create() - create a new phy * @dev: device that is creating the new phy + * @node: device node of the phy * @ops: function pointers for performing phy operations * @init_data: contains the list of PHY consumers or NULL * @@ -664,8 +674,9 @@ EXPORT_SYMBOL_GPL(phy_create); * On driver detach, release function is invoked on the devres data, * then, devres data is freed. */ -struct phy *devm_phy_create(struct device *dev, const struct phy_ops *ops, - struct phy_init_data *init_data) +struct phy *devm_phy_create(struct device *dev, struct device_node *node, + const struct phy_ops *ops, + struct phy_init_data *init_data) { struct phy **ptr, *phy; @@ -673,7 +684,7 @@ struct phy *devm_phy_create(struct device *dev, const struct phy_ops *ops, if (!ptr) return ERR_PTR(-ENOMEM); - phy = phy_create(dev, ops, init_data); + phy = phy_create(dev, node, ops, init_data); if (!IS_ERR(phy)) { *ptr = phy; devres_add(dev, ptr); diff --git a/drivers/phy/phy-exynos-dp-video.c b/drivers/phy/phy-exynos-dp-video.c index 098f822a2fa4..8b3026e2af7f 100644 --- a/drivers/phy/phy-exynos-dp-video.c +++ b/drivers/phy/phy-exynos-dp-video.c @@ -77,7 +77,7 @@ static int exynos_dp_video_phy_probe(struct platform_device *pdev) if (IS_ERR(state->regs)) return PTR_ERR(state->regs); - phy = devm_phy_create(dev, &exynos_dp_video_phy_ops, NULL); + phy = devm_phy_create(dev, NULL, &exynos_dp_video_phy_ops, NULL); if (IS_ERR(phy)) { dev_err(dev, "failed to create Display Port PHY\n"); return PTR_ERR(phy); diff --git a/drivers/phy/phy-exynos-mipi-video.c b/drivers/phy/phy-exynos-mipi-video.c index 6d6bcf52a10e..b55a92e12496 100644 --- a/drivers/phy/phy-exynos-mipi-video.c +++ b/drivers/phy/phy-exynos-mipi-video.c @@ -136,7 +136,7 @@ static int exynos_mipi_video_phy_probe(struct platform_device *pdev) spin_lock_init(&state->slock); for (i = 0; i < EXYNOS_MIPI_PHYS_NUM; i++) { - struct phy *phy = devm_phy_create(dev, + struct phy *phy = devm_phy_create(dev, NULL, &exynos_mipi_video_phy_ops, NULL); if (IS_ERR(phy)) { dev_err(dev, "failed to create PHY %d\n", i); diff --git a/drivers/phy/phy-exynos5-usbdrd.c b/drivers/phy/phy-exynos5-usbdrd.c index 205159db37a3..b05302b09c9f 100644 --- a/drivers/phy/phy-exynos5-usbdrd.c +++ b/drivers/phy/phy-exynos5-usbdrd.c @@ -635,7 +635,8 @@ static int exynos5_usbdrd_phy_probe(struct platform_device *pdev) dev_vdbg(dev, "Creating usbdrd_phy phy\n"); for (i = 0; i < EXYNOS5_DRDPHYS_NUM; i++) { - struct phy *phy = devm_phy_create(dev, &exynos5_usbdrd_phy_ops, + struct phy *phy = devm_phy_create(dev, NULL, + &exynos5_usbdrd_phy_ops, NULL); if (IS_ERR(phy)) { dev_err(dev, "Failed to create usbdrd_phy phy\n"); diff --git a/drivers/phy/phy-exynos5250-sata.c b/drivers/phy/phy-exynos5250-sata.c index 05689450f93b..19a679aca4ac 100644 --- a/drivers/phy/phy-exynos5250-sata.c +++ b/drivers/phy/phy-exynos5250-sata.c @@ -210,7 +210,7 @@ static int exynos_sata_phy_probe(struct platform_device *pdev) return ret; } - sata_phy->phy = devm_phy_create(dev, &exynos_sata_phy_ops, NULL); + sata_phy->phy = devm_phy_create(dev, NULL, &exynos_sata_phy_ops, NULL); if (IS_ERR(sata_phy->phy)) { clk_disable_unprepare(sata_phy->phyclk); dev_err(dev, "failed to create PHY\n"); diff --git a/drivers/phy/phy-hix5hd2-sata.c b/drivers/phy/phy-hix5hd2-sata.c index d44283453d71..6a08fa5f81eb 100644 --- a/drivers/phy/phy-hix5hd2-sata.c +++ b/drivers/phy/phy-hix5hd2-sata.c @@ -156,7 +156,7 @@ static int hix5hd2_sata_phy_probe(struct platform_device *pdev) if (IS_ERR(priv->peri_ctrl)) priv->peri_ctrl = NULL; - phy = devm_phy_create(dev, &hix5hd2_sata_phy_ops, NULL); + phy = devm_phy_create(dev, NULL, &hix5hd2_sata_phy_ops, NULL); if (IS_ERR(phy)) { dev_err(dev, "failed to create PHY\n"); return PTR_ERR(phy); diff --git a/drivers/phy/phy-mvebu-sata.c b/drivers/phy/phy-mvebu-sata.c index d70ecd6a1b3f..cc3c0e166daf 100644 --- a/drivers/phy/phy-mvebu-sata.c +++ b/drivers/phy/phy-mvebu-sata.c @@ -99,7 +99,7 @@ static int phy_mvebu_sata_probe(struct platform_device *pdev) if (IS_ERR(priv->clk)) return PTR_ERR(priv->clk); - phy = devm_phy_create(&pdev->dev, &phy_mvebu_sata_ops, NULL); + phy = devm_phy_create(&pdev->dev, NULL, &phy_mvebu_sata_ops, NULL); if (IS_ERR(phy)) return PTR_ERR(phy); diff --git a/drivers/phy/phy-omap-usb2.c b/drivers/phy/phy-omap-usb2.c index 34b396146c8a..93d78359246c 100644 --- a/drivers/phy/phy-omap-usb2.c +++ b/drivers/phy/phy-omap-usb2.c @@ -263,7 +263,7 @@ static int omap_usb2_probe(struct platform_device *pdev) platform_set_drvdata(pdev, phy); - generic_phy = devm_phy_create(phy->dev, &ops, NULL); + generic_phy = devm_phy_create(phy->dev, NULL, &ops, NULL); if (IS_ERR(generic_phy)) return PTR_ERR(generic_phy); diff --git a/drivers/phy/phy-qcom-apq8064-sata.c b/drivers/phy/phy-qcom-apq8064-sata.c index c9b4dd6becf5..d7c01aa1f8d7 100644 --- a/drivers/phy/phy-qcom-apq8064-sata.c +++ b/drivers/phy/phy-qcom-apq8064-sata.c @@ -228,7 +228,8 @@ static int qcom_apq8064_sata_phy_probe(struct platform_device *pdev) if (IS_ERR(phy->mmio)) return PTR_ERR(phy->mmio); - generic_phy = devm_phy_create(dev, &qcom_apq8064_sata_phy_ops, NULL); + generic_phy = devm_phy_create(dev, NULL, &qcom_apq8064_sata_phy_ops, + NULL); if (IS_ERR(generic_phy)) { dev_err(dev, "%s: failed to create phy\n", __func__); return PTR_ERR(generic_phy); diff --git a/drivers/phy/phy-samsung-usb2.c b/drivers/phy/phy-samsung-usb2.c index 16aae7a285f0..ae30640a411d 100644 --- a/drivers/phy/phy-samsung-usb2.c +++ b/drivers/phy/phy-samsung-usb2.c @@ -196,7 +196,8 @@ static int samsung_usb2_phy_probe(struct platform_device *pdev) struct samsung_usb2_phy_instance *p = &drv->instances[i]; dev_dbg(dev, "Creating phy \"%s\"\n", label); - p->phy = devm_phy_create(dev, &samsung_usb2_phy_ops, NULL); + p->phy = devm_phy_create(dev, NULL, &samsung_usb2_phy_ops, + NULL); if (IS_ERR(p->phy)) { dev_err(drv->dev, "Failed to create usb2_phy \"%s\"\n", label); diff --git a/drivers/phy/phy-sun4i-usb.c b/drivers/phy/phy-sun4i-usb.c index 7a4ea552f621..61ebea49709b 100644 --- a/drivers/phy/phy-sun4i-usb.c +++ b/drivers/phy/phy-sun4i-usb.c @@ -295,7 +295,7 @@ static int sun4i_usb_phy_probe(struct platform_device *pdev) return PTR_ERR(phy->pmu); } - phy->phy = devm_phy_create(dev, &sun4i_usb_phy_ops, NULL); + phy->phy = devm_phy_create(dev, NULL, &sun4i_usb_phy_ops, NULL); if (IS_ERR(phy->phy)) { dev_err(dev, "failed to create PHY %d\n", i); return PTR_ERR(phy->phy); diff --git a/drivers/phy/phy-ti-pipe3.c b/drivers/phy/phy-ti-pipe3.c index 93bcd67f1b22..b964aa967b46 100644 --- a/drivers/phy/phy-ti-pipe3.c +++ b/drivers/phy/phy-ti-pipe3.c @@ -400,7 +400,7 @@ static int ti_pipe3_probe(struct platform_device *pdev) platform_set_drvdata(pdev, phy); pm_runtime_enable(phy->dev); - generic_phy = devm_phy_create(phy->dev, &ops, NULL); + generic_phy = devm_phy_create(phy->dev, NULL, &ops, NULL); if (IS_ERR(generic_phy)) return PTR_ERR(generic_phy); diff --git a/drivers/phy/phy-twl4030-usb.c b/drivers/phy/phy-twl4030-usb.c index 2e0e9b3774c8..e1a6623d4696 100644 --- a/drivers/phy/phy-twl4030-usb.c +++ b/drivers/phy/phy-twl4030-usb.c @@ -695,7 +695,7 @@ static int twl4030_usb_probe(struct platform_device *pdev) otg->set_host = twl4030_set_host; otg->set_peripheral = twl4030_set_peripheral; - phy = devm_phy_create(twl->dev, &ops, init_data); + phy = devm_phy_create(twl->dev, NULL, &ops, init_data); if (IS_ERR(phy)) { dev_dbg(&pdev->dev, "Failed to create PHY\n"); return PTR_ERR(phy); diff --git a/drivers/phy/phy-xgene.c b/drivers/phy/phy-xgene.c index 4aa1ccd1511f..db809b97219e 100644 --- a/drivers/phy/phy-xgene.c +++ b/drivers/phy/phy-xgene.c @@ -1707,7 +1707,7 @@ static int xgene_phy_probe(struct platform_device *pdev) ctx->dev = &pdev->dev; platform_set_drvdata(pdev, ctx); - ctx->phy = devm_phy_create(ctx->dev, &xgene_phy_ops, NULL); + ctx->phy = devm_phy_create(ctx->dev, NULL, &xgene_phy_ops, NULL); if (IS_ERR(ctx->phy)) { dev_dbg(&pdev->dev, "Failed to create PHY\n"); rc = PTR_ERR(ctx->phy); diff --git a/include/linux/phy/phy.h b/include/linux/phy/phy.h index 9a8694524742..8cb6f815475b 100644 --- a/include/linux/phy/phy.h +++ b/include/linux/phy/phy.h @@ -158,9 +158,10 @@ void devm_phy_put(struct device *dev, struct phy *phy); struct phy *of_phy_get(struct device_node *np, const char *con_id); struct phy *of_phy_simple_xlate(struct device *dev, struct of_phandle_args *args); -struct phy *phy_create(struct device *dev, const struct phy_ops *ops, - struct phy_init_data *init_data); -struct phy *devm_phy_create(struct device *dev, +struct phy *phy_create(struct device *dev, struct device_node *node, + const struct phy_ops *ops, + struct phy_init_data *init_data); +struct phy *devm_phy_create(struct device *dev, struct device_node *node, const struct phy_ops *ops, struct phy_init_data *init_data); void phy_destroy(struct phy *phy); void devm_phy_destroy(struct device *dev, struct phy *phy); @@ -299,13 +300,17 @@ static inline struct phy *of_phy_simple_xlate(struct device *dev, } static inline struct phy *phy_create(struct device *dev, - const struct phy_ops *ops, struct phy_init_data *init_data) + struct device_node *node, + const struct phy_ops *ops, + struct phy_init_data *init_data) { return ERR_PTR(-ENOSYS); } static inline struct phy *devm_phy_create(struct device *dev, - const struct phy_ops *ops, struct phy_init_data *init_data) + struct device_node *node, + const struct phy_ops *ops, + struct phy_init_data *init_data) { return ERR_PTR(-ENOSYS); } From 175f02ebdfdf8431dbf607c04fe5caf667ba8e6c Mon Sep 17 00:00:00 2001 From: Srinivas Kandagatla Date: Wed, 16 Jul 2014 09:18:43 +0100 Subject: [PATCH 23/28] phy: qcom-apq8064: fix possible timeout without check This patch fixes a possible timeout in poll loop without actually checking the register before return. In theory the there is a possibility of loop being scheduled after a long lock/delay, which would then force the loop to exit without actually checking the register. Reported-by: Bartlomiej Zolnierkiewicz Signed-off-by: Srinivas Kandagatla Signed-off-by: Kishon Vijay Abraham I --- drivers/phy/phy-qcom-apq8064-sata.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/phy/phy-qcom-apq8064-sata.c b/drivers/phy/phy-qcom-apq8064-sata.c index d7c01aa1f8d7..b3ef7d805765 100644 --- a/drivers/phy/phy-qcom-apq8064-sata.c +++ b/drivers/phy/phy-qcom-apq8064-sata.c @@ -91,7 +91,7 @@ static int read_poll_timeout(void __iomem *addr, u32 mask) usleep_range(DELAY_INTERVAL_US, DELAY_INTERVAL_US + 50); } while (!time_after(jiffies, timeout)); - return -ETIMEDOUT; + return (readl_relaxed(addr) & mask) ? 0 : -ETIMEDOUT; } static int qcom_apq8064_sata_phy_init(struct phy *generic_phy) From 4f6160d4089ec0e39e33a197138413bd0701ce21 Mon Sep 17 00:00:00 2001 From: Kumar Gala Date: Wed, 16 Jul 2014 11:10:08 -0500 Subject: [PATCH 24/28] phy: qcom: Add driver for QCOM IPQ806x SATA PHY Add a PHY driver for uses with AHCI based SATA controller driver on the IPQ806x family of SoCs. Signed-off-by: Kumar Gala Signed-off-by: Kishon Vijay Abraham I --- drivers/phy/Kconfig | 7 + drivers/phy/Makefile | 1 + drivers/phy/phy-qcom-ipq806x-sata.c | 211 ++++++++++++++++++++++++++++ 3 files changed, 219 insertions(+) create mode 100644 drivers/phy/phy-qcom-ipq806x-sata.c diff --git a/drivers/phy/Kconfig b/drivers/phy/Kconfig index 1704fd4be99e..3e251aa64ffd 100644 --- a/drivers/phy/Kconfig +++ b/drivers/phy/Kconfig @@ -191,4 +191,11 @@ config PHY_QCOM_APQ8064_SATA depends on OF select GENERIC_PHY +config PHY_QCOM_IPQ806X_SATA + tristate "Qualcomm IPQ806x SATA SerDes/PHY driver" + depends on ARCH_QCOM + depends on HAS_IOMEM + depends on OF + select GENERIC_PHY + endmenu diff --git a/drivers/phy/Makefile b/drivers/phy/Makefile index 3c2ad5915be2..54ab9785932c 100644 --- a/drivers/phy/Makefile +++ b/drivers/phy/Makefile @@ -23,3 +23,4 @@ phy-exynos-usb2-$(CONFIG_PHY_EXYNOS5250_USB2) += phy-exynos5250-usb2.o obj-$(CONFIG_PHY_EXYNOS5_USBDRD) += phy-exynos5-usbdrd.o obj-$(CONFIG_PHY_XGENE) += phy-xgene.o obj-$(CONFIG_PHY_QCOM_APQ8064_SATA) += phy-qcom-apq8064-sata.o +obj-$(CONFIG_PHY_QCOM_IPQ806X_SATA) += phy-qcom-ipq806x-sata.o diff --git a/drivers/phy/phy-qcom-ipq806x-sata.c b/drivers/phy/phy-qcom-ipq806x-sata.c new file mode 100644 index 000000000000..909b5a87fc6a --- /dev/null +++ b/drivers/phy/phy-qcom-ipq806x-sata.c @@ -0,0 +1,211 @@ +/* + * Copyright (c) 2014, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +struct qcom_ipq806x_sata_phy { + void __iomem *mmio; + struct clk *cfg_clk; + struct device *dev; +}; + +#define __set(v, a, b) (((v) << (b)) & GENMASK(a, b)) + +#define SATA_PHY_P0_PARAM0 0x200 +#define SATA_PHY_P0_PARAM0_P0_TX_PREEMPH_GEN3(x) __set(x, 17, 12) +#define SATA_PHY_P0_PARAM0_P0_TX_PREEMPH_GEN3_MASK GENMASK(17, 12) +#define SATA_PHY_P0_PARAM0_P0_TX_PREEMPH_GEN2(x) __set(x, 11, 6) +#define SATA_PHY_P0_PARAM0_P0_TX_PREEMPH_GEN2_MASK GENMASK(11, 6) +#define SATA_PHY_P0_PARAM0_P0_TX_PREEMPH_GEN1(x) __set(x, 5, 0) +#define SATA_PHY_P0_PARAM0_P0_TX_PREEMPH_GEN1_MASK GENMASK(5, 0) + +#define SATA_PHY_P0_PARAM1 0x204 +#define SATA_PHY_P0_PARAM1_RESERVED_BITS31_21(x) __set(x, 31, 21) +#define SATA_PHY_P0_PARAM1_P0_TX_AMPLITUDE_GEN3(x) __set(x, 20, 14) +#define SATA_PHY_P0_PARAM1_P0_TX_AMPLITUDE_GEN3_MASK GENMASK(20, 14) +#define SATA_PHY_P0_PARAM1_P0_TX_AMPLITUDE_GEN2(x) __set(x, 13, 7) +#define SATA_PHY_P0_PARAM1_P0_TX_AMPLITUDE_GEN2_MASK GENMASK(13, 7) +#define SATA_PHY_P0_PARAM1_P0_TX_AMPLITUDE_GEN1(x) __set(x, 6, 0) +#define SATA_PHY_P0_PARAM1_P0_TX_AMPLITUDE_GEN1_MASK GENMASK(6, 0) + +#define SATA_PHY_P0_PARAM2 0x208 +#define SATA_PHY_P0_PARAM2_RX_EQ(x) __set(x, 20, 18) +#define SATA_PHY_P0_PARAM2_RX_EQ_MASK GENMASK(20, 18) + +#define SATA_PHY_P0_PARAM3 0x20C +#define SATA_PHY_SSC_EN 0x8 +#define SATA_PHY_P0_PARAM4 0x210 +#define SATA_PHY_REF_SSP_EN 0x2 +#define SATA_PHY_RESET 0x1 + +static int qcom_ipq806x_sata_phy_init(struct phy *generic_phy) +{ + struct qcom_ipq806x_sata_phy *phy = phy_get_drvdata(generic_phy); + u32 reg; + + /* Setting SSC_EN to 1 */ + reg = readl_relaxed(phy->mmio + SATA_PHY_P0_PARAM3); + reg = reg | SATA_PHY_SSC_EN; + writel_relaxed(reg, phy->mmio + SATA_PHY_P0_PARAM3); + + reg = readl_relaxed(phy->mmio + SATA_PHY_P0_PARAM0) & + ~(SATA_PHY_P0_PARAM0_P0_TX_PREEMPH_GEN3_MASK | + SATA_PHY_P0_PARAM0_P0_TX_PREEMPH_GEN2_MASK | + SATA_PHY_P0_PARAM0_P0_TX_PREEMPH_GEN1_MASK); + reg |= SATA_PHY_P0_PARAM0_P0_TX_PREEMPH_GEN3(0xf); + writel_relaxed(reg, phy->mmio + SATA_PHY_P0_PARAM0); + + reg = readl_relaxed(phy->mmio + SATA_PHY_P0_PARAM1) & + ~(SATA_PHY_P0_PARAM1_P0_TX_AMPLITUDE_GEN3_MASK | + SATA_PHY_P0_PARAM1_P0_TX_AMPLITUDE_GEN2_MASK | + SATA_PHY_P0_PARAM1_P0_TX_AMPLITUDE_GEN1_MASK); + reg |= SATA_PHY_P0_PARAM1_P0_TX_AMPLITUDE_GEN3(0x55) | + SATA_PHY_P0_PARAM1_P0_TX_AMPLITUDE_GEN2(0x55) | + SATA_PHY_P0_PARAM1_P0_TX_AMPLITUDE_GEN1(0x55); + writel_relaxed(reg, phy->mmio + SATA_PHY_P0_PARAM1); + + reg = readl_relaxed(phy->mmio + SATA_PHY_P0_PARAM2) & + ~SATA_PHY_P0_PARAM2_RX_EQ_MASK; + reg |= SATA_PHY_P0_PARAM2_RX_EQ(0x3); + writel_relaxed(reg, phy->mmio + SATA_PHY_P0_PARAM2); + + /* Setting PHY_RESET to 1 */ + reg = readl_relaxed(phy->mmio + SATA_PHY_P0_PARAM4); + reg = reg | SATA_PHY_RESET; + writel_relaxed(reg, phy->mmio + SATA_PHY_P0_PARAM4); + + /* Setting REF_SSP_EN to 1 */ + reg = readl_relaxed(phy->mmio + SATA_PHY_P0_PARAM4); + reg = reg | SATA_PHY_REF_SSP_EN | SATA_PHY_RESET; + writel_relaxed(reg, phy->mmio + SATA_PHY_P0_PARAM4); + + /* make sure all changes complete before we let the PHY out of reset */ + mb(); + + /* sleep for max. 50us more to combine processor wakeups */ + usleep_range(20, 20 + 50); + + /* Clearing PHY_RESET to 0 */ + reg = readl_relaxed(phy->mmio + SATA_PHY_P0_PARAM4); + reg = reg & ~SATA_PHY_RESET; + writel_relaxed(reg, phy->mmio + SATA_PHY_P0_PARAM4); + + return 0; +} + +static int qcom_ipq806x_sata_phy_exit(struct phy *generic_phy) +{ + struct qcom_ipq806x_sata_phy *phy = phy_get_drvdata(generic_phy); + u32 reg; + + /* Setting PHY_RESET to 1 */ + reg = readl_relaxed(phy->mmio + SATA_PHY_P0_PARAM4); + reg = reg | SATA_PHY_RESET; + writel_relaxed(reg, phy->mmio + SATA_PHY_P0_PARAM4); + + return 0; +} + +static struct phy_ops qcom_ipq806x_sata_phy_ops = { + .init = qcom_ipq806x_sata_phy_init, + .exit = qcom_ipq806x_sata_phy_exit, + .owner = THIS_MODULE, +}; + +static int qcom_ipq806x_sata_phy_probe(struct platform_device *pdev) +{ + struct qcom_ipq806x_sata_phy *phy; + struct device *dev = &pdev->dev; + struct resource *res; + struct phy_provider *phy_provider; + struct phy *generic_phy; + int ret; + + phy = devm_kzalloc(dev, sizeof(*phy), GFP_KERNEL); + if (!phy) + return -ENOMEM; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + phy->mmio = devm_ioremap_resource(dev, res); + if (IS_ERR(phy->mmio)) + return PTR_ERR(phy->mmio); + + generic_phy = devm_phy_create(dev, NULL, &qcom_ipq806x_sata_phy_ops, + NULL); + if (IS_ERR(generic_phy)) { + dev_err(dev, "%s: failed to create phy\n", __func__); + return PTR_ERR(generic_phy); + } + + phy->dev = dev; + phy_set_drvdata(generic_phy, phy); + platform_set_drvdata(pdev, phy); + + phy->cfg_clk = devm_clk_get(dev, "cfg"); + if (IS_ERR(phy->cfg_clk)) { + dev_err(dev, "Failed to get sata cfg clock\n"); + return PTR_ERR(phy->cfg_clk); + } + + ret = clk_prepare_enable(phy->cfg_clk); + if (ret) + return ret; + + phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate); + if (IS_ERR(phy_provider)) { + clk_disable_unprepare(phy->cfg_clk); + dev_err(dev, "%s: failed to register phy\n", __func__); + return PTR_ERR(phy_provider); + } + + return 0; +} + +static int qcom_ipq806x_sata_phy_remove(struct platform_device *pdev) +{ + struct qcom_ipq806x_sata_phy *phy = platform_get_drvdata(pdev); + + clk_disable_unprepare(phy->cfg_clk); + + return 0; +} + +static const struct of_device_id qcom_ipq806x_sata_phy_of_match[] = { + { .compatible = "qcom,ipq806x-sata-phy" }, + { }, +}; +MODULE_DEVICE_TABLE(of, qcom_ipq806x_sata_phy_of_match); + +static struct platform_driver qcom_ipq806x_sata_phy_driver = { + .probe = qcom_ipq806x_sata_phy_probe, + .remove = qcom_ipq806x_sata_phy_remove, + .driver = { + .name = "qcom-ipq806x-sata-phy", + .owner = THIS_MODULE, + .of_match_table = qcom_ipq806x_sata_phy_of_match, + } +}; +module_platform_driver(qcom_ipq806x_sata_phy_driver); + +MODULE_DESCRIPTION("QCOM IPQ806x SATA PHY driver"); +MODULE_LICENSE("GPL v2"); From c4aee1aacb4798d23f514ab4eb59acef752d2397 Mon Sep 17 00:00:00 2001 From: Kumar Gala Date: Wed, 16 Jul 2014 11:10:09 -0500 Subject: [PATCH 25/28] phy: qcom: Add device tree bindings for IPQ806x SATA PHY Add binding spec for Qualcomm SoC PHYs, starting with the SATA PHY on the IPQ806x family of SoCs. Signed-off-by: Kumar Gala Signed-off-by: Kishon Vijay Abraham I --- .../bindings/phy/qcom-ipq806x-sata-phy.txt | 23 +++++++++++++++++++ 1 file changed, 23 insertions(+) create mode 100644 Documentation/devicetree/bindings/phy/qcom-ipq806x-sata-phy.txt diff --git a/Documentation/devicetree/bindings/phy/qcom-ipq806x-sata-phy.txt b/Documentation/devicetree/bindings/phy/qcom-ipq806x-sata-phy.txt new file mode 100644 index 000000000000..76bfbd056202 --- /dev/null +++ b/Documentation/devicetree/bindings/phy/qcom-ipq806x-sata-phy.txt @@ -0,0 +1,23 @@ +Qualcomm IPQ806x SATA PHY Controller +------------------------------------ + +SATA PHY nodes are defined to describe on-chip SATA Physical layer controllers. +Each SATA PHY controller should have its own node. + +Required properties: +- compatible: compatible list, contains "qcom,ipq806x-sata-phy" +- reg: offset and length of the SATA PHY register set; +- #phy-cells: must be zero +- clocks: must be exactly one entry +- clock-names: must be "cfg" + +Example: + sata_phy: sata-phy@1b400000 { + compatible = "qcom,ipq806x-sata-phy"; + reg = <0x1b400000 0x200>; + + clocks = <&gcc SATA_PHY_CFG_CLK>; + clock-names = "cfg"; + + #phy-cells = <0>; + }; From f5c9f3be608017577731ebe8be37e55f800586d3 Mon Sep 17 00:00:00 2001 From: Lee Jones Date: Thu, 10 Jul 2014 10:09:31 +0100 Subject: [PATCH 26/28] phy: miphy365x: Add Device Tree bindings for the MiPHY365x The MiPHY365x is a Generic PHY which can serve various SATA or PCIe devices. It has 2 ports which it can use for either; both SATA, both PCIe or one of each in any configuration. Acked-by: Mark Rutland Acked-by: Alexandre Torgue Signed-off-by: Lee Jones Signed-off-by: Kishon Vijay Abraham I --- .../devicetree/bindings/phy/phy-miphy365x.txt | 76 +++++++++++++++++++ 1 file changed, 76 insertions(+) create mode 100644 Documentation/devicetree/bindings/phy/phy-miphy365x.txt diff --git a/Documentation/devicetree/bindings/phy/phy-miphy365x.txt b/Documentation/devicetree/bindings/phy/phy-miphy365x.txt new file mode 100644 index 000000000000..42c880886cf7 --- /dev/null +++ b/Documentation/devicetree/bindings/phy/phy-miphy365x.txt @@ -0,0 +1,76 @@ +STMicroelectronics STi MIPHY365x PHY binding +============================================ + +This binding describes a miphy device that is used to control PHY hardware +for SATA and PCIe. + +Required properties (controller (parent) node): +- compatible : Should be "st,miphy365x-phy" +- st,syscfg : Should be a phandle of the system configuration register group + which contain the SATA, PCIe mode setting bits + +Required nodes : A sub-node is required for each channel the controller + provides. Address range information including the usual + 'reg' and 'reg-names' properties are used inside these + nodes to describe the controller's topology. These nodes + are translated by the driver's .xlate() function. + +Required properties (port (child) node): +- #phy-cells : Should be 1 (See second example) + Cell after port phandle is device type from: + - MIPHY_TYPE_SATA + - MIPHY_TYPE_PCI +- reg : Address and length of register sets for each device in + "reg-names" +- reg-names : The names of the register addresses corresponding to the + registers filled in "reg": + - sata: For SATA devices + - pcie: For PCIe devices + - syscfg: To specify the syscfg based config register + +Optional properties (port (child) node): +- st,sata-gen : Generation of locally attached SATA IP. Expected values + are {1,2,3). If not supplied generation 1 hardware will + be expected +- st,pcie-tx-pol-inv : Bool property to invert the polarity PCIe Tx (Txn/Txp) +- st,sata-tx-pol-inv : Bool property to invert the polarity SATA Tx (Txn/Txp) + +Example: + + miphy365x_phy: miphy365x@fe382000 { + compatible = "st,miphy365x-phy"; + st,syscfg = <&syscfg_rear>; + #address-cells = <1>; + #size-cells = <1>; + ranges; + + phy_port0: port@fe382000 { + reg = <0xfe382000 0x100>, <0xfe394000 0x100>, <0x824 0x4>; + reg-names = "sata", "pcie", "syscfg"; + #phy-cells = <1>; + st,sata-gen = <3>; + }; + + phy_port1: port@fe38a000 { + reg = <0xfe38a000 0x100>, <0xfe804000 0x100>, <0x828 0x4>;; + reg-names = "sata", "pcie", "syscfg"; + #phy-cells = <1>; + st,pcie-tx-pol-inv; + }; + }; + +Specifying phy control of devices +================================= + +Device nodes should specify the configuration required in their "phys" +property, containing a phandle to the phy port node and a device type. + +Example: + +#include + + sata0: sata@fe380000 { + ... + phys = <&phy_port0 MIPHY_TYPE_SATA>; + ... + }; From 6e877fedb1cff0f4a0988d30418ad87abaefafcb Mon Sep 17 00:00:00 2001 From: Lee Jones Date: Wed, 9 Jul 2014 12:41:12 +0100 Subject: [PATCH 27/28] phy: miphy365x: Provide support for the MiPHY356x Generic PHY The MiPHY365x is a Generic PHY which can serve various SATA or PCIe devices. It has 2 ports which it can use for either; both SATA, both PCIe or one of each in any configuration. Acked-by: Mark Rutland Signed-off-by: Alexandre Torgue Signed-off-by: Lee Jones Signed-off-by: Kishon Vijay Abraham I --- drivers/phy/Kconfig | 10 + drivers/phy/Makefile | 1 + drivers/phy/phy-miphy365x.c | 616 ++++++++++++++++++++++++++++++++++++ 3 files changed, 627 insertions(+) create mode 100644 drivers/phy/phy-miphy365x.c diff --git a/drivers/phy/Kconfig b/drivers/phy/Kconfig index 3e251aa64ffd..cc97c897945a 100644 --- a/drivers/phy/Kconfig +++ b/drivers/phy/Kconfig @@ -38,6 +38,16 @@ config PHY_MVEBU_SATA depends on OF select GENERIC_PHY +config PHY_MIPHY365X + tristate "STMicroelectronics MIPHY365X PHY driver for STiH41x series" + depends on ARCH_STI + depends on GENERIC_PHY + depends on HAS_IOMEM + depends on OF + help + Enable this to support the miphy transceiver (for SATA/PCIE) + that is part of STMicroelectronics STiH41x SoC series. + config OMAP_CONTROL_PHY tristate "OMAP CONTROL PHY Driver" depends on ARCH_OMAP2PLUS || COMPILE_TEST diff --git a/drivers/phy/Makefile b/drivers/phy/Makefile index 54ab9785932c..971ad0aac388 100644 --- a/drivers/phy/Makefile +++ b/drivers/phy/Makefile @@ -8,6 +8,7 @@ obj-$(CONFIG_BCM_KONA_USB2_PHY) += phy-bcm-kona-usb2.o obj-$(CONFIG_PHY_EXYNOS_DP_VIDEO) += phy-exynos-dp-video.o obj-$(CONFIG_PHY_EXYNOS_MIPI_VIDEO) += phy-exynos-mipi-video.o obj-$(CONFIG_PHY_MVEBU_SATA) += phy-mvebu-sata.o +obj-$(CONFIG_PHY_MIPHY365X) += phy-miphy365x.o obj-$(CONFIG_OMAP_CONTROL_PHY) += phy-omap-control.o obj-$(CONFIG_OMAP_USB2) += phy-omap-usb2.o obj-$(CONFIG_TI_PIPE3) += phy-ti-pipe3.o diff --git a/drivers/phy/phy-miphy365x.c b/drivers/phy/phy-miphy365x.c new file mode 100644 index 000000000000..65ecd04da9bc --- /dev/null +++ b/drivers/phy/phy-miphy365x.c @@ -0,0 +1,616 @@ +/* + * Copyright (C) 2014 STMicroelectronics – All Rights Reserved + * + * STMicroelectronics PHY driver MiPHY365 (for SoC STiH416). + * + * Authors: Alexandre Torgue + * Lee Jones + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, as + * published by the Free Software Foundation. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#define HFC_TIMEOUT 100 + +#define SYSCFG_2521 0x824 +#define SYSCFG_2522 0x828 +#define SYSCFG_PCIE_SATA_MASK BIT(1) +#define SYSCFG_PCIE_SATA_POS 1 + +/* MiPHY365x register definitions */ +#define RESET_REG 0x00 +#define RST_PLL BIT(1) +#define RST_PLL_CAL BIT(2) +#define RST_RX BIT(4) +#define RST_MACRO BIT(7) + +#define STATUS_REG 0x01 +#define IDLL_RDY BIT(0) +#define PLL_RDY BIT(1) +#define DES_BIT_LOCK BIT(2) +#define DES_SYMBOL_LOCK BIT(3) + +#define CTRL_REG 0x02 +#define TERM_EN BIT(0) +#define PCI_EN BIT(2) +#define DES_BIT_LOCK_EN BIT(3) +#define TX_POL BIT(5) + +#define INT_CTRL_REG 0x03 + +#define BOUNDARY1_REG 0x10 +#define SPDSEL_SEL BIT(0) + +#define BOUNDARY3_REG 0x12 +#define TX_SPDSEL_GEN1_VAL 0 +#define TX_SPDSEL_GEN2_VAL 0x01 +#define TX_SPDSEL_GEN3_VAL 0x02 +#define RX_SPDSEL_GEN1_VAL 0 +#define RX_SPDSEL_GEN2_VAL (0x01 << 3) +#define RX_SPDSEL_GEN3_VAL (0x02 << 3) + +#define PCIE_REG 0x16 + +#define BUF_SEL_REG 0x20 +#define CONF_GEN_SEL_GEN3 0x02 +#define CONF_GEN_SEL_GEN2 0x01 +#define PD_VDDTFILTER BIT(4) + +#define TXBUF1_REG 0x21 +#define SWING_VAL 0x04 +#define SWING_VAL_GEN1 0x03 +#define PREEMPH_VAL (0x3 << 5) + +#define TXBUF2_REG 0x22 +#define TXSLEW_VAL 0x2 +#define TXSLEW_VAL_GEN1 0x4 + +#define RXBUF_OFFSET_CTRL_REG 0x23 + +#define RXBUF_REG 0x25 +#define SDTHRES_VAL 0x01 +#define EQ_ON3 (0x03 << 4) +#define EQ_ON1 (0x01 << 4) + +#define COMP_CTRL1_REG 0x40 +#define START_COMSR BIT(0) +#define START_COMZC BIT(1) +#define COMSR_DONE BIT(2) +#define COMZC_DONE BIT(3) +#define COMP_AUTO_LOAD BIT(4) + +#define COMP_CTRL2_REG 0x41 +#define COMP_2MHZ_RAT_GEN1 0x1e +#define COMP_2MHZ_RAT 0xf + +#define COMP_CTRL3_REG 0x42 +#define COMSR_COMP_REF 0x33 + +#define COMP_IDLL_REG 0x47 +#define COMZC_IDLL 0x2a + +#define PLL_CTRL1_REG 0x50 +#define PLL_START_CAL BIT(0) +#define BUF_EN BIT(2) +#define SYNCHRO_TX BIT(3) +#define SSC_EN BIT(6) +#define CONFIG_PLL BIT(7) + +#define PLL_CTRL2_REG 0x51 +#define BYPASS_PLL_CAL BIT(1) + +#define PLL_RAT_REG 0x52 + +#define PLL_SSC_STEP_MSB_REG 0x56 +#define PLL_SSC_STEP_MSB_VAL 0x03 + +#define PLL_SSC_STEP_LSB_REG 0x57 +#define PLL_SSC_STEP_LSB_VAL 0x63 + +#define PLL_SSC_PER_MSB_REG 0x58 +#define PLL_SSC_PER_MSB_VAL 0 + +#define PLL_SSC_PER_LSB_REG 0x59 +#define PLL_SSC_PER_LSB_VAL 0xf1 + +#define IDLL_TEST_REG 0x72 +#define START_CLK_HF BIT(6) + +#define DES_BITLOCK_REG 0x86 +#define BIT_LOCK_LEVEL 0x01 +#define BIT_LOCK_CNT_512 (0x03 << 5) + +static u8 ports[] = { MIPHY_PORT_0, MIPHY_PORT_1 }; + +struct miphy365x_phy { + struct phy *phy; + void __iomem *base; + void __iomem *sata; + void __iomem *pcie; + u8 type; + u8 port; +}; + +struct miphy365x_dev { + struct device *dev; + struct regmap *regmap; + struct mutex miphy_mutex; + struct miphy365x phys[ARRAY_SIZE(ports)]; + bool pcie_tx_pol_inv; + bool sata_tx_pol_inv; + u32 sata_gen; +}; + +/* + * These values are represented in Device tree. They are considered to be ABI + * and although they can be extended any existing values must not change. + */ +enum miphy_sata_gen { + SATA_GEN1 = 1, + SATA_GEN2, + SATA_GEN3 +}; + +static u8 rx_tx_spd[] = { + TX_SPDSEL_GEN1_VAL | RX_SPDSEL_GEN1_VAL, + TX_SPDSEL_GEN2_VAL | RX_SPDSEL_GEN2_VAL, + TX_SPDSEL_GEN3_VAL | RX_SPDSEL_GEN3_VAL +}; + +/* + * This function selects the system configuration, + * either two SATA, one SATA and one PCIe, or two PCIe lanes. + */ +static int miphy365x_set_path(struct miphy365x_phy *miphy_phy, + struct miphy365x_dev *miphy_dev) +{ + u8 config = miphy_phy->type | miphy_phy->port; + u32 mask = SYSCFG_PCIE_SATA_MASK; + u32 reg; + bool sata; + + switch (config) { + case MIPHY_SATA_PORT0: + reg = SYSCFG_2521; + sata = true; + break; + case MIPHY_PCIE_PORT1: + reg = SYSCFG_2522; + sata = false; + break; + default: + dev_err(miphy_dev->dev, "Configuration not supported\n"); + return -EINVAL; + } + + return regmap_update_bits(miphy_dev->regmap, reg, mask, + sata << SYSCFG_PCIE_SATA_POS); +} + +static int miphy365x_init_pcie_port(struct miphy365x_phy *miphy_phy, + struct miphy365x_dev *miphy_dev) +{ + u8 val; + + if (miphy_phy->pcie_tx_pol_inv) { + /* Invert Tx polarity and clear pci_txdetect_pol bit */ + val = TERM_EN | PCI_EN | DES_BIT_LOCK_EN | TX_POL; + writeb_relaxed(val, miphy_phy->base + CTRL_REG); + writeb_relaxed(0x00, miphy_phy->base + PCIE_REG); + } + + return 0; +} + +static inline int miphy365x_hfc_not_rdy(struct miphy365x_phy *miphy_phy, + struct miphy365x_dev *miphy_dev) +{ + unsigned long timeout = jiffies + msecs_to_jiffies(HFC_TIMEOUT); + u8 mask = IDLL_RDY | PLL_RDY; + u8 regval; + + do { + regval = readb_relaxed(miphy_phy->base + STATUS_REG); + if (!(regval & mask)) + return 0; + + usleep_range(2000, 2500); + } while (time_before(jiffies, timeout)); + + dev_err(miphy_dev->dev, "HFC ready timeout!\n"); + return -EBUSY; +} + +static inline int miphy365x_rdy(struct miphy365x_phy *miphy_phy, + struct miphy365x_dev *miphy_dev) +{ + unsigned long timeout = jiffies + msecs_to_jiffies(HFC_TIMEOUT); + u8 mask = IDLL_RDY | PLL_RDY; + u8 regval; + + do { + regval = readb_relaxed(miphy_phy->base + STATUS_REG); + if ((regval & mask) == mask) + return 0; + + usleep_range(2000, 2500); + } while (time_before(jiffies, timeout)); + + dev_err(miphy_dev->dev, "PHY not ready timeout!\n"); + return -EBUSY; +} + +static inline void miphy365x_set_comp(struct miphy365x_phy *miphy_phy, + struct miphy365x_dev *miphy_dev) +{ + u8 val, mask; + + if (miphy_dev->sata_gen == SATA_GEN1) + writeb_relaxed(COMP_2MHZ_RAT_GEN1, + miphy_phy->base + COMP_CTRL2_REG); + else + writeb_relaxed(COMP_2MHZ_RAT, + miphy_phy->base + COMP_CTRL2_REG); + + if (miphy_dev->sata_gen != SATA_GEN3) { + writeb_relaxed(COMSR_COMP_REF, + miphy_phy->base + COMP_CTRL3_REG); + /* + * Force VCO current to value defined by address 0x5A + * and disable PCIe100Mref bit + * Enable auto load compensation for pll_i_bias + */ + writeb_relaxed(BYPASS_PLL_CAL, miphy_phy->base + PLL_CTRL2_REG); + writeb_relaxed(COMZC_IDLL, miphy_phy->base + COMP_IDLL_REG); + } + + /* + * Force restart compensation and enable auto load + * for Comzc_Tx, Comzc_Rx and Comsr on macro + */ + val = START_COMSR | START_COMZC | COMP_AUTO_LOAD; + writeb_relaxed(val, miphy_phy->base + COMP_CTRL1_REG); + + mask = COMSR_DONE | COMZC_DONE; + while ((readb_relaxed(miphy_phy->base + COMP_CTRL1_REG) & mask) != mask) + cpu_relax(); +} + +static inline void miphy365x_set_ssc(struct miphy365x_phy *miphy_phy, + struct miphy365x_dev *miphy_dev) +{ + u8 val; + + /* + * SSC Settings. SSC will be enabled through Link + * SSC Ampl. = 0.4% + * SSC Freq = 31KHz + */ + writeb_relaxed(PLL_SSC_STEP_MSB_VAL, + miphy_phy->base + PLL_SSC_STEP_MSB_REG); + writeb_relaxed(PLL_SSC_STEP_LSB_VAL, + miphy_phy->base + PLL_SSC_STEP_LSB_REG); + writeb_relaxed(PLL_SSC_PER_MSB_VAL, + miphy_phy->base + PLL_SSC_PER_MSB_REG); + writeb_relaxed(PLL_SSC_PER_LSB_VAL, + miphy_phy->base + PLL_SSC_PER_LSB_REG); + + /* SSC Settings complete */ + if (miphy_dev->sata_gen == SATA_GEN1) { + val = PLL_START_CAL | BUF_EN | SYNCHRO_TX | CONFIG_PLL; + writeb_relaxed(val, miphy_phy->base + PLL_CTRL1_REG); + } else { + val = SSC_EN | PLL_START_CAL | BUF_EN | SYNCHRO_TX | CONFIG_PLL; + writeb_relaxed(val, miphy_phy->base + PLL_CTRL1_REG); + } +} + +static int miphy365x_init_sata_port(struct miphy365x_phy *miphy_phy, + struct miphy365x_dev *miphy_dev) +{ + int ret; + u8 val; + + /* + * Force PHY macro reset, PLL calibration reset, PLL reset + * and assert Deserializer Reset + */ + val = RST_PLL | RST_PLL_CAL | RST_RX | RST_MACRO; + writeb_relaxed(val, miphy_phy->base + RESET_REG); + + if (miphy_dev->sata_tx_pol_inv) + writeb_relaxed(TX_POL, miphy_phy->base + CTRL_REG); + + /* + * Force macro1 to use rx_lspd, tx_lspd + * Force Rx_Clock on first I-DLL phase + * Force Des in HP mode on macro, rx_lspd, tx_lspd for Gen2/3 + */ + writeb_relaxed(SPDSEL_SEL, miphy_phy->base + BOUNDARY1_REG); + writeb_relaxed(START_CLK_HF, miphy_phy->base + IDLL_TEST_REG); + val = rx_tx_spd[miphy_dev->sata_gen]; + writeb_relaxed(val, miphy_phy->base + BOUNDARY3_REG); + + /* Wait for HFC_READY = 0 */ + ret = miphy365x_hfc_not_rdy(miphy_phy, miphy_dev); + if (ret) + return ret; + + /* Compensation Recalibration */ + miphy365x_set_comp(miphy_phy, miphy_dev); + + switch (miphy_dev->sata_gen) { + case SATA_GEN3: + /* + * TX Swing target 550-600mv peak to peak diff + * Tx Slew target 90-110ps rising/falling time + * Rx Eq ON3, Sigdet threshold SDTH1 + */ + val = PD_VDDTFILTER | CONF_GEN_SEL_GEN3; + writeb_relaxed(val, miphy_phy->base + BUF_SEL_REG); + val = SWING_VAL | PREEMPH_VAL; + writeb_relaxed(val, miphy_phy->base + TXBUF1_REG); + writeb_relaxed(TXSLEW_VAL, miphy_phy->base + TXBUF2_REG); + writeb_relaxed(0x00, miphy_phy->base + RXBUF_OFFSET_CTRL_REG); + val = SDTHRES_VAL | EQ_ON3; + writeb_relaxed(val, miphy_phy->base + RXBUF_REG); + break; + case SATA_GEN2: + /* + * conf gen sel=0x1 to program Gen2 banked registers + * VDDT filter ON + * Tx Swing target 550-600mV peak-to-peak diff + * Tx Slew target 90-110 ps rising/falling time + * RX Equalization ON1, Sigdet threshold SDTH1 + */ + writeb_relaxed(CONF_GEN_SEL_GEN2, + miphy_phy->base + BUF_SEL_REG); + writeb_relaxed(SWING_VAL, miphy_phy->base + TXBUF1_REG); + writeb_relaxed(TXSLEW_VAL, miphy_phy->base + TXBUF2_REG); + val = SDTHRES_VAL | EQ_ON1; + writeb_relaxed(val, miphy_phy->base + RXBUF_REG); + break; + case SATA_GEN1: + /* + * conf gen sel = 00b to program Gen1 banked registers + * VDDT filter ON + * Tx Swing target 500-550mV peak-to-peak diff + * Tx Slew target120-140 ps rising/falling time + */ + writeb_relaxed(PD_VDDTFILTER, miphy_phy->base + BUF_SEL_REG); + writeb_relaxed(SWING_VAL_GEN1, miphy_phy->base + TXBUF1_REG); + writeb_relaxed(TXSLEW_VAL_GEN1, miphy_phy->base + TXBUF2_REG); + break; + default: + break; + } + + /* Force Macro1 in partial mode & release pll cal reset */ + writeb_relaxed(RST_RX, miphy_phy->base + RESET_REG); + usleep_range(100, 150); + + miphy365x_set_ssc(miphy_phy, miphy_dev); + + /* Wait for phy_ready */ + ret = miphy365x_rdy(miphy_phy, miphy_dev); + if (ret) + return ret; + + /* + * Enable macro1 to use rx_lspd & tx_lspd + * Release Rx_Clock on first I-DLL phase on macro1 + * Assert deserializer reset + * des_bit_lock_en is set + * bit lock detection strength + * Deassert deserializer reset + */ + writeb_relaxed(0x00, miphy_phy->base + BOUNDARY1_REG); + writeb_relaxed(0x00, miphy_phy->base + IDLL_TEST_REG); + writeb_relaxed(RST_RX, miphy_phy->base + RESET_REG); + val = miphy_dev->sata_tx_pol_inv ? + (TX_POL | DES_BIT_LOCK_EN) : DES_BIT_LOCK_EN; + writeb_relaxed(val, miphy_phy->base + CTRL_REG); + + val = BIT_LOCK_CNT_512 | BIT_LOCK_LEVEL; + writeb_relaxed(val, miphy_phy->base + DES_BITLOCK_REG); + writeb_relaxed(0x00, miphy_phy->base + RESET_REG); + + return 0; +} + +static int miphy365x_init(struct phy *phy) +{ + struct miphy365x_phy *miphy_phy = phy_get_drvdata(phy); + struct miphy365x_dev *miphy_dev = dev_get_drvdata(phy->dev.parent); + int ret = 0; + + mutex_lock(&miphy_dev->miphy_mutex); + + ret = miphy365x_set_path(miphy_phy, miphy_dev); + if (ret) { + mutex_unlock(&miphy_dev->miphy_mutex); + return ret; + } + + /* Initialise Miphy for PCIe or SATA */ + if (miphy_phy->type == MIPHY_TYPE_PCIE) + ret = miphy365x_init_pcie_port(miphy_phy, miphy_dev); + else + ret = miphy365x_init_sata_port(miphy_phy, miphy_dev); + + mutex_unlock(&miphy_dev->miphy_mutex); + + return ret; +} + +static struct phy *miphy365x_xlate(struct device *dev, + struct of_phandle_args *args) +{ + struct miphy365x_dev *state = dev_get_drvdata(dev); + u8 port, type; + + if (args->count != 2) { + dev_err(dev, "Invalid number of cells in 'phy' property\n"); + return ERR_PTR(-EINVAL); + } + + if (args->args[0] & 0xFFFFFF00 || args->args[1] & 0xFFFFFF00) { + dev_err(dev, "Unsupported flags set in 'phy' property\n"); + return ERR_PTR(-EINVAL); + } + + port = args->args[0]; + type = args->args[1]; + + if (WARN_ON(port >= ARRAY_SIZE(ports))) + return ERR_PTR(-EINVAL); + + if (type == MIPHY_TYPE_SATA) + state->phys[port].base = state->phys[port].sata; + else if (type == MIPHY_TYPE_PCIE) + state->phys[port].base = state->phys[port].pcie; + else { + WARN(1, "Invalid type specified in DT"); + return ERR_PTR(-EINVAL); + } + + state->phys[port].type = type; + + return state->phys[port].phy; +} + +static struct phy_ops miphy365x_ops = { + .init = miphy365x_init, + .owner = THIS_MODULE, +}; + +static int miphy365x_get_base_addr(struct platform_device *pdev, + struct miphy365x_phy *phy, u8 port) +{ + struct resource *res; + char type[6]; + + sprintf(type, "sata%d", port); + + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, type); + phy->sata = devm_ioremap_resource(&pdev->dev, res)); + if (!phy->sata) + return -ENOMEM; + + sprintf(type, "pcie%d", port); + + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, type); + phy->pcie = devm_ioremap_resource(&pdev->dev, res)); + if (!phy->pcie) + return -ENOMEM; + + return 0; +} + +static int miphy365x_of_probe(struct device_node *np, + struct miphy365x_dev *phy_dev) +{ + phy_dev->regmap = syscon_regmap_lookup_by_phandle(np, "st,syscfg"); + if (IS_ERR(phy_dev->regmap)) { + dev_err(phy_dev->dev, "No syscfg phandle specified\n"); + return PTR_ERR(phy_dev->regmap); + } + + of_property_read_u32(np, "st,sata-gen", &phy_dev->sata_gen); + if (!phy_dev->sata_gen) + phy_dev->sata_gen = SATA_GEN1; + + phy_dev->pcie_tx_pol_inv = + of_property_read_bool(np, "st,pcie-tx-pol-inv"); + + phy_dev->sata_tx_pol_inv = + of_property_read_bool(np, "st,sata-tx-pol-inv"); + + return 0; +} + +static int miphy365x_probe(struct platform_device *pdev) +{ + struct device_node *np = pdev->dev.of_node; + struct miphy365x_dev *phy_dev; + struct device *dev = &pdev->dev; + struct phy_provider *provider; + u8 port; + int ret; + + phy_dev = devm_kzalloc(dev, sizeof(*phy_dev), GFP_KERNEL); + if (!phy_dev) + return -ENOMEM; + + ret = miphy365x_of_probe(np, phy_dev); + if (ret) + return ret; + + phy_dev->dev = dev; + + dev_set_drvdata(dev, phy_dev); + + mutex_init(&phy_dev->miphy_mutex); + + for (port = 0; port < ARRAY_SIZE(ports); port++) { + struct phy *phy; + + phy = devm_phy_create(dev, &miphy365x_ops, NULL); + if (IS_ERR(phy)) { + dev_err(dev, "failed to create PHY on port %d\n", port); + return PTR_ERR(phy); + } + + phy_dev->phys[port].phy = phy; + phy_dev->phys[port].port = port; + + ret = miphy365x_get_base_addr(pdev, + &phy_dev->phys[port], port); + if (ret) + return ret; + + phy_set_drvdata(phy, &phy_dev->phys[port]); + } + + provider = devm_of_phy_provider_register(dev, miphy365x_xlate); + if (IS_ERR(provider)) + return PTR_ERR(provider); + + return 0; +} + +static const struct of_device_id miphy365x_of_match[] = { + { .compatible = "st,miphy365x-phy", }, + { }, +}; +MODULE_DEVICE_TABLE(of, miphy365x_of_match); + +static struct platform_driver miphy365x_driver = { + .probe = miphy365x_probe, + .driver = { + .name = "miphy365x-phy", + .owner = THIS_MODULE, + .of_match_table = miphy365x_of_match, + } +}; +module_platform_driver(miphy365x_driver); + +MODULE_AUTHOR("Alexandre Torgue "); +MODULE_DESCRIPTION("STMicroelectronics miphy365x driver"); +MODULE_LICENSE("GPL v2"); From 7ebdb52e192c4d496a9b3a87d47eba3eba3e1fd4 Mon Sep 17 00:00:00 2001 From: Lee Jones Date: Wed, 9 Jul 2014 12:41:13 +0100 Subject: [PATCH 28/28] phy: miphy365x: Represent each PHY channel as a DT subnode This has the added advantages of being able to enable/disable each of the channels as simply as enabling/disabling the DT node. Suggested-by: Kishon Vijay Abraham I Signed-off-by: Lee Jones Signed-off-by: Kishon Vijay Abraham I --- drivers/phy/phy-miphy365x.c | 256 +++++++++++++++++++----------------- 1 file changed, 138 insertions(+), 118 deletions(-) diff --git a/drivers/phy/phy-miphy365x.c b/drivers/phy/phy-miphy365x.c index 65ecd04da9bc..e111baf187ce 100644 --- a/drivers/phy/phy-miphy365x.c +++ b/drivers/phy/phy-miphy365x.c @@ -18,6 +18,7 @@ #include #include #include +#include #include #include #include @@ -28,10 +29,8 @@ #define HFC_TIMEOUT 100 -#define SYSCFG_2521 0x824 -#define SYSCFG_2522 0x828 -#define SYSCFG_PCIE_SATA_MASK BIT(1) -#define SYSCFG_PCIE_SATA_POS 1 +#define SYSCFG_SELECT_SATA_MASK BIT(1) +#define SYSCFG_SELECT_SATA_POS 1 /* MiPHY365x register definitions */ #define RESET_REG 0x00 @@ -136,25 +135,21 @@ #define BIT_LOCK_LEVEL 0x01 #define BIT_LOCK_CNT_512 (0x03 << 5) -static u8 ports[] = { MIPHY_PORT_0, MIPHY_PORT_1 }; - struct miphy365x_phy { struct phy *phy; void __iomem *base; - void __iomem *sata; - void __iomem *pcie; + bool pcie_tx_pol_inv; + bool sata_tx_pol_inv; + u32 sata_gen; + u64 ctrlreg; u8 type; - u8 port; }; struct miphy365x_dev { struct device *dev; struct regmap *regmap; struct mutex miphy_mutex; - struct miphy365x phys[ARRAY_SIZE(ports)]; - bool pcie_tx_pol_inv; - bool sata_tx_pol_inv; - u32 sata_gen; + struct miphy365x_phy **phys; }; /* @@ -180,27 +175,12 @@ static u8 rx_tx_spd[] = { static int miphy365x_set_path(struct miphy365x_phy *miphy_phy, struct miphy365x_dev *miphy_dev) { - u8 config = miphy_phy->type | miphy_phy->port; - u32 mask = SYSCFG_PCIE_SATA_MASK; - u32 reg; - bool sata; + bool sata = (miphy_phy->type == MIPHY_TYPE_SATA); - switch (config) { - case MIPHY_SATA_PORT0: - reg = SYSCFG_2521; - sata = true; - break; - case MIPHY_PCIE_PORT1: - reg = SYSCFG_2522; - sata = false; - break; - default: - dev_err(miphy_dev->dev, "Configuration not supported\n"); - return -EINVAL; - } - - return regmap_update_bits(miphy_dev->regmap, reg, mask, - sata << SYSCFG_PCIE_SATA_POS); + return regmap_update_bits(miphy_dev->regmap, + (unsigned int)miphy_phy->ctrlreg, + SYSCFG_SELECT_SATA_MASK, + sata << SYSCFG_SELECT_SATA_POS); } static int miphy365x_init_pcie_port(struct miphy365x_phy *miphy_phy, @@ -261,14 +241,14 @@ static inline void miphy365x_set_comp(struct miphy365x_phy *miphy_phy, { u8 val, mask; - if (miphy_dev->sata_gen == SATA_GEN1) + if (miphy_phy->sata_gen == SATA_GEN1) writeb_relaxed(COMP_2MHZ_RAT_GEN1, miphy_phy->base + COMP_CTRL2_REG); else writeb_relaxed(COMP_2MHZ_RAT, miphy_phy->base + COMP_CTRL2_REG); - if (miphy_dev->sata_gen != SATA_GEN3) { + if (miphy_phy->sata_gen != SATA_GEN3) { writeb_relaxed(COMSR_COMP_REF, miphy_phy->base + COMP_CTRL3_REG); /* @@ -312,7 +292,7 @@ static inline void miphy365x_set_ssc(struct miphy365x_phy *miphy_phy, miphy_phy->base + PLL_SSC_PER_LSB_REG); /* SSC Settings complete */ - if (miphy_dev->sata_gen == SATA_GEN1) { + if (miphy_phy->sata_gen == SATA_GEN1) { val = PLL_START_CAL | BUF_EN | SYNCHRO_TX | CONFIG_PLL; writeb_relaxed(val, miphy_phy->base + PLL_CTRL1_REG); } else { @@ -334,7 +314,7 @@ static int miphy365x_init_sata_port(struct miphy365x_phy *miphy_phy, val = RST_PLL | RST_PLL_CAL | RST_RX | RST_MACRO; writeb_relaxed(val, miphy_phy->base + RESET_REG); - if (miphy_dev->sata_tx_pol_inv) + if (miphy_phy->sata_tx_pol_inv) writeb_relaxed(TX_POL, miphy_phy->base + CTRL_REG); /* @@ -344,7 +324,7 @@ static int miphy365x_init_sata_port(struct miphy365x_phy *miphy_phy, */ writeb_relaxed(SPDSEL_SEL, miphy_phy->base + BOUNDARY1_REG); writeb_relaxed(START_CLK_HF, miphy_phy->base + IDLL_TEST_REG); - val = rx_tx_spd[miphy_dev->sata_gen]; + val = rx_tx_spd[miphy_phy->sata_gen]; writeb_relaxed(val, miphy_phy->base + BOUNDARY3_REG); /* Wait for HFC_READY = 0 */ @@ -355,7 +335,7 @@ static int miphy365x_init_sata_port(struct miphy365x_phy *miphy_phy, /* Compensation Recalibration */ miphy365x_set_comp(miphy_phy, miphy_dev); - switch (miphy_dev->sata_gen) { + switch (miphy_phy->sata_gen) { case SATA_GEN3: /* * TX Swing target 550-600mv peak to peak diff @@ -423,7 +403,7 @@ static int miphy365x_init_sata_port(struct miphy365x_phy *miphy_phy, writeb_relaxed(0x00, miphy_phy->base + BOUNDARY1_REG); writeb_relaxed(0x00, miphy_phy->base + IDLL_TEST_REG); writeb_relaxed(RST_RX, miphy_phy->base + RESET_REG); - val = miphy_dev->sata_tx_pol_inv ? + val = miphy_phy->sata_tx_pol_inv ? (TX_POL | DES_BIT_LOCK_EN) : DES_BIT_LOCK_EN; writeb_relaxed(val, miphy_phy->base + CTRL_REG); @@ -459,40 +439,95 @@ static int miphy365x_init(struct phy *phy) return ret; } +int miphy365x_get_addr(struct device *dev, struct miphy365x_phy *miphy_phy, + int index) +{ + struct device_node *phynode = miphy_phy->phy->dev.of_node; + const char *name; + const __be32 *taddr; + int type = miphy_phy->type; + int ret; + + ret = of_property_read_string_index(phynode, "reg-names", index, &name); + if (ret) { + dev_err(dev, "no reg-names property not found\n"); + return ret; + } + + if (!strncmp(name, "syscfg", 6)) { + taddr = of_get_address(phynode, index, NULL, NULL); + if (!taddr) { + dev_err(dev, "failed to fetch syscfg address\n"); + return -EINVAL; + } + + miphy_phy->ctrlreg = of_translate_address(phynode, taddr); + if (miphy_phy->ctrlreg == OF_BAD_ADDR) { + dev_err(dev, "failed to translate syscfg address\n"); + return -EINVAL; + } + + return 0; + } + + if (!((!strncmp(name, "sata", 4) && type == MIPHY_TYPE_SATA) || + (!strncmp(name, "pcie", 4) && type == MIPHY_TYPE_PCIE))) + return 0; + + miphy_phy->base = of_iomap(phynode, index); + if (!miphy_phy->base) { + dev_err(dev, "Failed to map %s\n", phynode->full_name); + return -EINVAL; + } + + return 0; +} + static struct phy *miphy365x_xlate(struct device *dev, struct of_phandle_args *args) { - struct miphy365x_dev *state = dev_get_drvdata(dev); - u8 port, type; + struct miphy365x_dev *miphy_dev = dev_get_drvdata(dev); + struct miphy365x_phy *miphy_phy = NULL; + struct device_node *phynode = args->np; + int ret, index; - if (args->count != 2) { + if (!of_device_is_available(phynode)) { + dev_warn(dev, "Requested PHY is disabled\n"); + return ERR_PTR(-ENODEV); + } + + if (args->args_count != 1) { dev_err(dev, "Invalid number of cells in 'phy' property\n"); return ERR_PTR(-EINVAL); } - if (args->args[0] & 0xFFFFFF00 || args->args[1] & 0xFFFFFF00) { - dev_err(dev, "Unsupported flags set in 'phy' property\n"); + for (index = 0; index < of_get_child_count(dev->of_node); index++) + if (phynode == miphy_dev->phys[index]->phy->dev.of_node) { + miphy_phy = miphy_dev->phys[index]; + break; + } + + if (!miphy_phy) { + dev_err(dev, "Failed to find appropriate phy\n"); return ERR_PTR(-EINVAL); } - port = args->args[0]; - type = args->args[1]; + miphy_phy->type = args->args[0]; - if (WARN_ON(port >= ARRAY_SIZE(ports))) - return ERR_PTR(-EINVAL); - - if (type == MIPHY_TYPE_SATA) - state->phys[port].base = state->phys[port].sata; - else if (type == MIPHY_TYPE_PCIE) - state->phys[port].base = state->phys[port].pcie; - else { - WARN(1, "Invalid type specified in DT"); + if (!(miphy_phy->type == MIPHY_TYPE_SATA || + miphy_phy->type == MIPHY_TYPE_PCIE)) { + dev_err(dev, "Unsupported device type: %d\n", miphy_phy->type); return ERR_PTR(-EINVAL); } - state->phys[port].type = type; + /* Each port handles SATA and PCIE - third entry is always sysconf. */ + for (index = 0; index < 3; index++) { + ret = miphy365x_get_addr(dev, miphy_phy, index); + if (ret < 0) + return ERR_PTR(ret); + } - return state->phys[port].phy; + return miphy_phy->phy; } static struct phy_ops miphy365x_ops = { @@ -500,95 +535,80 @@ static struct phy_ops miphy365x_ops = { .owner = THIS_MODULE, }; -static int miphy365x_get_base_addr(struct platform_device *pdev, - struct miphy365x_phy *phy, u8 port) +static int miphy365x_of_probe(struct device_node *phynode, + struct miphy365x_phy *miphy_phy) { - struct resource *res; - char type[6]; + of_property_read_u32(phynode, "st,sata-gen", &miphy_phy->sata_gen); + if (!miphy_phy->sata_gen) + miphy_phy->sata_gen = SATA_GEN1; - sprintf(type, "sata%d", port); + miphy_phy->pcie_tx_pol_inv = + of_property_read_bool(phynode, "st,pcie-tx-pol-inv"); - res = platform_get_resource_byname(pdev, IORESOURCE_MEM, type); - phy->sata = devm_ioremap_resource(&pdev->dev, res)); - if (!phy->sata) - return -ENOMEM; - - sprintf(type, "pcie%d", port); - - res = platform_get_resource_byname(pdev, IORESOURCE_MEM, type); - phy->pcie = devm_ioremap_resource(&pdev->dev, res)); - if (!phy->pcie) - return -ENOMEM; - - return 0; -} - -static int miphy365x_of_probe(struct device_node *np, - struct miphy365x_dev *phy_dev) -{ - phy_dev->regmap = syscon_regmap_lookup_by_phandle(np, "st,syscfg"); - if (IS_ERR(phy_dev->regmap)) { - dev_err(phy_dev->dev, "No syscfg phandle specified\n"); - return PTR_ERR(phy_dev->regmap); - } - - of_property_read_u32(np, "st,sata-gen", &phy_dev->sata_gen); - if (!phy_dev->sata_gen) - phy_dev->sata_gen = SATA_GEN1; - - phy_dev->pcie_tx_pol_inv = - of_property_read_bool(np, "st,pcie-tx-pol-inv"); - - phy_dev->sata_tx_pol_inv = - of_property_read_bool(np, "st,sata-tx-pol-inv"); + miphy_phy->sata_tx_pol_inv = + of_property_read_bool(phynode, "st,sata-tx-pol-inv"); return 0; } static int miphy365x_probe(struct platform_device *pdev) { - struct device_node *np = pdev->dev.of_node; - struct miphy365x_dev *phy_dev; - struct device *dev = &pdev->dev; + struct device_node *child, *np = pdev->dev.of_node; + struct miphy365x_dev *miphy_dev; struct phy_provider *provider; - u8 port; + struct phy *phy; + int chancount, port = 0; int ret; - phy_dev = devm_kzalloc(dev, sizeof(*phy_dev), GFP_KERNEL); - if (!phy_dev) + miphy_dev = devm_kzalloc(&pdev->dev, sizeof(*miphy_dev), GFP_KERNEL); + if (!miphy_dev) return -ENOMEM; - ret = miphy365x_of_probe(np, phy_dev); - if (ret) - return ret; + chancount = of_get_child_count(np); + miphy_dev->phys = devm_kzalloc(&pdev->dev, sizeof(phy) * chancount, + GFP_KERNEL); + if (!miphy_dev->phys) + return -ENOMEM; - phy_dev->dev = dev; + miphy_dev->regmap = syscon_regmap_lookup_by_phandle(np, "st,syscfg"); + if (IS_ERR(miphy_dev->regmap)) { + dev_err(miphy_dev->dev, "No syscfg phandle specified\n"); + return PTR_ERR(miphy_dev->regmap); + } - dev_set_drvdata(dev, phy_dev); + miphy_dev->dev = &pdev->dev; - mutex_init(&phy_dev->miphy_mutex); + dev_set_drvdata(&pdev->dev, miphy_dev); - for (port = 0; port < ARRAY_SIZE(ports); port++) { - struct phy *phy; + mutex_init(&miphy_dev->miphy_mutex); - phy = devm_phy_create(dev, &miphy365x_ops, NULL); + for_each_child_of_node(np, child) { + struct miphy365x_phy *miphy_phy; + + miphy_phy = devm_kzalloc(&pdev->dev, sizeof(*miphy_phy), + GFP_KERNEL); + if (!miphy_phy) + return -ENOMEM; + + miphy_dev->phys[port] = miphy_phy; + + phy = devm_phy_create(&pdev->dev, child, &miphy365x_ops, NULL); if (IS_ERR(phy)) { - dev_err(dev, "failed to create PHY on port %d\n", port); + dev_err(&pdev->dev, "failed to create PHY\n"); return PTR_ERR(phy); } - phy_dev->phys[port].phy = phy; - phy_dev->phys[port].port = port; + miphy_dev->phys[port]->phy = phy; - ret = miphy365x_get_base_addr(pdev, - &phy_dev->phys[port], port); + ret = miphy365x_of_probe(child, miphy_phy); if (ret) return ret; - phy_set_drvdata(phy, &phy_dev->phys[port]); + phy_set_drvdata(phy, miphy_dev->phys[port]); + port++; } - provider = devm_of_phy_provider_register(dev, miphy365x_xlate); + provider = devm_of_phy_provider_register(&pdev->dev, miphy365x_xlate); if (IS_ERR(provider)) return PTR_ERR(provider);