18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * phy-uniphier-pcie.c - PHY driver for UniPhier PCIe controller 48c2ecf20Sopenharmony_ci * Copyright 2018, Socionext Inc. 58c2ecf20Sopenharmony_ci * Author: Kunihiko Hayashi <hayashi.kunihiko@socionext.com> 68c2ecf20Sopenharmony_ci */ 78c2ecf20Sopenharmony_ci 88c2ecf20Sopenharmony_ci#include <linux/bitops.h> 98c2ecf20Sopenharmony_ci#include <linux/bitfield.h> 108c2ecf20Sopenharmony_ci#include <linux/clk.h> 118c2ecf20Sopenharmony_ci#include <linux/iopoll.h> 128c2ecf20Sopenharmony_ci#include <linux/mfd/syscon.h> 138c2ecf20Sopenharmony_ci#include <linux/module.h> 148c2ecf20Sopenharmony_ci#include <linux/of_device.h> 158c2ecf20Sopenharmony_ci#include <linux/phy/phy.h> 168c2ecf20Sopenharmony_ci#include <linux/platform_device.h> 178c2ecf20Sopenharmony_ci#include <linux/regmap.h> 188c2ecf20Sopenharmony_ci#include <linux/reset.h> 198c2ecf20Sopenharmony_ci#include <linux/resource.h> 208c2ecf20Sopenharmony_ci 218c2ecf20Sopenharmony_ci/* PHY */ 228c2ecf20Sopenharmony_ci#define PCL_PHY_CLKCTRL 0x0000 238c2ecf20Sopenharmony_ci#define PORT_SEL_MASK GENMASK(11, 9) 248c2ecf20Sopenharmony_ci#define PORT_SEL_1 FIELD_PREP(PORT_SEL_MASK, 1) 258c2ecf20Sopenharmony_ci 268c2ecf20Sopenharmony_ci#define PCL_PHY_TEST_I 0x2000 278c2ecf20Sopenharmony_ci#define TESTI_DAT_MASK GENMASK(13, 6) 288c2ecf20Sopenharmony_ci#define TESTI_ADR_MASK GENMASK(5, 1) 298c2ecf20Sopenharmony_ci#define TESTI_WR_EN BIT(0) 308c2ecf20Sopenharmony_ci 318c2ecf20Sopenharmony_ci#define PCL_PHY_TEST_O 0x2004 328c2ecf20Sopenharmony_ci#define TESTO_DAT_MASK GENMASK(7, 0) 338c2ecf20Sopenharmony_ci 348c2ecf20Sopenharmony_ci#define PCL_PHY_RESET 0x200c 358c2ecf20Sopenharmony_ci#define PCL_PHY_RESET_N_MNMODE BIT(8) /* =1:manual */ 368c2ecf20Sopenharmony_ci#define PCL_PHY_RESET_N BIT(0) /* =1:deasssert */ 378c2ecf20Sopenharmony_ci 388c2ecf20Sopenharmony_ci/* SG */ 398c2ecf20Sopenharmony_ci#define SG_USBPCIESEL 0x590 408c2ecf20Sopenharmony_ci#define SG_USBPCIESEL_PCIE BIT(0) 418c2ecf20Sopenharmony_ci 428c2ecf20Sopenharmony_ci#define PCL_PHY_R00 0 438c2ecf20Sopenharmony_ci#define RX_EQ_ADJ_EN BIT(3) /* enable for EQ adjustment */ 448c2ecf20Sopenharmony_ci#define PCL_PHY_R06 6 458c2ecf20Sopenharmony_ci#define RX_EQ_ADJ GENMASK(5, 0) /* EQ adjustment value */ 468c2ecf20Sopenharmony_ci#define RX_EQ_ADJ_VAL 0 478c2ecf20Sopenharmony_ci#define PCL_PHY_R26 26 488c2ecf20Sopenharmony_ci#define VCO_CTRL GENMASK(7, 4) /* Tx VCO adjustment value */ 498c2ecf20Sopenharmony_ci#define VCO_CTRL_INIT_VAL 5 508c2ecf20Sopenharmony_ci 518c2ecf20Sopenharmony_cistruct uniphier_pciephy_priv { 528c2ecf20Sopenharmony_ci void __iomem *base; 538c2ecf20Sopenharmony_ci struct device *dev; 548c2ecf20Sopenharmony_ci struct clk *clk, *clk_gio; 558c2ecf20Sopenharmony_ci struct reset_control *rst, *rst_gio; 568c2ecf20Sopenharmony_ci const struct uniphier_pciephy_soc_data *data; 578c2ecf20Sopenharmony_ci}; 588c2ecf20Sopenharmony_ci 598c2ecf20Sopenharmony_cistruct uniphier_pciephy_soc_data { 608c2ecf20Sopenharmony_ci bool is_legacy; 618c2ecf20Sopenharmony_ci void (*set_phymode)(struct regmap *regmap); 628c2ecf20Sopenharmony_ci}; 638c2ecf20Sopenharmony_ci 648c2ecf20Sopenharmony_cistatic void uniphier_pciephy_testio_write(struct uniphier_pciephy_priv *priv, 658c2ecf20Sopenharmony_ci u32 data) 668c2ecf20Sopenharmony_ci{ 678c2ecf20Sopenharmony_ci /* need to read TESTO twice after accessing TESTI */ 688c2ecf20Sopenharmony_ci writel(data, priv->base + PCL_PHY_TEST_I); 698c2ecf20Sopenharmony_ci readl(priv->base + PCL_PHY_TEST_O); 708c2ecf20Sopenharmony_ci readl(priv->base + PCL_PHY_TEST_O); 718c2ecf20Sopenharmony_ci} 728c2ecf20Sopenharmony_ci 738c2ecf20Sopenharmony_cistatic void uniphier_pciephy_set_param(struct uniphier_pciephy_priv *priv, 748c2ecf20Sopenharmony_ci u32 reg, u32 mask, u32 param) 758c2ecf20Sopenharmony_ci{ 768c2ecf20Sopenharmony_ci u32 val; 778c2ecf20Sopenharmony_ci 788c2ecf20Sopenharmony_ci /* read previous data */ 798c2ecf20Sopenharmony_ci val = FIELD_PREP(TESTI_DAT_MASK, 1); 808c2ecf20Sopenharmony_ci val |= FIELD_PREP(TESTI_ADR_MASK, reg); 818c2ecf20Sopenharmony_ci uniphier_pciephy_testio_write(priv, val); 828c2ecf20Sopenharmony_ci val = readl(priv->base + PCL_PHY_TEST_O) & TESTO_DAT_MASK; 838c2ecf20Sopenharmony_ci 848c2ecf20Sopenharmony_ci /* update value */ 858c2ecf20Sopenharmony_ci val &= ~mask; 868c2ecf20Sopenharmony_ci val |= mask & param; 878c2ecf20Sopenharmony_ci val = FIELD_PREP(TESTI_DAT_MASK, val); 888c2ecf20Sopenharmony_ci val |= FIELD_PREP(TESTI_ADR_MASK, reg); 898c2ecf20Sopenharmony_ci uniphier_pciephy_testio_write(priv, val); 908c2ecf20Sopenharmony_ci uniphier_pciephy_testio_write(priv, val | TESTI_WR_EN); 918c2ecf20Sopenharmony_ci uniphier_pciephy_testio_write(priv, val); 928c2ecf20Sopenharmony_ci 938c2ecf20Sopenharmony_ci /* read current data as dummy */ 948c2ecf20Sopenharmony_ci val = FIELD_PREP(TESTI_DAT_MASK, 1); 958c2ecf20Sopenharmony_ci val |= FIELD_PREP(TESTI_ADR_MASK, reg); 968c2ecf20Sopenharmony_ci uniphier_pciephy_testio_write(priv, val); 978c2ecf20Sopenharmony_ci readl(priv->base + PCL_PHY_TEST_O); 988c2ecf20Sopenharmony_ci} 998c2ecf20Sopenharmony_ci 1008c2ecf20Sopenharmony_cistatic void uniphier_pciephy_assert(struct uniphier_pciephy_priv *priv) 1018c2ecf20Sopenharmony_ci{ 1028c2ecf20Sopenharmony_ci u32 val; 1038c2ecf20Sopenharmony_ci 1048c2ecf20Sopenharmony_ci val = readl(priv->base + PCL_PHY_RESET); 1058c2ecf20Sopenharmony_ci val &= ~PCL_PHY_RESET_N; 1068c2ecf20Sopenharmony_ci val |= PCL_PHY_RESET_N_MNMODE; 1078c2ecf20Sopenharmony_ci writel(val, priv->base + PCL_PHY_RESET); 1088c2ecf20Sopenharmony_ci} 1098c2ecf20Sopenharmony_ci 1108c2ecf20Sopenharmony_cistatic void uniphier_pciephy_deassert(struct uniphier_pciephy_priv *priv) 1118c2ecf20Sopenharmony_ci{ 1128c2ecf20Sopenharmony_ci u32 val; 1138c2ecf20Sopenharmony_ci 1148c2ecf20Sopenharmony_ci val = readl(priv->base + PCL_PHY_RESET); 1158c2ecf20Sopenharmony_ci val |= PCL_PHY_RESET_N_MNMODE | PCL_PHY_RESET_N; 1168c2ecf20Sopenharmony_ci writel(val, priv->base + PCL_PHY_RESET); 1178c2ecf20Sopenharmony_ci} 1188c2ecf20Sopenharmony_ci 1198c2ecf20Sopenharmony_cistatic int uniphier_pciephy_init(struct phy *phy) 1208c2ecf20Sopenharmony_ci{ 1218c2ecf20Sopenharmony_ci struct uniphier_pciephy_priv *priv = phy_get_drvdata(phy); 1228c2ecf20Sopenharmony_ci u32 val; 1238c2ecf20Sopenharmony_ci int ret; 1248c2ecf20Sopenharmony_ci 1258c2ecf20Sopenharmony_ci ret = clk_prepare_enable(priv->clk); 1268c2ecf20Sopenharmony_ci if (ret) 1278c2ecf20Sopenharmony_ci return ret; 1288c2ecf20Sopenharmony_ci 1298c2ecf20Sopenharmony_ci ret = clk_prepare_enable(priv->clk_gio); 1308c2ecf20Sopenharmony_ci if (ret) 1318c2ecf20Sopenharmony_ci goto out_clk_disable; 1328c2ecf20Sopenharmony_ci 1338c2ecf20Sopenharmony_ci ret = reset_control_deassert(priv->rst); 1348c2ecf20Sopenharmony_ci if (ret) 1358c2ecf20Sopenharmony_ci goto out_clk_gio_disable; 1368c2ecf20Sopenharmony_ci 1378c2ecf20Sopenharmony_ci ret = reset_control_deassert(priv->rst_gio); 1388c2ecf20Sopenharmony_ci if (ret) 1398c2ecf20Sopenharmony_ci goto out_rst_assert; 1408c2ecf20Sopenharmony_ci 1418c2ecf20Sopenharmony_ci /* support only 1 port */ 1428c2ecf20Sopenharmony_ci val = readl(priv->base + PCL_PHY_CLKCTRL); 1438c2ecf20Sopenharmony_ci val &= ~PORT_SEL_MASK; 1448c2ecf20Sopenharmony_ci val |= PORT_SEL_1; 1458c2ecf20Sopenharmony_ci writel(val, priv->base + PCL_PHY_CLKCTRL); 1468c2ecf20Sopenharmony_ci 1478c2ecf20Sopenharmony_ci /* legacy controller doesn't have phy_reset and parameters */ 1488c2ecf20Sopenharmony_ci if (priv->data->is_legacy) 1498c2ecf20Sopenharmony_ci return 0; 1508c2ecf20Sopenharmony_ci 1518c2ecf20Sopenharmony_ci uniphier_pciephy_set_param(priv, PCL_PHY_R00, 1528c2ecf20Sopenharmony_ci RX_EQ_ADJ_EN, RX_EQ_ADJ_EN); 1538c2ecf20Sopenharmony_ci uniphier_pciephy_set_param(priv, PCL_PHY_R06, RX_EQ_ADJ, 1548c2ecf20Sopenharmony_ci FIELD_PREP(RX_EQ_ADJ, RX_EQ_ADJ_VAL)); 1558c2ecf20Sopenharmony_ci uniphier_pciephy_set_param(priv, PCL_PHY_R26, VCO_CTRL, 1568c2ecf20Sopenharmony_ci FIELD_PREP(VCO_CTRL, VCO_CTRL_INIT_VAL)); 1578c2ecf20Sopenharmony_ci usleep_range(1, 10); 1588c2ecf20Sopenharmony_ci 1598c2ecf20Sopenharmony_ci uniphier_pciephy_deassert(priv); 1608c2ecf20Sopenharmony_ci usleep_range(1, 10); 1618c2ecf20Sopenharmony_ci 1628c2ecf20Sopenharmony_ci return 0; 1638c2ecf20Sopenharmony_ci 1648c2ecf20Sopenharmony_ciout_rst_assert: 1658c2ecf20Sopenharmony_ci reset_control_assert(priv->rst); 1668c2ecf20Sopenharmony_ciout_clk_gio_disable: 1678c2ecf20Sopenharmony_ci clk_disable_unprepare(priv->clk_gio); 1688c2ecf20Sopenharmony_ciout_clk_disable: 1698c2ecf20Sopenharmony_ci clk_disable_unprepare(priv->clk); 1708c2ecf20Sopenharmony_ci 1718c2ecf20Sopenharmony_ci return ret; 1728c2ecf20Sopenharmony_ci} 1738c2ecf20Sopenharmony_ci 1748c2ecf20Sopenharmony_cistatic int uniphier_pciephy_exit(struct phy *phy) 1758c2ecf20Sopenharmony_ci{ 1768c2ecf20Sopenharmony_ci struct uniphier_pciephy_priv *priv = phy_get_drvdata(phy); 1778c2ecf20Sopenharmony_ci 1788c2ecf20Sopenharmony_ci if (!priv->data->is_legacy) 1798c2ecf20Sopenharmony_ci uniphier_pciephy_assert(priv); 1808c2ecf20Sopenharmony_ci reset_control_assert(priv->rst_gio); 1818c2ecf20Sopenharmony_ci reset_control_assert(priv->rst); 1828c2ecf20Sopenharmony_ci clk_disable_unprepare(priv->clk_gio); 1838c2ecf20Sopenharmony_ci clk_disable_unprepare(priv->clk); 1848c2ecf20Sopenharmony_ci 1858c2ecf20Sopenharmony_ci return 0; 1868c2ecf20Sopenharmony_ci} 1878c2ecf20Sopenharmony_ci 1888c2ecf20Sopenharmony_cistatic const struct phy_ops uniphier_pciephy_ops = { 1898c2ecf20Sopenharmony_ci .init = uniphier_pciephy_init, 1908c2ecf20Sopenharmony_ci .exit = uniphier_pciephy_exit, 1918c2ecf20Sopenharmony_ci .owner = THIS_MODULE, 1928c2ecf20Sopenharmony_ci}; 1938c2ecf20Sopenharmony_ci 1948c2ecf20Sopenharmony_cistatic int uniphier_pciephy_probe(struct platform_device *pdev) 1958c2ecf20Sopenharmony_ci{ 1968c2ecf20Sopenharmony_ci struct uniphier_pciephy_priv *priv; 1978c2ecf20Sopenharmony_ci struct phy_provider *phy_provider; 1988c2ecf20Sopenharmony_ci struct device *dev = &pdev->dev; 1998c2ecf20Sopenharmony_ci struct regmap *regmap; 2008c2ecf20Sopenharmony_ci struct phy *phy; 2018c2ecf20Sopenharmony_ci 2028c2ecf20Sopenharmony_ci priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); 2038c2ecf20Sopenharmony_ci if (!priv) 2048c2ecf20Sopenharmony_ci return -ENOMEM; 2058c2ecf20Sopenharmony_ci 2068c2ecf20Sopenharmony_ci priv->data = of_device_get_match_data(dev); 2078c2ecf20Sopenharmony_ci if (WARN_ON(!priv->data)) 2088c2ecf20Sopenharmony_ci return -EINVAL; 2098c2ecf20Sopenharmony_ci 2108c2ecf20Sopenharmony_ci priv->dev = dev; 2118c2ecf20Sopenharmony_ci 2128c2ecf20Sopenharmony_ci priv->base = devm_platform_ioremap_resource(pdev, 0); 2138c2ecf20Sopenharmony_ci if (IS_ERR(priv->base)) 2148c2ecf20Sopenharmony_ci return PTR_ERR(priv->base); 2158c2ecf20Sopenharmony_ci 2168c2ecf20Sopenharmony_ci if (priv->data->is_legacy) { 2178c2ecf20Sopenharmony_ci priv->clk_gio = devm_clk_get(dev, "gio"); 2188c2ecf20Sopenharmony_ci if (IS_ERR(priv->clk_gio)) 2198c2ecf20Sopenharmony_ci return PTR_ERR(priv->clk_gio); 2208c2ecf20Sopenharmony_ci 2218c2ecf20Sopenharmony_ci priv->rst_gio = 2228c2ecf20Sopenharmony_ci devm_reset_control_get_shared(dev, "gio"); 2238c2ecf20Sopenharmony_ci if (IS_ERR(priv->rst_gio)) 2248c2ecf20Sopenharmony_ci return PTR_ERR(priv->rst_gio); 2258c2ecf20Sopenharmony_ci 2268c2ecf20Sopenharmony_ci priv->clk = devm_clk_get(dev, "link"); 2278c2ecf20Sopenharmony_ci if (IS_ERR(priv->clk)) 2288c2ecf20Sopenharmony_ci return PTR_ERR(priv->clk); 2298c2ecf20Sopenharmony_ci 2308c2ecf20Sopenharmony_ci priv->rst = devm_reset_control_get_shared(dev, "link"); 2318c2ecf20Sopenharmony_ci if (IS_ERR(priv->rst)) 2328c2ecf20Sopenharmony_ci return PTR_ERR(priv->rst); 2338c2ecf20Sopenharmony_ci } else { 2348c2ecf20Sopenharmony_ci priv->clk = devm_clk_get(dev, NULL); 2358c2ecf20Sopenharmony_ci if (IS_ERR(priv->clk)) 2368c2ecf20Sopenharmony_ci return PTR_ERR(priv->clk); 2378c2ecf20Sopenharmony_ci 2388c2ecf20Sopenharmony_ci priv->rst = devm_reset_control_get_shared(dev, NULL); 2398c2ecf20Sopenharmony_ci if (IS_ERR(priv->rst)) 2408c2ecf20Sopenharmony_ci return PTR_ERR(priv->rst); 2418c2ecf20Sopenharmony_ci } 2428c2ecf20Sopenharmony_ci 2438c2ecf20Sopenharmony_ci phy = devm_phy_create(dev, dev->of_node, &uniphier_pciephy_ops); 2448c2ecf20Sopenharmony_ci if (IS_ERR(phy)) 2458c2ecf20Sopenharmony_ci return PTR_ERR(phy); 2468c2ecf20Sopenharmony_ci 2478c2ecf20Sopenharmony_ci regmap = syscon_regmap_lookup_by_phandle(dev->of_node, 2488c2ecf20Sopenharmony_ci "socionext,syscon"); 2498c2ecf20Sopenharmony_ci if (!IS_ERR(regmap) && priv->data->set_phymode) 2508c2ecf20Sopenharmony_ci priv->data->set_phymode(regmap); 2518c2ecf20Sopenharmony_ci 2528c2ecf20Sopenharmony_ci phy_set_drvdata(phy, priv); 2538c2ecf20Sopenharmony_ci phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate); 2548c2ecf20Sopenharmony_ci 2558c2ecf20Sopenharmony_ci return PTR_ERR_OR_ZERO(phy_provider); 2568c2ecf20Sopenharmony_ci} 2578c2ecf20Sopenharmony_ci 2588c2ecf20Sopenharmony_cistatic void uniphier_pciephy_ld20_setmode(struct regmap *regmap) 2598c2ecf20Sopenharmony_ci{ 2608c2ecf20Sopenharmony_ci regmap_update_bits(regmap, SG_USBPCIESEL, 2618c2ecf20Sopenharmony_ci SG_USBPCIESEL_PCIE, SG_USBPCIESEL_PCIE); 2628c2ecf20Sopenharmony_ci} 2638c2ecf20Sopenharmony_ci 2648c2ecf20Sopenharmony_cistatic const struct uniphier_pciephy_soc_data uniphier_pro5_data = { 2658c2ecf20Sopenharmony_ci .is_legacy = true, 2668c2ecf20Sopenharmony_ci}; 2678c2ecf20Sopenharmony_ci 2688c2ecf20Sopenharmony_cistatic const struct uniphier_pciephy_soc_data uniphier_ld20_data = { 2698c2ecf20Sopenharmony_ci .is_legacy = false, 2708c2ecf20Sopenharmony_ci .set_phymode = uniphier_pciephy_ld20_setmode, 2718c2ecf20Sopenharmony_ci}; 2728c2ecf20Sopenharmony_ci 2738c2ecf20Sopenharmony_cistatic const struct uniphier_pciephy_soc_data uniphier_pxs3_data = { 2748c2ecf20Sopenharmony_ci .is_legacy = false, 2758c2ecf20Sopenharmony_ci}; 2768c2ecf20Sopenharmony_ci 2778c2ecf20Sopenharmony_cistatic const struct of_device_id uniphier_pciephy_match[] = { 2788c2ecf20Sopenharmony_ci { 2798c2ecf20Sopenharmony_ci .compatible = "socionext,uniphier-pro5-pcie-phy", 2808c2ecf20Sopenharmony_ci .data = &uniphier_pro5_data, 2818c2ecf20Sopenharmony_ci }, 2828c2ecf20Sopenharmony_ci { 2838c2ecf20Sopenharmony_ci .compatible = "socionext,uniphier-ld20-pcie-phy", 2848c2ecf20Sopenharmony_ci .data = &uniphier_ld20_data, 2858c2ecf20Sopenharmony_ci }, 2868c2ecf20Sopenharmony_ci { 2878c2ecf20Sopenharmony_ci .compatible = "socionext,uniphier-pxs3-pcie-phy", 2888c2ecf20Sopenharmony_ci .data = &uniphier_pxs3_data, 2898c2ecf20Sopenharmony_ci }, 2908c2ecf20Sopenharmony_ci { /* sentinel */ }, 2918c2ecf20Sopenharmony_ci}; 2928c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(of, uniphier_pciephy_match); 2938c2ecf20Sopenharmony_ci 2948c2ecf20Sopenharmony_cistatic struct platform_driver uniphier_pciephy_driver = { 2958c2ecf20Sopenharmony_ci .probe = uniphier_pciephy_probe, 2968c2ecf20Sopenharmony_ci .driver = { 2978c2ecf20Sopenharmony_ci .name = "uniphier-pcie-phy", 2988c2ecf20Sopenharmony_ci .of_match_table = uniphier_pciephy_match, 2998c2ecf20Sopenharmony_ci }, 3008c2ecf20Sopenharmony_ci}; 3018c2ecf20Sopenharmony_cimodule_platform_driver(uniphier_pciephy_driver); 3028c2ecf20Sopenharmony_ci 3038c2ecf20Sopenharmony_ciMODULE_AUTHOR("Kunihiko Hayashi <hayashi.kunihiko@socionext.com>"); 3048c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("UniPhier PHY driver for PCIe controller"); 3058c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL v2"); 306