File Index
Files and directories
Sign in
FILE

cdns-dphy-rx.c

File preview with syntax highlighting.

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