brintos

brintos / linux-shallow public Read only

0
0
Text · 14.6 KiB · 1715275 Raw
537 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2/*3 * Copyright (C) 2013--2024 Intel Corporation4 */5 6#include <linux/bitfield.h>7#include <linux/bits.h>8#include <linux/delay.h>9#include <linux/device.h>10#include <linux/iopoll.h>11#include <linux/math64.h>12 13#include "ipu6-bus.h"14#include "ipu6-isys.h"15#include "ipu6-platform-isys-csi2-reg.h"16 17#define IPU6_DWC_DPHY_BASE(i)			(0x238038 + 0x34 * (i))18#define IPU6_DWC_DPHY_RSTZ			0x0019#define IPU6_DWC_DPHY_SHUTDOWNZ			0x0420#define IPU6_DWC_DPHY_HSFREQRANGE		0x0821#define IPU6_DWC_DPHY_CFGCLKFREQRANGE		0x0c22#define IPU6_DWC_DPHY_TEST_IFC_ACCESS_MODE	0x1023#define IPU6_DWC_DPHY_TEST_IFC_REQ		0x1424#define IPU6_DWC_DPHY_TEST_IFC_REQ_COMPLETION	0x1825#define IPU6_DWC_DPHY_DFT_CTRL0			0x2826#define IPU6_DWC_DPHY_DFT_CTRL1			0x2c27#define IPU6_DWC_DPHY_DFT_CTRL2			0x3028 29/*30 * test IFC request definition:31 * - req: 0 for read, 1 for write32 * - 12 bits address33 * - 8bits data (will ignore for read)34 * --24----16------4-----035 * --|-data-|-addr-|-req-|36 */37#define IFC_REQ(req, addr, data) (FIELD_PREP(GENMASK(23, 16), data) | \38				  FIELD_PREP(GENMASK(15, 4), addr) | \39				  FIELD_PREP(GENMASK(1, 0), req))40 41#define TEST_IFC_REQ_READ	042#define TEST_IFC_REQ_WRITE	143#define TEST_IFC_REQ_RESET	244 45#define TEST_IFC_ACCESS_MODE_FSM	046#define TEST_IFC_ACCESS_MODE_IFC_CTL	147 48enum phy_fsm_state {49	PHY_FSM_STATE_POWERON = 0,50	PHY_FSM_STATE_BGPON = 1,51	PHY_FSM_STATE_CAL_TYPE = 2,52	PHY_FSM_STATE_BURNIN_CAL = 3,53	PHY_FSM_STATE_TERMCAL = 4,54	PHY_FSM_STATE_OFFSETCAL = 5,55	PHY_FSM_STATE_OFFSET_LANE = 6,56	PHY_FSM_STATE_IDLE = 7,57	PHY_FSM_STATE_ULP = 8,58	PHY_FSM_STATE_DDLTUNNING = 9,59	PHY_FSM_STATE_SKEW_BACKWARD = 10,60	PHY_FSM_STATE_INVALID,61};62 63static void dwc_dphy_write(struct ipu6_isys *isys, u32 phy_id, u32 addr,64			   u32 data)65{66	struct device *dev = &isys->adev->auxdev.dev;67	void __iomem *isys_base = isys->pdata->base;68	void __iomem *base = isys_base + IPU6_DWC_DPHY_BASE(phy_id);69 70	dev_dbg(dev, "write: reg 0x%lx = data 0x%x", base + addr - isys_base,71		data);72	writel(data, base + addr);73}74 75static u32 dwc_dphy_read(struct ipu6_isys *isys, u32 phy_id, u32 addr)76{77	struct device *dev = &isys->adev->auxdev.dev;78	void __iomem *isys_base = isys->pdata->base;79	void __iomem *base = isys_base + IPU6_DWC_DPHY_BASE(phy_id);80	u32 data;81 82	data = readl(base + addr);83	dev_dbg(dev, "read: reg 0x%lx = data 0x%x", base + addr - isys_base,84		data);85 86	return data;87}88 89static void dwc_dphy_write_mask(struct ipu6_isys *isys, u32 phy_id, u32 addr,90				u32 data, u8 shift, u8 width)91{92	u32 temp;93	u32 mask;94 95	mask = (1 << width) - 1;96	temp = dwc_dphy_read(isys, phy_id, addr);97	temp &= ~(mask << shift);98	temp |= (data & mask) << shift;99	dwc_dphy_write(isys, phy_id, addr, temp);100}101 102static u32 __maybe_unused dwc_dphy_read_mask(struct ipu6_isys *isys, u32 phy_id,103					     u32 addr, u8 shift,  u8 width)104{105	u32 val;106 107	val = dwc_dphy_read(isys, phy_id, addr) >> shift;108	return val & ((1 << width) - 1);109}110 111#define DWC_DPHY_TIMEOUT (5 * USEC_PER_SEC)112static int dwc_dphy_ifc_read(struct ipu6_isys *isys, u32 phy_id, u32 addr,113			     u32 *val)114{115	struct device *dev = &isys->adev->auxdev.dev;116	void __iomem *isys_base = isys->pdata->base;117	void __iomem *base = isys_base + IPU6_DWC_DPHY_BASE(phy_id);118	void __iomem *reg;119	u32 completion;120	int ret;121 122	dwc_dphy_write(isys, phy_id, IPU6_DWC_DPHY_TEST_IFC_REQ,123		       IFC_REQ(TEST_IFC_REQ_READ, addr, 0));124	reg = base + IPU6_DWC_DPHY_TEST_IFC_REQ_COMPLETION;125	ret = readl_poll_timeout(reg, completion, !(completion & BIT(0)),126				 10, DWC_DPHY_TIMEOUT);127	if (ret) {128		dev_err(dev, "DWC ifc request read timeout\n");129		return ret;130	}131 132	*val = completion >> 8 & 0xff;133	*val = FIELD_GET(GENMASK(15, 8), completion);134	dev_dbg(dev, "DWC ifc read 0x%x = 0x%x", addr, *val);135 136	return 0;137}138 139static int dwc_dphy_ifc_write(struct ipu6_isys *isys, u32 phy_id, u32 addr,140			      u32 data)141{142	struct device *dev = &isys->adev->auxdev.dev;143	void __iomem *isys_base = isys->pdata->base;144	void __iomem *base = isys_base + IPU6_DWC_DPHY_BASE(phy_id);145	void __iomem *reg;146	u32 completion;147	int ret;148 149	dwc_dphy_write(isys, phy_id, IPU6_DWC_DPHY_TEST_IFC_REQ,150		       IFC_REQ(TEST_IFC_REQ_WRITE, addr, data));151	completion = readl(base + IPU6_DWC_DPHY_TEST_IFC_REQ_COMPLETION);152	reg = base + IPU6_DWC_DPHY_TEST_IFC_REQ_COMPLETION;153	ret = readl_poll_timeout(reg, completion, !(completion & BIT(0)),154				 10, DWC_DPHY_TIMEOUT);155	if (ret)156		dev_err(dev, "DWC ifc request write timeout\n");157 158	return ret;159}160 161static void dwc_dphy_ifc_write_mask(struct ipu6_isys *isys, u32 phy_id,162				    u32 addr, u32 data, u8 shift, u8 width)163{164	u32 temp, mask;165	int ret;166 167	ret = dwc_dphy_ifc_read(isys, phy_id, addr, &temp);168	if (ret)169		return;170 171	mask = (1 << width) - 1;172	temp &= ~(mask << shift);173	temp |= (data & mask) << shift;174	dwc_dphy_ifc_write(isys, phy_id, addr, temp);175}176 177static u32 dwc_dphy_ifc_read_mask(struct ipu6_isys *isys, u32 phy_id, u32 addr,178				  u8 shift, u8 width)179{180	int ret;181	u32 val;182 183	ret = dwc_dphy_ifc_read(isys, phy_id, addr, &val);184	if (ret)185		return 0;186 187	return ((val >> shift) & ((1 << width) - 1));188}189 190static int dwc_dphy_pwr_up(struct ipu6_isys *isys, u32 phy_id)191{192	struct device *dev = &isys->adev->auxdev.dev;193	u32 fsm_state;194	int ret;195 196	dwc_dphy_write(isys, phy_id, IPU6_DWC_DPHY_RSTZ, 1);197	usleep_range(10, 20);198	dwc_dphy_write(isys, phy_id, IPU6_DWC_DPHY_SHUTDOWNZ, 1);199 200	ret = read_poll_timeout(dwc_dphy_ifc_read_mask, fsm_state,201				(fsm_state == PHY_FSM_STATE_IDLE ||202				 fsm_state == PHY_FSM_STATE_ULP),203				100, DWC_DPHY_TIMEOUT, false, isys,204				phy_id, 0x1e, 0, 4);205 206	if (ret)207		dev_err(dev, "Dphy %d power up failed, state 0x%x", phy_id,208			fsm_state);209 210	return ret;211}212 213struct dwc_dphy_freq_range {214	u8 hsfreq;215	u16 min;216	u16 max;217	u16 default_mbps;218	u16 osc_freq_target;219};220 221#define DPHY_FREQ_RANGE_NUM		(63)222#define DPHY_FREQ_RANGE_INVALID_INDEX	(0xff)223static const struct dwc_dphy_freq_range freqranges[DPHY_FREQ_RANGE_NUM] = {224	{0x00,	80,	97,	80,	335},225	{0x10,	80,	107,	90,	335},226	{0x20,	84,	118,	100,	335},227	{0x30,	93,	128,	110,	335},228	{0x01,	103,	139,	120,	335},229	{0x11,	112,	149,	130,	335},230	{0x21,	122,	160,	140,	335},231	{0x31,	131,	170,	150,	335},232	{0x02,	141,	181,	160,	335},233	{0x12,	150,	191,	170,	335},234	{0x22,	160,	202,	180,	335},235	{0x32,	169,	212,	190,	335},236	{0x03,	183,	228,	205,	335},237	{0x13,	198,	244,	220,	335},238	{0x23,	212,	259,	235,	335},239	{0x33,	226,	275,	250,	335},240	{0x04,	250,	301,	275,	335},241	{0x14,	274,	328,	300,	335},242	{0x25,	297,	354,	325,	335},243	{0x35,	321,	380,	350,	335},244	{0x05,	369,	433,	400,	335},245	{0x16,	416,	485,	450,	335},246	{0x26,	464,	538,	500,	335},247	{0x37,	511,	590,	550,	335},248	{0x07,	559,	643,	600,	335},249	{0x18,	606,	695,	650,	335},250	{0x28,	654,	748,	700,	335},251	{0x39,	701,	800,	750,	335},252	{0x09,	749,	853,	800,	335},253	{0x19,	796,	905,	850,	335},254	{0x29,	844,	958,	900,	335},255	{0x3a,	891,	1010,	950,	335},256	{0x0a,	939,	1063,	1000,	335},257	{0x1a,	986,	1115,	1050,	335},258	{0x2a,	1034,	1168,	1100,	335},259	{0x3b,	1081,	1220,	1150,	335},260	{0x0b,	1129,	1273,	1200,	335},261	{0x1b,	1176,	1325,	1250,	335},262	{0x2b,	1224,	1378,	1300,	335},263	{0x3c,	1271,	1430,	1350,	335},264	{0x0c,	1319,	1483,	1400,	335},265	{0x1c,	1366,	1535,	1450,	335},266	{0x2c,	1414,	1588,	1500,	335},267	{0x3d,	1461,	1640,	1550,	208},268	{0x0d,	1509,	1693,	1600,	214},269	{0x1d,	1556,	1745,	1650,	221},270	{0x2e,	1604,	1798,	1700,	228},271	{0x3e,	1651,	1850,	1750,	234},272	{0x0e,	1699,	1903,	1800,	241},273	{0x1e,	1746,	1955,	1850,	248},274	{0x2f,	1794,	2008,	1900,	255},275	{0x3f,	1841,	2060,	1950,	261},276	{0x0f,	1889,	2113,	2000,	268},277	{0x40,	1936,	2165,	2050,	275},278	{0x41,	1984,	2218,	2100,	281},279	{0x42,	2031,	2270,	2150,	288},280	{0x43,	2079,	2323,	2200,	294},281	{0x44,	2126,	2375,	2250,	302},282	{0x45,	2174,	2428,	2300,	308},283	{0x46,	2221,	2480,	2350,	315},284	{0x47,	2269,	2500,	2400,	321},285	{0x48,	2316,	2500,	2450,	328},286	{0x49,	2364,	2500,	2500,	335}287};288 289static u16 get_hsfreq_by_mbps(u32 mbps)290{291	unsigned int i = DPHY_FREQ_RANGE_NUM;292 293	while (i--) {294		if (freqranges[i].default_mbps == mbps ||295		    (mbps >= freqranges[i].min && mbps <= freqranges[i].max))296			return i;297	}298 299	return DPHY_FREQ_RANGE_INVALID_INDEX;300}301 302static int ipu6_isys_dwc_phy_config(struct ipu6_isys *isys,303				    u32 phy_id, u32 mbps)304{305	struct ipu6_bus_device *adev = isys->adev;306	struct device *dev = &adev->auxdev.dev;307	struct ipu6_device *isp = adev->isp;308	u32 cfg_clk_freqrange;309	u32 osc_freq_target;310	u32 index;311 312	dev_dbg(dev, "config Dphy %u with %u mbps", phy_id, mbps);313 314	index = get_hsfreq_by_mbps(mbps);315	if (index == DPHY_FREQ_RANGE_INVALID_INDEX) {316		dev_err(dev, "link freq not found for mbps %u", mbps);317		return -EINVAL;318	}319 320	dwc_dphy_write_mask(isys, phy_id, IPU6_DWC_DPHY_HSFREQRANGE,321			    freqranges[index].hsfreq, 0, 7);322 323	/* Force termination Calibration */324	if (isys->phy_termcal_val) {325		dwc_dphy_ifc_write_mask(isys, phy_id, 0x20a, 0x1, 0, 1);326		dwc_dphy_ifc_write_mask(isys, phy_id, 0x209, 0x3, 0, 2);327		dwc_dphy_ifc_write_mask(isys, phy_id, 0x209,328					isys->phy_termcal_val, 4, 4);329	}330 331	/*332	 * Enable override to configure the DDL target oscillation333	 * frequency on bit 0 of register 0xe4334	 */335	dwc_dphy_ifc_write_mask(isys, phy_id, 0xe4, 0x1, 0, 1);336	/*337	 * configure registers 0xe2, 0xe3 with the338	 * appropriate DDL target oscillation frequency339	 * 0x1cc(460)340	 */341	osc_freq_target = freqranges[index].osc_freq_target;342	dwc_dphy_ifc_write_mask(isys, phy_id, 0xe2,343				osc_freq_target & 0xff, 0, 8);344	dwc_dphy_ifc_write_mask(isys, phy_id, 0xe3,345				(osc_freq_target >> 8) & 0xf, 0, 4);346 347	if (mbps < 1500) {348		/* deskew_polarity_rw, for < 1.5Gbps */349		dwc_dphy_ifc_write_mask(isys, phy_id, 0x8, 0x1, 5, 1);350	}351 352	/*353	 * Set cfgclkfreqrange[5:0] = round[(Fcfg_clk(MHz)-17)*4]354	 * (38.4 - 17) * 4 = ~85 (0x55)355	 */356	cfg_clk_freqrange = (isp->buttress.ref_clk - 170) * 4 / 10;357	dev_dbg(dev, "ref_clk = %u clk_freqrange = %u",358		isp->buttress.ref_clk, cfg_clk_freqrange);359	dwc_dphy_write_mask(isys, phy_id, IPU6_DWC_DPHY_CFGCLKFREQRANGE,360			    cfg_clk_freqrange, 0, 8);361 362	dwc_dphy_write_mask(isys, phy_id, IPU6_DWC_DPHY_DFT_CTRL2, 0x1, 4, 1);363	dwc_dphy_write_mask(isys, phy_id, IPU6_DWC_DPHY_DFT_CTRL2, 0x1, 8, 1);364 365	return 0;366}367 368static void ipu6_isys_dwc_phy_aggr_setup(struct ipu6_isys *isys, u32 master,369					 u32 slave, u32 mbps)370{371	/* Config mastermacro */372	dwc_dphy_ifc_write_mask(isys, master, 0x133, 0x1, 0, 1);373	dwc_dphy_ifc_write_mask(isys, slave, 0x133, 0x0, 0, 1);374 375	/* Config master PHY clk lane to drive long channel clk */376	dwc_dphy_ifc_write_mask(isys, master, 0x307, 0x1, 2, 1);377	dwc_dphy_ifc_write_mask(isys, slave, 0x307, 0x0, 2, 1);378 379	/* Config both PHYs data lanes to get clk from long channel */380	dwc_dphy_ifc_write_mask(isys, master, 0x508, 0x1, 5, 1);381	dwc_dphy_ifc_write_mask(isys, slave, 0x508, 0x1, 5, 1);382	dwc_dphy_ifc_write_mask(isys, master, 0x708, 0x1, 5, 1);383	dwc_dphy_ifc_write_mask(isys, slave, 0x708, 0x1, 5, 1);384 385	/* Config slave PHY clk lane to bypass long channel clk to DDR clk */386	dwc_dphy_ifc_write_mask(isys, master, 0x308, 0x0, 3, 1);387	dwc_dphy_ifc_write_mask(isys, slave, 0x308, 0x1, 3, 1);388 389	/* Override slave PHY clk lane enable (DPHYRXCLK_CLL_demux module) */390	dwc_dphy_ifc_write_mask(isys, slave, 0xe0, 0x3, 0, 2);391 392	/* Override slave PHY DDR clk lane enable (DPHYHSRX_div124 module) */393	dwc_dphy_ifc_write_mask(isys, slave, 0xe1, 0x1, 1, 1);394	dwc_dphy_ifc_write_mask(isys, slave, 0x307, 0x1, 3, 1);395 396	/* Turn off slave PHY LP-RX clk lane */397	dwc_dphy_ifc_write_mask(isys, slave, 0x304, 0x1, 7, 1);398	dwc_dphy_ifc_write_mask(isys, slave, 0x305, 0xa, 0, 5);399}400 401#define PHY_E	4402static int ipu6_isys_dwc_phy_powerup_ack(struct ipu6_isys *isys, u32 phy_id)403{404	struct device *dev = &isys->adev->auxdev.dev;405	u32 rescal_done;406	int ret;407 408	ret = dwc_dphy_pwr_up(isys, phy_id);409	if (ret != 0) {410		dev_err(dev, "Dphy %u power up failed(%d)", phy_id, ret);411		return ret;412	}413 414	/* reset forcerxmode */415	dwc_dphy_write_mask(isys, phy_id, IPU6_DWC_DPHY_DFT_CTRL2, 0, 4, 1);416	dwc_dphy_write_mask(isys, phy_id, IPU6_DWC_DPHY_DFT_CTRL2, 0, 8, 1);417 418	dev_dbg(dev, "Dphy %u is ready!", phy_id);419 420	if (phy_id != PHY_E || isys->phy_termcal_val)421		return 0;422 423	usleep_range(100, 200);424	rescal_done = dwc_dphy_ifc_read_mask(isys, phy_id, 0x221, 7, 1);425	if (rescal_done) {426		isys->phy_termcal_val = dwc_dphy_ifc_read_mask(isys, phy_id,427							       0x220, 2, 4);428		dev_dbg(dev, "termcal done with value = %u",429			isys->phy_termcal_val);430	}431 432	return 0;433}434 435static void ipu6_isys_dwc_phy_reset(struct ipu6_isys *isys, u32 phy_id)436{437	dev_dbg(&isys->adev->auxdev.dev, "Reset phy %u", phy_id);438 439	dwc_dphy_write(isys, phy_id, IPU6_DWC_DPHY_SHUTDOWNZ, 0);440	dwc_dphy_write(isys, phy_id, IPU6_DWC_DPHY_RSTZ, 0);441	dwc_dphy_write(isys, phy_id, IPU6_DWC_DPHY_TEST_IFC_ACCESS_MODE,442		       TEST_IFC_ACCESS_MODE_FSM);443	dwc_dphy_write(isys, phy_id, IPU6_DWC_DPHY_TEST_IFC_REQ,444		       TEST_IFC_REQ_RESET);445}446 447int ipu6_isys_dwc_phy_set_power(struct ipu6_isys *isys,448				struct ipu6_isys_csi2_config *cfg,449				const struct ipu6_isys_csi2_timing *timing,450				bool on)451{452	struct device *dev = &isys->adev->auxdev.dev;453	void __iomem *isys_base = isys->pdata->base;454	u32 phy_id, primary, secondary;455	u32 nlanes, port, mbps;456	s64 link_freq;457	int ret;458 459	port = cfg->port;460 461	if (!isys_base || port >= isys->pdata->ipdata->csi2.nports) {462		dev_warn(dev, "invalid port ID %d\n", port);463		return -EINVAL;464	}465 466	nlanes = cfg->nlanes;467	/* only port 0, 2 and 4 support 4 lanes */468	if (nlanes == 4 && port % 2) {469		dev_err(dev, "invalid csi-port %u with %u lanes\n", port,470			nlanes);471		return -EINVAL;472	}473 474	link_freq = ipu6_isys_csi2_get_link_freq(&isys->csi2[port]);475	if (link_freq < 0) {476		dev_err(dev, "get link freq failed(%lld).\n", link_freq);477		return link_freq;478	}479 480	mbps = div_u64(link_freq, 500000);481 482	phy_id = port;483	primary = port & ~1;484	secondary = primary + 1;485	if (on) {486		if (nlanes == 4) {487			dev_dbg(dev, "config phy %u and %u in aggr mode\n",488				primary, secondary);489 490			ipu6_isys_dwc_phy_reset(isys, primary);491			ipu6_isys_dwc_phy_reset(isys, secondary);492			ipu6_isys_dwc_phy_aggr_setup(isys, primary,493						     secondary, mbps);494 495			ret = ipu6_isys_dwc_phy_config(isys, primary, mbps);496			if (ret)497				return ret;498			ret = ipu6_isys_dwc_phy_config(isys, secondary, mbps);499			if (ret)500				return ret;501 502			ret = ipu6_isys_dwc_phy_powerup_ack(isys, primary);503			if (ret)504				return ret;505 506			ret = ipu6_isys_dwc_phy_powerup_ack(isys, secondary);507			return ret;508		}509 510		dev_dbg(dev, "config phy %u with %u lanes in non-aggr mode\n",511			phy_id, nlanes);512 513		ipu6_isys_dwc_phy_reset(isys, phy_id);514		ret = ipu6_isys_dwc_phy_config(isys, phy_id, mbps);515		if (ret)516			return ret;517 518		ret = ipu6_isys_dwc_phy_powerup_ack(isys, phy_id);519		return ret;520	}521 522	if (nlanes == 4) {523		dev_dbg(dev, "Power down phy %u and phy %u for port %u\n",524			primary, secondary, port);525		ipu6_isys_dwc_phy_reset(isys, secondary);526		ipu6_isys_dwc_phy_reset(isys, primary);527 528		return 0;529	}530 531	dev_dbg(dev, "Powerdown phy %u with %u lanes\n", phy_id, nlanes);532 533	ipu6_isys_dwc_phy_reset(isys, phy_id);534 535	return 0;536}537