diff options
Diffstat (limited to 'drivers/memory')
| -rw-r--r-- | drivers/memory/fsl-corenet-cf.c | 4 | ||||
| -rw-r--r-- | drivers/memory/mtk-smi.c | 19 | ||||
| -rw-r--r-- | drivers/memory/omap-gpmc.c | 7 | ||||
| -rw-r--r-- | drivers/memory/pl353-smc.c | 2 | ||||
| -rw-r--r-- | drivers/memory/renesas-rpc-if.c | 2 | ||||
| -rw-r--r-- | drivers/memory/samsung/exynos5422-dmc.c | 17 | ||||
| -rw-r--r-- | drivers/memory/tegra/mc.c | 9 | ||||
| -rw-r--r-- | drivers/memory/tegra/mc.h | 4 | ||||
| -rw-r--r-- | drivers/memory/tegra/tegra124-emc.c | 16 | ||||
| -rw-r--r-- | drivers/memory/tegra/tegra20-emc.c | 20 | ||||
| -rw-r--r-- | drivers/memory/tegra/tegra20.c | 332 | ||||
| -rw-r--r-- | drivers/memory/tegra/tegra30-emc.c | 18 | 
12 files changed, 394 insertions, 56 deletions
| diff --git a/drivers/memory/fsl-corenet-cf.c b/drivers/memory/fsl-corenet-cf.c index 0309bd5a1800..f8ea592c9cb5 100644 --- a/drivers/memory/fsl-corenet-cf.c +++ b/drivers/memory/fsl-corenet-cf.c @@ -192,10 +192,8 @@ static int ccf_probe(struct platform_device *pdev)  	}  	ccf->regs = devm_ioremap_resource(&pdev->dev, r); -	if (IS_ERR(ccf->regs)) { -		dev_err(&pdev->dev, "%s: can't map mem resource\n", __func__); +	if (IS_ERR(ccf->regs))  		return PTR_ERR(ccf->regs); -	}  	ccf->dev = &pdev->dev;  	ccf->info = match->data; diff --git a/drivers/memory/mtk-smi.c b/drivers/memory/mtk-smi.c index b396253fcf4b..c5fb51f73b34 100644 --- a/drivers/memory/mtk-smi.c +++ b/drivers/memory/mtk-smi.c @@ -319,6 +319,7 @@ static int mtk_smi_larb_probe(struct platform_device *pdev)  	struct device *dev = &pdev->dev;  	struct device_node *smi_node;  	struct platform_device *smi_pdev; +	struct device_link *link;  	larb = devm_kzalloc(dev, sizeof(*larb), GFP_KERNEL);  	if (!larb) @@ -358,6 +359,12 @@ static int mtk_smi_larb_probe(struct platform_device *pdev)  		if (!platform_get_drvdata(smi_pdev))  			return -EPROBE_DEFER;  		larb->smi_common_dev = &smi_pdev->dev; +		link = device_link_add(dev, larb->smi_common_dev, +				       DL_FLAG_PM_RUNTIME | DL_FLAG_STATELESS); +		if (!link) { +			dev_err(dev, "Unable to link smi-common dev\n"); +			return -ENODEV; +		}  	} else {  		dev_err(dev, "Failed to get the smi_common device\n");  		return -EINVAL; @@ -370,6 +377,9 @@ static int mtk_smi_larb_probe(struct platform_device *pdev)  static int mtk_smi_larb_remove(struct platform_device *pdev)  { +	struct mtk_smi_larb *larb = platform_get_drvdata(pdev); + +	device_link_remove(&pdev->dev, larb->smi_common_dev);  	pm_runtime_disable(&pdev->dev);  	component_del(&pdev->dev, &mtk_smi_larb_component_ops);  	return 0; @@ -381,17 +391,9 @@ static int __maybe_unused mtk_smi_larb_resume(struct device *dev)  	const struct mtk_smi_larb_gen *larb_gen = larb->larb_gen;  	int ret; -	/* Power on smi-common. */ -	ret = pm_runtime_resume_and_get(larb->smi_common_dev); -	if (ret < 0) { -		dev_err(dev, "Failed to pm get for smi-common(%d).\n", ret); -		return ret; -	} -  	ret = mtk_smi_clk_enable(&larb->smi);  	if (ret < 0) {  		dev_err(dev, "Failed to enable clock(%d).\n", ret); -		pm_runtime_put_sync(larb->smi_common_dev);  		return ret;  	} @@ -406,7 +408,6 @@ static int __maybe_unused mtk_smi_larb_suspend(struct device *dev)  	struct mtk_smi_larb *larb = dev_get_drvdata(dev);  	mtk_smi_clk_disable(&larb->smi); -	pm_runtime_put_sync(larb->smi_common_dev);  	return 0;  } diff --git a/drivers/memory/omap-gpmc.c b/drivers/memory/omap-gpmc.c index cfa730cfd145..f80c2ea39ca4 100644 --- a/drivers/memory/omap-gpmc.c +++ b/drivers/memory/omap-gpmc.c @@ -1009,8 +1009,8 @@ EXPORT_SYMBOL(gpmc_cs_request);  void gpmc_cs_free(int cs)  { -	struct gpmc_cs_data *gpmc = &gpmc_cs[cs]; -	struct resource *res = &gpmc->mem; +	struct gpmc_cs_data *gpmc; +	struct resource *res;  	spin_lock(&gpmc_mem_lock);  	if (cs >= gpmc_cs_num || cs < 0 || !gpmc_cs_reserved(cs)) { @@ -1018,6 +1018,9 @@ void gpmc_cs_free(int cs)  		spin_unlock(&gpmc_mem_lock);  		return;  	} +	gpmc = &gpmc_cs[cs]; +	res = &gpmc->mem; +  	gpmc_cs_disable_mem(cs);  	if (res->flags)  		release_resource(res); diff --git a/drivers/memory/pl353-smc.c b/drivers/memory/pl353-smc.c index 3b5b1045edd9..9c0a28416777 100644 --- a/drivers/memory/pl353-smc.c +++ b/drivers/memory/pl353-smc.c @@ -63,7 +63,7 @@  /* ECC memory config register specific constants */  #define PL353_SMC_ECC_MEMCFG_MODE_MASK	0xC  #define PL353_SMC_ECC_MEMCFG_MODE_SHIFT	2 -#define PL353_SMC_ECC_MEMCFG_PGSIZE_MASK	0xC +#define PL353_SMC_ECC_MEMCFG_PGSIZE_MASK	0x3  #define PL353_SMC_DC_UPT_NAND_REGS	((4 << 23) |	/* CS: NAND chip */ \  				 (2 << 21))	/* UpdateRegs operation */ diff --git a/drivers/memory/renesas-rpc-if.c b/drivers/memory/renesas-rpc-if.c index 8d36e221def1..45eed659b0c6 100644 --- a/drivers/memory/renesas-rpc-if.c +++ b/drivers/memory/renesas-rpc-if.c @@ -192,10 +192,10 @@ int rpcif_sw_init(struct rpcif *rpc, struct device *dev)  	}  	res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "dirmap"); -	rpc->size = resource_size(res);  	rpc->dirmap = devm_ioremap_resource(&pdev->dev, res);  	if (IS_ERR(rpc->dirmap))  		rpc->dirmap = NULL; +	rpc->size = resource_size(res);  	rpc->rstc = devm_reset_control_get_exclusive(&pdev->dev, NULL); diff --git a/drivers/memory/samsung/exynos5422-dmc.c b/drivers/memory/samsung/exynos5422-dmc.c index 1dabb509dec3..9c8318923ed0 100644 --- a/drivers/memory/samsung/exynos5422-dmc.c +++ b/drivers/memory/samsung/exynos5422-dmc.c @@ -343,7 +343,7 @@ static int exynos5_init_freq_table(struct exynos5_dmc *dmc,  	int idx;  	unsigned long freq; -	ret = dev_pm_opp_of_add_table(dmc->dev); +	ret = devm_pm_opp_of_add_table(dmc->dev);  	if (ret < 0) {  		dev_err(dmc->dev, "Failed to get OPP table\n");  		return ret; @@ -354,7 +354,7 @@ static int exynos5_init_freq_table(struct exynos5_dmc *dmc,  	dmc->opp = devm_kmalloc_array(dmc->dev, dmc->opp_count,  				      sizeof(struct dmc_opp_table), GFP_KERNEL);  	if (!dmc->opp) -		goto err_opp; +		return -ENOMEM;  	idx = dmc->opp_count - 1;  	for (i = 0, freq = ULONG_MAX; i < dmc->opp_count; i++, freq--) { @@ -362,7 +362,7 @@ static int exynos5_init_freq_table(struct exynos5_dmc *dmc,  		opp = dev_pm_opp_find_freq_floor(dmc->dev, &freq);  		if (IS_ERR(opp)) -			goto err_opp; +			return PTR_ERR(opp);  		dmc->opp[idx - i].freq_hz = freq;  		dmc->opp[idx - i].volt_uv = dev_pm_opp_get_voltage(opp); @@ -371,11 +371,6 @@ static int exynos5_init_freq_table(struct exynos5_dmc *dmc,  	}  	return 0; - -err_opp: -	dev_pm_opp_of_remove_table(dmc->dev); - -	return -EINVAL;  }  /** @@ -1298,7 +1293,9 @@ static int exynos5_dmc_init_clks(struct exynos5_dmc *dmc)  	dmc->curr_volt = target_volt; -	clk_set_parent(dmc->mout_mx_mspll_ccore, dmc->mout_spll); +	ret = clk_set_parent(dmc->mout_mx_mspll_ccore, dmc->mout_spll); +	if (ret) +		return ret;  	clk_prepare_enable(dmc->fout_bpll);  	clk_prepare_enable(dmc->mout_bpll); @@ -1567,8 +1564,6 @@ static int exynos5_dmc_remove(struct platform_device *pdev)  	clk_disable_unprepare(dmc->mout_bpll);  	clk_disable_unprepare(dmc->fout_bpll); -	dev_pm_opp_remove_table(dmc->dev); -  	return 0;  } diff --git a/drivers/memory/tegra/mc.c b/drivers/memory/tegra/mc.c index a21163ccadc4..e58c3e5baea0 100644 --- a/drivers/memory/tegra/mc.c +++ b/drivers/memory/tegra/mc.c @@ -827,6 +827,15 @@ static int tegra_mc_probe(struct platform_device *pdev)  		return err;  	} +	mc->debugfs.root = debugfs_create_dir("mc", NULL); + +	if (mc->soc->init) { +		err = mc->soc->init(mc); +		if (err < 0) +			dev_err(&pdev->dev, "failed to initialize SoC driver: %d\n", +				err); +	} +  	err = tegra_mc_reset_setup(mc);  	if (err < 0)  		dev_err(&pdev->dev, "failed to register reset controller: %d\n", diff --git a/drivers/memory/tegra/mc.h b/drivers/memory/tegra/mc.h index 33e40d600592..1ee34f0da4f7 100644 --- a/drivers/memory/tegra/mc.h +++ b/drivers/memory/tegra/mc.h @@ -92,12 +92,12 @@ icc_provider_to_tegra_mc(struct icc_provider *provider)  	return container_of(provider, struct tegra_mc, provider);  } -static inline u32 mc_readl(struct tegra_mc *mc, unsigned long offset) +static inline u32 mc_readl(const struct tegra_mc *mc, unsigned long offset)  {  	return readl_relaxed(mc->regs + offset);  } -static inline void mc_writel(struct tegra_mc *mc, u32 value, +static inline void mc_writel(const struct tegra_mc *mc, u32 value,  			     unsigned long offset)  {  	writel_relaxed(value, mc->regs + offset); diff --git a/drivers/memory/tegra/tegra124-emc.c b/drivers/memory/tegra/tegra124-emc.c index bee8d9f79b04..5699d909abc2 100644 --- a/drivers/memory/tegra/tegra124-emc.c +++ b/drivers/memory/tegra/tegra124-emc.c @@ -905,7 +905,7 @@ static int emc_init(struct tegra_emc *emc)  	else  		emc->dram_bus_width = 32; -	dev_info(emc->dev, "%ubit DRAM bus\n", emc->dram_bus_width); +	dev_info_once(emc->dev, "%ubit DRAM bus\n", emc->dram_bus_width);  	emc->dram_type &= EMC_FBIO_CFG5_DRAM_TYPE_MASK;  	emc->dram_type >>= EMC_FBIO_CFG5_DRAM_TYPE_SHIFT; @@ -1204,7 +1204,7 @@ static int tegra_emc_debug_min_rate_set(void *data, u64 rate)  	return 0;  } -DEFINE_SIMPLE_ATTRIBUTE(tegra_emc_debug_min_rate_fops, +DEFINE_DEBUGFS_ATTRIBUTE(tegra_emc_debug_min_rate_fops,  			tegra_emc_debug_min_rate_get,  			tegra_emc_debug_min_rate_set, "%llu\n"); @@ -1234,7 +1234,7 @@ static int tegra_emc_debug_max_rate_set(void *data, u64 rate)  	return 0;  } -DEFINE_SIMPLE_ATTRIBUTE(tegra_emc_debug_max_rate_fops, +DEFINE_DEBUGFS_ATTRIBUTE(tegra_emc_debug_max_rate_fops,  			tegra_emc_debug_max_rate_get,  			tegra_emc_debug_max_rate_set, "%llu\n"); @@ -1419,8 +1419,8 @@ static int tegra_emc_opp_table_init(struct tegra_emc *emc)  		goto put_hw_table;  	} -	dev_info(emc->dev, "OPP HW ver. 0x%x, current clock rate %lu MHz\n", -		 hw_version, clk_get_rate(emc->clk) / 1000000); +	dev_info_once(emc->dev, "OPP HW ver. 0x%x, current clock rate %lu MHz\n", +		      hw_version, clk_get_rate(emc->clk) / 1000000);  	/* first dummy rate-set initializes voltage state */  	err = dev_pm_opp_set_rate(emc->dev, clk_get_rate(emc->clk)); @@ -1475,9 +1475,9 @@ static int tegra_emc_probe(struct platform_device *pdev)  		if (err)  			return err;  	} else { -		dev_info(&pdev->dev, -			 "no memory timings for RAM code %u found in DT\n", -			 ram_code); +		dev_info_once(&pdev->dev, +			      "no memory timings for RAM code %u found in DT\n", +			      ram_code);  	}  	err = emc_init(emc); diff --git a/drivers/memory/tegra/tegra20-emc.c b/drivers/memory/tegra/tegra20-emc.c index d653a6be8d7f..da8a0da8da79 100644 --- a/drivers/memory/tegra/tegra20-emc.c +++ b/drivers/memory/tegra/tegra20-emc.c @@ -411,12 +411,12 @@ static int tegra_emc_load_timings_from_dt(struct tegra_emc *emc,  	sort(emc->timings, emc->num_timings, sizeof(*timing), cmp_timings,  	     NULL); -	dev_info(emc->dev, -		 "got %u timings for RAM code %u (min %luMHz max %luMHz)\n", -		 emc->num_timings, -		 tegra_read_ram_code(), -		 emc->timings[0].rate / 1000000, -		 emc->timings[emc->num_timings - 1].rate / 1000000); +	dev_info_once(emc->dev, +		      "got %u timings for RAM code %u (min %luMHz max %luMHz)\n", +		      emc->num_timings, +		      tegra_read_ram_code(), +		      emc->timings[0].rate / 1000000, +		      emc->timings[emc->num_timings - 1].rate / 1000000);  	return 0;  } @@ -429,7 +429,7 @@ tegra_emc_find_node_by_ram_code(struct device *dev)  	int err;  	if (of_get_child_count(dev->of_node) == 0) { -		dev_info(dev, "device-tree doesn't have memory timings\n"); +		dev_info_once(dev, "device-tree doesn't have memory timings\n");  		return NULL;  	} @@ -496,7 +496,7 @@ static int emc_setup_hw(struct tegra_emc *emc)  	else  		emc->dram_bus_width = 32; -	dev_info(emc->dev, "%ubit DRAM bus\n", emc->dram_bus_width); +	dev_info_once(emc->dev, "%ubit DRAM bus\n", emc->dram_bus_width);  	return 0;  } @@ -931,8 +931,8 @@ static int tegra_emc_opp_table_init(struct tegra_emc *emc)  		goto put_hw_table;  	} -	dev_info(emc->dev, "OPP HW ver. 0x%x, current clock rate %lu MHz\n", -		 hw_version, clk_get_rate(emc->clk) / 1000000); +	dev_info_once(emc->dev, "OPP HW ver. 0x%x, current clock rate %lu MHz\n", +		      hw_version, clk_get_rate(emc->clk) / 1000000);  	/* first dummy rate-set initializes voltage state */  	err = dev_pm_opp_set_rate(emc->dev, clk_get_rate(emc->clk)); diff --git a/drivers/memory/tegra/tegra20.c b/drivers/memory/tegra/tegra20.c index 29ecf02805a0..2db68a913b7a 100644 --- a/drivers/memory/tegra/tegra20.c +++ b/drivers/memory/tegra/tegra20.c @@ -3,6 +3,9 @@   * Copyright (C) 2012 NVIDIA CORPORATION.  All rights reserved.   */ +#include <linux/bitfield.h> +#include <linux/delay.h> +#include <linux/mutex.h>  #include <linux/of_device.h>  #include <linux/slab.h>  #include <linux/string.h> @@ -11,6 +14,79 @@  #include "mc.h" +#define MC_STAT_CONTROL				0x90 +#define MC_STAT_EMC_CLOCK_LIMIT			0xa0 +#define MC_STAT_EMC_CLOCKS			0xa4 +#define MC_STAT_EMC_CONTROL_0			0xa8 +#define MC_STAT_EMC_CONTROL_1			0xac +#define MC_STAT_EMC_COUNT_0			0xb8 +#define MC_STAT_EMC_COUNT_1			0xbc + +#define MC_STAT_CONTROL_CLIENT_ID		GENMASK(13,  8) +#define MC_STAT_CONTROL_EVENT			GENMASK(23, 16) +#define MC_STAT_CONTROL_PRI_EVENT		GENMASK(25, 24) +#define MC_STAT_CONTROL_FILTER_CLIENT_ENABLE	GENMASK(26, 26) +#define MC_STAT_CONTROL_FILTER_PRI		GENMASK(29, 28) + +#define MC_STAT_CONTROL_PRI_EVENT_HP		0 +#define MC_STAT_CONTROL_PRI_EVENT_TM		1 +#define MC_STAT_CONTROL_PRI_EVENT_BW		2 + +#define MC_STAT_CONTROL_FILTER_PRI_DISABLE	0 +#define MC_STAT_CONTROL_FILTER_PRI_NO		1 +#define MC_STAT_CONTROL_FILTER_PRI_YES		2 + +#define MC_STAT_CONTROL_EVENT_QUALIFIED		0 +#define MC_STAT_CONTROL_EVENT_ANY_READ		1 +#define MC_STAT_CONTROL_EVENT_ANY_WRITE		2 +#define MC_STAT_CONTROL_EVENT_RD_WR_CHANGE	3 +#define MC_STAT_CONTROL_EVENT_SUCCESSIVE	4 +#define MC_STAT_CONTROL_EVENT_ARB_BANK_AA	5 +#define MC_STAT_CONTROL_EVENT_ARB_BANK_BB	6 +#define MC_STAT_CONTROL_EVENT_PAGE_MISS		7 +#define MC_STAT_CONTROL_EVENT_AUTO_PRECHARGE	8 + +#define EMC_GATHER_RST				(0 << 8) +#define EMC_GATHER_CLEAR			(1 << 8) +#define EMC_GATHER_DISABLE			(2 << 8) +#define EMC_GATHER_ENABLE			(3 << 8) + +#define MC_STAT_SAMPLE_TIME_USEC		16000 + +/* we store collected statistics as a fixed point values */ +#define MC_FX_FRAC_SCALE			100 + +static DEFINE_MUTEX(tegra20_mc_stat_lock); + +struct tegra20_mc_stat_gather { +	unsigned int pri_filter; +	unsigned int pri_event; +	unsigned int result; +	unsigned int client; +	unsigned int event; +	bool client_enb; +}; + +struct tegra20_mc_stat { +	struct tegra20_mc_stat_gather gather0; +	struct tegra20_mc_stat_gather gather1; +	unsigned int sample_time_usec; +	const struct tegra_mc *mc; +}; + +struct tegra20_mc_client_stat { +	unsigned int events; +	unsigned int arb_high_prio; +	unsigned int arb_timeout; +	unsigned int arb_bandwidth; +	unsigned int rd_wr_change; +	unsigned int successive; +	unsigned int page_miss; +	unsigned int auto_precharge; +	unsigned int arb_bank_aa; +	unsigned int arb_bank_bb; +}; +  static const struct tegra_mc_client tegra20_mc_clients[] = {  	{  		.id = 0x00, @@ -356,6 +432,261 @@ static const struct tegra_mc_icc_ops tegra20_mc_icc_ops = {  	.set = tegra20_mc_icc_set,  }; +static u32 tegra20_mc_stat_gather_control(const struct tegra20_mc_stat_gather *g) +{ +	u32 control; + +	control  = FIELD_PREP(MC_STAT_CONTROL_EVENT, g->event); +	control |= FIELD_PREP(MC_STAT_CONTROL_CLIENT_ID, g->client); +	control |= FIELD_PREP(MC_STAT_CONTROL_PRI_EVENT, g->pri_event); +	control |= FIELD_PREP(MC_STAT_CONTROL_FILTER_PRI, g->pri_filter); +	control |= FIELD_PREP(MC_STAT_CONTROL_FILTER_CLIENT_ENABLE, g->client_enb); + +	return control; +} + +static void tegra20_mc_stat_gather(struct tegra20_mc_stat *stat) +{ +	u32 clocks, count0, count1, control_0, control_1; +	const struct tegra_mc *mc = stat->mc; + +	control_0 = tegra20_mc_stat_gather_control(&stat->gather0); +	control_1 = tegra20_mc_stat_gather_control(&stat->gather1); + +	/* +	 * Reset statistic gathers state, select statistics collection mode +	 * and set clocks counter saturation limit to maximum. +	 */ +	mc_writel(mc, 0x00000000, MC_STAT_CONTROL); +	mc_writel(mc,  control_0, MC_STAT_EMC_CONTROL_0); +	mc_writel(mc,  control_1, MC_STAT_EMC_CONTROL_1); +	mc_writel(mc, 0xffffffff, MC_STAT_EMC_CLOCK_LIMIT); + +	mc_writel(mc, EMC_GATHER_ENABLE, MC_STAT_CONTROL); +	fsleep(stat->sample_time_usec); +	mc_writel(mc, EMC_GATHER_DISABLE, MC_STAT_CONTROL); + +	count0 = mc_readl(mc, MC_STAT_EMC_COUNT_0); +	count1 = mc_readl(mc, MC_STAT_EMC_COUNT_1); +	clocks = mc_readl(mc, MC_STAT_EMC_CLOCKS); +	clocks = max(clocks / 100 / MC_FX_FRAC_SCALE, 1u); + +	stat->gather0.result = DIV_ROUND_UP(count0, clocks); +	stat->gather1.result = DIV_ROUND_UP(count1, clocks); +} + +static void tegra20_mc_stat_events(const struct tegra_mc *mc, +				   const struct tegra_mc_client *client0, +				   const struct tegra_mc_client *client1, +				   unsigned int pri_filter, +				   unsigned int pri_event, +				   unsigned int event, +				   unsigned int *result0, +				   unsigned int *result1) +{ +	struct tegra20_mc_stat stat = {}; + +	stat.gather0.client = client0 ? client0->id : 0; +	stat.gather0.pri_filter = pri_filter; +	stat.gather0.client_enb = !!client0; +	stat.gather0.pri_event = pri_event; +	stat.gather0.event = event; + +	stat.gather1.client = client1 ? client1->id : 0; +	stat.gather1.pri_filter = pri_filter; +	stat.gather1.client_enb = !!client1; +	stat.gather1.pri_event = pri_event; +	stat.gather1.event = event; + +	stat.sample_time_usec = MC_STAT_SAMPLE_TIME_USEC; +	stat.mc = mc; + +	tegra20_mc_stat_gather(&stat); + +	*result0 = stat.gather0.result; +	*result1 = stat.gather1.result; +} + +static void tegra20_mc_collect_stats(const struct tegra_mc *mc, +				     struct tegra20_mc_client_stat *stats) +{ +	const struct tegra_mc_client *client0, *client1; +	unsigned int i; + +	/* collect memory controller utilization percent for each client */ +	for (i = 0; i < mc->soc->num_clients; i += 2) { +		client0 = &mc->soc->clients[i]; +		client1 = &mc->soc->clients[i + 1]; + +		if (i + 1 == mc->soc->num_clients) +			client1 = NULL; + +		tegra20_mc_stat_events(mc, client0, client1, +				       MC_STAT_CONTROL_FILTER_PRI_DISABLE, +				       MC_STAT_CONTROL_PRI_EVENT_HP, +				       MC_STAT_CONTROL_EVENT_QUALIFIED, +				       &stats[i + 0].events, +				       &stats[i + 1].events); +	} + +	/* collect more info from active clients */ +	for (i = 0; i < mc->soc->num_clients; i++) { +		unsigned int clienta, clientb = mc->soc->num_clients; + +		for (client0 = NULL; i < mc->soc->num_clients; i++) { +			if (stats[i].events) { +				client0 = &mc->soc->clients[i]; +				clienta = i++; +				break; +			} +		} + +		for (client1 = NULL; i < mc->soc->num_clients; i++) { +			if (stats[i].events) { +				client1 = &mc->soc->clients[i]; +				clientb = i; +				break; +			} +		} + +		if (!client0 && !client1) +			break; + +		tegra20_mc_stat_events(mc, client0, client1, +				       MC_STAT_CONTROL_FILTER_PRI_YES, +				       MC_STAT_CONTROL_PRI_EVENT_HP, +				       MC_STAT_CONTROL_EVENT_QUALIFIED, +				       &stats[clienta].arb_high_prio, +				       &stats[clientb].arb_high_prio); + +		tegra20_mc_stat_events(mc, client0, client1, +				       MC_STAT_CONTROL_FILTER_PRI_YES, +				       MC_STAT_CONTROL_PRI_EVENT_TM, +				       MC_STAT_CONTROL_EVENT_QUALIFIED, +				       &stats[clienta].arb_timeout, +				       &stats[clientb].arb_timeout); + +		tegra20_mc_stat_events(mc, client0, client1, +				       MC_STAT_CONTROL_FILTER_PRI_YES, +				       MC_STAT_CONTROL_PRI_EVENT_BW, +				       MC_STAT_CONTROL_EVENT_QUALIFIED, +				       &stats[clienta].arb_bandwidth, +				       &stats[clientb].arb_bandwidth); + +		tegra20_mc_stat_events(mc, client0, client1, +				       MC_STAT_CONTROL_FILTER_PRI_DISABLE, +				       MC_STAT_CONTROL_PRI_EVENT_HP, +				       MC_STAT_CONTROL_EVENT_RD_WR_CHANGE, +				       &stats[clienta].rd_wr_change, +				       &stats[clientb].rd_wr_change); + +		tegra20_mc_stat_events(mc, client0, client1, +				       MC_STAT_CONTROL_FILTER_PRI_DISABLE, +				       MC_STAT_CONTROL_PRI_EVENT_HP, +				       MC_STAT_CONTROL_EVENT_SUCCESSIVE, +				       &stats[clienta].successive, +				       &stats[clientb].successive); + +		tegra20_mc_stat_events(mc, client0, client1, +				       MC_STAT_CONTROL_FILTER_PRI_DISABLE, +				       MC_STAT_CONTROL_PRI_EVENT_HP, +				       MC_STAT_CONTROL_EVENT_PAGE_MISS, +				       &stats[clienta].page_miss, +				       &stats[clientb].page_miss); +	} +} + +static void tegra20_mc_printf_percents(struct seq_file *s, +				       const char *fmt, +				       unsigned int percents_fx) +{ +	char percents_str[8]; + +	snprintf(percents_str, ARRAY_SIZE(percents_str), "%3u.%02u%%", +		 percents_fx / MC_FX_FRAC_SCALE, percents_fx % MC_FX_FRAC_SCALE); + +	seq_printf(s, fmt, percents_str); +} + +static int tegra20_mc_stats_show(struct seq_file *s, void *unused) +{ +	const struct tegra_mc *mc = dev_get_drvdata(s->private); +	struct tegra20_mc_client_stat *stats; +	unsigned int i; + +	stats = kcalloc(mc->soc->num_clients + 1, sizeof(*stats), GFP_KERNEL); +	if (!stats) +		return -ENOMEM; + +	mutex_lock(&tegra20_mc_stat_lock); + +	tegra20_mc_collect_stats(mc, stats); + +	mutex_unlock(&tegra20_mc_stat_lock); + +	seq_puts(s, "Memory client   Events   Timeout   High priority   Bandwidth ARB   RW change   Successive   Page miss\n"); +	seq_puts(s, "-----------------------------------------------------------------------------------------------------\n"); + +	for (i = 0; i < mc->soc->num_clients; i++) { +		seq_printf(s, "%-14s  ", mc->soc->clients[i].name); + +		/* An event is generated when client performs R/W request. */ +		tegra20_mc_printf_percents(s,  "%-9s", stats[i].events); + +		/* +		 * An event is generated based on the timeout (TM) signal +		 * accompanying a request for arbitration. +		 */ +		tegra20_mc_printf_percents(s, "%-10s", stats[i].arb_timeout); + +		/* +		 * An event is generated based on the high-priority (HP) signal +		 * accompanying a request for arbitration. +		 */ +		tegra20_mc_printf_percents(s, "%-16s", stats[i].arb_high_prio); + +		/* +		 * An event is generated based on the bandwidth (BW) signal +		 * accompanying a request for arbitration. +		 */ +		tegra20_mc_printf_percents(s, "%-16s", stats[i].arb_bandwidth); + +		/* +		 * An event is generated when the memory controller switches +		 * between making a read request to making a write request. +		 */ +		tegra20_mc_printf_percents(s, "%-12s", stats[i].rd_wr_change); + +		/* +		 * An even generated when the chosen client has wins arbitration +		 * when it was also the winner at the previous request.  If a +		 * client makes N requests in a row that are honored, SUCCESSIVE +		 * will be counted (N-1) times.  Large values for this event +		 * imply that if we were patient enough, all of those requests +		 * could have been coalesced. +		 */ +		tegra20_mc_printf_percents(s, "%-13s", stats[i].successive); + +		/* +		 * An event is generated when the memory controller detects a +		 * page miss for the current request. +		 */ +		tegra20_mc_printf_percents(s, "%-12s\n", stats[i].page_miss); +	} + +	kfree(stats); + +	return 0; +} + +static int tegra20_mc_init(struct tegra_mc *mc) +{ +	debugfs_create_devm_seqfile(mc->dev, "stats", mc->debugfs.root, +				    tegra20_mc_stats_show); + +	return 0; +} +  const struct tegra_mc_soc tegra20_mc_soc = {  	.clients = tegra20_mc_clients,  	.num_clients = ARRAY_SIZE(tegra20_mc_clients), @@ -367,4 +698,5 @@ const struct tegra_mc_soc tegra20_mc_soc = {  	.resets = tegra20_mc_resets,  	.num_resets = ARRAY_SIZE(tegra20_mc_resets),  	.icc_ops = &tegra20_mc_icc_ops, +	.init = tegra20_mc_init,  }; diff --git a/drivers/memory/tegra/tegra30-emc.c b/drivers/memory/tegra/tegra30-emc.c index 6985da0ffb35..829f6d673c96 100644 --- a/drivers/memory/tegra/tegra30-emc.c +++ b/drivers/memory/tegra/tegra30-emc.c @@ -998,12 +998,12 @@ static int emc_load_timings_from_dt(struct tegra_emc *emc,  	if (err)  		return err; -	dev_info(emc->dev, -		 "got %u timings for RAM code %u (min %luMHz max %luMHz)\n", -		 emc->num_timings, -		 tegra_read_ram_code(), -		 emc->timings[0].rate / 1000000, -		 emc->timings[emc->num_timings - 1].rate / 1000000); +	dev_info_once(emc->dev, +		      "got %u timings for RAM code %u (min %luMHz max %luMHz)\n", +		      emc->num_timings, +		      tegra_read_ram_code(), +		      emc->timings[0].rate / 1000000, +		      emc->timings[emc->num_timings - 1].rate / 1000000);  	return 0;  } @@ -1015,7 +1015,7 @@ static struct device_node *emc_find_node_by_ram_code(struct device *dev)  	int err;  	if (of_get_child_count(dev->of_node) == 0) { -		dev_info(dev, "device-tree doesn't have memory timings\n"); +		dev_info_once(dev, "device-tree doesn't have memory timings\n");  		return NULL;  	} @@ -1503,8 +1503,8 @@ static int tegra_emc_opp_table_init(struct tegra_emc *emc)  		goto put_hw_table;  	} -	dev_info(emc->dev, "OPP HW ver. 0x%x, current clock rate %lu MHz\n", -		 hw_version, clk_get_rate(emc->clk) / 1000000); +	dev_info_once(emc->dev, "OPP HW ver. 0x%x, current clock rate %lu MHz\n", +		      hw_version, clk_get_rate(emc->clk) / 1000000);  	/* first dummy rate-set initializes voltage state */  	err = dev_pm_opp_set_rate(emc->dev, clk_get_rate(emc->clk)); | 
