141d393aaSPratyush Yadav // SPDX-License-Identifier: GPL-2.0-only 241d393aaSPratyush Yadav /* 341d393aaSPratyush Yadav * Copyright (C) 2022 Texas Instruments Incorporated - https://www.ti.com/ 441d393aaSPratyush Yadav */ 541d393aaSPratyush Yadav 641d393aaSPratyush Yadav #include <linux/bitfield.h> 741d393aaSPratyush Yadav #include <linux/bitops.h> 841d393aaSPratyush Yadav #include <linux/io.h> 941d393aaSPratyush Yadav #include <linux/iopoll.h> 10*7559e757SRob Herring #include <linux/mod_devicetable.h> 1141d393aaSPratyush Yadav #include <linux/module.h> 1241d393aaSPratyush Yadav #include <linux/phy/phy.h> 1341d393aaSPratyush Yadav #include <linux/phy/phy-mipi-dphy.h> 1441d393aaSPratyush Yadav #include <linux/platform_device.h> 15a0106132SSinthu Raja #include <linux/sys_soc.h> 1641d393aaSPratyush Yadav 1741d393aaSPratyush Yadav #define DPHY_PMA_CMN(reg) (reg) 1841d393aaSPratyush Yadav #define DPHY_PCS(reg) (0xb00 + (reg)) 1941d393aaSPratyush Yadav #define DPHY_ISO(reg) (0xc00 + (reg)) 20a0106132SSinthu Raja #define DPHY_WRAP(reg) (0x1000 + (reg)) 2141d393aaSPratyush Yadav 2241d393aaSPratyush Yadav #define DPHY_CMN_SSM DPHY_PMA_CMN(0x20) 2341d393aaSPratyush Yadav #define DPHY_CMN_RX_MODE_EN BIT(10) 2441d393aaSPratyush Yadav #define DPHY_CMN_RX_BANDGAP_TIMER_MASK GENMASK(8, 1) 2541d393aaSPratyush Yadav #define DPHY_CMN_SSM_EN BIT(0) 2641d393aaSPratyush Yadav 2741d393aaSPratyush Yadav #define DPHY_CMN_RX_BANDGAP_TIMER 0x14 2841d393aaSPratyush Yadav 2941d393aaSPratyush Yadav #define DPHY_BAND_CFG DPHY_PCS(0x0) 3041d393aaSPratyush Yadav #define DPHY_BAND_CFG_RIGHT_BAND GENMASK(9, 5) 3141d393aaSPratyush Yadav #define DPHY_BAND_CFG_LEFT_BAND GENMASK(4, 0) 3241d393aaSPratyush Yadav 3341d393aaSPratyush Yadav #define DPHY_POWER_ISLAND_EN_DATA DPHY_PCS(0x8) 3441d393aaSPratyush Yadav #define DPHY_POWER_ISLAND_EN_DATA_VAL 0xaaaaaaaa 3541d393aaSPratyush Yadav 3641d393aaSPratyush Yadav #define DPHY_POWER_ISLAND_EN_CLK DPHY_PCS(0xc) 3741d393aaSPratyush Yadav #define DPHY_POWER_ISLAND_EN_CLK_VAL 0xaa 3841d393aaSPratyush Yadav 39a0106132SSinthu Raja #define DPHY_LANE DPHY_WRAP(0x0) 40a0106132SSinthu Raja #define DPHY_LANE_RESET_CMN_EN BIT(23) 41a0106132SSinthu Raja 4241d393aaSPratyush Yadav #define DPHY_ISO_CL_CTRL_L DPHY_ISO(0x10) 4341d393aaSPratyush Yadav #define DPHY_ISO_DL_CTRL_L0 DPHY_ISO(0x14) 4441d393aaSPratyush Yadav #define DPHY_ISO_DL_CTRL_L1 DPHY_ISO(0x20) 4541d393aaSPratyush Yadav #define DPHY_ISO_DL_CTRL_L2 DPHY_ISO(0x30) 4641d393aaSPratyush Yadav #define DPHY_ISO_DL_CTRL_L3 DPHY_ISO(0x3c) 4741d393aaSPratyush Yadav 4841d393aaSPratyush Yadav #define DPHY_ISO_LANE_READY_BIT 0 4941d393aaSPratyush Yadav #define DPHY_ISO_LANE_READY_TIMEOUT_MS 100UL 5041d393aaSPratyush Yadav 5141d393aaSPratyush Yadav #define DPHY_LANES_MIN 1 5241d393aaSPratyush Yadav #define DPHY_LANES_MAX 4 5341d393aaSPratyush Yadav 5441d393aaSPratyush Yadav struct cdns_dphy_rx { 5541d393aaSPratyush Yadav void __iomem *regs; 5641d393aaSPratyush Yadav struct device *dev; 5741d393aaSPratyush Yadav struct phy *phy; 5841d393aaSPratyush Yadav }; 5941d393aaSPratyush Yadav 6041d393aaSPratyush Yadav struct cdns_dphy_rx_band { 6141d393aaSPratyush Yadav /* Rates are in Mbps. */ 6241d393aaSPratyush Yadav unsigned int min_rate; 6341d393aaSPratyush Yadav unsigned int max_rate; 6441d393aaSPratyush Yadav }; 6541d393aaSPratyush Yadav 66a0106132SSinthu Raja struct cdns_dphy_soc_data { 67a0106132SSinthu Raja bool has_hw_cmn_rstb; 68a0106132SSinthu Raja }; 69a0106132SSinthu Raja 7041d393aaSPratyush Yadav /* Order of bands is important since the index is the band number. */ 7141d393aaSPratyush Yadav static const struct cdns_dphy_rx_band bands[] = { 7241d393aaSPratyush Yadav { 80, 100 }, { 100, 120 }, { 120, 160 }, { 160, 200 }, { 200, 240 }, 7341d393aaSPratyush Yadav { 240, 280 }, { 280, 320 }, { 320, 360 }, { 360, 400 }, { 400, 480 }, 7441d393aaSPratyush Yadav { 480, 560 }, { 560, 640 }, { 640, 720 }, { 720, 800 }, { 800, 880 }, 7541d393aaSPratyush Yadav { 880, 1040 }, { 1040, 1200 }, { 1200, 1350 }, { 1350, 1500 }, 7641d393aaSPratyush Yadav { 1500, 1750 }, { 1750, 2000 }, { 2000, 2250 }, { 2250, 2500 } 7741d393aaSPratyush Yadav }; 7841d393aaSPratyush Yadav 7941d393aaSPratyush Yadav static int cdns_dphy_rx_power_on(struct phy *phy) 8041d393aaSPratyush Yadav { 8141d393aaSPratyush Yadav struct cdns_dphy_rx *dphy = phy_get_drvdata(phy); 8241d393aaSPratyush Yadav 8341d393aaSPratyush Yadav /* Start RX state machine. */ 8441d393aaSPratyush Yadav writel(DPHY_CMN_SSM_EN | DPHY_CMN_RX_MODE_EN | 8541d393aaSPratyush Yadav FIELD_PREP(DPHY_CMN_RX_BANDGAP_TIMER_MASK, 8641d393aaSPratyush Yadav DPHY_CMN_RX_BANDGAP_TIMER), 8741d393aaSPratyush Yadav dphy->regs + DPHY_CMN_SSM); 8841d393aaSPratyush Yadav 8941d393aaSPratyush Yadav return 0; 9041d393aaSPratyush Yadav } 9141d393aaSPratyush Yadav 9241d393aaSPratyush Yadav static int cdns_dphy_rx_power_off(struct phy *phy) 9341d393aaSPratyush Yadav { 9441d393aaSPratyush Yadav struct cdns_dphy_rx *dphy = phy_get_drvdata(phy); 9541d393aaSPratyush Yadav 9641d393aaSPratyush Yadav writel(0, dphy->regs + DPHY_CMN_SSM); 9741d393aaSPratyush Yadav 9841d393aaSPratyush Yadav return 0; 9941d393aaSPratyush Yadav } 10041d393aaSPratyush Yadav 10141d393aaSPratyush Yadav static int cdns_dphy_rx_get_band_ctrl(unsigned long hs_clk_rate) 10241d393aaSPratyush Yadav { 10341d393aaSPratyush Yadav unsigned int rate, i; 10441d393aaSPratyush Yadav 10541d393aaSPratyush Yadav rate = hs_clk_rate / 1000000UL; 10641d393aaSPratyush Yadav /* Since CSI-2 clock is DDR, the bit rate is twice the clock rate. */ 10741d393aaSPratyush Yadav rate *= 2; 10841d393aaSPratyush Yadav 10941d393aaSPratyush Yadav if (rate < bands[0].min_rate) 11041d393aaSPratyush Yadav return -EOPNOTSUPP; 11141d393aaSPratyush Yadav 11241d393aaSPratyush Yadav for (i = 0; i < ARRAY_SIZE(bands); i++) 11341d393aaSPratyush Yadav if (rate < bands[i].max_rate) 11441d393aaSPratyush Yadav return i; 11541d393aaSPratyush Yadav 11641d393aaSPratyush Yadav return -EOPNOTSUPP; 11741d393aaSPratyush Yadav } 11841d393aaSPratyush Yadav 11941d393aaSPratyush Yadav static inline int cdns_dphy_rx_wait_for_bit(void __iomem *addr, 12041d393aaSPratyush Yadav unsigned int bit) 12141d393aaSPratyush Yadav { 12241d393aaSPratyush Yadav u32 val; 12341d393aaSPratyush Yadav 12441d393aaSPratyush Yadav return readl_relaxed_poll_timeout(addr, val, val & BIT(bit), 10, 12541d393aaSPratyush Yadav DPHY_ISO_LANE_READY_TIMEOUT_MS * 1000); 12641d393aaSPratyush Yadav } 12741d393aaSPratyush Yadav 12841d393aaSPratyush Yadav static int cdns_dphy_rx_wait_lane_ready(struct cdns_dphy_rx *dphy, 12941d393aaSPratyush Yadav unsigned int lanes) 13041d393aaSPratyush Yadav { 13141d393aaSPratyush Yadav static const u32 data_lane_ctrl[] = {DPHY_ISO_DL_CTRL_L0, 13241d393aaSPratyush Yadav DPHY_ISO_DL_CTRL_L1, 13341d393aaSPratyush Yadav DPHY_ISO_DL_CTRL_L2, 13441d393aaSPratyush Yadav DPHY_ISO_DL_CTRL_L3}; 13541d393aaSPratyush Yadav void __iomem *reg = dphy->regs; 13641d393aaSPratyush Yadav unsigned int i; 13741d393aaSPratyush Yadav int ret; 13841d393aaSPratyush Yadav 13941d393aaSPratyush Yadav /* Clock lane */ 14041d393aaSPratyush Yadav ret = cdns_dphy_rx_wait_for_bit(reg + DPHY_ISO_CL_CTRL_L, 14141d393aaSPratyush Yadav DPHY_ISO_LANE_READY_BIT); 14241d393aaSPratyush Yadav if (ret) 14341d393aaSPratyush Yadav return ret; 14441d393aaSPratyush Yadav 14541d393aaSPratyush Yadav for (i = 0; i < lanes; i++) { 14641d393aaSPratyush Yadav ret = cdns_dphy_rx_wait_for_bit(reg + data_lane_ctrl[i], 14741d393aaSPratyush Yadav DPHY_ISO_LANE_READY_BIT); 14841d393aaSPratyush Yadav if (ret) 14941d393aaSPratyush Yadav return ret; 15041d393aaSPratyush Yadav } 15141d393aaSPratyush Yadav 15241d393aaSPratyush Yadav return 0; 15341d393aaSPratyush Yadav } 15441d393aaSPratyush Yadav 155a0106132SSinthu Raja static struct cdns_dphy_soc_data j721e_soc_data = { 156a0106132SSinthu Raja .has_hw_cmn_rstb = true, 157a0106132SSinthu Raja }; 158a0106132SSinthu Raja 159a0106132SSinthu Raja static const struct soc_device_attribute cdns_dphy_socinfo[] = { 160a0106132SSinthu Raja { 161a0106132SSinthu Raja .family = "J721E", 162a0106132SSinthu Raja .revision = "SR1.0", 163a0106132SSinthu Raja .data = &j721e_soc_data, 164a0106132SSinthu Raja }, 165a0106132SSinthu Raja {/* sentinel */} 166a0106132SSinthu Raja }; 167a0106132SSinthu Raja 16841d393aaSPratyush Yadav static int cdns_dphy_rx_configure(struct phy *phy, 16941d393aaSPratyush Yadav union phy_configure_opts *opts) 17041d393aaSPratyush Yadav { 17141d393aaSPratyush Yadav struct cdns_dphy_rx *dphy = phy_get_drvdata(phy); 17241d393aaSPratyush Yadav unsigned int reg, lanes = opts->mipi_dphy.lanes; 173a0106132SSinthu Raja const struct cdns_dphy_soc_data *soc_data = NULL; 174a0106132SSinthu Raja const struct soc_device_attribute *soc; 17541d393aaSPratyush Yadav int band_ctrl, ret; 17641d393aaSPratyush Yadav 177a0106132SSinthu Raja soc = soc_device_match(cdns_dphy_socinfo); 178a0106132SSinthu Raja if (soc && soc->data) 179a0106132SSinthu Raja soc_data = soc->data; 180a0106132SSinthu Raja if (!soc || (soc_data && !soc_data->has_hw_cmn_rstb)) { 181a0106132SSinthu Raja reg = DPHY_LANE_RESET_CMN_EN; 182a0106132SSinthu Raja writel(reg, dphy->regs + DPHY_LANE); 183a0106132SSinthu Raja } 184a0106132SSinthu Raja 18541d393aaSPratyush Yadav /* Data lanes. Minimum one lane is mandatory. */ 18641d393aaSPratyush Yadav if (lanes < DPHY_LANES_MIN || lanes > DPHY_LANES_MAX) 18741d393aaSPratyush Yadav return -EINVAL; 18841d393aaSPratyush Yadav 18941d393aaSPratyush Yadav band_ctrl = cdns_dphy_rx_get_band_ctrl(opts->mipi_dphy.hs_clk_rate); 19041d393aaSPratyush Yadav if (band_ctrl < 0) 19141d393aaSPratyush Yadav return band_ctrl; 19241d393aaSPratyush Yadav 19341d393aaSPratyush Yadav reg = FIELD_PREP(DPHY_BAND_CFG_LEFT_BAND, band_ctrl) | 19441d393aaSPratyush Yadav FIELD_PREP(DPHY_BAND_CFG_RIGHT_BAND, band_ctrl); 19541d393aaSPratyush Yadav writel(reg, dphy->regs + DPHY_BAND_CFG); 19641d393aaSPratyush Yadav 19741d393aaSPratyush Yadav /* 19841d393aaSPratyush Yadav * Set the required power island phase 2 time. This is mandated by DPHY 19941d393aaSPratyush Yadav * specs. 20041d393aaSPratyush Yadav */ 20141d393aaSPratyush Yadav reg = DPHY_POWER_ISLAND_EN_DATA_VAL; 20241d393aaSPratyush Yadav writel(reg, dphy->regs + DPHY_POWER_ISLAND_EN_DATA); 20341d393aaSPratyush Yadav reg = DPHY_POWER_ISLAND_EN_CLK_VAL; 20441d393aaSPratyush Yadav writel(reg, dphy->regs + DPHY_POWER_ISLAND_EN_CLK); 20541d393aaSPratyush Yadav 20641d393aaSPratyush Yadav ret = cdns_dphy_rx_wait_lane_ready(dphy, lanes); 20741d393aaSPratyush Yadav if (ret) { 20841d393aaSPratyush Yadav dev_err(dphy->dev, "DPHY wait for lane ready timeout\n"); 20941d393aaSPratyush Yadav return ret; 21041d393aaSPratyush Yadav } 21141d393aaSPratyush Yadav 21241d393aaSPratyush Yadav return 0; 21341d393aaSPratyush Yadav } 21441d393aaSPratyush Yadav 21541d393aaSPratyush Yadav static int cdns_dphy_rx_validate(struct phy *phy, enum phy_mode mode, 21641d393aaSPratyush Yadav int submode, union phy_configure_opts *opts) 21741d393aaSPratyush Yadav { 21841d393aaSPratyush Yadav int ret; 21941d393aaSPratyush Yadav 22041d393aaSPratyush Yadav if (mode != PHY_MODE_MIPI_DPHY) 22141d393aaSPratyush Yadav return -EINVAL; 22241d393aaSPratyush Yadav 22341d393aaSPratyush Yadav ret = cdns_dphy_rx_get_band_ctrl(opts->mipi_dphy.hs_clk_rate); 22441d393aaSPratyush Yadav if (ret < 0) 22541d393aaSPratyush Yadav return ret; 22641d393aaSPratyush Yadav 22741d393aaSPratyush Yadav return phy_mipi_dphy_config_validate(&opts->mipi_dphy); 22841d393aaSPratyush Yadav } 22941d393aaSPratyush Yadav 23041d393aaSPratyush Yadav static const struct phy_ops cdns_dphy_rx_ops = { 23141d393aaSPratyush Yadav .power_on = cdns_dphy_rx_power_on, 23241d393aaSPratyush Yadav .power_off = cdns_dphy_rx_power_off, 23341d393aaSPratyush Yadav .configure = cdns_dphy_rx_configure, 23441d393aaSPratyush Yadav .validate = cdns_dphy_rx_validate, 23541d393aaSPratyush Yadav }; 23641d393aaSPratyush Yadav 23741d393aaSPratyush Yadav static int cdns_dphy_rx_probe(struct platform_device *pdev) 23841d393aaSPratyush Yadav { 23941d393aaSPratyush Yadav struct device *dev = &pdev->dev; 24041d393aaSPratyush Yadav struct phy_provider *provider; 24141d393aaSPratyush Yadav struct cdns_dphy_rx *dphy; 24241d393aaSPratyush Yadav 24341d393aaSPratyush Yadav dphy = devm_kzalloc(dev, sizeof(*dphy), GFP_KERNEL); 24441d393aaSPratyush Yadav if (!dphy) 24541d393aaSPratyush Yadav return -ENOMEM; 24641d393aaSPratyush Yadav 24741d393aaSPratyush Yadav dev_set_drvdata(dev, dphy); 24841d393aaSPratyush Yadav dphy->dev = dev; 24941d393aaSPratyush Yadav 25041d393aaSPratyush Yadav dphy->regs = devm_platform_ioremap_resource(pdev, 0); 25141d393aaSPratyush Yadav if (IS_ERR(dphy->regs)) 25241d393aaSPratyush Yadav return PTR_ERR(dphy->regs); 25341d393aaSPratyush Yadav 25441d393aaSPratyush Yadav dphy->phy = devm_phy_create(dev, NULL, &cdns_dphy_rx_ops); 25541d393aaSPratyush Yadav if (IS_ERR(dphy->phy)) { 25641d393aaSPratyush Yadav dev_err(dev, "Failed to create PHY: %ld\n", PTR_ERR(dphy->phy)); 25741d393aaSPratyush Yadav return PTR_ERR(dphy->phy); 25841d393aaSPratyush Yadav } 25941d393aaSPratyush Yadav 26041d393aaSPratyush Yadav phy_set_drvdata(dphy->phy, dphy); 26141d393aaSPratyush Yadav provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate); 26241d393aaSPratyush Yadav if (IS_ERR(provider)) { 26341d393aaSPratyush Yadav dev_err(dev, "Failed to register PHY provider: %ld\n", 26441d393aaSPratyush Yadav PTR_ERR(provider)); 26541d393aaSPratyush Yadav return PTR_ERR(provider); 26641d393aaSPratyush Yadav } 26741d393aaSPratyush Yadav 26841d393aaSPratyush Yadav return 0; 26941d393aaSPratyush Yadav } 27041d393aaSPratyush Yadav 27141d393aaSPratyush Yadav static const struct of_device_id cdns_dphy_rx_of_match[] = { 27241d393aaSPratyush Yadav { .compatible = "cdns,dphy-rx" }, 27341d393aaSPratyush Yadav { /* sentinel */ }, 27441d393aaSPratyush Yadav }; 27541d393aaSPratyush Yadav MODULE_DEVICE_TABLE(of, cdns_dphy_rx_of_match); 27641d393aaSPratyush Yadav 27741d393aaSPratyush Yadav static struct platform_driver cdns_dphy_rx_platform_driver = { 27841d393aaSPratyush Yadav .probe = cdns_dphy_rx_probe, 27941d393aaSPratyush Yadav .driver = { 28041d393aaSPratyush Yadav .name = "cdns-mipi-dphy-rx", 28141d393aaSPratyush Yadav .of_match_table = cdns_dphy_rx_of_match, 28241d393aaSPratyush Yadav }, 28341d393aaSPratyush Yadav }; 28441d393aaSPratyush Yadav module_platform_driver(cdns_dphy_rx_platform_driver); 28541d393aaSPratyush Yadav 28641d393aaSPratyush Yadav MODULE_AUTHOR("Pratyush Yadav <p.yadav@ti.com>"); 28741d393aaSPratyush Yadav MODULE_DESCRIPTION("Cadence D-PHY Rx Driver"); 28841d393aaSPratyush Yadav MODULE_LICENSE("GPL"); 289