77#include <linux/debugfs.h>
88#include <linux/module.h>
99#include <linux/mod_devicetable.h>
10+ #include <linux/of_platform.h>
1011#include <linux/platform_device.h>
1112
1213#include <soc/tegra/bpmp.h>
14+ #include "mc.h"
1315
1416struct tegra186_emc_dvfs {
1517 unsigned long latency ;
@@ -29,8 +31,15 @@ struct tegra186_emc {
2931 unsigned long min_rate ;
3032 unsigned long max_rate ;
3133 } debugfs ;
34+
35+ struct icc_provider provider ;
3236};
3337
38+ static inline struct tegra186_emc * to_tegra186_emc (struct icc_provider * provider )
39+ {
40+ return container_of (provider , struct tegra186_emc , provider );
41+ }
42+
3443/*
3544 * debugfs interface
3645 *
@@ -146,8 +155,102 @@ DEFINE_DEBUGFS_ATTRIBUTE(tegra186_emc_debug_max_rate_fops,
146155 tegra186_emc_debug_max_rate_get ,
147156 tegra186_emc_debug_max_rate_set , "%llu\n" );
148157
158+ /*
159+ * tegra_emc_icc_set_bw() - Set BW api for EMC provider
160+ * @src: ICC node for External Memory Controller (EMC)
161+ * @dst: ICC node for External Memory (DRAM)
162+ *
163+ * Do nothing here as info to BPMP-FW is now passed in the BW set function
164+ * of the MC driver. BPMP-FW sets the final Freq based on the passed values.
165+ */
166+ static int tegra_emc_icc_set_bw (struct icc_node * src , struct icc_node * dst )
167+ {
168+ return 0 ;
169+ }
170+
171+ static struct icc_node *
172+ tegra_emc_of_icc_xlate (struct of_phandle_args * spec , void * data )
173+ {
174+ struct icc_provider * provider = data ;
175+ struct icc_node * node ;
176+
177+ /* External Memory is the only possible ICC route */
178+ list_for_each_entry (node , & provider -> nodes , node_list ) {
179+ if (node -> id != TEGRA_ICC_EMEM )
180+ continue ;
181+
182+ return node ;
183+ }
184+
185+ return ERR_PTR (- EPROBE_DEFER );
186+ }
187+
188+ static int tegra_emc_icc_get_init_bw (struct icc_node * node , u32 * avg , u32 * peak )
189+ {
190+ * avg = 0 ;
191+ * peak = 0 ;
192+
193+ return 0 ;
194+ }
195+
196+ static int tegra_emc_interconnect_init (struct tegra186_emc * emc )
197+ {
198+ struct tegra_mc * mc = dev_get_drvdata (emc -> dev -> parent );
199+ const struct tegra_mc_soc * soc = mc -> soc ;
200+ struct icc_node * node ;
201+ int err ;
202+
203+ emc -> provider .dev = emc -> dev ;
204+ emc -> provider .set = tegra_emc_icc_set_bw ;
205+ emc -> provider .data = & emc -> provider ;
206+ emc -> provider .aggregate = soc -> icc_ops -> aggregate ;
207+ emc -> provider .xlate = tegra_emc_of_icc_xlate ;
208+ emc -> provider .get_bw = tegra_emc_icc_get_init_bw ;
209+
210+ icc_provider_init (& emc -> provider );
211+
212+ /* create External Memory Controller node */
213+ node = icc_node_create (TEGRA_ICC_EMC );
214+ if (IS_ERR (node )) {
215+ err = PTR_ERR (node );
216+ goto err_msg ;
217+ }
218+
219+ node -> name = "External Memory Controller" ;
220+ icc_node_add (node , & emc -> provider );
221+
222+ /* link External Memory Controller to External Memory (DRAM) */
223+ err = icc_link_create (node , TEGRA_ICC_EMEM );
224+ if (err )
225+ goto remove_nodes ;
226+
227+ /* create External Memory node */
228+ node = icc_node_create (TEGRA_ICC_EMEM );
229+ if (IS_ERR (node )) {
230+ err = PTR_ERR (node );
231+ goto remove_nodes ;
232+ }
233+
234+ node -> name = "External Memory (DRAM)" ;
235+ icc_node_add (node , & emc -> provider );
236+
237+ err = icc_provider_register (& emc -> provider );
238+ if (err )
239+ goto remove_nodes ;
240+
241+ return 0 ;
242+
243+ remove_nodes :
244+ icc_nodes_remove (& emc -> provider );
245+ err_msg :
246+ dev_err (emc -> dev , "failed to initialize ICC: %d\n" , err );
247+
248+ return err ;
249+ }
250+
149251static int tegra186_emc_probe (struct platform_device * pdev )
150252{
253+ struct tegra_mc * mc = dev_get_drvdata (pdev -> dev .parent );
151254 struct mrq_emc_dvfs_latency_response response ;
152255 struct tegra_bpmp_message msg ;
153256 struct tegra186_emc * emc ;
@@ -236,6 +339,32 @@ static int tegra186_emc_probe(struct platform_device *pdev)
236339 debugfs_create_file ("max_rate" , S_IRUGO | S_IWUSR , emc -> debugfs .root ,
237340 emc , & tegra186_emc_debug_max_rate_fops );
238341
342+ if (mc && mc -> soc -> icc_ops ) {
343+ if (tegra_bpmp_mrq_is_supported (emc -> bpmp , MRQ_BWMGR_INT )) {
344+ mc -> bwmgr_mrq_supported = true;
345+
346+ /*
347+ * MC driver probe can't get BPMP reference as it gets probed
348+ * earlier than BPMP. So, save the BPMP ref got from the EMC
349+ * DT node in the mc->bpmp and use it in MC's icc_set hook.
350+ */
351+ mc -> bpmp = emc -> bpmp ;
352+ barrier ();
353+ }
354+
355+ /*
356+ * Initialize the ICC even if BPMP-FW doesn't support 'MRQ_BWMGR_INT'.
357+ * Use the flag 'mc->bwmgr_mrq_supported' within MC driver and return
358+ * EINVAL instead of passing the request to BPMP-FW later when the BW
359+ * request is made by client with 'icc_set_bw()' call.
360+ */
361+ err = tegra_emc_interconnect_init (emc );
362+ if (err ) {
363+ mc -> bpmp = NULL ;
364+ goto put_bpmp ;
365+ }
366+ }
367+
239368 return 0 ;
240369
241370put_bpmp :
@@ -245,9 +374,12 @@ static int tegra186_emc_probe(struct platform_device *pdev)
245374
246375static int tegra186_emc_remove (struct platform_device * pdev )
247376{
377+ struct tegra_mc * mc = dev_get_drvdata (pdev -> dev .parent );
248378 struct tegra186_emc * emc = platform_get_drvdata (pdev );
249379
250380 debugfs_remove_recursive (emc -> debugfs .root );
381+
382+ mc -> bpmp = NULL ;
251383 tegra_bpmp_put (emc -> bpmp );
252384
253385 return 0 ;
@@ -272,6 +404,7 @@ static struct platform_driver tegra186_emc_driver = {
272404 .name = "tegra186-emc" ,
273405 .of_match_table = tegra186_emc_of_match ,
274406 .suppress_bind_attrs = true,
407+ .sync_state = icc_sync_state ,
275408 },
276409 .probe = tegra186_emc_probe ,
277410 .remove = tegra186_emc_remove ,
0 commit comments