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