xref: /linux/drivers/phy/cadence/cdns-dphy-rx.c (revision a1c613ae4c322ddd58d5a8539dbfba2a0380a8c0)
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 
cdns_dphy_rx_power_on(struct phy * phy)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 
cdns_dphy_rx_power_off(struct phy * phy)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 
cdns_dphy_rx_get_band_ctrl(unsigned long hs_clk_rate)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 
cdns_dphy_rx_wait_for_bit(void __iomem * addr,unsigned int bit)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 
cdns_dphy_rx_wait_lane_ready(struct cdns_dphy_rx * dphy,unsigned int lanes)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 
cdns_dphy_rx_configure(struct phy * phy,union phy_configure_opts * opts)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 
cdns_dphy_rx_validate(struct phy * phy,enum phy_mode mode,int submode,union phy_configure_opts * opts)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 
cdns_dphy_rx_probe(struct platform_device * pdev)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