From patchwork Fri Dec 14 20:14:44 2012 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: Lucas Stach X-Patchwork-Id: 206554 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by ozlabs.org (Postfix) with ESMTP id B6CF12C007D for ; Sat, 15 Dec 2012 07:14:58 +1100 (EST) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S932340Ab2LNUO4 (ORCPT ); Fri, 14 Dec 2012 15:14:56 -0500 Received: from ns.km20343-01.keymachine.de ([84.19.182.79]:55279 "EHLO km20343-01.keymachine.de" rhost-flags-OK-OK-OK-FAIL) by vger.kernel.org with ESMTP id S932223Ab2LNUOz (ORCPT ); Fri, 14 Dec 2012 15:14:55 -0500 Received: from localhost.localdomain (pD9F873DA.dip.t-dialin.net [217.248.115.218]) by km20343-01.keymachine.de (Postfix) with ESMTPA id 6F35C7D42E3; Fri, 14 Dec 2012 21:14:53 +0100 (CET) From: Lucas Stach To: linux-tegra@vger.kernel.org Cc: Thierry Reding , Stephen Warren , Mark Zhang Subject: =?UTF-8?q?=5BPATCH=201/3=5D=20ARM=3A=20tegra=3A=20add=20EMC=20clock=20scaling=20API?= Date: Fri, 14 Dec 2012 21:14:44 +0100 Message-Id: <1355516086-11116-2-git-send-email-dev@lynxeye.de> X-Mailer: git-send-email 1.7.11.7 In-Reply-To: <1355516086-11116-1-git-send-email-dev@lynxeye.de> References: <1355516086-11116-1-git-send-email-dev@lynxeye.de> MIME-Version: 1.0 Sender: linux-tegra-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-tegra@vger.kernel.org This allows memory clients to collaboratively control the EMC performance. Each client may set its performance demands. EMC driver then tries to find a DRAM performance level which is able to statisfy all those demands. Signed-off-by: Lucas Stach --- arch/arm/mach-tegra/tegra2_emc.c | 84 ++++++++++++++++++++++++++++++++-- include/memory/tegra_emc_performance.h | 79 ++++++++++++++++++++++++++++++++ 2 Dateien geändert, 160 Zeilen hinzugefügt(+), 3 Zeilen entfernt(-) create mode 100644 include/memory/tegra_emc_performance.h diff --git a/arch/arm/mach-tegra/tegra2_emc.c b/arch/arm/mach-tegra/tegra2_emc.c index 837c7b9..d2cf7a1 100644 --- a/arch/arm/mach-tegra/tegra2_emc.c +++ b/arch/arm/mach-tegra/tegra2_emc.c @@ -15,6 +15,7 @@ * */ +#include #include #include #include @@ -24,6 +25,8 @@ #include #include #include +#include +#include #include "tegra2_emc.h" #include "fuse.h" @@ -35,8 +38,16 @@ static bool emc_enable; #endif module_param(emc_enable, bool, 0644); +struct emc_superclient_constraint { + int bandwidth; + enum tegra_emc_perf_level perf_level; +}; + +static struct emc_superclient_constraint constraints[TEGRA_EMC_SC_NUM]; + static struct platform_device *emc_pdev; static void __iomem *emc_regbase; +static struct clk *emc_clk; static inline void emc_writel(u32 val, unsigned long addr) { @@ -176,6 +187,64 @@ int tegra_emc_set_rate(unsigned long rate) return 0; } +static void tegra_emc_scale_clock(void) +{ + u64 bandwidth_floor = 0; + enum tegra_emc_perf_level highest_level = TEGRA_EMC_PL_LOW; + unsigned long clock_rate; + int i; + + for (i = 0; i < ARRAY_SIZE(constraints); i++) { + bandwidth_floor += constraints[i].bandwidth; + if (constraints[i].perf_level > highest_level) + highest_level = constraints[i].perf_level; + } + + /* + * Add some headroom to the bandwidth, we use a conservative 60% + * DRAM bandwidth efficiency factor + */ + bandwidth_floor *= 5; + do_div(bandwidth_floor, 3); + + clock_rate = bandwidth_floor >> 2; /* 4byte/clock */ + + /* boost clock according to perf level */ + switch (highest_level) { + case TEGRA_EMC_PL_MID: + clock_rate += 300000000; + break; + case TEGRA_EMC_PL_HIGH: + clock_rate += 600000000; + break; + default: + break; + } + + /* cap clock_rate at 600MHz, enough to select highest rate table */ + clock_rate = min(600000000UL, clock_rate); + + clk_set_rate(emc_clk, clock_rate); +} + +void tegra_emc_request_bandwidth(enum tegra_emc_superclient client, + int bandwidth) +{ + constraints[client].bandwidth = bandwidth; + + tegra_emc_scale_clock(); +} +EXPORT_SYMBOL_GPL(tegra_emc_request_bandwidth); + +void tegra_emc_request_perf_level(enum tegra_emc_superclient client, + enum tegra_emc_perf_level level) +{ + constraints[client].perf_level = level; + + tegra_emc_scale_clock(); +} +EXPORT_SYMBOL_GPL(tegra_emc_request_perf_level); + #ifdef CONFIG_OF static struct device_node *tegra_emc_ramcode_devnode(struct device_node *np) { @@ -270,19 +339,17 @@ static struct tegra_emc_pdata *tegra_emc_dt_parse_pdata( static struct tegra_emc_pdata __devinit *tegra_emc_fill_pdata(struct platform_device *pdev) { - struct clk *c = clk_get_sys(NULL, "emc"); struct tegra_emc_pdata *pdata; unsigned long khz; int i; WARN_ON(pdev->dev.platform_data); - BUG_ON(IS_ERR_OR_NULL(c)); pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL); pdata->tables = devm_kzalloc(&pdev->dev, sizeof(*pdata->tables), GFP_KERNEL); - pdata->tables[0].rate = clk_get_rate(c) / 2 / 1000; + pdata->tables[0].rate = clk_get_rate(emc_clk) / 2 / 1000; for (i = 0; i < TEGRA_EMC_NUM_REGS; i++) pdata->tables[0].regs[i] = emc_readl(emc_reg_addr[i]); @@ -306,6 +373,9 @@ static int __devinit tegra_emc_probe(struct platform_device *pdev) return -ENODEV; } + emc_clk = clk_get_sys(NULL, "emc"); + BUG_ON(IS_ERR_OR_NULL(emc_clk)); + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); if (!res) { dev_err(&pdev->dev, "missing register base\n"); @@ -333,6 +403,13 @@ static int __devinit tegra_emc_probe(struct platform_device *pdev) return 0; } +static int tegra_emc_remove(struct platform_device *pdev) +{ + clk_put(emc_clk); + + return 0; +} + static struct of_device_id tegra_emc_of_match[] __devinitdata = { { .compatible = "nvidia,tegra20-emc", }, { }, @@ -345,6 +422,7 @@ static struct platform_driver tegra_emc_driver = { .of_match_table = tegra_emc_of_match, }, .probe = tegra_emc_probe, + .remove = tegra_emc_remove, }; static int __init tegra_emc_init(void) diff --git a/include/memory/tegra_emc_performance.h b/include/memory/tegra_emc_performance.h new file mode 100644 index 0000000..6c23099 --- /dev/null +++ b/include/memory/tegra_emc_performance.h @@ -0,0 +1,79 @@ +/* + * Copyright (C) 2012 Lucas Stach + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#ifndef __TEGRA_EMC_PERFORMANCE_H +#define __TEGRA_EMC_PERFORMANCE_H + +enum tegra_emc_superclient { + TEGRA_EMC_SC_AFI = 0, /* Tegra30 only */ + TEGRA_EMC_SC_AVP, + TEGRA_EMC_SC_DC, + TEGRA_EMC_SC_DCB, + TEGRA_EMC_SC_EPP, + TEGRA_EMC_SC_G2, + TEGRA_EMC_SC_HC, + TEGRA_EMC_SC_HDA, /* Tegra30 only */ + TEGRA_EMC_SC_ISP, + TEGRA_EMC_SC_MPCORE, + TEGRA_EMC_SC_MPCORELP, /* Tegra30 only */ + TEGRA_EMC_SC_MPE, + TEGRA_EMC_SC_NV, + TEGRA_EMC_SC_NV2, /* Tegra30 only */ + TEGRA_EMC_SC_PPCS, + TEGRA_EMC_SC_SATA, /* Tegra30 only */ + TEGRA_EMC_SC_VDE, + TEGRA_EMC_SC_VI, + + TEGRA_EMC_SC_NUM +}; + +enum tegra_emc_perf_level { + TEGRA_EMC_PL_LOW = 0, /* no boost */ + TEGRA_EMC_PL_MID, /* half frequency boost */ + TEGRA_EMC_PL_HIGH /* max frequency boost */ +}; + +/** + * tegra_emc_request_bandwidth - request bandwidth for isochronous clients + * @client: the EMC superclient which requests bandwidth + * @bandwidth: needed bandwidth in bytes/s + * + * Use this function to request bandwidth for isochronous clients, which + * need a specific bandwidth to operate properly. EMC will make sure to not + * drop below this limit while scaling DRAM frequency. + * Superclients have to add up the required bandwidth for their subunits + * themselves. + */ +void tegra_emc_request_bandwidth(enum tegra_emc_superclient client, + int bandwidth); + +/** + * tegra_emc_request_perf_level - request DRAM frequency boost + * @client: the EMC superclient which requests boost + * @level: the requested boost level + * + * Use this function to boost DRAM frequency for clients, which don't need a + * specific bandwidth but want to scale up DRAM bandwidth to implement + * race-to-idle. + */ +void tegra_emc_request_perf_level(enum tegra_emc_superclient client, + enum tegra_emc_perf_level level); + +/* FIXME: Tegra 30 has no EMC driver yet, stub out performance functions */ +#ifndef CONFIG_ARCH_TEGRA_2x_SOC +void tegra_emc_request_bandwidth(enum tegra_emc_superclient client, + int bandwidth) +{ +} +void tegra_emc_request_perf_level(enum tegra_emc_superclient client, + enum tegra_emc_perf_level level) +{ +} +#endif /* !CONFIG_ARCH_TEGRA_2x_SOC */ + +#endif /* __TEGRA_EMC_PERFORMANCE_H */