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

phy: cadence: Add Cadence D-PHY Rx driver

The Cadence D-PHY can be configured in Tx (DSI) mode or Rx (CSI) mode.
Both modes have a different programming sequence and share little among
them. In addition, a PHY configured in Tx mode cannot be used in Rx mode
and vice versa. For this reason, create a separate driver for the Rx
mode to make it easier to read and maintain.

Signed-off-by: Pratyush Yadav <p.yadav@ti.com>
Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>

Link: https://lore.kernel.org/r/20220301111621.2992275-2-p.yadav@ti.com
Signed-off-by: Vinod Koul <vkoul@kernel.org>

authored by

Pratyush Yadav and committed by
Vinod Koul
41d393aa 7a37e890

+264
+8
drivers/phy/cadence/Kconfig
··· 22 22 system. If M is selected, the module will be called 23 23 cdns-dphy. 24 24 25 + config PHY_CADENCE_DPHY_RX 26 + tristate "Cadence D-PHY Rx Support" 27 + depends on HAS_IOMEM && OF 28 + select GENERIC_PHY 29 + select GENERIC_PHY_MIPI_DPHY 30 + help 31 + Support for Cadence D-PHY in Rx configuration. 32 + 25 33 config PHY_CADENCE_SIERRA 26 34 tristate "Cadence Sierra PHY Driver" 27 35 depends on OF && HAS_IOMEM && RESET_CONTROLLER
+1
drivers/phy/cadence/Makefile
··· 1 1 # SPDX-License-Identifier: GPL-2.0-only 2 2 obj-$(CONFIG_PHY_CADENCE_TORRENT) += phy-cadence-torrent.o 3 3 obj-$(CONFIG_PHY_CADENCE_DPHY) += cdns-dphy.o 4 + obj-$(CONFIG_PHY_CADENCE_DPHY_RX) += cdns-dphy-rx.o 4 5 obj-$(CONFIG_PHY_CADENCE_SIERRA) += phy-cadence-sierra.o 5 6 obj-$(CONFIG_PHY_CADENCE_SALVO) += phy-cadence-salvo.o
+255
drivers/phy/cadence/cdns-dphy-rx.c
··· 1 + // SPDX-License-Identifier: GPL-2.0-only 2 + /* 3 + * Copyright (C) 2022 Texas Instruments Incorporated - https://www.ti.com/ 4 + */ 5 + 6 + #include <linux/bitfield.h> 7 + #include <linux/bitops.h> 8 + #include <linux/io.h> 9 + #include <linux/iopoll.h> 10 + #include <linux/module.h> 11 + #include <linux/phy/phy.h> 12 + #include <linux/phy/phy-mipi-dphy.h> 13 + #include <linux/platform_device.h> 14 + 15 + #define DPHY_PMA_CMN(reg) (reg) 16 + #define DPHY_PCS(reg) (0xb00 + (reg)) 17 + #define DPHY_ISO(reg) (0xc00 + (reg)) 18 + 19 + #define DPHY_CMN_SSM DPHY_PMA_CMN(0x20) 20 + #define DPHY_CMN_RX_MODE_EN BIT(10) 21 + #define DPHY_CMN_RX_BANDGAP_TIMER_MASK GENMASK(8, 1) 22 + #define DPHY_CMN_SSM_EN BIT(0) 23 + 24 + #define DPHY_CMN_RX_BANDGAP_TIMER 0x14 25 + 26 + #define DPHY_BAND_CFG DPHY_PCS(0x0) 27 + #define DPHY_BAND_CFG_RIGHT_BAND GENMASK(9, 5) 28 + #define DPHY_BAND_CFG_LEFT_BAND GENMASK(4, 0) 29 + 30 + #define DPHY_POWER_ISLAND_EN_DATA DPHY_PCS(0x8) 31 + #define DPHY_POWER_ISLAND_EN_DATA_VAL 0xaaaaaaaa 32 + 33 + #define DPHY_POWER_ISLAND_EN_CLK DPHY_PCS(0xc) 34 + #define DPHY_POWER_ISLAND_EN_CLK_VAL 0xaa 35 + 36 + #define DPHY_ISO_CL_CTRL_L DPHY_ISO(0x10) 37 + #define DPHY_ISO_DL_CTRL_L0 DPHY_ISO(0x14) 38 + #define DPHY_ISO_DL_CTRL_L1 DPHY_ISO(0x20) 39 + #define DPHY_ISO_DL_CTRL_L2 DPHY_ISO(0x30) 40 + #define DPHY_ISO_DL_CTRL_L3 DPHY_ISO(0x3c) 41 + 42 + #define DPHY_ISO_LANE_READY_BIT 0 43 + #define DPHY_ISO_LANE_READY_TIMEOUT_MS 100UL 44 + 45 + #define DPHY_LANES_MIN 1 46 + #define DPHY_LANES_MAX 4 47 + 48 + struct cdns_dphy_rx { 49 + void __iomem *regs; 50 + struct device *dev; 51 + struct phy *phy; 52 + }; 53 + 54 + struct cdns_dphy_rx_band { 55 + /* Rates are in Mbps. */ 56 + unsigned int min_rate; 57 + unsigned int max_rate; 58 + }; 59 + 60 + /* Order of bands is important since the index is the band number. */ 61 + static const struct cdns_dphy_rx_band bands[] = { 62 + { 80, 100 }, { 100, 120 }, { 120, 160 }, { 160, 200 }, { 200, 240 }, 63 + { 240, 280 }, { 280, 320 }, { 320, 360 }, { 360, 400 }, { 400, 480 }, 64 + { 480, 560 }, { 560, 640 }, { 640, 720 }, { 720, 800 }, { 800, 880 }, 65 + { 880, 1040 }, { 1040, 1200 }, { 1200, 1350 }, { 1350, 1500 }, 66 + { 1500, 1750 }, { 1750, 2000 }, { 2000, 2250 }, { 2250, 2500 } 67 + }; 68 + 69 + static int cdns_dphy_rx_power_on(struct phy *phy) 70 + { 71 + struct cdns_dphy_rx *dphy = phy_get_drvdata(phy); 72 + 73 + /* Start RX state machine. */ 74 + writel(DPHY_CMN_SSM_EN | DPHY_CMN_RX_MODE_EN | 75 + FIELD_PREP(DPHY_CMN_RX_BANDGAP_TIMER_MASK, 76 + DPHY_CMN_RX_BANDGAP_TIMER), 77 + dphy->regs + DPHY_CMN_SSM); 78 + 79 + return 0; 80 + } 81 + 82 + static int cdns_dphy_rx_power_off(struct phy *phy) 83 + { 84 + struct cdns_dphy_rx *dphy = phy_get_drvdata(phy); 85 + 86 + writel(0, dphy->regs + DPHY_CMN_SSM); 87 + 88 + return 0; 89 + } 90 + 91 + static int cdns_dphy_rx_get_band_ctrl(unsigned long hs_clk_rate) 92 + { 93 + unsigned int rate, i; 94 + 95 + rate = hs_clk_rate / 1000000UL; 96 + /* Since CSI-2 clock is DDR, the bit rate is twice the clock rate. */ 97 + rate *= 2; 98 + 99 + if (rate < bands[0].min_rate) 100 + return -EOPNOTSUPP; 101 + 102 + for (i = 0; i < ARRAY_SIZE(bands); i++) 103 + if (rate < bands[i].max_rate) 104 + return i; 105 + 106 + return -EOPNOTSUPP; 107 + } 108 + 109 + static inline int cdns_dphy_rx_wait_for_bit(void __iomem *addr, 110 + unsigned int bit) 111 + { 112 + u32 val; 113 + 114 + return readl_relaxed_poll_timeout(addr, val, val & BIT(bit), 10, 115 + DPHY_ISO_LANE_READY_TIMEOUT_MS * 1000); 116 + } 117 + 118 + static int cdns_dphy_rx_wait_lane_ready(struct cdns_dphy_rx *dphy, 119 + unsigned int lanes) 120 + { 121 + static const u32 data_lane_ctrl[] = {DPHY_ISO_DL_CTRL_L0, 122 + DPHY_ISO_DL_CTRL_L1, 123 + DPHY_ISO_DL_CTRL_L2, 124 + DPHY_ISO_DL_CTRL_L3}; 125 + void __iomem *reg = dphy->regs; 126 + unsigned int i; 127 + int ret; 128 + 129 + /* Clock lane */ 130 + ret = cdns_dphy_rx_wait_for_bit(reg + DPHY_ISO_CL_CTRL_L, 131 + DPHY_ISO_LANE_READY_BIT); 132 + if (ret) 133 + return ret; 134 + 135 + for (i = 0; i < lanes; i++) { 136 + ret = cdns_dphy_rx_wait_for_bit(reg + data_lane_ctrl[i], 137 + DPHY_ISO_LANE_READY_BIT); 138 + if (ret) 139 + return ret; 140 + } 141 + 142 + return 0; 143 + } 144 + 145 + static int cdns_dphy_rx_configure(struct phy *phy, 146 + union phy_configure_opts *opts) 147 + { 148 + struct cdns_dphy_rx *dphy = phy_get_drvdata(phy); 149 + unsigned int reg, lanes = opts->mipi_dphy.lanes; 150 + int band_ctrl, ret; 151 + 152 + /* Data lanes. Minimum one lane is mandatory. */ 153 + if (lanes < DPHY_LANES_MIN || lanes > DPHY_LANES_MAX) 154 + return -EINVAL; 155 + 156 + band_ctrl = cdns_dphy_rx_get_band_ctrl(opts->mipi_dphy.hs_clk_rate); 157 + if (band_ctrl < 0) 158 + return band_ctrl; 159 + 160 + reg = FIELD_PREP(DPHY_BAND_CFG_LEFT_BAND, band_ctrl) | 161 + FIELD_PREP(DPHY_BAND_CFG_RIGHT_BAND, band_ctrl); 162 + writel(reg, dphy->regs + DPHY_BAND_CFG); 163 + 164 + /* 165 + * Set the required power island phase 2 time. This is mandated by DPHY 166 + * specs. 167 + */ 168 + reg = DPHY_POWER_ISLAND_EN_DATA_VAL; 169 + writel(reg, dphy->regs + DPHY_POWER_ISLAND_EN_DATA); 170 + reg = DPHY_POWER_ISLAND_EN_CLK_VAL; 171 + writel(reg, dphy->regs + DPHY_POWER_ISLAND_EN_CLK); 172 + 173 + ret = cdns_dphy_rx_wait_lane_ready(dphy, lanes); 174 + if (ret) { 175 + dev_err(dphy->dev, "DPHY wait for lane ready timeout\n"); 176 + return ret; 177 + } 178 + 179 + return 0; 180 + } 181 + 182 + static int cdns_dphy_rx_validate(struct phy *phy, enum phy_mode mode, 183 + int submode, union phy_configure_opts *opts) 184 + { 185 + int ret; 186 + 187 + if (mode != PHY_MODE_MIPI_DPHY) 188 + return -EINVAL; 189 + 190 + ret = cdns_dphy_rx_get_band_ctrl(opts->mipi_dphy.hs_clk_rate); 191 + if (ret < 0) 192 + return ret; 193 + 194 + return phy_mipi_dphy_config_validate(&opts->mipi_dphy); 195 + } 196 + 197 + static const struct phy_ops cdns_dphy_rx_ops = { 198 + .power_on = cdns_dphy_rx_power_on, 199 + .power_off = cdns_dphy_rx_power_off, 200 + .configure = cdns_dphy_rx_configure, 201 + .validate = cdns_dphy_rx_validate, 202 + }; 203 + 204 + static int cdns_dphy_rx_probe(struct platform_device *pdev) 205 + { 206 + struct device *dev = &pdev->dev; 207 + struct phy_provider *provider; 208 + struct cdns_dphy_rx *dphy; 209 + 210 + dphy = devm_kzalloc(dev, sizeof(*dphy), GFP_KERNEL); 211 + if (!dphy) 212 + return -ENOMEM; 213 + 214 + dev_set_drvdata(dev, dphy); 215 + dphy->dev = dev; 216 + 217 + dphy->regs = devm_platform_ioremap_resource(pdev, 0); 218 + if (IS_ERR(dphy->regs)) 219 + return PTR_ERR(dphy->regs); 220 + 221 + dphy->phy = devm_phy_create(dev, NULL, &cdns_dphy_rx_ops); 222 + if (IS_ERR(dphy->phy)) { 223 + dev_err(dev, "Failed to create PHY: %ld\n", PTR_ERR(dphy->phy)); 224 + return PTR_ERR(dphy->phy); 225 + } 226 + 227 + phy_set_drvdata(dphy->phy, dphy); 228 + provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate); 229 + if (IS_ERR(provider)) { 230 + dev_err(dev, "Failed to register PHY provider: %ld\n", 231 + PTR_ERR(provider)); 232 + return PTR_ERR(provider); 233 + } 234 + 235 + return 0; 236 + } 237 + 238 + static const struct of_device_id cdns_dphy_rx_of_match[] = { 239 + { .compatible = "cdns,dphy-rx" }, 240 + { /* sentinel */ }, 241 + }; 242 + MODULE_DEVICE_TABLE(of, cdns_dphy_rx_of_match); 243 + 244 + static struct platform_driver cdns_dphy_rx_platform_driver = { 245 + .probe = cdns_dphy_rx_probe, 246 + .driver = { 247 + .name = "cdns-mipi-dphy-rx", 248 + .of_match_table = cdns_dphy_rx_of_match, 249 + }, 250 + }; 251 + module_platform_driver(cdns_dphy_rx_platform_driver); 252 + 253 + MODULE_AUTHOR("Pratyush Yadav <p.yadav@ti.com>"); 254 + MODULE_DESCRIPTION("Cadence D-PHY Rx Driver"); 255 + MODULE_LICENSE("GPL");