Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

at v5.4 240 lines 6.3 kB view raw
1// SPDX-License-Identifier: GPL-2.0 2/* 3 * phy-uniphier-pcie.c - PHY driver for UniPhier PCIe controller 4 * Copyright 2018, Socionext Inc. 5 * Author: Kunihiko Hayashi <hayashi.kunihiko@socionext.com> 6 */ 7 8#include <linux/bitops.h> 9#include <linux/bitfield.h> 10#include <linux/clk.h> 11#include <linux/iopoll.h> 12#include <linux/mfd/syscon.h> 13#include <linux/module.h> 14#include <linux/of_device.h> 15#include <linux/phy/phy.h> 16#include <linux/platform_device.h> 17#include <linux/regmap.h> 18#include <linux/reset.h> 19#include <linux/resource.h> 20 21/* PHY */ 22#define PCL_PHY_TEST_I 0x2000 23#define PCL_PHY_TEST_O 0x2004 24#define TESTI_DAT_MASK GENMASK(13, 6) 25#define TESTI_ADR_MASK GENMASK(5, 1) 26#define TESTI_WR_EN BIT(0) 27 28#define PCL_PHY_RESET 0x200c 29#define PCL_PHY_RESET_N_MNMODE BIT(8) /* =1:manual */ 30#define PCL_PHY_RESET_N BIT(0) /* =1:deasssert */ 31 32/* SG */ 33#define SG_USBPCIESEL 0x590 34#define SG_USBPCIESEL_PCIE BIT(0) 35 36#define PCL_PHY_R00 0 37#define RX_EQ_ADJ_EN BIT(3) /* enable for EQ adjustment */ 38#define PCL_PHY_R06 6 39#define RX_EQ_ADJ GENMASK(5, 0) /* EQ adjustment value */ 40#define RX_EQ_ADJ_VAL 0 41#define PCL_PHY_R26 26 42#define VCO_CTRL GENMASK(7, 4) /* Tx VCO adjustment value */ 43#define VCO_CTRL_INIT_VAL 5 44 45struct uniphier_pciephy_priv { 46 void __iomem *base; 47 struct device *dev; 48 struct clk *clk; 49 struct reset_control *rst; 50 const struct uniphier_pciephy_soc_data *data; 51}; 52 53struct uniphier_pciephy_soc_data { 54 bool has_syscon; 55}; 56 57static void uniphier_pciephy_testio_write(struct uniphier_pciephy_priv *priv, 58 u32 data) 59{ 60 /* need to read TESTO twice after accessing TESTI */ 61 writel(data, priv->base + PCL_PHY_TEST_I); 62 readl(priv->base + PCL_PHY_TEST_O); 63 readl(priv->base + PCL_PHY_TEST_O); 64} 65 66static void uniphier_pciephy_set_param(struct uniphier_pciephy_priv *priv, 67 u32 reg, u32 mask, u32 param) 68{ 69 u32 val; 70 71 /* read previous data */ 72 val = FIELD_PREP(TESTI_DAT_MASK, 1); 73 val |= FIELD_PREP(TESTI_ADR_MASK, reg); 74 uniphier_pciephy_testio_write(priv, val); 75 val = readl(priv->base + PCL_PHY_TEST_O); 76 77 /* update value */ 78 val &= ~FIELD_PREP(TESTI_DAT_MASK, mask); 79 val = FIELD_PREP(TESTI_DAT_MASK, mask & param); 80 val |= FIELD_PREP(TESTI_ADR_MASK, reg); 81 uniphier_pciephy_testio_write(priv, val); 82 uniphier_pciephy_testio_write(priv, val | TESTI_WR_EN); 83 uniphier_pciephy_testio_write(priv, val); 84 85 /* read current data as dummy */ 86 val = FIELD_PREP(TESTI_DAT_MASK, 1); 87 val |= FIELD_PREP(TESTI_ADR_MASK, reg); 88 uniphier_pciephy_testio_write(priv, val); 89 readl(priv->base + PCL_PHY_TEST_O); 90} 91 92static void uniphier_pciephy_assert(struct uniphier_pciephy_priv *priv) 93{ 94 u32 val; 95 96 val = readl(priv->base + PCL_PHY_RESET); 97 val &= ~PCL_PHY_RESET_N; 98 val |= PCL_PHY_RESET_N_MNMODE; 99 writel(val, priv->base + PCL_PHY_RESET); 100} 101 102static void uniphier_pciephy_deassert(struct uniphier_pciephy_priv *priv) 103{ 104 u32 val; 105 106 val = readl(priv->base + PCL_PHY_RESET); 107 val |= PCL_PHY_RESET_N_MNMODE | PCL_PHY_RESET_N; 108 writel(val, priv->base + PCL_PHY_RESET); 109} 110 111static int uniphier_pciephy_init(struct phy *phy) 112{ 113 struct uniphier_pciephy_priv *priv = phy_get_drvdata(phy); 114 int ret; 115 116 ret = clk_prepare_enable(priv->clk); 117 if (ret) 118 return ret; 119 120 ret = reset_control_deassert(priv->rst); 121 if (ret) 122 goto out_clk_disable; 123 124 uniphier_pciephy_set_param(priv, PCL_PHY_R00, 125 RX_EQ_ADJ_EN, RX_EQ_ADJ_EN); 126 uniphier_pciephy_set_param(priv, PCL_PHY_R06, RX_EQ_ADJ, 127 FIELD_PREP(RX_EQ_ADJ, RX_EQ_ADJ_VAL)); 128 uniphier_pciephy_set_param(priv, PCL_PHY_R26, VCO_CTRL, 129 FIELD_PREP(VCO_CTRL, VCO_CTRL_INIT_VAL)); 130 usleep_range(1, 10); 131 132 uniphier_pciephy_deassert(priv); 133 usleep_range(1, 10); 134 135 return 0; 136 137out_clk_disable: 138 clk_disable_unprepare(priv->clk); 139 140 return ret; 141} 142 143static int uniphier_pciephy_exit(struct phy *phy) 144{ 145 struct uniphier_pciephy_priv *priv = phy_get_drvdata(phy); 146 147 uniphier_pciephy_assert(priv); 148 reset_control_assert(priv->rst); 149 clk_disable_unprepare(priv->clk); 150 151 return 0; 152} 153 154static const struct phy_ops uniphier_pciephy_ops = { 155 .init = uniphier_pciephy_init, 156 .exit = uniphier_pciephy_exit, 157 .owner = THIS_MODULE, 158}; 159 160static int uniphier_pciephy_probe(struct platform_device *pdev) 161{ 162 struct uniphier_pciephy_priv *priv; 163 struct phy_provider *phy_provider; 164 struct device *dev = &pdev->dev; 165 struct regmap *regmap; 166 struct resource *res; 167 struct phy *phy; 168 169 priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); 170 if (!priv) 171 return -ENOMEM; 172 173 priv->data = of_device_get_match_data(dev); 174 if (WARN_ON(!priv->data)) 175 return -EINVAL; 176 177 priv->dev = dev; 178 179 res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 180 priv->base = devm_ioremap_resource(dev, res); 181 if (IS_ERR(priv->base)) 182 return PTR_ERR(priv->base); 183 184 priv->clk = devm_clk_get(dev, NULL); 185 if (IS_ERR(priv->clk)) 186 return PTR_ERR(priv->clk); 187 188 priv->rst = devm_reset_control_get_shared(dev, NULL); 189 if (IS_ERR(priv->rst)) 190 return PTR_ERR(priv->rst); 191 192 phy = devm_phy_create(dev, dev->of_node, &uniphier_pciephy_ops); 193 if (IS_ERR(phy)) 194 return PTR_ERR(phy); 195 196 regmap = syscon_regmap_lookup_by_phandle(dev->of_node, 197 "socionext,syscon"); 198 if (!IS_ERR(regmap) && priv->data->has_syscon) 199 regmap_update_bits(regmap, SG_USBPCIESEL, 200 SG_USBPCIESEL_PCIE, SG_USBPCIESEL_PCIE); 201 202 phy_set_drvdata(phy, priv); 203 phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate); 204 205 return PTR_ERR_OR_ZERO(phy_provider); 206} 207 208static const struct uniphier_pciephy_soc_data uniphier_ld20_data = { 209 .has_syscon = true, 210}; 211 212static const struct uniphier_pciephy_soc_data uniphier_pxs3_data = { 213 .has_syscon = false, 214}; 215 216static const struct of_device_id uniphier_pciephy_match[] = { 217 { 218 .compatible = "socionext,uniphier-ld20-pcie-phy", 219 .data = &uniphier_ld20_data, 220 }, 221 { 222 .compatible = "socionext,uniphier-pxs3-pcie-phy", 223 .data = &uniphier_pxs3_data, 224 }, 225 { /* sentinel */ }, 226}; 227MODULE_DEVICE_TABLE(of, uniphier_pciephy_match); 228 229static struct platform_driver uniphier_pciephy_driver = { 230 .probe = uniphier_pciephy_probe, 231 .driver = { 232 .name = "uniphier-pcie-phy", 233 .of_match_table = uniphier_pciephy_match, 234 }, 235}; 236module_platform_driver(uniphier_pciephy_driver); 237 238MODULE_AUTHOR("Kunihiko Hayashi <hayashi.kunihiko@socionext.com>"); 239MODULE_DESCRIPTION("UniPhier PHY driver for PCIe controller"); 240MODULE_LICENSE("GPL v2");