1af3f5722SRemi Pommarel // SPDX-License-Identifier: GPL-2.0 2af3f5722SRemi Pommarel /* 3af3f5722SRemi Pommarel * Amlogic AXG MIPI + PCIE analog PHY driver 4af3f5722SRemi Pommarel * 5af3f5722SRemi Pommarel * Copyright (C) 2019 Remi Pommarel <repk@triplefau.lt> 6af3f5722SRemi Pommarel */ 78eff8b4eSNeil Armstrong #include <linux/bitfield.h> 88eff8b4eSNeil Armstrong #include <linux/bitops.h> 9af3f5722SRemi Pommarel #include <linux/module.h> 10af3f5722SRemi Pommarel #include <linux/phy/phy.h> 11af3f5722SRemi Pommarel #include <linux/regmap.h> 128eff8b4eSNeil Armstrong #include <linux/delay.h> 138eff8b4eSNeil Armstrong #include <linux/mfd/syscon.h> 14af3f5722SRemi Pommarel #include <linux/platform_device.h> 15af3f5722SRemi Pommarel #include <dt-bindings/phy/phy.h> 16af3f5722SRemi Pommarel 17af3f5722SRemi Pommarel #define HHI_MIPI_CNTL0 0x00 18af3f5722SRemi Pommarel #define HHI_MIPI_CNTL0_COMMON_BLOCK GENMASK(31, 28) 19af3f5722SRemi Pommarel #define HHI_MIPI_CNTL0_ENABLE BIT(29) 20af3f5722SRemi Pommarel #define HHI_MIPI_CNTL0_BANDGAP BIT(26) 218eff8b4eSNeil Armstrong #define HHI_MIPI_CNTL0_DIF_REF_CTL1 GENMASK(25, 16) 228eff8b4eSNeil Armstrong #define HHI_MIPI_CNTL0_DIF_REF_CTL0 GENMASK(15, 0) 23af3f5722SRemi Pommarel 248eff8b4eSNeil Armstrong #define HHI_MIPI_CNTL1 0x04 25af3f5722SRemi Pommarel #define HHI_MIPI_CNTL1_CH0_CML_PDR_EN BIT(12) 26af3f5722SRemi Pommarel #define HHI_MIPI_CNTL1_LP_ABILITY GENMASK(5, 4) 27af3f5722SRemi Pommarel #define HHI_MIPI_CNTL1_LP_RESISTER BIT(3) 28af3f5722SRemi Pommarel #define HHI_MIPI_CNTL1_INPUT_SETTING BIT(2) 29af3f5722SRemi Pommarel #define HHI_MIPI_CNTL1_INPUT_SEL BIT(1) 30af3f5722SRemi Pommarel #define HHI_MIPI_CNTL1_PRBS7_EN BIT(0) 31af3f5722SRemi Pommarel 328eff8b4eSNeil Armstrong #define HHI_MIPI_CNTL2 0x08 33af3f5722SRemi Pommarel #define HHI_MIPI_CNTL2_CH_PU GENMASK(31, 25) 34af3f5722SRemi Pommarel #define HHI_MIPI_CNTL2_CH_CTL GENMASK(24, 19) 35af3f5722SRemi Pommarel #define HHI_MIPI_CNTL2_CH0_DIGDR_EN BIT(18) 36af3f5722SRemi Pommarel #define HHI_MIPI_CNTL2_CH_DIGDR_EN BIT(17) 37af3f5722SRemi Pommarel #define HHI_MIPI_CNTL2_LPULPS_EN BIT(16) 388eff8b4eSNeil Armstrong #define HHI_MIPI_CNTL2_CH_EN GENMASK(15, 11) 39af3f5722SRemi Pommarel #define HHI_MIPI_CNTL2_CH0_LP_CTL GENMASK(10, 1) 40af3f5722SRemi Pommarel 414eed2812SNeil Armstrong #define DSI_LANE_0 BIT(4) 424eed2812SNeil Armstrong #define DSI_LANE_1 BIT(3) 434eed2812SNeil Armstrong #define DSI_LANE_CLK BIT(2) 444eed2812SNeil Armstrong #define DSI_LANE_2 BIT(1) 454eed2812SNeil Armstrong #define DSI_LANE_3 BIT(0) 468eff8b4eSNeil Armstrong 47af3f5722SRemi Pommarel struct phy_axg_mipi_pcie_analog_priv { 48af3f5722SRemi Pommarel struct phy *phy; 49af3f5722SRemi Pommarel struct regmap *regmap; 508eff8b4eSNeil Armstrong bool dsi_configured; 518eff8b4eSNeil Armstrong bool dsi_enabled; 528eff8b4eSNeil Armstrong bool powered; 538eff8b4eSNeil Armstrong struct phy_configure_opts_mipi_dphy config; 54af3f5722SRemi Pommarel }; 55af3f5722SRemi Pommarel 568eff8b4eSNeil Armstrong static void phy_bandgap_enable(struct phy_axg_mipi_pcie_analog_priv *priv) 57af3f5722SRemi Pommarel { 58af3f5722SRemi Pommarel regmap_update_bits(priv->regmap, HHI_MIPI_CNTL0, 59af3f5722SRemi Pommarel HHI_MIPI_CNTL0_BANDGAP, HHI_MIPI_CNTL0_BANDGAP); 60af3f5722SRemi Pommarel 61af3f5722SRemi Pommarel regmap_update_bits(priv->regmap, HHI_MIPI_CNTL0, 62af3f5722SRemi Pommarel HHI_MIPI_CNTL0_ENABLE, HHI_MIPI_CNTL0_ENABLE); 638eff8b4eSNeil Armstrong } 648eff8b4eSNeil Armstrong 658eff8b4eSNeil Armstrong static void phy_bandgap_disable(struct phy_axg_mipi_pcie_analog_priv *priv) 668eff8b4eSNeil Armstrong { 678eff8b4eSNeil Armstrong regmap_update_bits(priv->regmap, HHI_MIPI_CNTL0, 688eff8b4eSNeil Armstrong HHI_MIPI_CNTL0_BANDGAP, 0); 698eff8b4eSNeil Armstrong regmap_update_bits(priv->regmap, HHI_MIPI_CNTL0, 708eff8b4eSNeil Armstrong HHI_MIPI_CNTL0_ENABLE, 0); 718eff8b4eSNeil Armstrong } 728eff8b4eSNeil Armstrong 738eff8b4eSNeil Armstrong static void phy_dsi_analog_enable(struct phy_axg_mipi_pcie_analog_priv *priv) 748eff8b4eSNeil Armstrong { 758eff8b4eSNeil Armstrong u32 reg; 768eff8b4eSNeil Armstrong 778eff8b4eSNeil Armstrong regmap_update_bits(priv->regmap, HHI_MIPI_CNTL0, 788eff8b4eSNeil Armstrong HHI_MIPI_CNTL0_DIF_REF_CTL1, 798eff8b4eSNeil Armstrong FIELD_PREP(HHI_MIPI_CNTL0_DIF_REF_CTL1, 0x1b8)); 808eff8b4eSNeil Armstrong regmap_update_bits(priv->regmap, HHI_MIPI_CNTL0, 818eff8b4eSNeil Armstrong BIT(31), BIT(31)); 828eff8b4eSNeil Armstrong regmap_update_bits(priv->regmap, HHI_MIPI_CNTL0, 838eff8b4eSNeil Armstrong HHI_MIPI_CNTL0_DIF_REF_CTL0, 848eff8b4eSNeil Armstrong FIELD_PREP(HHI_MIPI_CNTL0_DIF_REF_CTL0, 0x8)); 858eff8b4eSNeil Armstrong 868eff8b4eSNeil Armstrong regmap_write(priv->regmap, HHI_MIPI_CNTL1, 0x001e); 878eff8b4eSNeil Armstrong 888eff8b4eSNeil Armstrong regmap_write(priv->regmap, HHI_MIPI_CNTL2, 898eff8b4eSNeil Armstrong (0x26e0 << 16) | (0x459 << 0)); 908eff8b4eSNeil Armstrong 918eff8b4eSNeil Armstrong reg = DSI_LANE_CLK; 928eff8b4eSNeil Armstrong switch (priv->config.lanes) { 938eff8b4eSNeil Armstrong case 4: 948eff8b4eSNeil Armstrong reg |= DSI_LANE_3; 958eff8b4eSNeil Armstrong fallthrough; 968eff8b4eSNeil Armstrong case 3: 978eff8b4eSNeil Armstrong reg |= DSI_LANE_2; 988eff8b4eSNeil Armstrong fallthrough; 998eff8b4eSNeil Armstrong case 2: 1008eff8b4eSNeil Armstrong reg |= DSI_LANE_1; 1018eff8b4eSNeil Armstrong fallthrough; 1028eff8b4eSNeil Armstrong case 1: 1038eff8b4eSNeil Armstrong reg |= DSI_LANE_0; 1048eff8b4eSNeil Armstrong break; 1058eff8b4eSNeil Armstrong default: 1068eff8b4eSNeil Armstrong reg = 0; 1078eff8b4eSNeil Armstrong } 1088eff8b4eSNeil Armstrong 1098eff8b4eSNeil Armstrong regmap_update_bits(priv->regmap, HHI_MIPI_CNTL2, 1108eff8b4eSNeil Armstrong HHI_MIPI_CNTL2_CH_EN, 1118eff8b4eSNeil Armstrong FIELD_PREP(HHI_MIPI_CNTL2_CH_EN, reg)); 1128eff8b4eSNeil Armstrong 1138eff8b4eSNeil Armstrong priv->dsi_enabled = true; 1148eff8b4eSNeil Armstrong } 1158eff8b4eSNeil Armstrong 1168eff8b4eSNeil Armstrong static void phy_dsi_analog_disable(struct phy_axg_mipi_pcie_analog_priv *priv) 1178eff8b4eSNeil Armstrong { 1188eff8b4eSNeil Armstrong regmap_update_bits(priv->regmap, HHI_MIPI_CNTL0, 1198eff8b4eSNeil Armstrong HHI_MIPI_CNTL0_DIF_REF_CTL1, 1208eff8b4eSNeil Armstrong FIELD_PREP(HHI_MIPI_CNTL0_DIF_REF_CTL1, 0)); 1218eff8b4eSNeil Armstrong regmap_update_bits(priv->regmap, HHI_MIPI_CNTL0, BIT(31), 0); 1228eff8b4eSNeil Armstrong regmap_update_bits(priv->regmap, HHI_MIPI_CNTL0, 1238eff8b4eSNeil Armstrong HHI_MIPI_CNTL0_DIF_REF_CTL1, 0); 1248eff8b4eSNeil Armstrong 1258eff8b4eSNeil Armstrong regmap_write(priv->regmap, HHI_MIPI_CNTL1, 0x6); 1268eff8b4eSNeil Armstrong 1278eff8b4eSNeil Armstrong regmap_write(priv->regmap, HHI_MIPI_CNTL2, 0x00200000); 1288eff8b4eSNeil Armstrong 1298eff8b4eSNeil Armstrong priv->dsi_enabled = false; 1308eff8b4eSNeil Armstrong } 1318eff8b4eSNeil Armstrong 1328eff8b4eSNeil Armstrong static int phy_axg_mipi_pcie_analog_configure(struct phy *phy, 1338eff8b4eSNeil Armstrong union phy_configure_opts *opts) 1348eff8b4eSNeil Armstrong { 1358eff8b4eSNeil Armstrong struct phy_axg_mipi_pcie_analog_priv *priv = phy_get_drvdata(phy); 1368eff8b4eSNeil Armstrong int ret; 1378eff8b4eSNeil Armstrong 1388eff8b4eSNeil Armstrong ret = phy_mipi_dphy_config_validate(&opts->mipi_dphy); 1398eff8b4eSNeil Armstrong if (ret) 1408eff8b4eSNeil Armstrong return ret; 1418eff8b4eSNeil Armstrong 1428eff8b4eSNeil Armstrong memcpy(&priv->config, opts, sizeof(priv->config)); 1438eff8b4eSNeil Armstrong 1448eff8b4eSNeil Armstrong priv->dsi_configured = true; 1458eff8b4eSNeil Armstrong 1468eff8b4eSNeil Armstrong /* If PHY was already powered on, setup the DSI analog part */ 1478eff8b4eSNeil Armstrong if (priv->powered) { 1488eff8b4eSNeil Armstrong /* If reconfiguring, disable & reconfigure */ 1498eff8b4eSNeil Armstrong if (priv->dsi_enabled) 1508eff8b4eSNeil Armstrong phy_dsi_analog_disable(priv); 1518eff8b4eSNeil Armstrong 1528eff8b4eSNeil Armstrong usleep_range(100, 200); 1538eff8b4eSNeil Armstrong 1548eff8b4eSNeil Armstrong phy_dsi_analog_enable(priv); 1558eff8b4eSNeil Armstrong } 1568eff8b4eSNeil Armstrong 1578eff8b4eSNeil Armstrong return 0; 1588eff8b4eSNeil Armstrong } 1598eff8b4eSNeil Armstrong 1608eff8b4eSNeil Armstrong static int phy_axg_mipi_pcie_analog_power_on(struct phy *phy) 1618eff8b4eSNeil Armstrong { 1628eff8b4eSNeil Armstrong struct phy_axg_mipi_pcie_analog_priv *priv = phy_get_drvdata(phy); 1638eff8b4eSNeil Armstrong 1648eff8b4eSNeil Armstrong phy_bandgap_enable(priv); 1658eff8b4eSNeil Armstrong 1668eff8b4eSNeil Armstrong if (priv->dsi_configured) 1678eff8b4eSNeil Armstrong phy_dsi_analog_enable(priv); 1688eff8b4eSNeil Armstrong 1698eff8b4eSNeil Armstrong priv->powered = true; 1708eff8b4eSNeil Armstrong 171af3f5722SRemi Pommarel return 0; 172af3f5722SRemi Pommarel } 173af3f5722SRemi Pommarel 174af3f5722SRemi Pommarel static int phy_axg_mipi_pcie_analog_power_off(struct phy *phy) 175af3f5722SRemi Pommarel { 176af3f5722SRemi Pommarel struct phy_axg_mipi_pcie_analog_priv *priv = phy_get_drvdata(phy); 177af3f5722SRemi Pommarel 1788eff8b4eSNeil Armstrong phy_bandgap_disable(priv); 179af3f5722SRemi Pommarel 1808eff8b4eSNeil Armstrong if (priv->dsi_enabled) 1818eff8b4eSNeil Armstrong phy_dsi_analog_disable(priv); 182af3f5722SRemi Pommarel 1838eff8b4eSNeil Armstrong priv->powered = false; 184af3f5722SRemi Pommarel 185af3f5722SRemi Pommarel return 0; 186af3f5722SRemi Pommarel } 187af3f5722SRemi Pommarel 188af3f5722SRemi Pommarel static const struct phy_ops phy_axg_mipi_pcie_analog_ops = { 1898eff8b4eSNeil Armstrong .configure = phy_axg_mipi_pcie_analog_configure, 190af3f5722SRemi Pommarel .power_on = phy_axg_mipi_pcie_analog_power_on, 191af3f5722SRemi Pommarel .power_off = phy_axg_mipi_pcie_analog_power_off, 192af3f5722SRemi Pommarel .owner = THIS_MODULE, 193af3f5722SRemi Pommarel }; 194af3f5722SRemi Pommarel 195af3f5722SRemi Pommarel static int phy_axg_mipi_pcie_analog_probe(struct platform_device *pdev) 196af3f5722SRemi Pommarel { 197af3f5722SRemi Pommarel struct phy_provider *phy; 198af3f5722SRemi Pommarel struct device *dev = &pdev->dev; 199af3f5722SRemi Pommarel struct phy_axg_mipi_pcie_analog_priv *priv; 200*c4c349beSLiang He struct device_node *np = dev->of_node, *parent_np; 201af3f5722SRemi Pommarel struct regmap *map; 202af3f5722SRemi Pommarel int ret; 203af3f5722SRemi Pommarel 204af3f5722SRemi Pommarel priv = devm_kmalloc(dev, sizeof(*priv), GFP_KERNEL); 205af3f5722SRemi Pommarel if (!priv) 206af3f5722SRemi Pommarel return -ENOMEM; 207af3f5722SRemi Pommarel 2088eff8b4eSNeil Armstrong /* Get the hhi system controller node */ 209*c4c349beSLiang He parent_np = of_get_parent(dev->of_node); 210*c4c349beSLiang He map = syscon_node_to_regmap(parent_np); 211*c4c349beSLiang He of_node_put(parent_np); 212af3f5722SRemi Pommarel if (IS_ERR(map)) { 2138eff8b4eSNeil Armstrong dev_err(dev, 2148eff8b4eSNeil Armstrong "failed to get HHI regmap\n"); 215af3f5722SRemi Pommarel return PTR_ERR(map); 216af3f5722SRemi Pommarel } 2178eff8b4eSNeil Armstrong 218af3f5722SRemi Pommarel priv->regmap = map; 219af3f5722SRemi Pommarel 220af3f5722SRemi Pommarel priv->phy = devm_phy_create(dev, np, &phy_axg_mipi_pcie_analog_ops); 221af3f5722SRemi Pommarel if (IS_ERR(priv->phy)) { 222af3f5722SRemi Pommarel ret = PTR_ERR(priv->phy); 223af3f5722SRemi Pommarel if (ret != -EPROBE_DEFER) 224af3f5722SRemi Pommarel dev_err(dev, "failed to create PHY\n"); 225af3f5722SRemi Pommarel return ret; 226af3f5722SRemi Pommarel } 227af3f5722SRemi Pommarel 228af3f5722SRemi Pommarel phy_set_drvdata(priv->phy, priv); 229af3f5722SRemi Pommarel dev_set_drvdata(dev, priv); 230af3f5722SRemi Pommarel 2318eff8b4eSNeil Armstrong phy = devm_of_phy_provider_register(dev, of_phy_simple_xlate); 232af3f5722SRemi Pommarel 233af3f5722SRemi Pommarel return PTR_ERR_OR_ZERO(phy); 234af3f5722SRemi Pommarel } 235af3f5722SRemi Pommarel 236af3f5722SRemi Pommarel static const struct of_device_id phy_axg_mipi_pcie_analog_of_match[] = { 237af3f5722SRemi Pommarel { 238af3f5722SRemi Pommarel .compatible = "amlogic,axg-mipi-pcie-analog-phy", 239af3f5722SRemi Pommarel }, 240af3f5722SRemi Pommarel { }, 241af3f5722SRemi Pommarel }; 242af3f5722SRemi Pommarel MODULE_DEVICE_TABLE(of, phy_axg_mipi_pcie_analog_of_match); 243af3f5722SRemi Pommarel 244af3f5722SRemi Pommarel static struct platform_driver phy_axg_mipi_pcie_analog_driver = { 245af3f5722SRemi Pommarel .probe = phy_axg_mipi_pcie_analog_probe, 246af3f5722SRemi Pommarel .driver = { 247af3f5722SRemi Pommarel .name = "phy-axg-mipi-pcie-analog", 248af3f5722SRemi Pommarel .of_match_table = phy_axg_mipi_pcie_analog_of_match, 249af3f5722SRemi Pommarel }, 250af3f5722SRemi Pommarel }; 251af3f5722SRemi Pommarel module_platform_driver(phy_axg_mipi_pcie_analog_driver); 252af3f5722SRemi Pommarel 253af3f5722SRemi Pommarel MODULE_AUTHOR("Remi Pommarel <repk@triplefau.lt>"); 254af3f5722SRemi Pommarel MODULE_DESCRIPTION("Amlogic AXG MIPI + PCIE analog PHY driver"); 255af3f5722SRemi Pommarel MODULE_LICENSE("GPL v2"); 256