1*d2912cb1SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only 2dea54fbaSHauke Mehrtens /* 3dea54fbaSHauke Mehrtens * Lantiq XWAY SoC RCU module based USB 1.1/2.0 PHY driver 4dea54fbaSHauke Mehrtens * 5dea54fbaSHauke Mehrtens * Copyright (C) 2016 Martin Blumenstingl <martin.blumenstingl@googlemail.com> 6dea54fbaSHauke Mehrtens * Copyright (C) 2017 Hauke Mehrtens <hauke@hauke-m.de> 7dea54fbaSHauke Mehrtens */ 8dea54fbaSHauke Mehrtens 9dea54fbaSHauke Mehrtens #include <linux/clk.h> 10dea54fbaSHauke Mehrtens #include <linux/delay.h> 11dea54fbaSHauke Mehrtens #include <linux/mfd/syscon.h> 12dea54fbaSHauke Mehrtens #include <linux/module.h> 13dea54fbaSHauke Mehrtens #include <linux/of.h> 14dea54fbaSHauke Mehrtens #include <linux/of_address.h> 15dea54fbaSHauke Mehrtens #include <linux/of_device.h> 16dea54fbaSHauke Mehrtens #include <linux/phy/phy.h> 17dea54fbaSHauke Mehrtens #include <linux/platform_device.h> 18dea54fbaSHauke Mehrtens #include <linux/property.h> 19dea54fbaSHauke Mehrtens #include <linux/regmap.h> 20dea54fbaSHauke Mehrtens #include <linux/reset.h> 21dea54fbaSHauke Mehrtens 22dea54fbaSHauke Mehrtens /* Transmitter HS Pre-Emphasis Enable */ 23dea54fbaSHauke Mehrtens #define RCU_CFG1_TX_PEE BIT(0) 24dea54fbaSHauke Mehrtens /* Disconnect Threshold */ 25dea54fbaSHauke Mehrtens #define RCU_CFG1_DIS_THR_MASK 0x00038000 26dea54fbaSHauke Mehrtens #define RCU_CFG1_DIS_THR_SHIFT 15 27dea54fbaSHauke Mehrtens 28dea54fbaSHauke Mehrtens struct ltq_rcu_usb2_bits { 29dea54fbaSHauke Mehrtens u8 hostmode; 30dea54fbaSHauke Mehrtens u8 slave_endianness; 31dea54fbaSHauke Mehrtens u8 host_endianness; 32dea54fbaSHauke Mehrtens bool have_ana_cfg; 33dea54fbaSHauke Mehrtens }; 34dea54fbaSHauke Mehrtens 35dea54fbaSHauke Mehrtens struct ltq_rcu_usb2_priv { 36dea54fbaSHauke Mehrtens struct regmap *regmap; 37dea54fbaSHauke Mehrtens unsigned int phy_reg_offset; 38dea54fbaSHauke Mehrtens unsigned int ana_cfg1_reg_offset; 39dea54fbaSHauke Mehrtens const struct ltq_rcu_usb2_bits *reg_bits; 40dea54fbaSHauke Mehrtens struct device *dev; 41dea54fbaSHauke Mehrtens struct phy *phy; 42dea54fbaSHauke Mehrtens struct clk *phy_gate_clk; 43dea54fbaSHauke Mehrtens struct reset_control *ctrl_reset; 44dea54fbaSHauke Mehrtens struct reset_control *phy_reset; 45dea54fbaSHauke Mehrtens }; 46dea54fbaSHauke Mehrtens 47dea54fbaSHauke Mehrtens static const struct ltq_rcu_usb2_bits xway_rcu_usb2_reg_bits = { 48dea54fbaSHauke Mehrtens .hostmode = 11, 49dea54fbaSHauke Mehrtens .slave_endianness = 9, 50dea54fbaSHauke Mehrtens .host_endianness = 10, 51dea54fbaSHauke Mehrtens .have_ana_cfg = false, 52dea54fbaSHauke Mehrtens }; 53dea54fbaSHauke Mehrtens 54dea54fbaSHauke Mehrtens static const struct ltq_rcu_usb2_bits xrx100_rcu_usb2_reg_bits = { 55dea54fbaSHauke Mehrtens .hostmode = 11, 56dea54fbaSHauke Mehrtens .slave_endianness = 17, 57dea54fbaSHauke Mehrtens .host_endianness = 10, 58dea54fbaSHauke Mehrtens .have_ana_cfg = false, 59dea54fbaSHauke Mehrtens }; 60dea54fbaSHauke Mehrtens 61dea54fbaSHauke Mehrtens static const struct ltq_rcu_usb2_bits xrx200_rcu_usb2_reg_bits = { 62dea54fbaSHauke Mehrtens .hostmode = 11, 63dea54fbaSHauke Mehrtens .slave_endianness = 9, 64dea54fbaSHauke Mehrtens .host_endianness = 10, 65dea54fbaSHauke Mehrtens .have_ana_cfg = true, 66dea54fbaSHauke Mehrtens }; 67dea54fbaSHauke Mehrtens 68dea54fbaSHauke Mehrtens static const struct of_device_id ltq_rcu_usb2_phy_of_match[] = { 69dea54fbaSHauke Mehrtens { 70dea54fbaSHauke Mehrtens .compatible = "lantiq,ase-usb2-phy", 71dea54fbaSHauke Mehrtens .data = &xway_rcu_usb2_reg_bits, 72dea54fbaSHauke Mehrtens }, 73dea54fbaSHauke Mehrtens { 74dea54fbaSHauke Mehrtens .compatible = "lantiq,danube-usb2-phy", 75dea54fbaSHauke Mehrtens .data = &xway_rcu_usb2_reg_bits, 76dea54fbaSHauke Mehrtens }, 77dea54fbaSHauke Mehrtens { 78dea54fbaSHauke Mehrtens .compatible = "lantiq,xrx100-usb2-phy", 79dea54fbaSHauke Mehrtens .data = &xrx100_rcu_usb2_reg_bits, 80dea54fbaSHauke Mehrtens }, 81dea54fbaSHauke Mehrtens { 82dea54fbaSHauke Mehrtens .compatible = "lantiq,xrx200-usb2-phy", 83dea54fbaSHauke Mehrtens .data = &xrx200_rcu_usb2_reg_bits, 84dea54fbaSHauke Mehrtens }, 85dea54fbaSHauke Mehrtens { 86dea54fbaSHauke Mehrtens .compatible = "lantiq,xrx300-usb2-phy", 87dea54fbaSHauke Mehrtens .data = &xrx200_rcu_usb2_reg_bits, 88dea54fbaSHauke Mehrtens }, 89dea54fbaSHauke Mehrtens { }, 90dea54fbaSHauke Mehrtens }; 91dea54fbaSHauke Mehrtens MODULE_DEVICE_TABLE(of, ltq_rcu_usb2_phy_of_match); 92dea54fbaSHauke Mehrtens 93dea54fbaSHauke Mehrtens static int ltq_rcu_usb2_phy_init(struct phy *phy) 94dea54fbaSHauke Mehrtens { 95dea54fbaSHauke Mehrtens struct ltq_rcu_usb2_priv *priv = phy_get_drvdata(phy); 96dea54fbaSHauke Mehrtens 97dea54fbaSHauke Mehrtens if (priv->reg_bits->have_ana_cfg) { 98dea54fbaSHauke Mehrtens regmap_update_bits(priv->regmap, priv->ana_cfg1_reg_offset, 99dea54fbaSHauke Mehrtens RCU_CFG1_TX_PEE, RCU_CFG1_TX_PEE); 100dea54fbaSHauke Mehrtens regmap_update_bits(priv->regmap, priv->ana_cfg1_reg_offset, 101dea54fbaSHauke Mehrtens RCU_CFG1_DIS_THR_MASK, 7 << RCU_CFG1_DIS_THR_SHIFT); 102dea54fbaSHauke Mehrtens } 103dea54fbaSHauke Mehrtens 104dea54fbaSHauke Mehrtens /* Configure core to host mode */ 105dea54fbaSHauke Mehrtens regmap_update_bits(priv->regmap, priv->phy_reg_offset, 106dea54fbaSHauke Mehrtens BIT(priv->reg_bits->hostmode), 0); 107dea54fbaSHauke Mehrtens 108dea54fbaSHauke Mehrtens /* Select DMA endianness (Host-endian: big-endian) */ 109dea54fbaSHauke Mehrtens regmap_update_bits(priv->regmap, priv->phy_reg_offset, 110dea54fbaSHauke Mehrtens BIT(priv->reg_bits->slave_endianness), 0); 111dea54fbaSHauke Mehrtens regmap_update_bits(priv->regmap, priv->phy_reg_offset, 112dea54fbaSHauke Mehrtens BIT(priv->reg_bits->host_endianness), 113dea54fbaSHauke Mehrtens BIT(priv->reg_bits->host_endianness)); 114dea54fbaSHauke Mehrtens 115dea54fbaSHauke Mehrtens return 0; 116dea54fbaSHauke Mehrtens } 117dea54fbaSHauke Mehrtens 118dea54fbaSHauke Mehrtens static int ltq_rcu_usb2_phy_power_on(struct phy *phy) 119dea54fbaSHauke Mehrtens { 120dea54fbaSHauke Mehrtens struct ltq_rcu_usb2_priv *priv = phy_get_drvdata(phy); 121dea54fbaSHauke Mehrtens struct device *dev = priv->dev; 122dea54fbaSHauke Mehrtens int ret; 123dea54fbaSHauke Mehrtens 124dea54fbaSHauke Mehrtens reset_control_deassert(priv->phy_reset); 125dea54fbaSHauke Mehrtens 126dea54fbaSHauke Mehrtens ret = clk_prepare_enable(priv->phy_gate_clk); 127dea54fbaSHauke Mehrtens if (ret) 128dea54fbaSHauke Mehrtens dev_err(dev, "failed to enable PHY gate\n"); 129dea54fbaSHauke Mehrtens 130dea54fbaSHauke Mehrtens return ret; 131dea54fbaSHauke Mehrtens } 132dea54fbaSHauke Mehrtens 133dea54fbaSHauke Mehrtens static int ltq_rcu_usb2_phy_power_off(struct phy *phy) 134dea54fbaSHauke Mehrtens { 135dea54fbaSHauke Mehrtens struct ltq_rcu_usb2_priv *priv = phy_get_drvdata(phy); 136dea54fbaSHauke Mehrtens 137dea54fbaSHauke Mehrtens reset_control_assert(priv->phy_reset); 138dea54fbaSHauke Mehrtens 139dea54fbaSHauke Mehrtens clk_disable_unprepare(priv->phy_gate_clk); 140dea54fbaSHauke Mehrtens 141dea54fbaSHauke Mehrtens return 0; 142dea54fbaSHauke Mehrtens } 143dea54fbaSHauke Mehrtens 144dea54fbaSHauke Mehrtens static struct phy_ops ltq_rcu_usb2_phy_ops = { 145dea54fbaSHauke Mehrtens .init = ltq_rcu_usb2_phy_init, 146dea54fbaSHauke Mehrtens .power_on = ltq_rcu_usb2_phy_power_on, 147dea54fbaSHauke Mehrtens .power_off = ltq_rcu_usb2_phy_power_off, 148dea54fbaSHauke Mehrtens .owner = THIS_MODULE, 149dea54fbaSHauke Mehrtens }; 150dea54fbaSHauke Mehrtens 151dea54fbaSHauke Mehrtens static int ltq_rcu_usb2_of_parse(struct ltq_rcu_usb2_priv *priv, 152dea54fbaSHauke Mehrtens struct platform_device *pdev) 153dea54fbaSHauke Mehrtens { 154dea54fbaSHauke Mehrtens struct device *dev = priv->dev; 155dea54fbaSHauke Mehrtens const __be32 *offset; 156dea54fbaSHauke Mehrtens 157dea54fbaSHauke Mehrtens priv->reg_bits = of_device_get_match_data(dev); 158dea54fbaSHauke Mehrtens 159dea54fbaSHauke Mehrtens priv->regmap = syscon_node_to_regmap(dev->of_node->parent); 160dea54fbaSHauke Mehrtens if (IS_ERR(priv->regmap)) { 161dea54fbaSHauke Mehrtens dev_err(dev, "Failed to lookup RCU regmap\n"); 162dea54fbaSHauke Mehrtens return PTR_ERR(priv->regmap); 163dea54fbaSHauke Mehrtens } 164dea54fbaSHauke Mehrtens 165dea54fbaSHauke Mehrtens offset = of_get_address(dev->of_node, 0, NULL, NULL); 166dea54fbaSHauke Mehrtens if (!offset) { 167dea54fbaSHauke Mehrtens dev_err(dev, "Failed to get RCU PHY reg offset\n"); 168dea54fbaSHauke Mehrtens return -ENOENT; 169dea54fbaSHauke Mehrtens } 170dea54fbaSHauke Mehrtens priv->phy_reg_offset = __be32_to_cpu(*offset); 171dea54fbaSHauke Mehrtens 172dea54fbaSHauke Mehrtens if (priv->reg_bits->have_ana_cfg) { 173dea54fbaSHauke Mehrtens offset = of_get_address(dev->of_node, 1, NULL, NULL); 174dea54fbaSHauke Mehrtens if (!offset) { 175dea54fbaSHauke Mehrtens dev_err(dev, "Failed to get RCU ANA CFG1 reg offset\n"); 176dea54fbaSHauke Mehrtens return -ENOENT; 177dea54fbaSHauke Mehrtens } 178dea54fbaSHauke Mehrtens priv->ana_cfg1_reg_offset = __be32_to_cpu(*offset); 179dea54fbaSHauke Mehrtens } 180dea54fbaSHauke Mehrtens 181dea54fbaSHauke Mehrtens priv->phy_gate_clk = devm_clk_get(dev, "phy"); 182dea54fbaSHauke Mehrtens if (IS_ERR(priv->phy_gate_clk)) { 183dea54fbaSHauke Mehrtens dev_err(dev, "Unable to get USB phy gate clk\n"); 184dea54fbaSHauke Mehrtens return PTR_ERR(priv->phy_gate_clk); 185dea54fbaSHauke Mehrtens } 186dea54fbaSHauke Mehrtens 187dea54fbaSHauke Mehrtens priv->ctrl_reset = devm_reset_control_get_shared(dev, "ctrl"); 188dea54fbaSHauke Mehrtens if (IS_ERR(priv->ctrl_reset)) { 189dea54fbaSHauke Mehrtens if (PTR_ERR(priv->ctrl_reset) != -EPROBE_DEFER) 190dea54fbaSHauke Mehrtens dev_err(dev, "failed to get 'ctrl' reset\n"); 191dea54fbaSHauke Mehrtens return PTR_ERR(priv->ctrl_reset); 192dea54fbaSHauke Mehrtens } 193dea54fbaSHauke Mehrtens 194dea54fbaSHauke Mehrtens priv->phy_reset = devm_reset_control_get_optional(dev, "phy"); 195dea54fbaSHauke Mehrtens 1969be08a27Szhong jiang return PTR_ERR_OR_ZERO(priv->phy_reset); 197dea54fbaSHauke Mehrtens } 198dea54fbaSHauke Mehrtens 199dea54fbaSHauke Mehrtens static int ltq_rcu_usb2_phy_probe(struct platform_device *pdev) 200dea54fbaSHauke Mehrtens { 201dea54fbaSHauke Mehrtens struct device *dev = &pdev->dev; 202dea54fbaSHauke Mehrtens struct ltq_rcu_usb2_priv *priv; 203dea54fbaSHauke Mehrtens struct phy_provider *provider; 204dea54fbaSHauke Mehrtens int ret; 205dea54fbaSHauke Mehrtens 206dea54fbaSHauke Mehrtens priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); 207dea54fbaSHauke Mehrtens if (!priv) 208dea54fbaSHauke Mehrtens return -ENOMEM; 209dea54fbaSHauke Mehrtens 210dea54fbaSHauke Mehrtens priv->dev = dev; 211dea54fbaSHauke Mehrtens 212dea54fbaSHauke Mehrtens ret = ltq_rcu_usb2_of_parse(priv, pdev); 213dea54fbaSHauke Mehrtens if (ret) 214dea54fbaSHauke Mehrtens return ret; 215dea54fbaSHauke Mehrtens 216dea54fbaSHauke Mehrtens /* Reset USB core through reset controller */ 217dea54fbaSHauke Mehrtens reset_control_deassert(priv->ctrl_reset); 218dea54fbaSHauke Mehrtens 219dea54fbaSHauke Mehrtens reset_control_assert(priv->phy_reset); 220dea54fbaSHauke Mehrtens 221dea54fbaSHauke Mehrtens priv->phy = devm_phy_create(dev, dev->of_node, <q_rcu_usb2_phy_ops); 222dea54fbaSHauke Mehrtens if (IS_ERR(priv->phy)) { 223dea54fbaSHauke Mehrtens dev_err(dev, "failed to create PHY\n"); 224dea54fbaSHauke Mehrtens return PTR_ERR(priv->phy); 225dea54fbaSHauke Mehrtens } 226dea54fbaSHauke Mehrtens 227dea54fbaSHauke Mehrtens phy_set_drvdata(priv->phy, priv); 228dea54fbaSHauke Mehrtens 229dea54fbaSHauke Mehrtens provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate); 230dea54fbaSHauke Mehrtens if (IS_ERR(provider)) 231dea54fbaSHauke Mehrtens return PTR_ERR(provider); 232dea54fbaSHauke Mehrtens 233dea54fbaSHauke Mehrtens dev_set_drvdata(priv->dev, priv); 234dea54fbaSHauke Mehrtens return 0; 235dea54fbaSHauke Mehrtens } 236dea54fbaSHauke Mehrtens 237dea54fbaSHauke Mehrtens static struct platform_driver ltq_rcu_usb2_phy_driver = { 238dea54fbaSHauke Mehrtens .probe = ltq_rcu_usb2_phy_probe, 239dea54fbaSHauke Mehrtens .driver = { 240dea54fbaSHauke Mehrtens .name = "lantiq-rcu-usb2-phy", 241dea54fbaSHauke Mehrtens .of_match_table = ltq_rcu_usb2_phy_of_match, 242dea54fbaSHauke Mehrtens } 243dea54fbaSHauke Mehrtens }; 244dea54fbaSHauke Mehrtens module_platform_driver(ltq_rcu_usb2_phy_driver); 245dea54fbaSHauke Mehrtens 246dea54fbaSHauke Mehrtens MODULE_AUTHOR("Martin Blumenstingl <martin.blumenstingl@googlemail.com>"); 247dea54fbaSHauke Mehrtens MODULE_DESCRIPTION("Lantiq XWAY USB2 PHY driver"); 248dea54fbaSHauke Mehrtens MODULE_LICENSE("GPL v2"); 249