xref: /linux/drivers/memory/tegra/tegra114-emc.c (revision 00599d4841790d05401820a96cf7edb193888b00)
1dce208b5SSvyatoslav Ryhel // SPDX-License-Identifier: GPL-2.0-only
2dce208b5SSvyatoslav Ryhel /*
3dce208b5SSvyatoslav Ryhel  * Tegra114 External Memory Controller driver
4dce208b5SSvyatoslav Ryhel  *
5dce208b5SSvyatoslav Ryhel  * Based on downstream driver from NVIDIA and tegra124-emc.c
6dce208b5SSvyatoslav Ryhel  * Copyright (C) 2011-2014 NVIDIA Corporation
7dce208b5SSvyatoslav Ryhel  *
8dce208b5SSvyatoslav Ryhel  * Copyright (C) 2024 Svyatoslav Ryhel <clamor95@gmail.com>
9dce208b5SSvyatoslav Ryhel  */
10dce208b5SSvyatoslav Ryhel 
11dce208b5SSvyatoslav Ryhel #include <linux/clk.h>
12dce208b5SSvyatoslav Ryhel #include <linux/clk/tegra.h>
13dce208b5SSvyatoslav Ryhel #include <linux/debugfs.h>
14dce208b5SSvyatoslav Ryhel #include <linux/delay.h>
15dce208b5SSvyatoslav Ryhel #include <linux/interrupt.h>
16dce208b5SSvyatoslav Ryhel #include <linux/io.h>
17dce208b5SSvyatoslav Ryhel #include <linux/iopoll.h>
18dce208b5SSvyatoslav Ryhel #include <linux/module.h>
19dce208b5SSvyatoslav Ryhel #include <linux/mutex.h>
20dce208b5SSvyatoslav Ryhel #include <linux/of.h>
21dce208b5SSvyatoslav Ryhel #include <linux/platform_device.h>
22dce208b5SSvyatoslav Ryhel #include <linux/pm_opp.h>
23dce208b5SSvyatoslav Ryhel #include <linux/sort.h>
24dce208b5SSvyatoslav Ryhel #include <linux/string.h>
25dce208b5SSvyatoslav Ryhel 
26dce208b5SSvyatoslav Ryhel #include <soc/tegra/common.h>
27dce208b5SSvyatoslav Ryhel #include <soc/tegra/fuse.h>
28dce208b5SSvyatoslav Ryhel #include <soc/tegra/mc.h>
29dce208b5SSvyatoslav Ryhel 
30dce208b5SSvyatoslav Ryhel #include "mc.h"
31dce208b5SSvyatoslav Ryhel #include "tegra-emc-common.h"
32dce208b5SSvyatoslav Ryhel 
33dce208b5SSvyatoslav Ryhel #define EMC_INTSTATUS				0x0
34dce208b5SSvyatoslav Ryhel #define EMC_INTSTATUS_REFRESH_OVERFLOW		BIT(3)
35dce208b5SSvyatoslav Ryhel #define EMC_INTSTATUS_CLKCHANGE_COMPLETE	BIT(4)
36dce208b5SSvyatoslav Ryhel 
37dce208b5SSvyatoslav Ryhel #define EMC_INTMASK				0x4
38dce208b5SSvyatoslav Ryhel 
39dce208b5SSvyatoslav Ryhel #define EMC_DBG					0x8
40dce208b5SSvyatoslav Ryhel #define EMC_DBG_READ_MUX_ASSEMBLY		BIT(0)
41dce208b5SSvyatoslav Ryhel #define EMC_DBG_WRITE_MUX_ACTIVE		BIT(1)
42dce208b5SSvyatoslav Ryhel #define EMC_DBG_FORCE_UPDATE			BIT(2)
43dce208b5SSvyatoslav Ryhel #define EMC_DBG_CFG_PRIORITY			BIT(24)
44dce208b5SSvyatoslav Ryhel 
45dce208b5SSvyatoslav Ryhel #define EMC_CFG					0xc
46dce208b5SSvyatoslav Ryhel #define EMC_CFG_DSR_VTTGEN_DRV_EN		BIT(18)
47dce208b5SSvyatoslav Ryhel #define EMC_CFG_PWR_MASK			((0xF << 28) | BIT(18))
48dce208b5SSvyatoslav Ryhel #define EMC_CFG_DYN_SREF			BIT(28)
49dce208b5SSvyatoslav Ryhel #define EMC_CFG_DRAM_ACPD			BIT(29)
50dce208b5SSvyatoslav Ryhel #define EMC_CFG_DRAM_CLKSTOP_SR			BIT(30)
51dce208b5SSvyatoslav Ryhel #define EMC_CFG_DRAM_CLKSTOP_PD			BIT(31)
52dce208b5SSvyatoslav Ryhel 
53dce208b5SSvyatoslav Ryhel #define EMC_ADR_CFG				0x10
54dce208b5SSvyatoslav Ryhel #define EMC_ADR_CFG_EMEM_NUMDEV			BIT(0)
55dce208b5SSvyatoslav Ryhel 
56dce208b5SSvyatoslav Ryhel #define EMC_REFCTRL				0x20
57dce208b5SSvyatoslav Ryhel #define EMC_REFCTRL_DEV_SEL_SHIFT		0
58dce208b5SSvyatoslav Ryhel #define EMC_REFCTRL_ENABLE			BIT(31)
59dce208b5SSvyatoslav Ryhel 
60dce208b5SSvyatoslav Ryhel #define EMC_TIMING_CONTROL			0x28
61dce208b5SSvyatoslav Ryhel #define EMC_RC					0x2c
62dce208b5SSvyatoslav Ryhel #define EMC_RFC					0x30
63dce208b5SSvyatoslav Ryhel #define EMC_RAS					0x34
64dce208b5SSvyatoslav Ryhel #define EMC_RP					0x38
65dce208b5SSvyatoslav Ryhel #define EMC_R2W					0x3c
66dce208b5SSvyatoslav Ryhel #define EMC_W2R					0x40
67dce208b5SSvyatoslav Ryhel #define EMC_R2P					0x44
68dce208b5SSvyatoslav Ryhel #define EMC_W2P					0x48
69dce208b5SSvyatoslav Ryhel #define EMC_RD_RCD				0x4c
70dce208b5SSvyatoslav Ryhel #define EMC_WR_RCD				0x50
71dce208b5SSvyatoslav Ryhel #define EMC_RRD					0x54
72dce208b5SSvyatoslav Ryhel #define EMC_REXT				0x58
73dce208b5SSvyatoslav Ryhel #define EMC_WDV					0x5c
74dce208b5SSvyatoslav Ryhel #define EMC_QUSE				0x60
75dce208b5SSvyatoslav Ryhel #define EMC_QRST				0x64
76dce208b5SSvyatoslav Ryhel #define EMC_QSAFE				0x68
77dce208b5SSvyatoslav Ryhel #define EMC_RDV					0x6c
78dce208b5SSvyatoslav Ryhel #define EMC_REFRESH				0x70
79dce208b5SSvyatoslav Ryhel #define EMC_BURST_REFRESH_NUM			0x74
80dce208b5SSvyatoslav Ryhel #define EMC_PDEX2WR				0x78
81dce208b5SSvyatoslav Ryhel #define EMC_PDEX2RD				0x7c
82dce208b5SSvyatoslav Ryhel #define EMC_PCHG2PDEN				0x80
83dce208b5SSvyatoslav Ryhel #define EMC_ACT2PDEN				0x84
84dce208b5SSvyatoslav Ryhel #define EMC_AR2PDEN				0x88
85dce208b5SSvyatoslav Ryhel #define EMC_RW2PDEN				0x8c
86dce208b5SSvyatoslav Ryhel #define EMC_TXSR				0x90
87dce208b5SSvyatoslav Ryhel #define EMC_TCKE				0x94
88dce208b5SSvyatoslav Ryhel #define EMC_TFAW				0x98
89dce208b5SSvyatoslav Ryhel #define EMC_TRPAB				0x9c
90dce208b5SSvyatoslav Ryhel #define EMC_TCLKSTABLE				0xa0
91dce208b5SSvyatoslav Ryhel #define EMC_TCLKSTOP				0xa4
92dce208b5SSvyatoslav Ryhel #define EMC_TREFBW				0xa8
93dce208b5SSvyatoslav Ryhel #define EMC_QUSE_EXTRA				0xac
94dce208b5SSvyatoslav Ryhel #define EMC_ODT_WRITE				0xb0
95dce208b5SSvyatoslav Ryhel #define EMC_ODT_READ				0xb4
96dce208b5SSvyatoslav Ryhel #define EMC_WEXT				0xb8
97dce208b5SSvyatoslav Ryhel #define EMC_CTT					0xbc
98dce208b5SSvyatoslav Ryhel #define EMC_RFC_SLR				0xc0
99dce208b5SSvyatoslav Ryhel #define EMC_MRS_WAIT_CNT2			0xc4
100dce208b5SSvyatoslav Ryhel 
101dce208b5SSvyatoslav Ryhel #define EMC_MRS_WAIT_CNT			0xc8
102dce208b5SSvyatoslav Ryhel #define EMC_MRS_WAIT_CNT_SHORT_WAIT_SHIFT	0
103dce208b5SSvyatoslav Ryhel #define EMC_MRS_WAIT_CNT_SHORT_WAIT_MASK	\
104dce208b5SSvyatoslav Ryhel 	(0x3FF << EMC_MRS_WAIT_CNT_SHORT_WAIT_SHIFT)
105dce208b5SSvyatoslav Ryhel #define EMC_MRS_WAIT_CNT_LONG_WAIT_SHIFT	16
106dce208b5SSvyatoslav Ryhel #define EMC_MRS_WAIT_CNT_LONG_WAIT_MASK		\
107dce208b5SSvyatoslav Ryhel 	(0x3FF << EMC_MRS_WAIT_CNT_LONG_WAIT_SHIFT)
108dce208b5SSvyatoslav Ryhel 
109dce208b5SSvyatoslav Ryhel #define EMC_MRS					0xcc
110dce208b5SSvyatoslav Ryhel #define EMC_MODE_SET_DLL_RESET			BIT(8)
111dce208b5SSvyatoslav Ryhel #define EMC_MODE_SET_LONG_CNT			BIT(26)
112dce208b5SSvyatoslav Ryhel #define EMC_EMRS				0xd0
113dce208b5SSvyatoslav Ryhel #define EMC_REF					0xd4
114dce208b5SSvyatoslav Ryhel #define EMC_PRE					0xd8
115dce208b5SSvyatoslav Ryhel 
116dce208b5SSvyatoslav Ryhel #define EMC_SELF_REF				0xe0
117dce208b5SSvyatoslav Ryhel #define EMC_SELF_REF_CMD_ENABLED		BIT(0)
118dce208b5SSvyatoslav Ryhel #define EMC_SELF_REF_DEV_SEL_SHIFT		30
119dce208b5SSvyatoslav Ryhel 
120dce208b5SSvyatoslav Ryhel #define EMC_MRW					0xe8
121dce208b5SSvyatoslav Ryhel 
122dce208b5SSvyatoslav Ryhel #define EMC_MRR					0xec
123dce208b5SSvyatoslav Ryhel #define EMC_MRR_MA_SHIFT			16
124dce208b5SSvyatoslav Ryhel #define LPDDR2_MR4_TEMP_SHIFT			0
125dce208b5SSvyatoslav Ryhel 
126dce208b5SSvyatoslav Ryhel #define EMC_XM2DQSPADCTRL3			0xf8
127dce208b5SSvyatoslav Ryhel #define EMC_FBIO_SPARE				0x100
128dce208b5SSvyatoslav Ryhel 
129dce208b5SSvyatoslav Ryhel #define EMC_FBIO_CFG5				0x104
130dce208b5SSvyatoslav Ryhel #define EMC_FBIO_CFG5_DRAM_TYPE_MASK		0x3
131dce208b5SSvyatoslav Ryhel #define EMC_FBIO_CFG5_DRAM_TYPE_SHIFT		0
132dce208b5SSvyatoslav Ryhel 
133dce208b5SSvyatoslav Ryhel #define EMC_FBIO_CFG6				0x114
134dce208b5SSvyatoslav Ryhel #define EMC_EMRS2				0x12c
135dce208b5SSvyatoslav Ryhel #define EMC_MRW2				0x134
136dce208b5SSvyatoslav Ryhel #define EMC_MRW4				0x13c
137dce208b5SSvyatoslav Ryhel #define EMC_EINPUT				0x14c
138dce208b5SSvyatoslav Ryhel #define EMC_EINPUT_DURATION			0x150
139dce208b5SSvyatoslav Ryhel #define EMC_PUTERM_EXTRA			0x154
140dce208b5SSvyatoslav Ryhel #define EMC_TCKESR				0x158
141dce208b5SSvyatoslav Ryhel #define EMC_TPD					0x15c
142dce208b5SSvyatoslav Ryhel 
143dce208b5SSvyatoslav Ryhel #define EMC_AUTO_CAL_CONFIG			0x2a4
144dce208b5SSvyatoslav Ryhel #define EMC_AUTO_CAL_CONFIG_AUTO_CAL_START	BIT(31)
145dce208b5SSvyatoslav Ryhel #define EMC_AUTO_CAL_INTERVAL			0x2a8
146dce208b5SSvyatoslav Ryhel #define EMC_AUTO_CAL_STATUS			0x2ac
147dce208b5SSvyatoslav Ryhel #define EMC_AUTO_CAL_STATUS_ACTIVE		BIT(31)
148dce208b5SSvyatoslav Ryhel #define EMC_STATUS				0x2b4
149dce208b5SSvyatoslav Ryhel #define EMC_STATUS_TIMING_UPDATE_STALLED	BIT(23)
150dce208b5SSvyatoslav Ryhel 
151dce208b5SSvyatoslav Ryhel #define EMC_CFG_2				0x2b8
152dce208b5SSvyatoslav Ryhel #define EMC_CFG_2_CLKCHANGE_REQ_ENABLE		BIT(0)
153dce208b5SSvyatoslav Ryhel #define EMC_CFG_2_CLKCHANGE_PD_ENABLE		BIT(1)
154dce208b5SSvyatoslav Ryhel #define EMC_CFG_2_CLKCHANGE_SR_ENABLE		BIT(2)
155dce208b5SSvyatoslav Ryhel 
156dce208b5SSvyatoslav Ryhel #define EMC_CFG_DIG_DLL				0x2bc
157dce208b5SSvyatoslav Ryhel #define EMC_CFG_DIG_DLL_PERIOD			0x2c0
158dce208b5SSvyatoslav Ryhel #define EMC_RDV_MASK				0x2cc
159dce208b5SSvyatoslav Ryhel #define EMC_WDV_MASK				0x2d0
160dce208b5SSvyatoslav Ryhel #define EMC_CTT_DURATION			0x2d8
161dce208b5SSvyatoslav Ryhel #define EMC_CTT_TERM_CTRL			0x2dc
162dce208b5SSvyatoslav Ryhel #define EMC_ZCAL_INTERVAL			0x2e0
163dce208b5SSvyatoslav Ryhel #define EMC_ZCAL_WAIT_CNT			0x2e4
164dce208b5SSvyatoslav Ryhel 
165dce208b5SSvyatoslav Ryhel #define EMC_ZQ_CAL				0x2ec
166dce208b5SSvyatoslav Ryhel #define EMC_ZQ_CAL_CMD				BIT(0)
167dce208b5SSvyatoslav Ryhel #define EMC_ZQ_CAL_LONG				BIT(4)
168dce208b5SSvyatoslav Ryhel #define EMC_ZQ_CAL_LONG_CMD_DEV0		\
169dce208b5SSvyatoslav Ryhel 	(DRAM_DEV_SEL_0 | EMC_ZQ_CAL_LONG | EMC_ZQ_CAL_CMD)
170dce208b5SSvyatoslav Ryhel #define EMC_ZQ_CAL_LONG_CMD_DEV1		\
171dce208b5SSvyatoslav Ryhel 	(DRAM_DEV_SEL_1 | EMC_ZQ_CAL_LONG | EMC_ZQ_CAL_CMD)
172dce208b5SSvyatoslav Ryhel 
173dce208b5SSvyatoslav Ryhel #define EMC_XM2CMDPADCTRL			0x2f0
174dce208b5SSvyatoslav Ryhel #define EMC_XM2DQSPADCTRL			0x2f8
175dce208b5SSvyatoslav Ryhel #define EMC_XM2DQSPADCTRL2			0x2fc
176dce208b5SSvyatoslav Ryhel #define EMC_XM2DQSPADCTRL2_RX_FT_REC_ENABLE	BIT(0)
177dce208b5SSvyatoslav Ryhel #define EMC_XM2DQSPADCTRL2_VREF_ENABLE		BIT(5)
178dce208b5SSvyatoslav Ryhel #define EMC_XM2DQPADCTRL			0x300
179dce208b5SSvyatoslav Ryhel #define EMC_XM2DQPADCTRL2			0x304
180dce208b5SSvyatoslav Ryhel #define EMC_XM2CLKPADCTRL			0x308
181dce208b5SSvyatoslav Ryhel #define EMC_XM2COMPPADCTRL			0x30c
182dce208b5SSvyatoslav Ryhel #define EMC_XM2VTTGENPADCTRL			0x310
183dce208b5SSvyatoslav Ryhel #define EMC_XM2VTTGENPADCTRL2			0x314
184dce208b5SSvyatoslav Ryhel #define EMC_XM2QUSEPADCTRL			0x318
185dce208b5SSvyatoslav Ryhel #define EMC_XM2DQSPADCTRL4			0x320
186dce208b5SSvyatoslav Ryhel #define EMC_DLL_XFORM_DQS0			0x328
187dce208b5SSvyatoslav Ryhel #define EMC_DLL_XFORM_DQS1			0x32c
188dce208b5SSvyatoslav Ryhel #define EMC_DLL_XFORM_DQS2			0x330
189dce208b5SSvyatoslav Ryhel #define EMC_DLL_XFORM_DQS3			0x334
190dce208b5SSvyatoslav Ryhel #define EMC_DLL_XFORM_DQS4			0x338
191dce208b5SSvyatoslav Ryhel #define EMC_DLL_XFORM_DQS5			0x33c
192dce208b5SSvyatoslav Ryhel #define EMC_DLL_XFORM_DQS6			0x340
193dce208b5SSvyatoslav Ryhel #define EMC_DLL_XFORM_DQS7			0x344
194dce208b5SSvyatoslav Ryhel #define EMC_DLL_XFORM_QUSE0			0x348
195dce208b5SSvyatoslav Ryhel #define EMC_DLL_XFORM_QUSE1			0x34c
196dce208b5SSvyatoslav Ryhel #define EMC_DLL_XFORM_QUSE2			0x350
197dce208b5SSvyatoslav Ryhel #define EMC_DLL_XFORM_QUSE3			0x354
198dce208b5SSvyatoslav Ryhel #define EMC_DLL_XFORM_QUSE4			0x358
199dce208b5SSvyatoslav Ryhel #define EMC_DLL_XFORM_QUSE5			0x35c
200dce208b5SSvyatoslav Ryhel #define EMC_DLL_XFORM_QUSE6			0x360
201dce208b5SSvyatoslav Ryhel #define EMC_DLL_XFORM_QUSE7			0x364
202dce208b5SSvyatoslav Ryhel #define EMC_DLL_XFORM_DQ0			0x368
203dce208b5SSvyatoslav Ryhel #define EMC_DLL_XFORM_DQ1			0x36c
204dce208b5SSvyatoslav Ryhel #define EMC_DLL_XFORM_DQ2			0x370
205dce208b5SSvyatoslav Ryhel #define EMC_DLL_XFORM_DQ3			0x374
206dce208b5SSvyatoslav Ryhel #define EMC_DLI_TRIM_TXDQS0			0x3a8
207dce208b5SSvyatoslav Ryhel #define EMC_DLI_TRIM_TXDQS1			0x3ac
208dce208b5SSvyatoslav Ryhel #define EMC_DLI_TRIM_TXDQS2			0x3b0
209dce208b5SSvyatoslav Ryhel #define EMC_DLI_TRIM_TXDQS3			0x3b4
210dce208b5SSvyatoslav Ryhel #define EMC_DLI_TRIM_TXDQS4			0x3b8
211dce208b5SSvyatoslav Ryhel #define EMC_DLI_TRIM_TXDQS5			0x3bc
212dce208b5SSvyatoslav Ryhel #define EMC_DLI_TRIM_TXDQS6			0x3c0
213dce208b5SSvyatoslav Ryhel #define EMC_DLI_TRIM_TXDQS7			0x3c4
214dce208b5SSvyatoslav Ryhel #define EMC_STALL_THEN_EXE_AFTER_CLKCHANGE	0x3cc
215dce208b5SSvyatoslav Ryhel #define EMC_SEL_DPD_CTRL			0x3d8
216dce208b5SSvyatoslav Ryhel #define EMC_SEL_DPD_CTRL_DATA_SEL_DPD		BIT(8)
217dce208b5SSvyatoslav Ryhel #define EMC_SEL_DPD_CTRL_ODT_SEL_DPD		BIT(5)
218dce208b5SSvyatoslav Ryhel #define EMC_SEL_DPD_CTRL_RESET_SEL_DPD		BIT(4)
219dce208b5SSvyatoslav Ryhel #define EMC_SEL_DPD_CTRL_CA_SEL_DPD		BIT(3)
220dce208b5SSvyatoslav Ryhel #define EMC_SEL_DPD_CTRL_CLK_SEL_DPD		BIT(2)
221dce208b5SSvyatoslav Ryhel #define EMC_SEL_DPD_CTRL_DDR3_MASK	\
222dce208b5SSvyatoslav Ryhel 	((0xf << 2) | BIT(8))
223dce208b5SSvyatoslav Ryhel #define EMC_SEL_DPD_CTRL_MASK \
224dce208b5SSvyatoslav Ryhel 	((0x3 << 2) | BIT(5) | BIT(8))
225dce208b5SSvyatoslav Ryhel #define EMC_PRE_REFRESH_REQ_CNT			0x3dc
226dce208b5SSvyatoslav Ryhel #define EMC_DYN_SELF_REF_CONTROL		0x3e0
227dce208b5SSvyatoslav Ryhel #define EMC_TXSRDLL				0x3e4
228dce208b5SSvyatoslav Ryhel #define EMC_CCFIFO_ADDR				0x3e8
229dce208b5SSvyatoslav Ryhel #define EMC_CCFIFO_DATA				0x3ec
230dce208b5SSvyatoslav Ryhel #define EMC_CCFIFO_STATUS			0x3f0
231dce208b5SSvyatoslav Ryhel #define EMC_CDB_CNTL_1				0x3f4
232dce208b5SSvyatoslav Ryhel #define EMC_CDB_CNTL_2				0x3f8
233dce208b5SSvyatoslav Ryhel #define EMC_XM2CLKPADCTRL2			0x3fc
234dce208b5SSvyatoslav Ryhel #define EMC_AUTO_CAL_CONFIG2			0x458
235dce208b5SSvyatoslav Ryhel #define EMC_AUTO_CAL_CONFIG3			0x45c
236dce208b5SSvyatoslav Ryhel #define EMC_IBDLY				0x468
237dce208b5SSvyatoslav Ryhel #define EMC_DLL_XFORM_ADDR0			0x46c
238dce208b5SSvyatoslav Ryhel #define EMC_DLL_XFORM_ADDR1			0x470
239dce208b5SSvyatoslav Ryhel #define EMC_DLL_XFORM_ADDR2			0x474
240dce208b5SSvyatoslav Ryhel #define EMC_DSR_VTTGEN_DRV			0x47c
241dce208b5SSvyatoslav Ryhel #define EMC_TXDSRVTTGEN				0x480
242dce208b5SSvyatoslav Ryhel #define EMC_XM2CMDPADCTRL4			0x484
243dce208b5SSvyatoslav Ryhel 
244dce208b5SSvyatoslav Ryhel #define DRAM_DEV_SEL_ALL			0
245dce208b5SSvyatoslav Ryhel #define DRAM_DEV_SEL_0				BIT(31)
246dce208b5SSvyatoslav Ryhel #define DRAM_DEV_SEL_1				BIT(30)
247dce208b5SSvyatoslav Ryhel 
248dce208b5SSvyatoslav Ryhel #define EMC_CFG_POWER_FEATURES_MASK		\
249dce208b5SSvyatoslav Ryhel 	(EMC_CFG_DYN_SREF | EMC_CFG_DRAM_ACPD | EMC_CFG_DRAM_CLKSTOP_SR | \
250dce208b5SSvyatoslav Ryhel 	EMC_CFG_DRAM_CLKSTOP_PD | EMC_CFG_DSR_VTTGEN_DRV_EN)
251dce208b5SSvyatoslav Ryhel #define EMC_REFCTRL_DEV_SEL(n) ((((n) > 1) ? 0 : 2) << EMC_REFCTRL_DEV_SEL_SHIFT)
252dce208b5SSvyatoslav Ryhel #define EMC_DRAM_DEV_SEL(n) (((n) > 1) ? DRAM_DEV_SEL_ALL : DRAM_DEV_SEL_0)
253dce208b5SSvyatoslav Ryhel 
254dce208b5SSvyatoslav Ryhel /* Maximum amount of time in us. to wait for changes to become effective */
255dce208b5SSvyatoslav Ryhel #define EMC_STATUS_UPDATE_TIMEOUT		1000
256dce208b5SSvyatoslav Ryhel 
257dce208b5SSvyatoslav Ryhel enum emc_dram_type {
258dce208b5SSvyatoslav Ryhel 	DRAM_TYPE_DDR3,
259dce208b5SSvyatoslav Ryhel 	DRAM_TYPE_DDR1,
260dce208b5SSvyatoslav Ryhel 	DRAM_TYPE_LPDDR2,
261dce208b5SSvyatoslav Ryhel 	DRAM_TYPE_DDR2
262dce208b5SSvyatoslav Ryhel };
263dce208b5SSvyatoslav Ryhel 
264dce208b5SSvyatoslav Ryhel enum emc_dll_change {
265dce208b5SSvyatoslav Ryhel 	DLL_CHANGE_NONE,
266dce208b5SSvyatoslav Ryhel 	DLL_CHANGE_ON,
267dce208b5SSvyatoslav Ryhel 	DLL_CHANGE_OFF
268dce208b5SSvyatoslav Ryhel };
269dce208b5SSvyatoslav Ryhel 
270dce208b5SSvyatoslav Ryhel static const unsigned long emc_burst_regs[] = {
271dce208b5SSvyatoslav Ryhel 	EMC_RC,
272dce208b5SSvyatoslav Ryhel 	EMC_RFC,
273dce208b5SSvyatoslav Ryhel 	EMC_RAS,
274dce208b5SSvyatoslav Ryhel 	EMC_RP,
275dce208b5SSvyatoslav Ryhel 	EMC_R2W,
276dce208b5SSvyatoslav Ryhel 	EMC_W2R,
277dce208b5SSvyatoslav Ryhel 	EMC_R2P,
278dce208b5SSvyatoslav Ryhel 	EMC_W2P,
279dce208b5SSvyatoslav Ryhel 	EMC_RD_RCD,
280dce208b5SSvyatoslav Ryhel 	EMC_WR_RCD,
281dce208b5SSvyatoslav Ryhel 	EMC_RRD,
282dce208b5SSvyatoslav Ryhel 	EMC_REXT,
283dce208b5SSvyatoslav Ryhel 	EMC_WEXT,
284dce208b5SSvyatoslav Ryhel 	EMC_WDV,
285dce208b5SSvyatoslav Ryhel 	EMC_WDV_MASK,
286dce208b5SSvyatoslav Ryhel 	EMC_QUSE,
287dce208b5SSvyatoslav Ryhel 	EMC_IBDLY,
288dce208b5SSvyatoslav Ryhel 	EMC_EINPUT,
289dce208b5SSvyatoslav Ryhel 	EMC_EINPUT_DURATION,
290dce208b5SSvyatoslav Ryhel 	EMC_PUTERM_EXTRA,
291dce208b5SSvyatoslav Ryhel 	EMC_CDB_CNTL_1,
292dce208b5SSvyatoslav Ryhel 	EMC_CDB_CNTL_2,
293dce208b5SSvyatoslav Ryhel 	EMC_QRST,
294dce208b5SSvyatoslav Ryhel 	EMC_QSAFE,
295dce208b5SSvyatoslav Ryhel 	EMC_RDV,
296dce208b5SSvyatoslav Ryhel 	EMC_RDV_MASK,
297dce208b5SSvyatoslav Ryhel 	EMC_REFRESH,
298dce208b5SSvyatoslav Ryhel 	EMC_BURST_REFRESH_NUM,
299dce208b5SSvyatoslav Ryhel 	EMC_PRE_REFRESH_REQ_CNT,
300dce208b5SSvyatoslav Ryhel 	EMC_PDEX2WR,
301dce208b5SSvyatoslav Ryhel 	EMC_PDEX2RD,
302dce208b5SSvyatoslav Ryhel 	EMC_PCHG2PDEN,
303dce208b5SSvyatoslav Ryhel 	EMC_ACT2PDEN,
304dce208b5SSvyatoslav Ryhel 	EMC_AR2PDEN,
305dce208b5SSvyatoslav Ryhel 	EMC_RW2PDEN,
306dce208b5SSvyatoslav Ryhel 	EMC_TXSR,
307dce208b5SSvyatoslav Ryhel 	EMC_TXSRDLL,
308dce208b5SSvyatoslav Ryhel 	EMC_TCKE,
309dce208b5SSvyatoslav Ryhel 	EMC_TCKESR,
310dce208b5SSvyatoslav Ryhel 	EMC_TPD,
311dce208b5SSvyatoslav Ryhel 	EMC_TFAW,
312dce208b5SSvyatoslav Ryhel 	EMC_TRPAB,
313dce208b5SSvyatoslav Ryhel 	EMC_TCLKSTABLE,
314dce208b5SSvyatoslav Ryhel 	EMC_TCLKSTOP,
315dce208b5SSvyatoslav Ryhel 	EMC_TREFBW,
316dce208b5SSvyatoslav Ryhel 	EMC_QUSE_EXTRA,
317dce208b5SSvyatoslav Ryhel 	EMC_FBIO_CFG6,
318dce208b5SSvyatoslav Ryhel 	EMC_ODT_WRITE,
319dce208b5SSvyatoslav Ryhel 	EMC_ODT_READ,
320dce208b5SSvyatoslav Ryhel 	EMC_FBIO_CFG5,
321dce208b5SSvyatoslav Ryhel 	EMC_CFG_DIG_DLL,
322dce208b5SSvyatoslav Ryhel 	EMC_CFG_DIG_DLL_PERIOD,
323dce208b5SSvyatoslav Ryhel 	EMC_DLL_XFORM_DQS0,
324dce208b5SSvyatoslav Ryhel 	EMC_DLL_XFORM_DQS1,
325dce208b5SSvyatoslav Ryhel 	EMC_DLL_XFORM_DQS2,
326dce208b5SSvyatoslav Ryhel 	EMC_DLL_XFORM_DQS3,
327dce208b5SSvyatoslav Ryhel 	EMC_DLL_XFORM_DQS4,
328dce208b5SSvyatoslav Ryhel 	EMC_DLL_XFORM_DQS5,
329dce208b5SSvyatoslav Ryhel 	EMC_DLL_XFORM_DQS6,
330dce208b5SSvyatoslav Ryhel 	EMC_DLL_XFORM_DQS7,
331dce208b5SSvyatoslav Ryhel 	EMC_DLL_XFORM_QUSE0,
332dce208b5SSvyatoslav Ryhel 	EMC_DLL_XFORM_QUSE1,
333dce208b5SSvyatoslav Ryhel 	EMC_DLL_XFORM_QUSE2,
334dce208b5SSvyatoslav Ryhel 	EMC_DLL_XFORM_QUSE3,
335dce208b5SSvyatoslav Ryhel 	EMC_DLL_XFORM_QUSE4,
336dce208b5SSvyatoslav Ryhel 	EMC_DLL_XFORM_QUSE5,
337dce208b5SSvyatoslav Ryhel 	EMC_DLL_XFORM_QUSE6,
338dce208b5SSvyatoslav Ryhel 	EMC_DLL_XFORM_QUSE7,
339dce208b5SSvyatoslav Ryhel 	EMC_DLI_TRIM_TXDQS0,
340dce208b5SSvyatoslav Ryhel 	EMC_DLI_TRIM_TXDQS1,
341dce208b5SSvyatoslav Ryhel 	EMC_DLI_TRIM_TXDQS2,
342dce208b5SSvyatoslav Ryhel 	EMC_DLI_TRIM_TXDQS3,
343dce208b5SSvyatoslav Ryhel 	EMC_DLI_TRIM_TXDQS4,
344dce208b5SSvyatoslav Ryhel 	EMC_DLI_TRIM_TXDQS5,
345dce208b5SSvyatoslav Ryhel 	EMC_DLI_TRIM_TXDQS6,
346dce208b5SSvyatoslav Ryhel 	EMC_DLI_TRIM_TXDQS7,
347dce208b5SSvyatoslav Ryhel 	EMC_DLL_XFORM_DQ0,
348dce208b5SSvyatoslav Ryhel 	EMC_DLL_XFORM_DQ1,
349dce208b5SSvyatoslav Ryhel 	EMC_DLL_XFORM_DQ2,
350dce208b5SSvyatoslav Ryhel 	EMC_DLL_XFORM_DQ3,
351dce208b5SSvyatoslav Ryhel 	EMC_XM2CMDPADCTRL,
352dce208b5SSvyatoslav Ryhel 	EMC_XM2CMDPADCTRL4,
353dce208b5SSvyatoslav Ryhel 	EMC_XM2DQPADCTRL2,
354dce208b5SSvyatoslav Ryhel 	EMC_XM2CLKPADCTRL,
355dce208b5SSvyatoslav Ryhel 	EMC_XM2COMPPADCTRL,
356dce208b5SSvyatoslav Ryhel 	EMC_XM2VTTGENPADCTRL,
357dce208b5SSvyatoslav Ryhel 	EMC_XM2VTTGENPADCTRL2,
358dce208b5SSvyatoslav Ryhel 	EMC_XM2DQSPADCTRL3,
359dce208b5SSvyatoslav Ryhel 	EMC_XM2DQSPADCTRL4,
360dce208b5SSvyatoslav Ryhel 	EMC_DSR_VTTGEN_DRV,
361dce208b5SSvyatoslav Ryhel 	EMC_TXDSRVTTGEN,
362dce208b5SSvyatoslav Ryhel 	EMC_FBIO_SPARE,
363dce208b5SSvyatoslav Ryhel 	EMC_ZCAL_WAIT_CNT,
364dce208b5SSvyatoslav Ryhel 	EMC_MRS_WAIT_CNT2,
365dce208b5SSvyatoslav Ryhel 	EMC_CTT,
366dce208b5SSvyatoslav Ryhel 	EMC_CTT_DURATION,
367dce208b5SSvyatoslav Ryhel 	EMC_DYN_SELF_REF_CONTROL,
368dce208b5SSvyatoslav Ryhel };
369dce208b5SSvyatoslav Ryhel 
370dce208b5SSvyatoslav Ryhel struct emc_timing {
371dce208b5SSvyatoslav Ryhel 	unsigned long rate;
372dce208b5SSvyatoslav Ryhel 
373dce208b5SSvyatoslav Ryhel 	u32 emc_burst_data[ARRAY_SIZE(emc_burst_regs)];
374dce208b5SSvyatoslav Ryhel 
375dce208b5SSvyatoslav Ryhel 	u32 emc_auto_cal_config;
376dce208b5SSvyatoslav Ryhel 	u32 emc_auto_cal_config2;
377dce208b5SSvyatoslav Ryhel 	u32 emc_auto_cal_config3;
378dce208b5SSvyatoslav Ryhel 	u32 emc_auto_cal_interval;
379dce208b5SSvyatoslav Ryhel 	u32 emc_cfg;
380dce208b5SSvyatoslav Ryhel 	u32 emc_ctt_term_ctrl;
381dce208b5SSvyatoslav Ryhel 	u32 emc_mode_1;
382dce208b5SSvyatoslav Ryhel 	u32 emc_mode_2;
383dce208b5SSvyatoslav Ryhel 	u32 emc_mode_4;
384dce208b5SSvyatoslav Ryhel 	u32 emc_mode_reset;
385dce208b5SSvyatoslav Ryhel 	u32 emc_mrs_wait_cnt;
386dce208b5SSvyatoslav Ryhel 	u32 emc_sel_dpd_ctrl;
387dce208b5SSvyatoslav Ryhel 	u32 emc_xm2dqspadctrl2;
388dce208b5SSvyatoslav Ryhel 	u32 emc_zcal_cnt_long;
389dce208b5SSvyatoslav Ryhel 	u32 emc_zcal_interval;
390dce208b5SSvyatoslav Ryhel };
391dce208b5SSvyatoslav Ryhel 
392dce208b5SSvyatoslav Ryhel struct tegra_emc {
393dce208b5SSvyatoslav Ryhel 	struct device *dev;
394dce208b5SSvyatoslav Ryhel 	struct tegra_mc *mc;
395dce208b5SSvyatoslav Ryhel 	void __iomem *regs;
396dce208b5SSvyatoslav Ryhel 	unsigned int irq;
397dce208b5SSvyatoslav Ryhel 	struct clk *clk;
398dce208b5SSvyatoslav Ryhel 
399dce208b5SSvyatoslav Ryhel 	enum emc_dram_type dram_type;
400dce208b5SSvyatoslav Ryhel 	unsigned int dram_num;
401dce208b5SSvyatoslav Ryhel 
402dce208b5SSvyatoslav Ryhel 	struct emc_timing last_timing;
403dce208b5SSvyatoslav Ryhel 	struct emc_timing *timings;
404dce208b5SSvyatoslav Ryhel 	unsigned int num_timings;
405dce208b5SSvyatoslav Ryhel 
406dce208b5SSvyatoslav Ryhel 	struct {
407dce208b5SSvyatoslav Ryhel 		struct dentry *root;
408dce208b5SSvyatoslav Ryhel 		unsigned long min_rate;
409dce208b5SSvyatoslav Ryhel 		unsigned long max_rate;
410dce208b5SSvyatoslav Ryhel 	} debugfs;
411dce208b5SSvyatoslav Ryhel 
412dce208b5SSvyatoslav Ryhel 	struct icc_provider provider;
413dce208b5SSvyatoslav Ryhel 	struct tegra_emc_rate_requests reqs;
414dce208b5SSvyatoslav Ryhel };
415dce208b5SSvyatoslav Ryhel 
416dce208b5SSvyatoslav Ryhel static irqreturn_t tegra114_emc_isr(int irq, void *data)
417dce208b5SSvyatoslav Ryhel {
418dce208b5SSvyatoslav Ryhel 	struct tegra_emc *emc = data;
419dce208b5SSvyatoslav Ryhel 	u32 intmask = EMC_INTSTATUS_REFRESH_OVERFLOW;
420dce208b5SSvyatoslav Ryhel 	u32 status;
421dce208b5SSvyatoslav Ryhel 
422dce208b5SSvyatoslav Ryhel 	status = readl_relaxed(emc->regs + EMC_INTSTATUS) & intmask;
423dce208b5SSvyatoslav Ryhel 	if (!status)
424dce208b5SSvyatoslav Ryhel 		return IRQ_NONE;
425dce208b5SSvyatoslav Ryhel 
426dce208b5SSvyatoslav Ryhel 	/* notify about HW problem */
427dce208b5SSvyatoslav Ryhel 	if (status & EMC_INTSTATUS_REFRESH_OVERFLOW)
428dce208b5SSvyatoslav Ryhel 		dev_err_ratelimited(emc->dev,
429dce208b5SSvyatoslav Ryhel 				    "refresh request overflow timeout\n");
430dce208b5SSvyatoslav Ryhel 
431dce208b5SSvyatoslav Ryhel 	/* clear interrupts */
432dce208b5SSvyatoslav Ryhel 	writel_relaxed(status, emc->regs + EMC_INTSTATUS);
433dce208b5SSvyatoslav Ryhel 
434dce208b5SSvyatoslav Ryhel 	return IRQ_HANDLED;
435dce208b5SSvyatoslav Ryhel }
436dce208b5SSvyatoslav Ryhel 
437dce208b5SSvyatoslav Ryhel /* Timing change sequence functions */
438dce208b5SSvyatoslav Ryhel 
439dce208b5SSvyatoslav Ryhel static void emc_ccfifo_writel(struct tegra_emc *emc, u32 value,
440dce208b5SSvyatoslav Ryhel 			      unsigned long offset)
441dce208b5SSvyatoslav Ryhel {
442dce208b5SSvyatoslav Ryhel 	writel(value, emc->regs + EMC_CCFIFO_DATA);
443dce208b5SSvyatoslav Ryhel 	writel(offset, emc->regs + EMC_CCFIFO_ADDR);
444dce208b5SSvyatoslav Ryhel }
445dce208b5SSvyatoslav Ryhel 
446dce208b5SSvyatoslav Ryhel static void emc_seq_update_timing(struct tegra_emc *emc)
447dce208b5SSvyatoslav Ryhel {
448dce208b5SSvyatoslav Ryhel 	int ret;
449dce208b5SSvyatoslav Ryhel 	u32 value;
450dce208b5SSvyatoslav Ryhel 
451dce208b5SSvyatoslav Ryhel 	writel(1, emc->regs + EMC_TIMING_CONTROL);
452dce208b5SSvyatoslav Ryhel 
453dce208b5SSvyatoslav Ryhel 	ret = readl_poll_timeout_atomic(emc->regs + EMC_STATUS, value,
454dce208b5SSvyatoslav Ryhel 					!(value & EMC_STATUS_TIMING_UPDATE_STALLED),
455dce208b5SSvyatoslav Ryhel 					1, EMC_STATUS_UPDATE_TIMEOUT);
456dce208b5SSvyatoslav Ryhel 	if (ret)
457dce208b5SSvyatoslav Ryhel 		dev_err(emc->dev, "timing update timed out\n");
458dce208b5SSvyatoslav Ryhel }
459dce208b5SSvyatoslav Ryhel 
460dce208b5SSvyatoslav Ryhel static void emc_seq_disable_auto_cal(struct tegra_emc *emc)
461dce208b5SSvyatoslav Ryhel {
462dce208b5SSvyatoslav Ryhel 	int ret;
463dce208b5SSvyatoslav Ryhel 	u32 value;
464dce208b5SSvyatoslav Ryhel 
465dce208b5SSvyatoslav Ryhel 	writel(0, emc->regs + EMC_AUTO_CAL_INTERVAL);
466dce208b5SSvyatoslav Ryhel 
467dce208b5SSvyatoslav Ryhel 	ret = readl_poll_timeout_atomic(emc->regs + EMC_AUTO_CAL_STATUS, value,
468dce208b5SSvyatoslav Ryhel 					!(value & EMC_AUTO_CAL_STATUS_ACTIVE),
469dce208b5SSvyatoslav Ryhel 					1, EMC_STATUS_UPDATE_TIMEOUT);
470dce208b5SSvyatoslav Ryhel 	if (ret)
471dce208b5SSvyatoslav Ryhel 		dev_err(emc->dev, "auto cal disable timed out\n");
472dce208b5SSvyatoslav Ryhel }
473dce208b5SSvyatoslav Ryhel 
474dce208b5SSvyatoslav Ryhel static void emc_seq_wait_clkchange(struct tegra_emc *emc)
475dce208b5SSvyatoslav Ryhel {
476dce208b5SSvyatoslav Ryhel 	int ret;
477dce208b5SSvyatoslav Ryhel 	u32 value;
478dce208b5SSvyatoslav Ryhel 
479dce208b5SSvyatoslav Ryhel 	ret = readl_poll_timeout_atomic(emc->regs + EMC_INTSTATUS, value,
480dce208b5SSvyatoslav Ryhel 					value & EMC_INTSTATUS_CLKCHANGE_COMPLETE,
481dce208b5SSvyatoslav Ryhel 					1, EMC_STATUS_UPDATE_TIMEOUT);
482dce208b5SSvyatoslav Ryhel 	if (ret)
483dce208b5SSvyatoslav Ryhel 		dev_err(emc->dev, "clock change timed out\n");
484dce208b5SSvyatoslav Ryhel }
485dce208b5SSvyatoslav Ryhel 
486dce208b5SSvyatoslav Ryhel static struct emc_timing *tegra114_emc_find_timing(struct tegra_emc *emc,
487dce208b5SSvyatoslav Ryhel 						   unsigned long rate)
488dce208b5SSvyatoslav Ryhel {
489dce208b5SSvyatoslav Ryhel 	struct emc_timing *timing = NULL;
490dce208b5SSvyatoslav Ryhel 	unsigned int i;
491dce208b5SSvyatoslav Ryhel 
492dce208b5SSvyatoslav Ryhel 	for (i = 0; i < emc->num_timings; i++) {
493dce208b5SSvyatoslav Ryhel 		if (emc->timings[i].rate == rate) {
494dce208b5SSvyatoslav Ryhel 			timing = &emc->timings[i];
495dce208b5SSvyatoslav Ryhel 			break;
496dce208b5SSvyatoslav Ryhel 		}
497dce208b5SSvyatoslav Ryhel 	}
498dce208b5SSvyatoslav Ryhel 
499dce208b5SSvyatoslav Ryhel 	if (!timing) {
500dce208b5SSvyatoslav Ryhel 		dev_err(emc->dev, "no timing for rate %lu\n", rate);
501dce208b5SSvyatoslav Ryhel 		return NULL;
502dce208b5SSvyatoslav Ryhel 	}
503dce208b5SSvyatoslav Ryhel 
504dce208b5SSvyatoslav Ryhel 	return timing;
505dce208b5SSvyatoslav Ryhel }
506dce208b5SSvyatoslav Ryhel 
507dce208b5SSvyatoslav Ryhel static int tegra114_emc_prepare_timing_change(struct tegra_emc *emc,
508dce208b5SSvyatoslav Ryhel 					      unsigned long rate)
509dce208b5SSvyatoslav Ryhel {
510dce208b5SSvyatoslav Ryhel 	struct emc_timing *timing = tegra114_emc_find_timing(emc, rate);
511dce208b5SSvyatoslav Ryhel 	struct emc_timing *last = &emc->last_timing;
512dce208b5SSvyatoslav Ryhel 	enum emc_dll_change dll_change;
513dce208b5SSvyatoslav Ryhel 	unsigned int pre_wait = 0;
514dce208b5SSvyatoslav Ryhel 	u32 val, mask;
515dce208b5SSvyatoslav Ryhel 	bool last_dll_enabled = !(last->emc_mode_1 & 0x1);
516dce208b5SSvyatoslav Ryhel 	bool update = false;
5174eda0dacSEthan Tidmore 	bool next_dll_enabled;
518dce208b5SSvyatoslav Ryhel 	unsigned int i;
519dce208b5SSvyatoslav Ryhel 
520dce208b5SSvyatoslav Ryhel 	if (!timing)
521dce208b5SSvyatoslav Ryhel 		return -ENOENT;
522dce208b5SSvyatoslav Ryhel 
5234eda0dacSEthan Tidmore 	next_dll_enabled = !(timing->emc_mode_1 & 0x1);
5244eda0dacSEthan Tidmore 
525dce208b5SSvyatoslav Ryhel 	if (next_dll_enabled == last_dll_enabled)
526dce208b5SSvyatoslav Ryhel 		dll_change = DLL_CHANGE_NONE;
527dce208b5SSvyatoslav Ryhel 	else if (next_dll_enabled)
528dce208b5SSvyatoslav Ryhel 		dll_change = DLL_CHANGE_ON;
529dce208b5SSvyatoslav Ryhel 	else
530dce208b5SSvyatoslav Ryhel 		dll_change = DLL_CHANGE_OFF;
531dce208b5SSvyatoslav Ryhel 
532dce208b5SSvyatoslav Ryhel 	/* Clear CLKCHANGE_COMPLETE interrupts */
533dce208b5SSvyatoslav Ryhel 	writel(EMC_INTSTATUS_CLKCHANGE_COMPLETE, emc->regs + EMC_INTSTATUS);
534dce208b5SSvyatoslav Ryhel 
535dce208b5SSvyatoslav Ryhel 	/* Disable dynamic self-refresh */
536dce208b5SSvyatoslav Ryhel 	val = readl(emc->regs + EMC_CFG);
537dce208b5SSvyatoslav Ryhel 	if (val & EMC_CFG_PWR_MASK) {
538dce208b5SSvyatoslav Ryhel 		val &= ~EMC_CFG_POWER_FEATURES_MASK;
539dce208b5SSvyatoslav Ryhel 		writel(val, emc->regs + EMC_CFG);
540dce208b5SSvyatoslav Ryhel 
541dce208b5SSvyatoslav Ryhel 		pre_wait = 5;
542dce208b5SSvyatoslav Ryhel 	}
543dce208b5SSvyatoslav Ryhel 
544dce208b5SSvyatoslav Ryhel 	/* Disable SEL_DPD_CTRL for clock change */
545dce208b5SSvyatoslav Ryhel 	if (emc->dram_type == DRAM_TYPE_DDR3)
546dce208b5SSvyatoslav Ryhel 		mask = EMC_SEL_DPD_CTRL_DDR3_MASK;
547dce208b5SSvyatoslav Ryhel 	else
548dce208b5SSvyatoslav Ryhel 		mask = EMC_SEL_DPD_CTRL_MASK;
549dce208b5SSvyatoslav Ryhel 
550dce208b5SSvyatoslav Ryhel 	val = readl(emc->regs + EMC_SEL_DPD_CTRL);
551dce208b5SSvyatoslav Ryhel 	if (val & mask) {
552dce208b5SSvyatoslav Ryhel 		val &= ~mask;
553dce208b5SSvyatoslav Ryhel 		writel(val, emc->regs + EMC_SEL_DPD_CTRL);
554dce208b5SSvyatoslav Ryhel 	}
555dce208b5SSvyatoslav Ryhel 
556dce208b5SSvyatoslav Ryhel 	/* Prepare DQ/DQS for clock change */
557dce208b5SSvyatoslav Ryhel 	val = readl(emc->regs + EMC_XM2DQSPADCTRL2);
558dce208b5SSvyatoslav Ryhel 	if (timing->emc_xm2dqspadctrl2 & EMC_XM2DQSPADCTRL2_VREF_ENABLE &&
559dce208b5SSvyatoslav Ryhel 	    !(val & EMC_XM2DQSPADCTRL2_VREF_ENABLE)) {
560dce208b5SSvyatoslav Ryhel 		val |= EMC_XM2DQSPADCTRL2_VREF_ENABLE;
561dce208b5SSvyatoslav Ryhel 		update = true;
562dce208b5SSvyatoslav Ryhel 	}
563dce208b5SSvyatoslav Ryhel 
564dce208b5SSvyatoslav Ryhel 	if (timing->emc_xm2dqspadctrl2 & EMC_XM2DQSPADCTRL2_RX_FT_REC_ENABLE &&
565dce208b5SSvyatoslav Ryhel 	    !(val & EMC_XM2DQSPADCTRL2_RX_FT_REC_ENABLE)) {
566dce208b5SSvyatoslav Ryhel 		val |= EMC_XM2DQSPADCTRL2_RX_FT_REC_ENABLE;
567dce208b5SSvyatoslav Ryhel 		update = true;
568dce208b5SSvyatoslav Ryhel 	}
569dce208b5SSvyatoslav Ryhel 
570dce208b5SSvyatoslav Ryhel 	if (update) {
571dce208b5SSvyatoslav Ryhel 		writel(val, emc->regs + EMC_XM2DQSPADCTRL2);
572dce208b5SSvyatoslav Ryhel 		if (pre_wait < 30)
573dce208b5SSvyatoslav Ryhel 			pre_wait = 30;
574dce208b5SSvyatoslav Ryhel 	}
575dce208b5SSvyatoslav Ryhel 
576dce208b5SSvyatoslav Ryhel 	/* Wait to settle */
577dce208b5SSvyatoslav Ryhel 	if (pre_wait) {
578dce208b5SSvyatoslav Ryhel 		emc_seq_update_timing(emc);
579dce208b5SSvyatoslav Ryhel 		udelay(pre_wait);
580dce208b5SSvyatoslav Ryhel 	}
581dce208b5SSvyatoslav Ryhel 
582dce208b5SSvyatoslav Ryhel 	/* Program CTT_TERM control */
583dce208b5SSvyatoslav Ryhel 	if (last->emc_ctt_term_ctrl != timing->emc_ctt_term_ctrl) {
584dce208b5SSvyatoslav Ryhel 		emc_seq_disable_auto_cal(emc);
585dce208b5SSvyatoslav Ryhel 		writel(timing->emc_ctt_term_ctrl,
586dce208b5SSvyatoslav Ryhel 		       emc->regs + EMC_CTT_TERM_CTRL);
587dce208b5SSvyatoslav Ryhel 		emc_seq_update_timing(emc);
588dce208b5SSvyatoslav Ryhel 	}
589dce208b5SSvyatoslav Ryhel 
590dce208b5SSvyatoslav Ryhel 	/* Program burst shadow registers */
591dce208b5SSvyatoslav Ryhel 	for (i = 0; i < ARRAY_SIZE(timing->emc_burst_data); ++i)
592dce208b5SSvyatoslav Ryhel 		writel(timing->emc_burst_data[i],
593dce208b5SSvyatoslav Ryhel 		       emc->regs + emc_burst_regs[i]);
594dce208b5SSvyatoslav Ryhel 
595dce208b5SSvyatoslav Ryhel 	writel(timing->emc_xm2dqspadctrl2, emc->regs + EMC_XM2DQSPADCTRL2);
596dce208b5SSvyatoslav Ryhel 	writel(timing->emc_zcal_interval, emc->regs + EMC_ZCAL_INTERVAL);
597dce208b5SSvyatoslav Ryhel 
598dce208b5SSvyatoslav Ryhel 	tegra_mc_write_emem_configuration(emc->mc, timing->rate);
599dce208b5SSvyatoslav Ryhel 
600dce208b5SSvyatoslav Ryhel 	val = timing->emc_cfg & ~EMC_CFG_POWER_FEATURES_MASK;
601dce208b5SSvyatoslav Ryhel 	emc_ccfifo_writel(emc, val, EMC_CFG);
602dce208b5SSvyatoslav Ryhel 
603dce208b5SSvyatoslav Ryhel 	/* Program AUTO_CAL_CONFIG */
604dce208b5SSvyatoslav Ryhel 	if (timing->emc_auto_cal_config2 != last->emc_auto_cal_config2)
605dce208b5SSvyatoslav Ryhel 		emc_ccfifo_writel(emc, timing->emc_auto_cal_config2,
606dce208b5SSvyatoslav Ryhel 				  EMC_AUTO_CAL_CONFIG2);
607dce208b5SSvyatoslav Ryhel 
608dce208b5SSvyatoslav Ryhel 	if (timing->emc_auto_cal_config3 != last->emc_auto_cal_config3)
609dce208b5SSvyatoslav Ryhel 		emc_ccfifo_writel(emc, timing->emc_auto_cal_config3,
610dce208b5SSvyatoslav Ryhel 				  EMC_AUTO_CAL_CONFIG3);
611dce208b5SSvyatoslav Ryhel 
612dce208b5SSvyatoslav Ryhel 	if (timing->emc_auto_cal_config != last->emc_auto_cal_config) {
613dce208b5SSvyatoslav Ryhel 		val = timing->emc_auto_cal_config;
614dce208b5SSvyatoslav Ryhel 		val &= EMC_AUTO_CAL_CONFIG_AUTO_CAL_START;
615dce208b5SSvyatoslav Ryhel 		emc_ccfifo_writel(emc, val, EMC_AUTO_CAL_CONFIG);
616dce208b5SSvyatoslav Ryhel 	}
617dce208b5SSvyatoslav Ryhel 
618dce208b5SSvyatoslav Ryhel 	/* DDR3: predict MRS long wait count */
619dce208b5SSvyatoslav Ryhel 	if (emc->dram_type == DRAM_TYPE_DDR3 &&
620dce208b5SSvyatoslav Ryhel 	    dll_change == DLL_CHANGE_ON) {
621dce208b5SSvyatoslav Ryhel 		u32 cnt = 512;
622dce208b5SSvyatoslav Ryhel 
623dce208b5SSvyatoslav Ryhel 		if (timing->emc_zcal_interval != 0 &&
624dce208b5SSvyatoslav Ryhel 		    last->emc_zcal_interval == 0)
625dce208b5SSvyatoslav Ryhel 			cnt -= emc->dram_num * 256;
626dce208b5SSvyatoslav Ryhel 
627dce208b5SSvyatoslav Ryhel 		val = (timing->emc_mrs_wait_cnt
628dce208b5SSvyatoslav Ryhel 			& EMC_MRS_WAIT_CNT_SHORT_WAIT_MASK)
629dce208b5SSvyatoslav Ryhel 			>> EMC_MRS_WAIT_CNT_SHORT_WAIT_SHIFT;
630dce208b5SSvyatoslav Ryhel 		if (cnt < val)
631dce208b5SSvyatoslav Ryhel 			cnt = val;
632dce208b5SSvyatoslav Ryhel 
633dce208b5SSvyatoslav Ryhel 		val = timing->emc_mrs_wait_cnt
634dce208b5SSvyatoslav Ryhel 			& ~EMC_MRS_WAIT_CNT_LONG_WAIT_MASK;
635dce208b5SSvyatoslav Ryhel 		val |= (cnt << EMC_MRS_WAIT_CNT_LONG_WAIT_SHIFT)
636dce208b5SSvyatoslav Ryhel 			& EMC_MRS_WAIT_CNT_LONG_WAIT_MASK;
637dce208b5SSvyatoslav Ryhel 
638dce208b5SSvyatoslav Ryhel 		writel(val, emc->regs + EMC_MRS_WAIT_CNT);
639dce208b5SSvyatoslav Ryhel 	}
640dce208b5SSvyatoslav Ryhel 
641dce208b5SSvyatoslav Ryhel 	/* DDR3: Turn off DLL and enter self-refresh */
642dce208b5SSvyatoslav Ryhel 	if (emc->dram_type == DRAM_TYPE_DDR3 && dll_change == DLL_CHANGE_OFF)
643dce208b5SSvyatoslav Ryhel 		emc_ccfifo_writel(emc, timing->emc_mode_1, EMC_EMRS);
644dce208b5SSvyatoslav Ryhel 
645dce208b5SSvyatoslav Ryhel 	/* Disable refresh controller */
646dce208b5SSvyatoslav Ryhel 	emc_ccfifo_writel(emc, EMC_REFCTRL_DEV_SEL(emc->dram_num),
647dce208b5SSvyatoslav Ryhel 			  EMC_REFCTRL);
648dce208b5SSvyatoslav Ryhel 	if (emc->dram_type == DRAM_TYPE_DDR3)
649dce208b5SSvyatoslav Ryhel 		emc_ccfifo_writel(emc, EMC_DRAM_DEV_SEL(emc->dram_num) |
650dce208b5SSvyatoslav Ryhel 				       EMC_SELF_REF_CMD_ENABLED,
651dce208b5SSvyatoslav Ryhel 				  EMC_SELF_REF);
652dce208b5SSvyatoslav Ryhel 
653dce208b5SSvyatoslav Ryhel 	/* Flow control marker */
654dce208b5SSvyatoslav Ryhel 	emc_ccfifo_writel(emc, 1, EMC_STALL_THEN_EXE_AFTER_CLKCHANGE);
655dce208b5SSvyatoslav Ryhel 
656dce208b5SSvyatoslav Ryhel 	/* DDR3: Exit self-refresh */
657dce208b5SSvyatoslav Ryhel 	if (emc->dram_type == DRAM_TYPE_DDR3)
658dce208b5SSvyatoslav Ryhel 		emc_ccfifo_writel(emc, EMC_DRAM_DEV_SEL(emc->dram_num),
659dce208b5SSvyatoslav Ryhel 				  EMC_SELF_REF);
660dce208b5SSvyatoslav Ryhel 	emc_ccfifo_writel(emc, EMC_REFCTRL_DEV_SEL(emc->dram_num) |
661dce208b5SSvyatoslav Ryhel 			       EMC_REFCTRL_ENABLE,
662dce208b5SSvyatoslav Ryhel 			  EMC_REFCTRL);
663dce208b5SSvyatoslav Ryhel 
664dce208b5SSvyatoslav Ryhel 	/* Set DRAM mode registers */
665dce208b5SSvyatoslav Ryhel 	if (emc->dram_type == DRAM_TYPE_DDR3) {
666dce208b5SSvyatoslav Ryhel 		if (timing->emc_mode_1 != last->emc_mode_1)
667dce208b5SSvyatoslav Ryhel 			emc_ccfifo_writel(emc, timing->emc_mode_1, EMC_EMRS);
668dce208b5SSvyatoslav Ryhel 		if (timing->emc_mode_2 != last->emc_mode_2)
669dce208b5SSvyatoslav Ryhel 			emc_ccfifo_writel(emc, timing->emc_mode_2, EMC_EMRS2);
670dce208b5SSvyatoslav Ryhel 
671dce208b5SSvyatoslav Ryhel 		if (timing->emc_mode_reset != last->emc_mode_reset ||
672dce208b5SSvyatoslav Ryhel 		    dll_change == DLL_CHANGE_ON) {
673dce208b5SSvyatoslav Ryhel 			val = timing->emc_mode_reset;
674dce208b5SSvyatoslav Ryhel 			if (dll_change == DLL_CHANGE_ON) {
675dce208b5SSvyatoslav Ryhel 				val |= EMC_MODE_SET_DLL_RESET;
676dce208b5SSvyatoslav Ryhel 				val |= EMC_MODE_SET_LONG_CNT;
677dce208b5SSvyatoslav Ryhel 			} else {
678dce208b5SSvyatoslav Ryhel 				val &= ~EMC_MODE_SET_DLL_RESET;
679dce208b5SSvyatoslav Ryhel 			}
680dce208b5SSvyatoslav Ryhel 			emc_ccfifo_writel(emc, val, EMC_MRS);
681dce208b5SSvyatoslav Ryhel 		}
682dce208b5SSvyatoslav Ryhel 	} else {
683dce208b5SSvyatoslav Ryhel 		if (timing->emc_mode_2 != last->emc_mode_2)
684dce208b5SSvyatoslav Ryhel 			emc_ccfifo_writel(emc, timing->emc_mode_2, EMC_MRW2);
685dce208b5SSvyatoslav Ryhel 		if (timing->emc_mode_1 != last->emc_mode_1)
686dce208b5SSvyatoslav Ryhel 			emc_ccfifo_writel(emc, timing->emc_mode_1, EMC_MRW);
687dce208b5SSvyatoslav Ryhel 		if (timing->emc_mode_4 != last->emc_mode_4)
688dce208b5SSvyatoslav Ryhel 			emc_ccfifo_writel(emc, timing->emc_mode_4, EMC_MRW4);
689dce208b5SSvyatoslav Ryhel 	}
690dce208b5SSvyatoslav Ryhel 
691dce208b5SSvyatoslav Ryhel 	/*  Issue ZCAL command if turning ZCAL on */
692dce208b5SSvyatoslav Ryhel 	if (timing->emc_zcal_interval != 0 && last->emc_zcal_interval == 0) {
693dce208b5SSvyatoslav Ryhel 		emc_ccfifo_writel(emc, EMC_ZQ_CAL_LONG_CMD_DEV0, EMC_ZQ_CAL);
694dce208b5SSvyatoslav Ryhel 		if (emc->dram_num > 1)
695dce208b5SSvyatoslav Ryhel 			emc_ccfifo_writel(emc, EMC_ZQ_CAL_LONG_CMD_DEV1,
696dce208b5SSvyatoslav Ryhel 					  EMC_ZQ_CAL);
697dce208b5SSvyatoslav Ryhel 	}
698dce208b5SSvyatoslav Ryhel 
699dce208b5SSvyatoslav Ryhel 	/*  Write to RO register to remove stall after change */
700dce208b5SSvyatoslav Ryhel 	emc_ccfifo_writel(emc, 0, EMC_CCFIFO_STATUS);
701dce208b5SSvyatoslav Ryhel 
702dce208b5SSvyatoslav Ryhel 	/* Disable AUTO_CAL for clock change */
703dce208b5SSvyatoslav Ryhel 	emc_seq_disable_auto_cal(emc);
704dce208b5SSvyatoslav Ryhel 
705dce208b5SSvyatoslav Ryhel 	/* Read register to wait until programming has settled */
706dce208b5SSvyatoslav Ryhel 	mc_readl(emc->mc, MC_EMEM_ADR_CFG);
707dce208b5SSvyatoslav Ryhel 
708dce208b5SSvyatoslav Ryhel 	return 0;
709dce208b5SSvyatoslav Ryhel }
710dce208b5SSvyatoslav Ryhel 
711dce208b5SSvyatoslav Ryhel static void tegra114_emc_complete_timing_change(struct tegra_emc *emc,
712dce208b5SSvyatoslav Ryhel 						unsigned long rate)
713dce208b5SSvyatoslav Ryhel {
714dce208b5SSvyatoslav Ryhel 	struct emc_timing *timing = tegra114_emc_find_timing(emc, rate);
715dce208b5SSvyatoslav Ryhel 	struct emc_timing *last = &emc->last_timing;
716dce208b5SSvyatoslav Ryhel 
717dce208b5SSvyatoslav Ryhel 	if (!timing)
718dce208b5SSvyatoslav Ryhel 		return;
719dce208b5SSvyatoslav Ryhel 
720dce208b5SSvyatoslav Ryhel 	/* Wait until the state machine has settled */
721dce208b5SSvyatoslav Ryhel 	emc_seq_wait_clkchange(emc);
722dce208b5SSvyatoslav Ryhel 
723dce208b5SSvyatoslav Ryhel 	/* Restore AUTO_CAL */
724dce208b5SSvyatoslav Ryhel 	if (timing->emc_ctt_term_ctrl != last->emc_ctt_term_ctrl)
725dce208b5SSvyatoslav Ryhel 		writel(timing->emc_auto_cal_interval,
726dce208b5SSvyatoslav Ryhel 		       emc->regs + EMC_AUTO_CAL_INTERVAL);
727dce208b5SSvyatoslav Ryhel 
728dce208b5SSvyatoslav Ryhel 	/* Restore dynamic self-refresh */
729dce208b5SSvyatoslav Ryhel 	if (timing->emc_cfg & EMC_CFG_PWR_MASK)
730dce208b5SSvyatoslav Ryhel 		writel(timing->emc_cfg, emc->regs + EMC_CFG);
731dce208b5SSvyatoslav Ryhel 
732dce208b5SSvyatoslav Ryhel 	/* Set ZCAL wait count */
733dce208b5SSvyatoslav Ryhel 	writel(timing->emc_zcal_cnt_long, emc->regs + EMC_ZCAL_WAIT_CNT);
734dce208b5SSvyatoslav Ryhel 
735dce208b5SSvyatoslav Ryhel 	/* Wait for timing to settle */
736dce208b5SSvyatoslav Ryhel 	udelay(2);
737dce208b5SSvyatoslav Ryhel 
738dce208b5SSvyatoslav Ryhel 	/* Reprogram SEL_DPD_CTRL */
739dce208b5SSvyatoslav Ryhel 	writel(timing->emc_sel_dpd_ctrl, emc->regs + EMC_SEL_DPD_CTRL);
740dce208b5SSvyatoslav Ryhel 	emc_seq_update_timing(emc);
741dce208b5SSvyatoslav Ryhel 
742dce208b5SSvyatoslav Ryhel 	emc->last_timing = *timing;
743dce208b5SSvyatoslav Ryhel }
744dce208b5SSvyatoslav Ryhel 
745dce208b5SSvyatoslav Ryhel /* Initialization and deinitialization */
746dce208b5SSvyatoslav Ryhel 
747dce208b5SSvyatoslav Ryhel static void emc_read_current_timing(struct tegra_emc *emc,
748dce208b5SSvyatoslav Ryhel 				    struct emc_timing *timing)
749dce208b5SSvyatoslav Ryhel {
750dce208b5SSvyatoslav Ryhel 	unsigned int i;
751dce208b5SSvyatoslav Ryhel 
752dce208b5SSvyatoslav Ryhel 	for (i = 0; i < ARRAY_SIZE(emc_burst_regs); ++i)
753dce208b5SSvyatoslav Ryhel 		timing->emc_burst_data[i] =
754dce208b5SSvyatoslav Ryhel 			readl(emc->regs + emc_burst_regs[i]);
755dce208b5SSvyatoslav Ryhel 
756dce208b5SSvyatoslav Ryhel 	timing->emc_cfg = readl(emc->regs + EMC_CFG);
757dce208b5SSvyatoslav Ryhel 
758dce208b5SSvyatoslav Ryhel 	timing->emc_auto_cal_interval = 0;
759dce208b5SSvyatoslav Ryhel 	timing->emc_zcal_cnt_long = 0;
760dce208b5SSvyatoslav Ryhel 	timing->emc_mode_1 = 0;
761dce208b5SSvyatoslav Ryhel 	timing->emc_mode_2 = 0;
762dce208b5SSvyatoslav Ryhel 	timing->emc_mode_4 = 0;
763dce208b5SSvyatoslav Ryhel 	timing->emc_mode_reset = 0;
764dce208b5SSvyatoslav Ryhel }
765dce208b5SSvyatoslav Ryhel 
766dce208b5SSvyatoslav Ryhel static void emc_init(struct tegra_emc *emc)
767dce208b5SSvyatoslav Ryhel {
768dce208b5SSvyatoslav Ryhel 	u32 emc_cfg, emc_dbg;
769dce208b5SSvyatoslav Ryhel 	u32 intmask = EMC_INTSTATUS_REFRESH_OVERFLOW;
770dce208b5SSvyatoslav Ryhel 	const char *dram_type_str;
771dce208b5SSvyatoslav Ryhel 
772dce208b5SSvyatoslav Ryhel 	emc->dram_type = readl(emc->regs + EMC_FBIO_CFG5);
773dce208b5SSvyatoslav Ryhel 
774dce208b5SSvyatoslav Ryhel 	emc->dram_type &= EMC_FBIO_CFG5_DRAM_TYPE_MASK;
775dce208b5SSvyatoslav Ryhel 	emc->dram_type >>= EMC_FBIO_CFG5_DRAM_TYPE_SHIFT;
776dce208b5SSvyatoslav Ryhel 
777dce208b5SSvyatoslav Ryhel 	emc->dram_num = tegra_mc_get_emem_device_count(emc->mc);
778dce208b5SSvyatoslav Ryhel 
779dce208b5SSvyatoslav Ryhel 	emc_cfg = readl_relaxed(emc->regs + EMC_CFG_2);
780dce208b5SSvyatoslav Ryhel 
781dce208b5SSvyatoslav Ryhel 	/* enable EMC and CAR to handshake on PLL divider/source changes */
782dce208b5SSvyatoslav Ryhel 	emc_cfg |= EMC_CFG_2_CLKCHANGE_REQ_ENABLE;
783dce208b5SSvyatoslav Ryhel 
784dce208b5SSvyatoslav Ryhel 	/* configure clock change mode accordingly to DRAM type */
785dce208b5SSvyatoslav Ryhel 	if (emc->dram_type == DRAM_TYPE_LPDDR2)
786dce208b5SSvyatoslav Ryhel 		emc_cfg |= EMC_CFG_2_CLKCHANGE_PD_ENABLE;
787dce208b5SSvyatoslav Ryhel 	else
788dce208b5SSvyatoslav Ryhel 		emc_cfg &= ~EMC_CFG_2_CLKCHANGE_PD_ENABLE;
789dce208b5SSvyatoslav Ryhel 
790dce208b5SSvyatoslav Ryhel 	writel_relaxed(emc_cfg, emc->regs + EMC_CFG_2);
791dce208b5SSvyatoslav Ryhel 
792dce208b5SSvyatoslav Ryhel 	/* initialize interrupt */
793dce208b5SSvyatoslav Ryhel 	writel_relaxed(intmask, emc->regs + EMC_INTMASK);
794dce208b5SSvyatoslav Ryhel 	writel_relaxed(0xffffffff, emc->regs + EMC_INTSTATUS);
795dce208b5SSvyatoslav Ryhel 
796dce208b5SSvyatoslav Ryhel 	/* ensure that unwanted debug features are disabled */
797dce208b5SSvyatoslav Ryhel 	emc_dbg = readl_relaxed(emc->regs + EMC_DBG);
798dce208b5SSvyatoslav Ryhel 	emc_dbg |= EMC_DBG_CFG_PRIORITY;
799dce208b5SSvyatoslav Ryhel 	emc_dbg &= ~EMC_DBG_READ_MUX_ASSEMBLY;
800dce208b5SSvyatoslav Ryhel 	emc_dbg &= ~EMC_DBG_WRITE_MUX_ACTIVE;
801dce208b5SSvyatoslav Ryhel 	emc_dbg &= ~EMC_DBG_FORCE_UPDATE;
802dce208b5SSvyatoslav Ryhel 	writel_relaxed(emc_dbg, emc->regs + EMC_DBG);
803dce208b5SSvyatoslav Ryhel 
804dce208b5SSvyatoslav Ryhel 	switch (emc->dram_type) {
805dce208b5SSvyatoslav Ryhel 	case DRAM_TYPE_DDR1:
806dce208b5SSvyatoslav Ryhel 		dram_type_str = "DDR1";
807dce208b5SSvyatoslav Ryhel 		break;
808dce208b5SSvyatoslav Ryhel 	case DRAM_TYPE_LPDDR2:
809dce208b5SSvyatoslav Ryhel 		dram_type_str = "LPDDR2";
810dce208b5SSvyatoslav Ryhel 		break;
811dce208b5SSvyatoslav Ryhel 	case DRAM_TYPE_DDR2:
812dce208b5SSvyatoslav Ryhel 		dram_type_str = "DDR2";
813dce208b5SSvyatoslav Ryhel 		break;
814dce208b5SSvyatoslav Ryhel 	case DRAM_TYPE_DDR3:
815dce208b5SSvyatoslav Ryhel 		dram_type_str = "DDR3";
816dce208b5SSvyatoslav Ryhel 		break;
817dce208b5SSvyatoslav Ryhel 	}
818dce208b5SSvyatoslav Ryhel 
819dce208b5SSvyatoslav Ryhel 	dev_info_once(emc->dev, "%u %s %s attached\n", emc->dram_num,
820dce208b5SSvyatoslav Ryhel 		      dram_type_str, emc->dram_num == 2 ? "devices" : "device");
821dce208b5SSvyatoslav Ryhel 
822dce208b5SSvyatoslav Ryhel 	emc_read_current_timing(emc, &emc->last_timing);
823dce208b5SSvyatoslav Ryhel }
824dce208b5SSvyatoslav Ryhel 
825dce208b5SSvyatoslav Ryhel static int load_one_timing_from_dt(struct tegra_emc *emc,
826dce208b5SSvyatoslav Ryhel 				   struct emc_timing *timing,
827dce208b5SSvyatoslav Ryhel 				   struct device_node *node)
828dce208b5SSvyatoslav Ryhel {
829dce208b5SSvyatoslav Ryhel 	u32 value;
830dce208b5SSvyatoslav Ryhel 	int err;
831dce208b5SSvyatoslav Ryhel 
832dce208b5SSvyatoslav Ryhel 	err = of_property_read_u32(node, "clock-frequency", &value);
833dce208b5SSvyatoslav Ryhel 	if (err) {
834dce208b5SSvyatoslav Ryhel 		dev_err(emc->dev, "timing %pOFn: failed to read rate: %d\n",
835dce208b5SSvyatoslav Ryhel 			node, err);
836dce208b5SSvyatoslav Ryhel 		return err;
837dce208b5SSvyatoslav Ryhel 	}
838dce208b5SSvyatoslav Ryhel 
839dce208b5SSvyatoslav Ryhel 	timing->rate = value;
840dce208b5SSvyatoslav Ryhel 
841dce208b5SSvyatoslav Ryhel 	err = of_property_read_u32_array(node, "nvidia,emc-configuration",
842dce208b5SSvyatoslav Ryhel 					 timing->emc_burst_data,
843dce208b5SSvyatoslav Ryhel 					 ARRAY_SIZE(timing->emc_burst_data));
844dce208b5SSvyatoslav Ryhel 	if (err) {
845dce208b5SSvyatoslav Ryhel 		dev_err(emc->dev,
846dce208b5SSvyatoslav Ryhel 			"timing %pOFn: failed to read emc burst data: %d\n",
847dce208b5SSvyatoslav Ryhel 			node, err);
848dce208b5SSvyatoslav Ryhel 		return err;
849dce208b5SSvyatoslav Ryhel 	}
850dce208b5SSvyatoslav Ryhel 
851dce208b5SSvyatoslav Ryhel #define EMC_READ_PROP(prop, dtprop) { \
852dce208b5SSvyatoslav Ryhel 	err = of_property_read_u32(node, dtprop, &timing->prop); \
853dce208b5SSvyatoslav Ryhel 	if (err) { \
854dce208b5SSvyatoslav Ryhel 		dev_err(emc->dev, "timing %pOFn: failed to read " #prop ": %d\n", \
855dce208b5SSvyatoslav Ryhel 			node, err); \
856dce208b5SSvyatoslav Ryhel 		return err; \
857dce208b5SSvyatoslav Ryhel 	} \
858dce208b5SSvyatoslav Ryhel }
859dce208b5SSvyatoslav Ryhel 
860dce208b5SSvyatoslav Ryhel 	EMC_READ_PROP(emc_auto_cal_config, "nvidia,emc-auto-cal-config")
861dce208b5SSvyatoslav Ryhel 	EMC_READ_PROP(emc_auto_cal_config2, "nvidia,emc-auto-cal-config2")
862dce208b5SSvyatoslav Ryhel 	EMC_READ_PROP(emc_auto_cal_config3, "nvidia,emc-auto-cal-config3")
863dce208b5SSvyatoslav Ryhel 	EMC_READ_PROP(emc_auto_cal_interval, "nvidia,emc-auto-cal-interval")
864dce208b5SSvyatoslav Ryhel 	EMC_READ_PROP(emc_cfg, "nvidia,emc-cfg")
865dce208b5SSvyatoslav Ryhel 	EMC_READ_PROP(emc_ctt_term_ctrl, "nvidia,emc-ctt-term-ctrl")
866dce208b5SSvyatoslav Ryhel 	EMC_READ_PROP(emc_mode_1, "nvidia,emc-mode-1")
867dce208b5SSvyatoslav Ryhel 	EMC_READ_PROP(emc_mode_2, "nvidia,emc-mode-2")
868dce208b5SSvyatoslav Ryhel 	EMC_READ_PROP(emc_mode_4, "nvidia,emc-mode-4")
869dce208b5SSvyatoslav Ryhel 	EMC_READ_PROP(emc_mode_reset, "nvidia,emc-mode-reset")
870dce208b5SSvyatoslav Ryhel 	EMC_READ_PROP(emc_mrs_wait_cnt, "nvidia,emc-mrs-wait-cnt")
871dce208b5SSvyatoslav Ryhel 	EMC_READ_PROP(emc_sel_dpd_ctrl, "nvidia,emc-sel-dpd-ctrl")
872dce208b5SSvyatoslav Ryhel 	EMC_READ_PROP(emc_xm2dqspadctrl2, "nvidia,emc-xm2dqspadctrl2")
873dce208b5SSvyatoslav Ryhel 	EMC_READ_PROP(emc_zcal_cnt_long, "nvidia,emc-zcal-cnt-long")
874dce208b5SSvyatoslav Ryhel 	EMC_READ_PROP(emc_zcal_interval, "nvidia,emc-zcal-interval")
875dce208b5SSvyatoslav Ryhel 
876dce208b5SSvyatoslav Ryhel #undef EMC_READ_PROP
877dce208b5SSvyatoslav Ryhel 
878dce208b5SSvyatoslav Ryhel 	return 0;
879dce208b5SSvyatoslav Ryhel }
880dce208b5SSvyatoslav Ryhel 
881dce208b5SSvyatoslav Ryhel static int cmp_timings(const void *_a, const void *_b)
882dce208b5SSvyatoslav Ryhel {
883dce208b5SSvyatoslav Ryhel 	const struct emc_timing *a = _a;
884dce208b5SSvyatoslav Ryhel 	const struct emc_timing *b = _b;
885dce208b5SSvyatoslav Ryhel 
886dce208b5SSvyatoslav Ryhel 	if (a->rate < b->rate)
887dce208b5SSvyatoslav Ryhel 		return -1;
888dce208b5SSvyatoslav Ryhel 	else if (a->rate == b->rate)
889dce208b5SSvyatoslav Ryhel 		return 0;
890dce208b5SSvyatoslav Ryhel 	else
891dce208b5SSvyatoslav Ryhel 		return 1;
892dce208b5SSvyatoslav Ryhel }
893dce208b5SSvyatoslav Ryhel 
894dce208b5SSvyatoslav Ryhel static int emc_check_mc_timings(struct tegra_emc *emc)
895dce208b5SSvyatoslav Ryhel {
896dce208b5SSvyatoslav Ryhel 	struct tegra_mc *mc = emc->mc;
897dce208b5SSvyatoslav Ryhel 	unsigned int i;
898dce208b5SSvyatoslav Ryhel 
899dce208b5SSvyatoslav Ryhel 	if (emc->num_timings != mc->num_timings) {
900dce208b5SSvyatoslav Ryhel 		dev_err(emc->dev, "emc/mc timings number mismatch: %u %u\n",
901dce208b5SSvyatoslav Ryhel 			emc->num_timings, mc->num_timings);
902dce208b5SSvyatoslav Ryhel 		return -EINVAL;
903dce208b5SSvyatoslav Ryhel 	}
904dce208b5SSvyatoslav Ryhel 
905dce208b5SSvyatoslav Ryhel 	for (i = 0; i < mc->num_timings; i++) {
906dce208b5SSvyatoslav Ryhel 		if (emc->timings[i].rate != mc->timings[i].rate) {
907dce208b5SSvyatoslav Ryhel 			dev_err(emc->dev,
908dce208b5SSvyatoslav Ryhel 				"emc/mc timing rate mismatch: %lu %lu\n",
909dce208b5SSvyatoslav Ryhel 				emc->timings[i].rate, mc->timings[i].rate);
910dce208b5SSvyatoslav Ryhel 			return -EINVAL;
911dce208b5SSvyatoslav Ryhel 		}
912dce208b5SSvyatoslav Ryhel 	}
913dce208b5SSvyatoslav Ryhel 
914dce208b5SSvyatoslav Ryhel 	return 0;
915dce208b5SSvyatoslav Ryhel }
916dce208b5SSvyatoslav Ryhel 
917dce208b5SSvyatoslav Ryhel static int tegra114_emc_load_timings_from_dt(struct tegra_emc *emc,
918dce208b5SSvyatoslav Ryhel 					     struct device_node *node)
919dce208b5SSvyatoslav Ryhel {
920dce208b5SSvyatoslav Ryhel 	int child_count = of_get_child_count(node);
921dce208b5SSvyatoslav Ryhel 	struct emc_timing *timing;
922dce208b5SSvyatoslav Ryhel 	unsigned int i = 0;
923dce208b5SSvyatoslav Ryhel 	int err;
924dce208b5SSvyatoslav Ryhel 
925dce208b5SSvyatoslav Ryhel 	emc->timings = devm_kcalloc(emc->dev, child_count, sizeof(*timing),
926dce208b5SSvyatoslav Ryhel 				    GFP_KERNEL);
927dce208b5SSvyatoslav Ryhel 	if (!emc->timings)
928dce208b5SSvyatoslav Ryhel 		return -ENOMEM;
929dce208b5SSvyatoslav Ryhel 
930dce208b5SSvyatoslav Ryhel 	emc->num_timings = child_count;
931dce208b5SSvyatoslav Ryhel 
932dce208b5SSvyatoslav Ryhel 	for_each_child_of_node_scoped(node, child) {
933dce208b5SSvyatoslav Ryhel 		timing = &emc->timings[i++];
934dce208b5SSvyatoslav Ryhel 
935dce208b5SSvyatoslav Ryhel 		err = load_one_timing_from_dt(emc, timing, child);
936dce208b5SSvyatoslav Ryhel 		if (err)
937dce208b5SSvyatoslav Ryhel 			return err;
938dce208b5SSvyatoslav Ryhel 	}
939dce208b5SSvyatoslav Ryhel 
940dce208b5SSvyatoslav Ryhel 	sort(emc->timings, emc->num_timings, sizeof(*timing), cmp_timings,
941dce208b5SSvyatoslav Ryhel 	     NULL);
942dce208b5SSvyatoslav Ryhel 
943dce208b5SSvyatoslav Ryhel 	err = emc_check_mc_timings(emc);
944dce208b5SSvyatoslav Ryhel 	if (err)
945dce208b5SSvyatoslav Ryhel 		return err;
946dce208b5SSvyatoslav Ryhel 
947dce208b5SSvyatoslav Ryhel 	dev_info_once(emc->dev,
948dce208b5SSvyatoslav Ryhel 		      "got %u timings for RAM code %u (min %luMHz max %luMHz)\n",
949dce208b5SSvyatoslav Ryhel 		      emc->num_timings,
950dce208b5SSvyatoslav Ryhel 		      tegra_read_ram_code(),
951dce208b5SSvyatoslav Ryhel 		      emc->timings[0].rate / 1000000,
952dce208b5SSvyatoslav Ryhel 		      emc->timings[emc->num_timings - 1].rate / 1000000);
953dce208b5SSvyatoslav Ryhel 
954dce208b5SSvyatoslav Ryhel 	return 0;
955dce208b5SSvyatoslav Ryhel }
956dce208b5SSvyatoslav Ryhel 
957dce208b5SSvyatoslav Ryhel static struct device_node *
958dce208b5SSvyatoslav Ryhel tegra114_emc_find_node_by_ram_code(struct device_node *node, u32 ram_code)
959dce208b5SSvyatoslav Ryhel {
960dce208b5SSvyatoslav Ryhel 	struct device_node *np;
961dce208b5SSvyatoslav Ryhel 	int err;
962dce208b5SSvyatoslav Ryhel 
963dce208b5SSvyatoslav Ryhel 	for_each_child_of_node(node, np) {
964dce208b5SSvyatoslav Ryhel 		u32 value;
965dce208b5SSvyatoslav Ryhel 
966dce208b5SSvyatoslav Ryhel 		err = of_property_read_u32(np, "nvidia,ram-code", &value);
967dce208b5SSvyatoslav Ryhel 		if (err || value != ram_code)
968dce208b5SSvyatoslav Ryhel 			continue;
969dce208b5SSvyatoslav Ryhel 
970dce208b5SSvyatoslav Ryhel 		return np;
971dce208b5SSvyatoslav Ryhel 	}
972dce208b5SSvyatoslav Ryhel 
973dce208b5SSvyatoslav Ryhel 	return NULL;
974dce208b5SSvyatoslav Ryhel }
975dce208b5SSvyatoslav Ryhel 
976dce208b5SSvyatoslav Ryhel /*
977dce208b5SSvyatoslav Ryhel  * debugfs interface
978dce208b5SSvyatoslav Ryhel  *
979dce208b5SSvyatoslav Ryhel  * The memory controller driver exposes some files in debugfs that can be used
980dce208b5SSvyatoslav Ryhel  * to control the EMC frequency. The top-level directory can be found here:
981dce208b5SSvyatoslav Ryhel  *
982dce208b5SSvyatoslav Ryhel  *   /sys/kernel/debug/emc
983dce208b5SSvyatoslav Ryhel  *
984dce208b5SSvyatoslav Ryhel  * It contains the following files:
985dce208b5SSvyatoslav Ryhel  *
986dce208b5SSvyatoslav Ryhel  *   - available_rates: This file contains a list of valid, space-separated
987dce208b5SSvyatoslav Ryhel  *     EMC frequencies.
988dce208b5SSvyatoslav Ryhel  *
989dce208b5SSvyatoslav Ryhel  *   - min_rate: Writing a value to this file sets the given frequency as the
990dce208b5SSvyatoslav Ryhel  *       floor of the permitted range. If this is higher than the currently
991dce208b5SSvyatoslav Ryhel  *       configured EMC frequency, this will cause the frequency to be
992dce208b5SSvyatoslav Ryhel  *       increased so that it stays within the valid range.
993dce208b5SSvyatoslav Ryhel  *
994dce208b5SSvyatoslav Ryhel  *   - max_rate: Similarly to the min_rate file, writing a value to this file
995dce208b5SSvyatoslav Ryhel  *       sets the given frequency as the ceiling of the permitted range. If
996dce208b5SSvyatoslav Ryhel  *       the value is lower than the currently configured EMC frequency, this
997dce208b5SSvyatoslav Ryhel  *       will cause the frequency to be decreased so that it stays within the
998dce208b5SSvyatoslav Ryhel  *       valid range.
999dce208b5SSvyatoslav Ryhel  */
1000dce208b5SSvyatoslav Ryhel 
1001dce208b5SSvyatoslav Ryhel static bool tegra114_emc_validate_rate(struct tegra_emc *emc, unsigned long rate)
1002dce208b5SSvyatoslav Ryhel {
1003dce208b5SSvyatoslav Ryhel 	unsigned int i;
1004dce208b5SSvyatoslav Ryhel 
1005dce208b5SSvyatoslav Ryhel 	for (i = 0; i < emc->num_timings; i++)
1006dce208b5SSvyatoslav Ryhel 		if (rate == emc->timings[i].rate)
1007dce208b5SSvyatoslav Ryhel 			return true;
1008dce208b5SSvyatoslav Ryhel 
1009dce208b5SSvyatoslav Ryhel 	return false;
1010dce208b5SSvyatoslav Ryhel }
1011dce208b5SSvyatoslav Ryhel 
1012dce208b5SSvyatoslav Ryhel static int tegra114_emc_debug_available_rates_show(struct seq_file *s,
1013dce208b5SSvyatoslav Ryhel 						   void *data)
1014dce208b5SSvyatoslav Ryhel {
1015dce208b5SSvyatoslav Ryhel 	struct tegra_emc *emc = s->private;
1016dce208b5SSvyatoslav Ryhel 	const char *prefix = "";
1017dce208b5SSvyatoslav Ryhel 	unsigned int i;
1018dce208b5SSvyatoslav Ryhel 
1019dce208b5SSvyatoslav Ryhel 	for (i = 0; i < emc->num_timings; i++) {
1020dce208b5SSvyatoslav Ryhel 		seq_printf(s, "%s%lu", prefix, emc->timings[i].rate);
1021dce208b5SSvyatoslav Ryhel 		prefix = " ";
1022dce208b5SSvyatoslav Ryhel 	}
1023dce208b5SSvyatoslav Ryhel 
1024dce208b5SSvyatoslav Ryhel 	seq_puts(s, "\n");
1025dce208b5SSvyatoslav Ryhel 
1026dce208b5SSvyatoslav Ryhel 	return 0;
1027dce208b5SSvyatoslav Ryhel }
1028dce208b5SSvyatoslav Ryhel 
1029dce208b5SSvyatoslav Ryhel DEFINE_SHOW_ATTRIBUTE(tegra114_emc_debug_available_rates);
1030dce208b5SSvyatoslav Ryhel 
1031dce208b5SSvyatoslav Ryhel static int tegra114_emc_debug_min_rate_get(void *data, u64 *rate)
1032dce208b5SSvyatoslav Ryhel {
1033dce208b5SSvyatoslav Ryhel 	struct tegra_emc *emc = data;
1034dce208b5SSvyatoslav Ryhel 
1035dce208b5SSvyatoslav Ryhel 	*rate = emc->debugfs.min_rate;
1036dce208b5SSvyatoslav Ryhel 
1037dce208b5SSvyatoslav Ryhel 	return 0;
1038dce208b5SSvyatoslav Ryhel }
1039dce208b5SSvyatoslav Ryhel 
1040dce208b5SSvyatoslav Ryhel static int tegra114_emc_debug_min_rate_set(void *data, u64 rate)
1041dce208b5SSvyatoslav Ryhel {
1042dce208b5SSvyatoslav Ryhel 	struct tegra_emc *emc = data;
1043dce208b5SSvyatoslav Ryhel 	int err;
1044dce208b5SSvyatoslav Ryhel 
1045dce208b5SSvyatoslav Ryhel 	if (!tegra114_emc_validate_rate(emc, rate))
1046dce208b5SSvyatoslav Ryhel 		return -EINVAL;
1047dce208b5SSvyatoslav Ryhel 
1048dce208b5SSvyatoslav Ryhel 	err = tegra_emc_set_min_rate(&emc->reqs, rate, TEGRA_EMC_RATE_DEBUG);
1049dce208b5SSvyatoslav Ryhel 	if (err < 0)
1050dce208b5SSvyatoslav Ryhel 		return err;
1051dce208b5SSvyatoslav Ryhel 
1052dce208b5SSvyatoslav Ryhel 	emc->debugfs.min_rate = rate;
1053dce208b5SSvyatoslav Ryhel 
1054dce208b5SSvyatoslav Ryhel 	return 0;
1055dce208b5SSvyatoslav Ryhel }
1056dce208b5SSvyatoslav Ryhel 
1057dce208b5SSvyatoslav Ryhel DEFINE_DEBUGFS_ATTRIBUTE(tegra114_emc_debug_min_rate_fops,
1058dce208b5SSvyatoslav Ryhel 			 tegra114_emc_debug_min_rate_get,
1059dce208b5SSvyatoslav Ryhel 			 tegra114_emc_debug_min_rate_set, "%llu\n");
1060dce208b5SSvyatoslav Ryhel 
1061dce208b5SSvyatoslav Ryhel static int tegra114_emc_debug_max_rate_get(void *data, u64 *rate)
1062dce208b5SSvyatoslav Ryhel {
1063dce208b5SSvyatoslav Ryhel 	struct tegra_emc *emc = data;
1064dce208b5SSvyatoslav Ryhel 
1065dce208b5SSvyatoslav Ryhel 	*rate = emc->debugfs.max_rate;
1066dce208b5SSvyatoslav Ryhel 
1067dce208b5SSvyatoslav Ryhel 	return 0;
1068dce208b5SSvyatoslav Ryhel }
1069dce208b5SSvyatoslav Ryhel 
1070dce208b5SSvyatoslav Ryhel static int tegra114_emc_debug_max_rate_set(void *data, u64 rate)
1071dce208b5SSvyatoslav Ryhel {
1072dce208b5SSvyatoslav Ryhel 	struct tegra_emc *emc = data;
1073dce208b5SSvyatoslav Ryhel 	int err;
1074dce208b5SSvyatoslav Ryhel 
1075dce208b5SSvyatoslav Ryhel 	if (!tegra114_emc_validate_rate(emc, rate))
1076dce208b5SSvyatoslav Ryhel 		return -EINVAL;
1077dce208b5SSvyatoslav Ryhel 
1078dce208b5SSvyatoslav Ryhel 	err = tegra_emc_set_max_rate(&emc->reqs, rate, TEGRA_EMC_RATE_DEBUG);
1079dce208b5SSvyatoslav Ryhel 	if (err < 0)
1080dce208b5SSvyatoslav Ryhel 		return err;
1081dce208b5SSvyatoslav Ryhel 
1082dce208b5SSvyatoslav Ryhel 	emc->debugfs.max_rate = rate;
1083dce208b5SSvyatoslav Ryhel 
1084dce208b5SSvyatoslav Ryhel 	return 0;
1085dce208b5SSvyatoslav Ryhel }
1086dce208b5SSvyatoslav Ryhel 
1087dce208b5SSvyatoslav Ryhel DEFINE_DEBUGFS_ATTRIBUTE(tegra114_emc_debug_max_rate_fops,
1088dce208b5SSvyatoslav Ryhel 			 tegra114_emc_debug_max_rate_get,
1089dce208b5SSvyatoslav Ryhel 			 tegra114_emc_debug_max_rate_set, "%llu\n");
1090dce208b5SSvyatoslav Ryhel 
1091dce208b5SSvyatoslav Ryhel static void emc_debugfs_init(struct device *dev, struct tegra_emc *emc)
1092dce208b5SSvyatoslav Ryhel {
1093dce208b5SSvyatoslav Ryhel 	unsigned int i;
1094dce208b5SSvyatoslav Ryhel 	int err;
1095dce208b5SSvyatoslav Ryhel 
1096dce208b5SSvyatoslav Ryhel 	emc->debugfs.min_rate = ULONG_MAX;
1097dce208b5SSvyatoslav Ryhel 	emc->debugfs.max_rate = 0;
1098dce208b5SSvyatoslav Ryhel 
1099dce208b5SSvyatoslav Ryhel 	for (i = 0; i < emc->num_timings; i++) {
1100dce208b5SSvyatoslav Ryhel 		if (emc->timings[i].rate < emc->debugfs.min_rate)
1101dce208b5SSvyatoslav Ryhel 			emc->debugfs.min_rate = emc->timings[i].rate;
1102dce208b5SSvyatoslav Ryhel 
1103dce208b5SSvyatoslav Ryhel 		if (emc->timings[i].rate > emc->debugfs.max_rate)
1104dce208b5SSvyatoslav Ryhel 			emc->debugfs.max_rate = emc->timings[i].rate;
1105dce208b5SSvyatoslav Ryhel 	}
1106dce208b5SSvyatoslav Ryhel 
1107dce208b5SSvyatoslav Ryhel 	if (!emc->num_timings) {
1108dce208b5SSvyatoslav Ryhel 		emc->debugfs.min_rate = clk_get_rate(emc->clk);
1109dce208b5SSvyatoslav Ryhel 		emc->debugfs.max_rate = emc->debugfs.min_rate;
1110dce208b5SSvyatoslav Ryhel 	}
1111dce208b5SSvyatoslav Ryhel 
1112dce208b5SSvyatoslav Ryhel 	err = clk_set_rate_range(emc->clk, emc->debugfs.min_rate,
1113dce208b5SSvyatoslav Ryhel 				 emc->debugfs.max_rate);
1114dce208b5SSvyatoslav Ryhel 	if (err < 0) {
1115dce208b5SSvyatoslav Ryhel 		dev_err(dev, "failed to set rate range [%lu-%lu] for %pC\n",
1116dce208b5SSvyatoslav Ryhel 			emc->debugfs.min_rate, emc->debugfs.max_rate,
1117dce208b5SSvyatoslav Ryhel 			emc->clk);
1118dce208b5SSvyatoslav Ryhel 		return;
1119dce208b5SSvyatoslav Ryhel 	}
1120dce208b5SSvyatoslav Ryhel 
1121dce208b5SSvyatoslav Ryhel 	emc->debugfs.root = debugfs_create_dir("emc", NULL);
1122dce208b5SSvyatoslav Ryhel 
1123dce208b5SSvyatoslav Ryhel 	debugfs_create_file("available_rates", 0444, emc->debugfs.root, emc,
1124dce208b5SSvyatoslav Ryhel 			    &tegra114_emc_debug_available_rates_fops);
1125dce208b5SSvyatoslav Ryhel 	debugfs_create_file("min_rate", 0644, emc->debugfs.root,
1126dce208b5SSvyatoslav Ryhel 			    emc, &tegra114_emc_debug_min_rate_fops);
1127dce208b5SSvyatoslav Ryhel 	debugfs_create_file("max_rate", 0644, emc->debugfs.root,
1128dce208b5SSvyatoslav Ryhel 			    emc, &tegra114_emc_debug_max_rate_fops);
1129dce208b5SSvyatoslav Ryhel }
1130dce208b5SSvyatoslav Ryhel 
1131dce208b5SSvyatoslav Ryhel static inline struct tegra_emc *
1132dce208b5SSvyatoslav Ryhel to_tegra_emc_provider(struct icc_provider *provider)
1133dce208b5SSvyatoslav Ryhel {
1134dce208b5SSvyatoslav Ryhel 	return container_of(provider, struct tegra_emc, provider);
1135dce208b5SSvyatoslav Ryhel }
1136dce208b5SSvyatoslav Ryhel 
1137dce208b5SSvyatoslav Ryhel static struct icc_node_data *
1138dce208b5SSvyatoslav Ryhel emc_of_icc_xlate_extended(const struct of_phandle_args *spec, void *data)
1139dce208b5SSvyatoslav Ryhel {
1140dce208b5SSvyatoslav Ryhel 	struct icc_provider *provider = data;
1141dce208b5SSvyatoslav Ryhel 	struct icc_node_data *ndata;
1142dce208b5SSvyatoslav Ryhel 	struct icc_node *node;
1143dce208b5SSvyatoslav Ryhel 
1144dce208b5SSvyatoslav Ryhel 	/* External Memory is the only possible ICC route */
1145dce208b5SSvyatoslav Ryhel 	list_for_each_entry(node, &provider->nodes, node_list) {
1146dce208b5SSvyatoslav Ryhel 		if (node->id != TEGRA_ICC_EMEM)
1147dce208b5SSvyatoslav Ryhel 			continue;
1148dce208b5SSvyatoslav Ryhel 
1149dce208b5SSvyatoslav Ryhel 		ndata = kzalloc_obj(*ndata);
1150dce208b5SSvyatoslav Ryhel 		if (!ndata)
1151dce208b5SSvyatoslav Ryhel 			return ERR_PTR(-ENOMEM);
1152dce208b5SSvyatoslav Ryhel 
1153dce208b5SSvyatoslav Ryhel 		/*
1154dce208b5SSvyatoslav Ryhel 		 * SRC and DST nodes should have matching TAG in order to have
1155dce208b5SSvyatoslav Ryhel 		 * it set by default for a requested path.
1156dce208b5SSvyatoslav Ryhel 		 */
1157dce208b5SSvyatoslav Ryhel 		ndata->tag = TEGRA_MC_ICC_TAG_ISO;
1158dce208b5SSvyatoslav Ryhel 		ndata->node = node;
1159dce208b5SSvyatoslav Ryhel 
1160dce208b5SSvyatoslav Ryhel 		return ndata;
1161dce208b5SSvyatoslav Ryhel 	}
1162dce208b5SSvyatoslav Ryhel 
1163dce208b5SSvyatoslav Ryhel 	return ERR_PTR(-EPROBE_DEFER);
1164dce208b5SSvyatoslav Ryhel }
1165dce208b5SSvyatoslav Ryhel 
1166dce208b5SSvyatoslav Ryhel static int emc_icc_set(struct icc_node *src, struct icc_node *dst)
1167dce208b5SSvyatoslav Ryhel {
1168dce208b5SSvyatoslav Ryhel 	struct tegra_emc *emc = to_tegra_emc_provider(dst->provider);
1169dce208b5SSvyatoslav Ryhel 	unsigned long long peak_bw = icc_units_to_bps(dst->peak_bw);
1170dce208b5SSvyatoslav Ryhel 	unsigned long long avg_bw = icc_units_to_bps(dst->avg_bw);
1171dce208b5SSvyatoslav Ryhel 	unsigned long long rate = max(avg_bw, peak_bw);
1172dce208b5SSvyatoslav Ryhel 	unsigned int dram_data_bus_width_bytes = 4;
1173dce208b5SSvyatoslav Ryhel 	const unsigned int ddr = 2;
1174dce208b5SSvyatoslav Ryhel 	int err;
1175dce208b5SSvyatoslav Ryhel 
1176dce208b5SSvyatoslav Ryhel 	/*
1177dce208b5SSvyatoslav Ryhel 	 * Tegra114 EMC runs on a clock rate of SDRAM bus. This means that
1178dce208b5SSvyatoslav Ryhel 	 * EMC clock rate is twice smaller than the peak data rate because
1179dce208b5SSvyatoslav Ryhel 	 * data is sampled on both EMC clock edges.
1180dce208b5SSvyatoslav Ryhel 	 */
1181dce208b5SSvyatoslav Ryhel 	do_div(rate, ddr * dram_data_bus_width_bytes);
1182dce208b5SSvyatoslav Ryhel 	rate = min_t(u64, rate, U32_MAX);
1183dce208b5SSvyatoslav Ryhel 
1184dce208b5SSvyatoslav Ryhel 	err = tegra_emc_set_min_rate(&emc->reqs, rate, TEGRA_EMC_RATE_ICC);
1185dce208b5SSvyatoslav Ryhel 	if (err)
1186dce208b5SSvyatoslav Ryhel 		return err;
1187dce208b5SSvyatoslav Ryhel 
1188dce208b5SSvyatoslav Ryhel 	return 0;
1189dce208b5SSvyatoslav Ryhel }
1190dce208b5SSvyatoslav Ryhel 
1191dce208b5SSvyatoslav Ryhel static int tegra114_emc_interconnect_init(struct tegra_emc *emc)
1192dce208b5SSvyatoslav Ryhel {
1193dce208b5SSvyatoslav Ryhel 	const struct tegra_mc_soc *soc = emc->mc->soc;
1194dce208b5SSvyatoslav Ryhel 	struct icc_node *node;
1195dce208b5SSvyatoslav Ryhel 	int err;
1196dce208b5SSvyatoslav Ryhel 
1197dce208b5SSvyatoslav Ryhel 	emc->provider.dev = emc->dev;
1198dce208b5SSvyatoslav Ryhel 	emc->provider.set = emc_icc_set;
1199dce208b5SSvyatoslav Ryhel 	emc->provider.data = &emc->provider;
1200dce208b5SSvyatoslav Ryhel 	emc->provider.aggregate = soc->icc_ops->aggregate;
1201dce208b5SSvyatoslav Ryhel 	emc->provider.xlate_extended = emc_of_icc_xlate_extended;
1202dce208b5SSvyatoslav Ryhel 
1203dce208b5SSvyatoslav Ryhel 	icc_provider_init(&emc->provider);
1204dce208b5SSvyatoslav Ryhel 
1205dce208b5SSvyatoslav Ryhel 	/* create External Memory Controller node */
1206dce208b5SSvyatoslav Ryhel 	node = icc_node_create(TEGRA_ICC_EMC);
120767de69efSKrzysztof Kozlowski 	if (IS_ERR(node))
120867de69efSKrzysztof Kozlowski 		return PTR_ERR(node);
1209dce208b5SSvyatoslav Ryhel 
1210dce208b5SSvyatoslav Ryhel 	node->name = "External Memory Controller";
1211dce208b5SSvyatoslav Ryhel 	icc_node_add(node, &emc->provider);
1212dce208b5SSvyatoslav Ryhel 
1213dce208b5SSvyatoslav Ryhel 	/* link External Memory Controller to External Memory (DRAM) */
1214dce208b5SSvyatoslav Ryhel 	err = icc_link_create(node, TEGRA_ICC_EMEM);
1215dce208b5SSvyatoslav Ryhel 	if (err)
1216dce208b5SSvyatoslav Ryhel 		goto remove_nodes;
1217dce208b5SSvyatoslav Ryhel 
1218dce208b5SSvyatoslav Ryhel 	/* create External Memory node */
1219dce208b5SSvyatoslav Ryhel 	node = icc_node_create(TEGRA_ICC_EMEM);
1220dce208b5SSvyatoslav Ryhel 	if (IS_ERR(node)) {
1221dce208b5SSvyatoslav Ryhel 		err = PTR_ERR(node);
1222dce208b5SSvyatoslav Ryhel 		goto remove_nodes;
1223dce208b5SSvyatoslav Ryhel 	}
1224dce208b5SSvyatoslav Ryhel 
1225dce208b5SSvyatoslav Ryhel 	node->name = "External Memory (DRAM)";
1226dce208b5SSvyatoslav Ryhel 	icc_node_add(node, &emc->provider);
1227dce208b5SSvyatoslav Ryhel 
1228dce208b5SSvyatoslav Ryhel 	err = icc_provider_register(&emc->provider);
1229dce208b5SSvyatoslav Ryhel 	if (err)
1230dce208b5SSvyatoslav Ryhel 		goto remove_nodes;
1231dce208b5SSvyatoslav Ryhel 
1232dce208b5SSvyatoslav Ryhel 	return 0;
1233dce208b5SSvyatoslav Ryhel 
1234dce208b5SSvyatoslav Ryhel remove_nodes:
1235dce208b5SSvyatoslav Ryhel 	icc_nodes_remove(&emc->provider);
1236dce208b5SSvyatoslav Ryhel 
1237*0a783a66SKrzysztof Kozlowski 	return dev_err_probe(emc->dev, err, "failed to initialize ICC");
1238dce208b5SSvyatoslav Ryhel }
1239dce208b5SSvyatoslav Ryhel 
1240dce208b5SSvyatoslav Ryhel static void devm_tegra114_emc_unset_callback(void *data)
1241dce208b5SSvyatoslav Ryhel {
1242dce208b5SSvyatoslav Ryhel 	tegra124_clk_set_emc_callbacks(NULL, NULL);
1243dce208b5SSvyatoslav Ryhel }
1244dce208b5SSvyatoslav Ryhel 
1245dce208b5SSvyatoslav Ryhel static int tegra114_emc_probe(struct platform_device *pdev)
1246dce208b5SSvyatoslav Ryhel {
1247dce208b5SSvyatoslav Ryhel 	struct tegra_core_opp_params opp_params = {};
1248dce208b5SSvyatoslav Ryhel 	struct device *dev = &pdev->dev;
1249dce208b5SSvyatoslav Ryhel 	struct device_node *np;
1250dce208b5SSvyatoslav Ryhel 	struct tegra_emc *emc;
1251dce208b5SSvyatoslav Ryhel 	u32 ram_code;
1252dce208b5SSvyatoslav Ryhel 	int err;
1253dce208b5SSvyatoslav Ryhel 
1254dce208b5SSvyatoslav Ryhel 	emc = devm_kzalloc(dev, sizeof(*emc), GFP_KERNEL);
1255dce208b5SSvyatoslav Ryhel 	if (!emc)
1256dce208b5SSvyatoslav Ryhel 		return -ENOMEM;
1257dce208b5SSvyatoslav Ryhel 
1258dce208b5SSvyatoslav Ryhel 	emc->dev = dev;
1259dce208b5SSvyatoslav Ryhel 
1260dce208b5SSvyatoslav Ryhel 	emc->regs = devm_platform_ioremap_resource(pdev, 0);
1261dce208b5SSvyatoslav Ryhel 	if (IS_ERR(emc->regs))
1262dce208b5SSvyatoslav Ryhel 		return PTR_ERR(emc->regs);
1263dce208b5SSvyatoslav Ryhel 
1264dce208b5SSvyatoslav Ryhel 	emc->mc = devm_tegra_memory_controller_get(dev);
1265dce208b5SSvyatoslav Ryhel 	if (IS_ERR(emc->mc))
1266dce208b5SSvyatoslav Ryhel 		return PTR_ERR(emc->mc);
1267dce208b5SSvyatoslav Ryhel 
1268dce208b5SSvyatoslav Ryhel 	ram_code = tegra_read_ram_code();
1269dce208b5SSvyatoslav Ryhel 
1270dce208b5SSvyatoslav Ryhel 	np = tegra114_emc_find_node_by_ram_code(dev->of_node, ram_code);
1271dce208b5SSvyatoslav Ryhel 	if (np) {
1272dce208b5SSvyatoslav Ryhel 		err = tegra114_emc_load_timings_from_dt(emc, np);
1273dce208b5SSvyatoslav Ryhel 		of_node_put(np);
1274dce208b5SSvyatoslav Ryhel 		if (err)
1275dce208b5SSvyatoslav Ryhel 			return err;
1276dce208b5SSvyatoslav Ryhel 	} else {
1277dce208b5SSvyatoslav Ryhel 		dev_info_once(dev, "no memory timings for RAM code %u found in DT\n",
1278dce208b5SSvyatoslav Ryhel 			      ram_code);
1279dce208b5SSvyatoslav Ryhel 	}
1280dce208b5SSvyatoslav Ryhel 
1281dce208b5SSvyatoslav Ryhel 	emc_init(emc);
1282dce208b5SSvyatoslav Ryhel 
1283dce208b5SSvyatoslav Ryhel 	platform_set_drvdata(pdev, emc);
1284dce208b5SSvyatoslav Ryhel 
1285dce208b5SSvyatoslav Ryhel 	tegra124_clk_set_emc_callbacks(tegra114_emc_prepare_timing_change,
1286dce208b5SSvyatoslav Ryhel 				       tegra114_emc_complete_timing_change);
1287dce208b5SSvyatoslav Ryhel 
1288dce208b5SSvyatoslav Ryhel 	err = devm_add_action_or_reset(dev, devm_tegra114_emc_unset_callback,
1289dce208b5SSvyatoslav Ryhel 				       NULL);
1290dce208b5SSvyatoslav Ryhel 	if (err)
1291dce208b5SSvyatoslav Ryhel 		return err;
1292dce208b5SSvyatoslav Ryhel 
1293dce208b5SSvyatoslav Ryhel 	err = platform_get_irq(pdev, 0);
1294dce208b5SSvyatoslav Ryhel 	if (err < 0)
1295dce208b5SSvyatoslav Ryhel 		return err;
1296dce208b5SSvyatoslav Ryhel 
1297dce208b5SSvyatoslav Ryhel 	emc->irq = err;
1298dce208b5SSvyatoslav Ryhel 
1299dce208b5SSvyatoslav Ryhel 	err = devm_request_irq(dev, emc->irq, tegra114_emc_isr, 0,
1300dce208b5SSvyatoslav Ryhel 			       dev_name(dev), emc);
1301dce208b5SSvyatoslav Ryhel 	if (err)
1302dce208b5SSvyatoslav Ryhel 		return dev_err_probe(dev, err, "failed to request irq\n");
1303dce208b5SSvyatoslav Ryhel 
1304dce208b5SSvyatoslav Ryhel 	emc->clk = devm_clk_get(dev, "emc");
1305dce208b5SSvyatoslav Ryhel 	if (IS_ERR(emc->clk))
1306dce208b5SSvyatoslav Ryhel 		return dev_err_probe(dev, PTR_ERR(emc->clk),
1307dce208b5SSvyatoslav Ryhel 				     "failed to get EMC clock\n");
1308dce208b5SSvyatoslav Ryhel 
1309dce208b5SSvyatoslav Ryhel 	opp_params.init_state = true;
1310dce208b5SSvyatoslav Ryhel 
1311dce208b5SSvyatoslav Ryhel 	err = devm_tegra_core_dev_init_opp_table(dev, &opp_params);
1312dce208b5SSvyatoslav Ryhel 	if (err)
1313dce208b5SSvyatoslav Ryhel 		return err;
1314dce208b5SSvyatoslav Ryhel 
1315dce208b5SSvyatoslav Ryhel 	tegra_emc_rate_requests_init(&emc->reqs, dev);
1316dce208b5SSvyatoslav Ryhel 
1317dce208b5SSvyatoslav Ryhel 	if (IS_ENABLED(CONFIG_DEBUG_FS))
1318dce208b5SSvyatoslav Ryhel 		emc_debugfs_init(dev, emc);
1319dce208b5SSvyatoslav Ryhel 
1320dce208b5SSvyatoslav Ryhel 	tegra114_emc_interconnect_init(emc);
1321dce208b5SSvyatoslav Ryhel 
1322dce208b5SSvyatoslav Ryhel 	/*
1323dce208b5SSvyatoslav Ryhel 	 * Don't allow the kernel module to be unloaded. Unloading adds some
1324dce208b5SSvyatoslav Ryhel 	 * extra complexity which doesn't really worth the effort in a case of
1325dce208b5SSvyatoslav Ryhel 	 * this driver.
1326dce208b5SSvyatoslav Ryhel 	 */
1327dce208b5SSvyatoslav Ryhel 	try_module_get(THIS_MODULE);
1328dce208b5SSvyatoslav Ryhel 
1329dce208b5SSvyatoslav Ryhel 	return 0;
1330dce208b5SSvyatoslav Ryhel };
1331dce208b5SSvyatoslav Ryhel 
1332dce208b5SSvyatoslav Ryhel static const struct of_device_id tegra114_emc_of_match[] = {
1333dce208b5SSvyatoslav Ryhel 	{ .compatible = "nvidia,tegra114-emc" },
1334dce208b5SSvyatoslav Ryhel 	{ }
1335dce208b5SSvyatoslav Ryhel };
1336dce208b5SSvyatoslav Ryhel MODULE_DEVICE_TABLE(of, tegra114_emc_of_match);
1337dce208b5SSvyatoslav Ryhel 
1338dce208b5SSvyatoslav Ryhel static struct platform_driver tegra114_emc_driver = {
1339dce208b5SSvyatoslav Ryhel 	.probe = tegra114_emc_probe,
1340dce208b5SSvyatoslav Ryhel 	.driver = {
1341dce208b5SSvyatoslav Ryhel 		.name = "tegra114-emc",
1342dce208b5SSvyatoslav Ryhel 		.of_match_table = tegra114_emc_of_match,
1343dce208b5SSvyatoslav Ryhel 		.suppress_bind_attrs = true,
1344dce208b5SSvyatoslav Ryhel 		.sync_state = icc_sync_state,
1345dce208b5SSvyatoslav Ryhel 	},
1346dce208b5SSvyatoslav Ryhel };
1347dce208b5SSvyatoslav Ryhel module_platform_driver(tegra114_emc_driver);
1348dce208b5SSvyatoslav Ryhel 
1349dce208b5SSvyatoslav Ryhel MODULE_AUTHOR("Svyatoslav Ryhel <clamor95@gmail.com>");
1350dce208b5SSvyatoslav Ryhel MODULE_DESCRIPTION("NVIDIA Tegra114 EMC driver");
1351dce208b5SSvyatoslav Ryhel MODULE_LICENSE("GPL");
1352