162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Copyright (C) 2022 Texas Instruments Incorporated - https://www.ti.com/ 462306a36Sopenharmony_ci */ 562306a36Sopenharmony_ci 662306a36Sopenharmony_ci#include <linux/bitfield.h> 762306a36Sopenharmony_ci#include <linux/bitops.h> 862306a36Sopenharmony_ci#include <linux/io.h> 962306a36Sopenharmony_ci#include <linux/iopoll.h> 1062306a36Sopenharmony_ci#include <linux/mod_devicetable.h> 1162306a36Sopenharmony_ci#include <linux/module.h> 1262306a36Sopenharmony_ci#include <linux/phy/phy.h> 1362306a36Sopenharmony_ci#include <linux/phy/phy-mipi-dphy.h> 1462306a36Sopenharmony_ci#include <linux/platform_device.h> 1562306a36Sopenharmony_ci#include <linux/sys_soc.h> 1662306a36Sopenharmony_ci 1762306a36Sopenharmony_ci#define DPHY_PMA_CMN(reg) (reg) 1862306a36Sopenharmony_ci#define DPHY_PCS(reg) (0xb00 + (reg)) 1962306a36Sopenharmony_ci#define DPHY_ISO(reg) (0xc00 + (reg)) 2062306a36Sopenharmony_ci#define DPHY_WRAP(reg) (0x1000 + (reg)) 2162306a36Sopenharmony_ci 2262306a36Sopenharmony_ci#define DPHY_CMN_SSM DPHY_PMA_CMN(0x20) 2362306a36Sopenharmony_ci#define DPHY_CMN_RX_MODE_EN BIT(10) 2462306a36Sopenharmony_ci#define DPHY_CMN_RX_BANDGAP_TIMER_MASK GENMASK(8, 1) 2562306a36Sopenharmony_ci#define DPHY_CMN_SSM_EN BIT(0) 2662306a36Sopenharmony_ci 2762306a36Sopenharmony_ci#define DPHY_CMN_RX_BANDGAP_TIMER 0x14 2862306a36Sopenharmony_ci 2962306a36Sopenharmony_ci#define DPHY_BAND_CFG DPHY_PCS(0x0) 3062306a36Sopenharmony_ci#define DPHY_BAND_CFG_RIGHT_BAND GENMASK(9, 5) 3162306a36Sopenharmony_ci#define DPHY_BAND_CFG_LEFT_BAND GENMASK(4, 0) 3262306a36Sopenharmony_ci 3362306a36Sopenharmony_ci#define DPHY_POWER_ISLAND_EN_DATA DPHY_PCS(0x8) 3462306a36Sopenharmony_ci#define DPHY_POWER_ISLAND_EN_DATA_VAL 0xaaaaaaaa 3562306a36Sopenharmony_ci 3662306a36Sopenharmony_ci#define DPHY_POWER_ISLAND_EN_CLK DPHY_PCS(0xc) 3762306a36Sopenharmony_ci#define DPHY_POWER_ISLAND_EN_CLK_VAL 0xaa 3862306a36Sopenharmony_ci 3962306a36Sopenharmony_ci#define DPHY_LANE DPHY_WRAP(0x0) 4062306a36Sopenharmony_ci#define DPHY_LANE_RESET_CMN_EN BIT(23) 4162306a36Sopenharmony_ci 4262306a36Sopenharmony_ci#define DPHY_ISO_CL_CTRL_L DPHY_ISO(0x10) 4362306a36Sopenharmony_ci#define DPHY_ISO_DL_CTRL_L0 DPHY_ISO(0x14) 4462306a36Sopenharmony_ci#define DPHY_ISO_DL_CTRL_L1 DPHY_ISO(0x20) 4562306a36Sopenharmony_ci#define DPHY_ISO_DL_CTRL_L2 DPHY_ISO(0x30) 4662306a36Sopenharmony_ci#define DPHY_ISO_DL_CTRL_L3 DPHY_ISO(0x3c) 4762306a36Sopenharmony_ci 4862306a36Sopenharmony_ci#define DPHY_ISO_LANE_READY_BIT 0 4962306a36Sopenharmony_ci#define DPHY_ISO_LANE_READY_TIMEOUT_MS 100UL 5062306a36Sopenharmony_ci 5162306a36Sopenharmony_ci#define DPHY_LANES_MIN 1 5262306a36Sopenharmony_ci#define DPHY_LANES_MAX 4 5362306a36Sopenharmony_ci 5462306a36Sopenharmony_cistruct cdns_dphy_rx { 5562306a36Sopenharmony_ci void __iomem *regs; 5662306a36Sopenharmony_ci struct device *dev; 5762306a36Sopenharmony_ci struct phy *phy; 5862306a36Sopenharmony_ci}; 5962306a36Sopenharmony_ci 6062306a36Sopenharmony_cistruct cdns_dphy_rx_band { 6162306a36Sopenharmony_ci /* Rates are in Mbps. */ 6262306a36Sopenharmony_ci unsigned int min_rate; 6362306a36Sopenharmony_ci unsigned int max_rate; 6462306a36Sopenharmony_ci}; 6562306a36Sopenharmony_ci 6662306a36Sopenharmony_cistruct cdns_dphy_soc_data { 6762306a36Sopenharmony_ci bool has_hw_cmn_rstb; 6862306a36Sopenharmony_ci}; 6962306a36Sopenharmony_ci 7062306a36Sopenharmony_ci/* Order of bands is important since the index is the band number. */ 7162306a36Sopenharmony_cistatic const struct cdns_dphy_rx_band bands[] = { 7262306a36Sopenharmony_ci { 80, 100 }, { 100, 120 }, { 120, 160 }, { 160, 200 }, { 200, 240 }, 7362306a36Sopenharmony_ci { 240, 280 }, { 280, 320 }, { 320, 360 }, { 360, 400 }, { 400, 480 }, 7462306a36Sopenharmony_ci { 480, 560 }, { 560, 640 }, { 640, 720 }, { 720, 800 }, { 800, 880 }, 7562306a36Sopenharmony_ci { 880, 1040 }, { 1040, 1200 }, { 1200, 1350 }, { 1350, 1500 }, 7662306a36Sopenharmony_ci { 1500, 1750 }, { 1750, 2000 }, { 2000, 2250 }, { 2250, 2500 } 7762306a36Sopenharmony_ci}; 7862306a36Sopenharmony_ci 7962306a36Sopenharmony_cistatic int cdns_dphy_rx_power_on(struct phy *phy) 8062306a36Sopenharmony_ci{ 8162306a36Sopenharmony_ci struct cdns_dphy_rx *dphy = phy_get_drvdata(phy); 8262306a36Sopenharmony_ci 8362306a36Sopenharmony_ci /* Start RX state machine. */ 8462306a36Sopenharmony_ci writel(DPHY_CMN_SSM_EN | DPHY_CMN_RX_MODE_EN | 8562306a36Sopenharmony_ci FIELD_PREP(DPHY_CMN_RX_BANDGAP_TIMER_MASK, 8662306a36Sopenharmony_ci DPHY_CMN_RX_BANDGAP_TIMER), 8762306a36Sopenharmony_ci dphy->regs + DPHY_CMN_SSM); 8862306a36Sopenharmony_ci 8962306a36Sopenharmony_ci return 0; 9062306a36Sopenharmony_ci} 9162306a36Sopenharmony_ci 9262306a36Sopenharmony_cistatic int cdns_dphy_rx_power_off(struct phy *phy) 9362306a36Sopenharmony_ci{ 9462306a36Sopenharmony_ci struct cdns_dphy_rx *dphy = phy_get_drvdata(phy); 9562306a36Sopenharmony_ci 9662306a36Sopenharmony_ci writel(0, dphy->regs + DPHY_CMN_SSM); 9762306a36Sopenharmony_ci 9862306a36Sopenharmony_ci return 0; 9962306a36Sopenharmony_ci} 10062306a36Sopenharmony_ci 10162306a36Sopenharmony_cistatic int cdns_dphy_rx_get_band_ctrl(unsigned long hs_clk_rate) 10262306a36Sopenharmony_ci{ 10362306a36Sopenharmony_ci unsigned int rate, i; 10462306a36Sopenharmony_ci 10562306a36Sopenharmony_ci rate = hs_clk_rate / 1000000UL; 10662306a36Sopenharmony_ci /* Since CSI-2 clock is DDR, the bit rate is twice the clock rate. */ 10762306a36Sopenharmony_ci rate *= 2; 10862306a36Sopenharmony_ci 10962306a36Sopenharmony_ci if (rate < bands[0].min_rate) 11062306a36Sopenharmony_ci return -EOPNOTSUPP; 11162306a36Sopenharmony_ci 11262306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(bands); i++) 11362306a36Sopenharmony_ci if (rate < bands[i].max_rate) 11462306a36Sopenharmony_ci return i; 11562306a36Sopenharmony_ci 11662306a36Sopenharmony_ci return -EOPNOTSUPP; 11762306a36Sopenharmony_ci} 11862306a36Sopenharmony_ci 11962306a36Sopenharmony_cistatic inline int cdns_dphy_rx_wait_for_bit(void __iomem *addr, 12062306a36Sopenharmony_ci unsigned int bit) 12162306a36Sopenharmony_ci{ 12262306a36Sopenharmony_ci u32 val; 12362306a36Sopenharmony_ci 12462306a36Sopenharmony_ci return readl_relaxed_poll_timeout(addr, val, val & BIT(bit), 10, 12562306a36Sopenharmony_ci DPHY_ISO_LANE_READY_TIMEOUT_MS * 1000); 12662306a36Sopenharmony_ci} 12762306a36Sopenharmony_ci 12862306a36Sopenharmony_cistatic int cdns_dphy_rx_wait_lane_ready(struct cdns_dphy_rx *dphy, 12962306a36Sopenharmony_ci unsigned int lanes) 13062306a36Sopenharmony_ci{ 13162306a36Sopenharmony_ci static const u32 data_lane_ctrl[] = {DPHY_ISO_DL_CTRL_L0, 13262306a36Sopenharmony_ci DPHY_ISO_DL_CTRL_L1, 13362306a36Sopenharmony_ci DPHY_ISO_DL_CTRL_L2, 13462306a36Sopenharmony_ci DPHY_ISO_DL_CTRL_L3}; 13562306a36Sopenharmony_ci void __iomem *reg = dphy->regs; 13662306a36Sopenharmony_ci unsigned int i; 13762306a36Sopenharmony_ci int ret; 13862306a36Sopenharmony_ci 13962306a36Sopenharmony_ci /* Clock lane */ 14062306a36Sopenharmony_ci ret = cdns_dphy_rx_wait_for_bit(reg + DPHY_ISO_CL_CTRL_L, 14162306a36Sopenharmony_ci DPHY_ISO_LANE_READY_BIT); 14262306a36Sopenharmony_ci if (ret) 14362306a36Sopenharmony_ci return ret; 14462306a36Sopenharmony_ci 14562306a36Sopenharmony_ci for (i = 0; i < lanes; i++) { 14662306a36Sopenharmony_ci ret = cdns_dphy_rx_wait_for_bit(reg + data_lane_ctrl[i], 14762306a36Sopenharmony_ci DPHY_ISO_LANE_READY_BIT); 14862306a36Sopenharmony_ci if (ret) 14962306a36Sopenharmony_ci return ret; 15062306a36Sopenharmony_ci } 15162306a36Sopenharmony_ci 15262306a36Sopenharmony_ci return 0; 15362306a36Sopenharmony_ci} 15462306a36Sopenharmony_ci 15562306a36Sopenharmony_cistatic struct cdns_dphy_soc_data j721e_soc_data = { 15662306a36Sopenharmony_ci .has_hw_cmn_rstb = true, 15762306a36Sopenharmony_ci}; 15862306a36Sopenharmony_ci 15962306a36Sopenharmony_cistatic const struct soc_device_attribute cdns_dphy_socinfo[] = { 16062306a36Sopenharmony_ci { 16162306a36Sopenharmony_ci .family = "J721E", 16262306a36Sopenharmony_ci .revision = "SR1.0", 16362306a36Sopenharmony_ci .data = &j721e_soc_data, 16462306a36Sopenharmony_ci }, 16562306a36Sopenharmony_ci {/* sentinel */} 16662306a36Sopenharmony_ci}; 16762306a36Sopenharmony_ci 16862306a36Sopenharmony_cistatic int cdns_dphy_rx_configure(struct phy *phy, 16962306a36Sopenharmony_ci union phy_configure_opts *opts) 17062306a36Sopenharmony_ci{ 17162306a36Sopenharmony_ci struct cdns_dphy_rx *dphy = phy_get_drvdata(phy); 17262306a36Sopenharmony_ci unsigned int reg, lanes = opts->mipi_dphy.lanes; 17362306a36Sopenharmony_ci const struct cdns_dphy_soc_data *soc_data = NULL; 17462306a36Sopenharmony_ci const struct soc_device_attribute *soc; 17562306a36Sopenharmony_ci int band_ctrl, ret; 17662306a36Sopenharmony_ci 17762306a36Sopenharmony_ci soc = soc_device_match(cdns_dphy_socinfo); 17862306a36Sopenharmony_ci if (soc && soc->data) 17962306a36Sopenharmony_ci soc_data = soc->data; 18062306a36Sopenharmony_ci if (!soc || (soc_data && !soc_data->has_hw_cmn_rstb)) { 18162306a36Sopenharmony_ci reg = DPHY_LANE_RESET_CMN_EN; 18262306a36Sopenharmony_ci writel(reg, dphy->regs + DPHY_LANE); 18362306a36Sopenharmony_ci } 18462306a36Sopenharmony_ci 18562306a36Sopenharmony_ci /* Data lanes. Minimum one lane is mandatory. */ 18662306a36Sopenharmony_ci if (lanes < DPHY_LANES_MIN || lanes > DPHY_LANES_MAX) 18762306a36Sopenharmony_ci return -EINVAL; 18862306a36Sopenharmony_ci 18962306a36Sopenharmony_ci band_ctrl = cdns_dphy_rx_get_band_ctrl(opts->mipi_dphy.hs_clk_rate); 19062306a36Sopenharmony_ci if (band_ctrl < 0) 19162306a36Sopenharmony_ci return band_ctrl; 19262306a36Sopenharmony_ci 19362306a36Sopenharmony_ci reg = FIELD_PREP(DPHY_BAND_CFG_LEFT_BAND, band_ctrl) | 19462306a36Sopenharmony_ci FIELD_PREP(DPHY_BAND_CFG_RIGHT_BAND, band_ctrl); 19562306a36Sopenharmony_ci writel(reg, dphy->regs + DPHY_BAND_CFG); 19662306a36Sopenharmony_ci 19762306a36Sopenharmony_ci /* 19862306a36Sopenharmony_ci * Set the required power island phase 2 time. This is mandated by DPHY 19962306a36Sopenharmony_ci * specs. 20062306a36Sopenharmony_ci */ 20162306a36Sopenharmony_ci reg = DPHY_POWER_ISLAND_EN_DATA_VAL; 20262306a36Sopenharmony_ci writel(reg, dphy->regs + DPHY_POWER_ISLAND_EN_DATA); 20362306a36Sopenharmony_ci reg = DPHY_POWER_ISLAND_EN_CLK_VAL; 20462306a36Sopenharmony_ci writel(reg, dphy->regs + DPHY_POWER_ISLAND_EN_CLK); 20562306a36Sopenharmony_ci 20662306a36Sopenharmony_ci ret = cdns_dphy_rx_wait_lane_ready(dphy, lanes); 20762306a36Sopenharmony_ci if (ret) { 20862306a36Sopenharmony_ci dev_err(dphy->dev, "DPHY wait for lane ready timeout\n"); 20962306a36Sopenharmony_ci return ret; 21062306a36Sopenharmony_ci } 21162306a36Sopenharmony_ci 21262306a36Sopenharmony_ci return 0; 21362306a36Sopenharmony_ci} 21462306a36Sopenharmony_ci 21562306a36Sopenharmony_cistatic int cdns_dphy_rx_validate(struct phy *phy, enum phy_mode mode, 21662306a36Sopenharmony_ci int submode, union phy_configure_opts *opts) 21762306a36Sopenharmony_ci{ 21862306a36Sopenharmony_ci int ret; 21962306a36Sopenharmony_ci 22062306a36Sopenharmony_ci if (mode != PHY_MODE_MIPI_DPHY) 22162306a36Sopenharmony_ci return -EINVAL; 22262306a36Sopenharmony_ci 22362306a36Sopenharmony_ci ret = cdns_dphy_rx_get_band_ctrl(opts->mipi_dphy.hs_clk_rate); 22462306a36Sopenharmony_ci if (ret < 0) 22562306a36Sopenharmony_ci return ret; 22662306a36Sopenharmony_ci 22762306a36Sopenharmony_ci return phy_mipi_dphy_config_validate(&opts->mipi_dphy); 22862306a36Sopenharmony_ci} 22962306a36Sopenharmony_ci 23062306a36Sopenharmony_cistatic const struct phy_ops cdns_dphy_rx_ops = { 23162306a36Sopenharmony_ci .power_on = cdns_dphy_rx_power_on, 23262306a36Sopenharmony_ci .power_off = cdns_dphy_rx_power_off, 23362306a36Sopenharmony_ci .configure = cdns_dphy_rx_configure, 23462306a36Sopenharmony_ci .validate = cdns_dphy_rx_validate, 23562306a36Sopenharmony_ci}; 23662306a36Sopenharmony_ci 23762306a36Sopenharmony_cistatic int cdns_dphy_rx_probe(struct platform_device *pdev) 23862306a36Sopenharmony_ci{ 23962306a36Sopenharmony_ci struct device *dev = &pdev->dev; 24062306a36Sopenharmony_ci struct phy_provider *provider; 24162306a36Sopenharmony_ci struct cdns_dphy_rx *dphy; 24262306a36Sopenharmony_ci 24362306a36Sopenharmony_ci dphy = devm_kzalloc(dev, sizeof(*dphy), GFP_KERNEL); 24462306a36Sopenharmony_ci if (!dphy) 24562306a36Sopenharmony_ci return -ENOMEM; 24662306a36Sopenharmony_ci 24762306a36Sopenharmony_ci dev_set_drvdata(dev, dphy); 24862306a36Sopenharmony_ci dphy->dev = dev; 24962306a36Sopenharmony_ci 25062306a36Sopenharmony_ci dphy->regs = devm_platform_ioremap_resource(pdev, 0); 25162306a36Sopenharmony_ci if (IS_ERR(dphy->regs)) 25262306a36Sopenharmony_ci return PTR_ERR(dphy->regs); 25362306a36Sopenharmony_ci 25462306a36Sopenharmony_ci dphy->phy = devm_phy_create(dev, NULL, &cdns_dphy_rx_ops); 25562306a36Sopenharmony_ci if (IS_ERR(dphy->phy)) { 25662306a36Sopenharmony_ci dev_err(dev, "Failed to create PHY: %ld\n", PTR_ERR(dphy->phy)); 25762306a36Sopenharmony_ci return PTR_ERR(dphy->phy); 25862306a36Sopenharmony_ci } 25962306a36Sopenharmony_ci 26062306a36Sopenharmony_ci phy_set_drvdata(dphy->phy, dphy); 26162306a36Sopenharmony_ci provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate); 26262306a36Sopenharmony_ci if (IS_ERR(provider)) { 26362306a36Sopenharmony_ci dev_err(dev, "Failed to register PHY provider: %ld\n", 26462306a36Sopenharmony_ci PTR_ERR(provider)); 26562306a36Sopenharmony_ci return PTR_ERR(provider); 26662306a36Sopenharmony_ci } 26762306a36Sopenharmony_ci 26862306a36Sopenharmony_ci return 0; 26962306a36Sopenharmony_ci} 27062306a36Sopenharmony_ci 27162306a36Sopenharmony_cistatic const struct of_device_id cdns_dphy_rx_of_match[] = { 27262306a36Sopenharmony_ci { .compatible = "cdns,dphy-rx" }, 27362306a36Sopenharmony_ci { /* sentinel */ }, 27462306a36Sopenharmony_ci}; 27562306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, cdns_dphy_rx_of_match); 27662306a36Sopenharmony_ci 27762306a36Sopenharmony_cistatic struct platform_driver cdns_dphy_rx_platform_driver = { 27862306a36Sopenharmony_ci .probe = cdns_dphy_rx_probe, 27962306a36Sopenharmony_ci .driver = { 28062306a36Sopenharmony_ci .name = "cdns-mipi-dphy-rx", 28162306a36Sopenharmony_ci .of_match_table = cdns_dphy_rx_of_match, 28262306a36Sopenharmony_ci }, 28362306a36Sopenharmony_ci}; 28462306a36Sopenharmony_cimodule_platform_driver(cdns_dphy_rx_platform_driver); 28562306a36Sopenharmony_ci 28662306a36Sopenharmony_ciMODULE_AUTHOR("Pratyush Yadav <p.yadav@ti.com>"); 28762306a36Sopenharmony_ciMODULE_DESCRIPTION("Cadence D-PHY Rx Driver"); 28862306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 289