Commit 8f2685c9 authored by Arnd Bergmann's avatar Arnd Bergmann

Merge tag 'memory-controller-drv-tegra-5.11-2' of...

Merge tag 'memory-controller-drv-tegra-5.11-2' of git://git.kernel.org/pub/scm/linux/kernel/git/krzk/linux-mem-ctrl into arm/drivers

Memory controller drivers for v5.11 - Tegra SoC

There is a bigger work from Dmitry Osipenko around Tegra SoC memory
controller drivers, mostly towards adding interconnect support and
integration with devfreq.  This work touches all Tegra memory controller
drivers and also few other SoC-related parts.  It's not yet finished but
the intermediate stage seems ready to merge.

Beside that Tegra 210 memory controller got few fixes and received new
swgroups (work of Nicolin Chen).

* tag 'memory-controller-drv-tegra-5.11-2' of git://git.kernel.org/pub/scm/linux/kernel/git/krzk/linux-mem-ctrl: (38 commits)
  memory: tegra30-emc: Remove unnecessary of_node_put in tegra_emc_probe
  memory: tegra: Complete tegra210_swgroups
  memory: tegra30-emc: Continue probing if timings are missing in device-tree
  memory: tegra30-emc: Make driver modular
  memory: tegra30: Add FIFO sizes to memory clients
  memory: tegra20-emc: Add devfreq support
  memory: tegra20-emc: Remove IRQ number from error message
  memory: tegra20-emc: Factor out clk initialization
  memory: tegra20-emc: Use dev_pm_opp_set_clkname()
  memory: tegra: Correct stub of devm_tegra_memory_controller_get()
  memory: tegra20: Support interconnect framework
  memory: tegra20-emc: Continue probing if timings are missing in device-tree
  memory: tegra20-emc: Make driver modular
  memory: tegra-mc: Add interconnect framework
  memory: tegra: Add missing latency allowness entry for Page Table Cache
  memory: tegra: Remove superfluous error messages around platform_get_irq()
  memory: tegra: Use devm_platform_ioremap_resource()
  memory: tegra: Add and use devm_tegra_memory_controller_get()
  dt-bindings: host1x: Document new interconnect properties
  dt-bindings: tegra30-actmon: Document OPP and interconnect properties
  ...

Link: https://lore.kernel.org/r/20201126191241.23302-1-krzk@kernel.orgSigned-off-by: default avatarArnd Bergmann <arnd@arndb.de>
parents 0a3251a1 0e1bcf2c
......@@ -18,8 +18,30 @@ clock-names. See ../../clock/clock-bindings.txt for details.
../../reset/reset.txt for details.
- reset-names: Must include the following entries:
- actmon
- operating-points-v2: See ../bindings/opp/opp.txt for details.
- interconnects: Should contain entries for memory clients sitting on
MC->EMC memory interconnect path.
- interconnect-names: Should include name of the interconnect path for each
interconnect entry. Consult TRM documentation for
information about available memory clients, see MEMORY
CONTROLLER section.
For each opp entry in 'operating-points-v2' table:
- opp-supported-hw: bitfield indicating SoC speedo ID mask
- opp-peak-kBps: peak bandwidth of the memory channel
Example:
dfs_opp_table: opp-table {
compatible = "operating-points-v2";
opp@12750000 {
opp-hz = /bits/ 64 <12750000>;
opp-supported-hw = <0x000F>;
opp-peak-kBps = <51000>;
};
...
};
actmon@6000c800 {
compatible = "nvidia,tegra124-actmon";
reg = <0x0 0x6000c800 0x0 0x400>;
......@@ -29,4 +51,7 @@ Example:
clock-names = "actmon", "emc";
resets = <&tegra_car 119>;
reset-names = "actmon";
operating-points-v2 = <&dfs_opp_table>;
interconnects = <&mc TEGRA124_MC_MPCORER &emc>;
interconnect-names = "cpu";
};
......@@ -20,6 +20,10 @@ Required properties:
- reset-names: Must include the following entries:
- host1x
Each host1x client module having to perform DMA through the Memory Controller
should have the interconnect endpoints set to the Memory Client and External
Memory respectively.
The host1x top-level node defines a number of children, each representing one
of the following host1x client modules:
......@@ -36,6 +40,12 @@ of the following host1x client modules:
- reset-names: Must include the following entries:
- mpe
Optional properties:
- interconnects: Must contain entry for the MPE memory clients.
- interconnect-names: Must include name of the interconnect path for each
interconnect entry. Consult TRM documentation for information about
available memory clients, see MEMORY CONTROLLER section.
- vi: video input
Required properties:
......@@ -113,6 +123,12 @@ of the following host1x client modules:
Required properties:
- remote-endpoint: phandle to vi port 'endpoint' node.
Optional properties:
- interconnects: Must contain entry for the VI memory clients.
- interconnect-names: Must include name of the interconnect path for each
interconnect entry. Consult TRM documentation for information about
available memory clients, see MEMORY CONTROLLER section.
- epp: encoder pre-processor
Required properties:
......@@ -126,6 +142,12 @@ of the following host1x client modules:
- reset-names: Must include the following entries:
- epp
Optional properties:
- interconnects: Must contain entry for the EPP memory clients.
- interconnect-names: Must include name of the interconnect path for each
interconnect entry. Consult TRM documentation for information about
available memory clients, see MEMORY CONTROLLER section.
- isp: image signal processor
Required properties:
......@@ -139,6 +161,12 @@ of the following host1x client modules:
- reset-names: Must include the following entries:
- isp
Optional properties:
- interconnects: Must contain entry for the ISP memory clients.
- interconnect-names: Must include name of the interconnect path for each
interconnect entry. Consult TRM documentation for information about
available memory clients, see MEMORY CONTROLLER section.
- gr2d: 2D graphics engine
Required properties:
......@@ -152,6 +180,12 @@ of the following host1x client modules:
- reset-names: Must include the following entries:
- 2d
Optional properties:
- interconnects: Must contain entry for the GR2D memory clients.
- interconnect-names: Must include name of the interconnect path for each
interconnect entry. Consult TRM documentation for information about
available memory clients, see MEMORY CONTROLLER section.
- gr3d: 3D graphics engine
Required properties:
......@@ -170,6 +204,12 @@ of the following host1x client modules:
- 3d
- 3d2 (Only required on SoCs with two 3D clocks)
Optional properties:
- interconnects: Must contain entry for the GR3D memory clients.
- interconnect-names: Must include name of the interconnect path for each
interconnect entry. Consult TRM documentation for information about
available memory clients, see MEMORY CONTROLLER section.
- dc: display controller
Required properties:
......@@ -197,6 +237,10 @@ of the following host1x client modules:
- nvidia,hpd-gpio: specifies a GPIO used for hotplug detection
- nvidia,edid: supplies a binary EDID blob
- nvidia,panel: phandle of a display panel
- interconnects: Must contain entry for the DC memory clients.
- interconnect-names: Must include name of the interconnect path for each
interconnect entry. Consult TRM documentation for information about
available memory clients, see MEMORY CONTROLLER section.
- hdmi: High Definition Multimedia Interface
......@@ -345,6 +389,12 @@ of the following host1x client modules:
- reset-names: Must include the following entries:
- vic
Optional properties:
- interconnects: Must contain entry for the VIC memory clients.
- interconnect-names: Must include name of the interconnect path for each
interconnect entry. Consult TRM documentation for information about
available memory clients, see MEMORY CONTROLLER section.
Example:
/ {
......@@ -498,6 +548,15 @@ Example:
resets = <&tegra_car 27>;
reset-names = "dc";
interconnects = <&mc TEGRA20_MC_DISPLAY0A &emc>,
<&mc TEGRA20_MC_DISPLAY0B &emc>,
<&mc TEGRA20_MC_DISPLAY0C &emc>,
<&mc TEGRA20_MC_DISPLAYHC &emc>;
interconnect-names = "wina",
"winb",
"winc",
"cursor";
rgb {
status = "disabled";
};
......@@ -513,6 +572,15 @@ Example:
resets = <&tegra_car 26>;
reset-names = "dc";
interconnects = <&mc TEGRA20_MC_DISPLAY0AB &emc>,
<&mc TEGRA20_MC_DISPLAY0BB &emc>,
<&mc TEGRA20_MC_DISPLAY0CB &emc>,
<&mc TEGRA20_MC_DISPLAYHCB &emc>;
interconnect-names = "wina",
"winb",
"winc",
"cursor";
rgb {
status = "disabled";
};
......
......@@ -29,11 +29,23 @@ properties:
items:
- const: emc
"#interconnect-cells":
const: 0
nvidia,memory-controller:
$ref: /schemas/types.yaml#/definitions/phandle
description:
phandle of the memory controller node
core-supply:
description:
Phandle of voltage regulator of the SoC "core" power domain.
operating-points-v2:
description:
Should contain freqs and voltages and opp-supported-hw property, which
is a bitfield indicating SoC speedo ID mask.
patternProperties:
"^emc-timings-[0-9]+$":
type: object
......@@ -327,6 +339,8 @@ required:
- clocks
- clock-names
- nvidia,memory-controller
- "#interconnect-cells"
- operating-points-v2
additionalProperties: false
......@@ -345,6 +359,7 @@ examples:
#iommu-cells = <1>;
#reset-cells = <1>;
#interconnect-cells = <1>;
};
external-memory-controller@7001b000 {
......@@ -354,6 +369,10 @@ examples:
clock-names = "emc";
nvidia,memory-controller = <&mc>;
operating-points-v2 = <&dvfs_opp_table>;
core-supply = <&vdd_core>;
#interconnect-cells = <0>;
emc-timings-0 {
nvidia,ram-code = <3>;
......
......@@ -40,6 +40,9 @@ properties:
"#iommu-cells":
const: 1
"#interconnect-cells":
const: 1
patternProperties:
"^emc-timings-[0-9]+$":
type: object
......@@ -104,6 +107,7 @@ required:
- clock-names
- "#reset-cells"
- "#iommu-cells"
- "#interconnect-cells"
additionalProperties: false
......@@ -119,6 +123,7 @@ examples:
#iommu-cells = <1>;
#reset-cells = <1>;
#interconnect-cells = <1>;
emc-timings-3 {
nvidia,ram-code = <3>;
......
......@@ -12,18 +12,38 @@ Properties:
irrespective of ram-code configuration.
- interrupts : Should contain EMC General interrupt.
- clocks : Should contain EMC clock.
- nvidia,memory-controller : Phandle of the Memory Controller node.
- #interconnect-cells : Should be 0.
- operating-points-v2: See ../bindings/opp/opp.txt for details.
Optional properties:
- core-supply: Phandle of voltage regulator of the SoC "core" power domain.
Child device nodes describe the memory settings for different configurations and clock rates.
Example:
opp_table: opp-table {
compatible = "operating-points-v2";
opp@36000000 {
opp-microvolt = <950000 950000 1300000>;
opp-hz = /bits/ 64 <36000000>;
};
...
};
memory-controller@7000f400 {
#address-cells = < 1 >;
#size-cells = < 0 >;
#interconnect-cells = <0>;
compatible = "nvidia,tegra20-emc";
reg = <0x7000f4000 0x200>;
reg = <0x7000f400 0x400>;
interrupts = <0 78 0x04>;
clocks = <&tegra_car TEGRA20_CLK_EMC>;
nvidia,memory-controller = <&mc>;
core-supply = <&core_vdd_reg>;
operating-points-v2 = <&opp_table>;
}
......
......@@ -16,6 +16,8 @@ Required properties:
IOMMU specifier needed to encode an address. GART supports only a single
address space that is shared by all devices, therefore no additional
information needed for the address encoding.
- #interconnect-cells : Should be 1. This cell represents memory client.
The assignments may be found in header file <dt-bindings/memory/tegra20-mc.h>.
Example:
mc: memory-controller@7000f000 {
......@@ -27,6 +29,7 @@ Example:
interrupts = <GIC_SPI 77 0x04>;
#reset-cells = <1>;
#iommu-cells = <0>;
#interconnect-cells = <1>;
};
video-codec@6001a000 {
......
......@@ -31,11 +31,23 @@ properties:
interrupts:
maxItems: 1
"#interconnect-cells":
const: 0
nvidia,memory-controller:
$ref: /schemas/types.yaml#/definitions/phandle
description:
Phandle of the Memory Controller node.
core-supply:
description:
Phandle of voltage regulator of the SoC "core" power domain.
operating-points-v2:
description:
Should contain freqs and voltages and opp-supported-hw property, which
is a bitfield indicating SoC speedo ID mask.
patternProperties:
"^emc-timings-[0-9]+$":
type: object
......@@ -214,6 +226,8 @@ required:
- interrupts
- clocks
- nvidia,memory-controller
- "#interconnect-cells"
- operating-points-v2
additionalProperties: false
......@@ -226,6 +240,10 @@ examples:
clocks = <&tegra_car 57>;
nvidia,memory-controller = <&mc>;
operating-points-v2 = <&dvfs_opp_table>;
core-supply = <&vdd_core>;
#interconnect-cells = <0>;
emc-timings-1 {
nvidia,ram-code = <1>;
......
......@@ -57,6 +57,9 @@ properties:
"#iommu-cells":
const: 1
"#interconnect-cells":
const: 1
patternProperties:
"^emc-timings-[0-9]+$":
type: object
......@@ -120,6 +123,7 @@ required:
- clock-names
- "#reset-cells"
- "#iommu-cells"
- "#interconnect-cells"
additionalProperties: false
......@@ -135,6 +139,7 @@ examples:
#iommu-cells = <1>;
#reset-cells = <1>;
#interconnect-cells = <1>;
emc-timings-1 {
nvidia,ram-code = <1>;
......
......@@ -13,6 +13,7 @@
#include <linux/clk-provider.h>
#include <linux/clk/tegra.h>
#include <linux/err.h>
#include <linux/export.h>
#include <linux/io.h>
#include <linux/kernel.h>
#include <linux/slab.h>
......@@ -235,6 +236,7 @@ void tegra20_clk_set_emc_round_callback(tegra20_clk_emc_round_cb *round_cb,
emc->cb_arg = cb_arg;
}
}
EXPORT_SYMBOL_GPL(tegra20_clk_set_emc_round_callback);
bool tegra20_clk_emc_driver_available(struct clk_hw *emc_hw)
{
......@@ -291,3 +293,4 @@ int tegra20_clk_prepare_emc_mc_same_freq(struct clk *emc_clk, bool same)
return 0;
}
EXPORT_SYMBOL_GPL(tegra20_clk_prepare_emc_mc_same_freq);
......@@ -3,14 +3,17 @@ config TEGRA_MC
bool "NVIDIA Tegra Memory Controller support"
default y
depends on ARCH_TEGRA
select INTERCONNECT
help
This driver supports the Memory Controller (MC) hardware found on
NVIDIA Tegra SoCs.
config TEGRA20_EMC
bool "NVIDIA Tegra20 External Memory Controller driver"
tristate "NVIDIA Tegra20 External Memory Controller driver"
default y
depends on ARCH_TEGRA_2x_SOC
depends on TEGRA_MC && ARCH_TEGRA_2x_SOC
select DEVFREQ_GOV_SIMPLE_ONDEMAND
select PM_DEVFREQ
help
This driver is for the External Memory Controller (EMC) found on
Tegra20 chips. The EMC controls the external DRAM on the board.
......@@ -18,7 +21,7 @@ config TEGRA20_EMC
external memory.
config TEGRA30_EMC
bool "NVIDIA Tegra30 External Memory Controller driver"
tristate "NVIDIA Tegra30 External Memory Controller driver"
default y
depends on TEGRA_MC && ARCH_TEGRA_3x_SOC
help
......
......@@ -6,6 +6,7 @@
#include <linux/clk.h>
#include <linux/delay.h>
#include <linux/dma-mapping.h>
#include <linux/export.h>
#include <linux/interrupt.h>
#include <linux/kernel.h>
#include <linux/module.h>
......@@ -42,6 +43,54 @@ static const struct of_device_id tegra_mc_of_match[] = {
};
MODULE_DEVICE_TABLE(of, tegra_mc_of_match);
static void tegra_mc_devm_action_put_device(void *data)
{
struct tegra_mc *mc = data;
put_device(mc->dev);
}
/**
* devm_tegra_memory_controller_get() - get Tegra Memory Controller handle
* @dev: device pointer for the consumer device
*
* This function will search for the Memory Controller node in a device-tree
* and retrieve the Memory Controller handle.
*
* Return: ERR_PTR() on error or a valid pointer to a struct tegra_mc.
*/
struct tegra_mc *devm_tegra_memory_controller_get(struct device *dev)
{
struct platform_device *pdev;
struct device_node *np;
struct tegra_mc *mc;
int err;
np = of_parse_phandle(dev->of_node, "nvidia,memory-controller", 0);
if (!np)
return ERR_PTR(-ENOENT);
pdev = of_find_device_by_node(np);
of_node_put(np);
if (!pdev)
return ERR_PTR(-ENODEV);
mc = platform_get_drvdata(pdev);
if (!mc) {
put_device(&pdev->dev);
return ERR_PTR(-EPROBE_DEFER);
}
err = devm_add_action(dev, tegra_mc_devm_action_put_device, mc);
if (err) {
put_device(mc->dev);
return ERR_PTR(err);
}
return mc;
}
EXPORT_SYMBOL_GPL(devm_tegra_memory_controller_get);
static int tegra_mc_block_dma_common(struct tegra_mc *mc,
const struct tegra_mc_reset *rst)
{
......@@ -298,6 +347,7 @@ int tegra_mc_write_emem_configuration(struct tegra_mc *mc, unsigned long rate)
return 0;
}
EXPORT_SYMBOL_GPL(tegra_mc_write_emem_configuration);
unsigned int tegra_mc_get_emem_device_count(struct tegra_mc *mc)
{
......@@ -309,6 +359,7 @@ unsigned int tegra_mc_get_emem_device_count(struct tegra_mc *mc)
return dram_count;
}
EXPORT_SYMBOL_GPL(tegra_mc_get_emem_device_count);
static int load_one_timing(struct tegra_mc *mc,
struct tegra_mc_timing *timing,
......@@ -591,6 +642,101 @@ static __maybe_unused irqreturn_t tegra20_mc_irq(int irq, void *data)
return IRQ_HANDLED;
}
/*
* Memory Controller (MC) has few Memory Clients that are issuing memory
* bandwidth allocation requests to the MC interconnect provider. The MC
* provider aggregates the requests and then sends the aggregated request
* up to the External Memory Controller (EMC) interconnect provider which
* re-configures hardware interface to External Memory (EMEM) in accordance
* to the required bandwidth. Each MC interconnect node represents an
* individual Memory Client.
*
* Memory interconnect topology:
*
* +----+
* +--------+ | |
* | TEXSRD +--->+ |
* +--------+ | |
* | | +-----+ +------+
* ... | MC +--->+ EMC +--->+ EMEM |
* | | +-----+ +------+
* +--------+ | |
* | DISP.. +--->+ |
* +--------+ | |
* +----+
*/
static int tegra_mc_interconnect_setup(struct tegra_mc *mc)
{
struct icc_node *node;
unsigned int i;
int err;
/* older device-trees don't have interconnect properties */
if (!device_property_present(mc->dev, "#interconnect-cells") ||
!mc->soc->icc_ops)
return 0;
mc->provider.dev = mc->dev;
mc->provider.data = &mc->provider;
mc->provider.set = mc->soc->icc_ops->set;
mc->provider.aggregate = mc->soc->icc_ops->aggregate;
mc->provider.xlate_extended = mc->soc->icc_ops->xlate_extended;
err = icc_provider_add(&mc->provider);
if (err)
return err;
/* create Memory Controller node */
node = icc_node_create(TEGRA_ICC_MC);
if (IS_ERR(node)) {
err = PTR_ERR(node);
goto del_provider;
}
node->name = "Memory Controller";
icc_node_add(node, &mc->provider);
/* link Memory Controller to External Memory Controller */
err = icc_link_create(node, TEGRA_ICC_EMC);
if (err)
goto remove_nodes;
for (i = 0; i < mc->soc->num_clients; i++) {
/* create MC client node */
node = icc_node_create(mc->soc->clients[i].id);
if (IS_ERR(node)) {
err = PTR_ERR(node);
goto remove_nodes;
}
node->name = mc->soc->clients[i].name;
icc_node_add(node, &mc->provider);
/* link Memory Client to Memory Controller */
err = icc_link_create(node, TEGRA_ICC_MC);
if (err)
goto remove_nodes;
}
/*
* MC driver is registered too early, so early that generic driver
* syncing doesn't work for the MC. But it doesn't really matter
* since syncing works for the EMC drivers, hence we can sync the
* MC driver by ourselves and then EMC will complete syncing of
* the whole ICC state.
*/
icc_sync_state(mc->dev);
return 0;
remove_nodes:
icc_nodes_remove(&mc->provider);
del_provider:
icc_provider_del(&mc->provider);
return err;
}
static int tegra_mc_probe(struct platform_device *pdev)
{
struct resource *res;
......@@ -659,10 +805,8 @@ static int tegra_mc_probe(struct platform_device *pdev)
}
mc->irq = platform_get_irq(pdev, 0);
if (mc->irq < 0) {
dev_err(&pdev->dev, "interrupt not specified\n");
if (mc->irq < 0)
return mc->irq;
}
WARN(!mc->soc->client_id_mask, "missing client ID mask for this SoC\n");
......@@ -681,6 +825,11 @@ static int tegra_mc_probe(struct platform_device *pdev)
dev_err(&pdev->dev, "failed to register reset controller: %d\n",
err);
err = tegra_mc_interconnect_setup(mc);
if (err < 0)
dev_err(&pdev->dev, "failed to initialize interconnect: %d\n",
err);
if (IS_ENABLED(CONFIG_TEGRA_IOMMU_SMMU) && mc->soc->smmu) {
mc->smmu = tegra_smmu_probe(&pdev->dev, mc->soc->smmu, mc);
if (IS_ERR(mc->smmu)) {
......
......@@ -78,6 +78,20 @@
#define MC_TIMING_UPDATE BIT(0)
static inline u32 tegra_mc_scale_percents(u64 val, unsigned int percents)
{
val = val * percents;
do_div(val, 100);
return min_t(u64, val, U32_MAX);
}
static inline struct tegra_mc *
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)
{
return readl_relaxed(mc->regs + offset);
......@@ -115,4 +129,12 @@ extern const struct tegra_mc_soc tegra132_mc_soc;
extern const struct tegra_mc_soc tegra210_mc_soc;
#endif
/*
* These IDs are for internal use of Tegra ICC drivers. The ID numbers are
* chosen such that they don't conflict with the device-tree ICC node IDs.
*/
#define TEGRA_ICC_MC 1000
#define TEGRA_ICC_EMC 1001
#define TEGRA_ICC_EMEM 1002
#endif /* MEMORY_TEGRA_MC_H */
......@@ -15,6 +15,12 @@ static const struct tegra_mc_client tegra114_mc_clients[] = {
.id = 0x00,
.name = "ptcr",
.swgroup = TEGRA_SWGROUP_PTC,
.la = {
.reg = 0x34c,
.shift = 0,
.mask = 0xff,
.def = 0x0,
},
}, {
.id = 0x01,
.name = "display0a",
......
......@@ -1177,10 +1177,8 @@ static void emc_debugfs_init(struct device *dev, struct tegra_emc *emc)
static int tegra_emc_probe(struct platform_device *pdev)
{
struct platform_device *mc;
struct device_node *np;
struct tegra_emc *emc;
struct resource *res;
u32 ram_code;
int err;
......@@ -1190,25 +1188,13 @@ static int tegra_emc_probe(struct platform_device *pdev)
emc->dev = &pdev->dev;
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
emc->regs = devm_ioremap_resource(&pdev->dev, res);
emc->regs = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(emc->regs))
return PTR_ERR(emc->regs);
np = of_parse_phandle(pdev->dev.of_node, "nvidia,memory-controller", 0);
if (!np) {
dev_err(&pdev->dev, "could not get memory controller\n");
return -ENOENT;
}
mc = of_find_device_by_node(np);
of_node_put(np);
if (!mc)
return -ENOENT;
emc->mc = platform_get_drvdata(mc);
if (!emc->mc)
return -EPROBE_DEFER;
emc->mc = devm_tegra_memory_controller_get(&pdev->dev);
if (IS_ERR(emc->mc))
return PTR_ERR(emc->mc);
ram_code = tegra_read_ram_code();
......
......@@ -15,6 +15,12 @@ static const struct tegra_mc_client tegra124_mc_clients[] = {
.id = 0x00,
.name = "ptcr",
.swgroup = TEGRA_SWGROUP_PTC,
.la = {
.reg = 0x34c,
.shift = 0,
.mask = 0xff,
.def = 0x0,
},
}, {
.id = 0x01,
.name = "display0a",
......
This diff is collapsed.
......@@ -3,6 +3,10 @@
* Copyright (C) 2012 NVIDIA CORPORATION. All rights reserved.
*/
#include <linux/of_device.h>
#include <linux/slab.h>
#include <linux/string.h>
#include <dt-bindings/memory/tegra20-mc.h>
#include "mc.h"
......@@ -280,6 +284,78 @@ static const struct tegra_mc_reset_ops tegra20_mc_reset_ops = {
.reset_status = tegra20_mc_reset_status,
};
static int tegra20_mc_icc_set(struct icc_node *src, struct icc_node *dst)
{
/*
* It should be possible to tune arbitration knobs here, but the
* default values are known to work well on all devices. Hence
* nothing to do here so far.
*/
return 0;
}
static int tegra20_mc_icc_aggreate(struct icc_node *node, u32 tag, u32 avg_bw,
u32 peak_bw, u32 *agg_avg, u32 *agg_peak)
{
/*
* ISO clients need to reserve extra bandwidth up-front because
* there could be high bandwidth pressure during initial filling
* of the client's FIFO buffers. Secondly, we need to take into
* account impurities of the memory subsystem.
*/
if (tag & TEGRA_MC_ICC_TAG_ISO)
peak_bw = tegra_mc_scale_percents(peak_bw, 300);
*agg_avg += avg_bw;
*agg_peak = max(*agg_peak, peak_bw);
return 0;
}
static struct icc_node_data *
tegra20_mc_of_icc_xlate_extended(struct of_phandle_args *spec, void *data)
{
struct tegra_mc *mc = icc_provider_to_tegra_mc(data);
unsigned int i, idx = spec->args[0];
struct icc_node_data *ndata;
struct icc_node *node;
list_for_each_entry(node, &mc->provider.nodes, node_list) {
if (node->id != idx)
continue;
ndata = kzalloc(sizeof(*ndata), GFP_KERNEL);
if (!ndata)
return ERR_PTR(-ENOMEM);
ndata->node = node;
/* these clients are isochronous by default */
if (strstarts(node->name, "display") ||
strstarts(node->name, "vi"))
ndata->tag = TEGRA_MC_ICC_TAG_ISO;
else
ndata->tag = TEGRA_MC_ICC_TAG_DEFAULT;
return ndata;
}
for (i = 0; i < mc->soc->num_clients; i++) {
if (mc->soc->clients[i].id == idx)
return ERR_PTR(-EPROBE_DEFER);
}
dev_err(mc->dev, "invalid ICC client ID %u\n", idx);
return ERR_PTR(-EINVAL);
}
static const struct tegra_mc_icc_ops tegra20_mc_icc_ops = {
.xlate_extended = tegra20_mc_of_icc_xlate_extended,
.aggregate = tegra20_mc_icc_aggreate,
.set = tegra20_mc_icc_set,
};
const struct tegra_mc_soc tegra20_mc_soc = {
.clients = tegra20_mc_clients,
.num_clients = ARRAY_SIZE(tegra20_mc_clients),
......@@ -290,4 +366,5 @@ const struct tegra_mc_soc tegra20_mc_soc = {
.reset_ops = &tegra20_mc_reset_ops,
.resets = tegra20_mc_resets,
.num_resets = ARRAY_SIZE(tegra20_mc_resets),
.icc_ops = &tegra20_mc_icc_ops,
};
......@@ -1828,7 +1828,6 @@ static int tegra210_emc_probe(struct platform_device *pdev)
{
struct thermal_cooling_device *cd;
unsigned long current_rate;
struct platform_device *mc;
struct tegra210_emc *emc;
struct device_node *np;
unsigned int i;
......@@ -1846,35 +1845,19 @@ static int tegra210_emc_probe(struct platform_device *pdev)
spin_lock_init(&emc->lock);
emc->dev = &pdev->dev;
np = of_parse_phandle(pdev->dev.of_node, "nvidia,memory-controller", 0);
if (!np) {
dev_err(&pdev->dev, "could not get memory controller\n");
return -ENOENT;
}
mc = of_find_device_by_node(np);
of_node_put(np);
if (!mc)
return -ENOENT;
emc->mc = platform_get_drvdata(mc);
if (!emc->mc) {
put_device(&mc->dev);
return -EPROBE_DEFER;
}
emc->mc = devm_tegra_memory_controller_get(&pdev->dev);
if (IS_ERR(emc->mc))
return PTR_ERR(emc->mc);
emc->regs = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(emc->regs)) {
err = PTR_ERR(emc->regs);
goto put_mc;
}
if (IS_ERR(emc->regs))
return PTR_ERR(emc->regs);
for (i = 0; i < 2; i++) {
emc->channel[i] = devm_platform_ioremap_resource(pdev, 1 + i);
if (IS_ERR(emc->channel[i])) {
err = PTR_ERR(emc->channel[i]);
goto put_mc;
}
if (IS_ERR(emc->channel[i]))
return PTR_ERR(emc->channel[i]);
}
tegra210_emc_detect(emc);
......@@ -1884,7 +1867,7 @@ static int tegra210_emc_probe(struct platform_device *pdev)
err = of_reserved_mem_device_init_by_name(emc->dev, np, "nominal");
if (err < 0) {
dev_err(emc->dev, "failed to get nominal EMC table: %d\n", err);
goto put_mc;
return err;
}
err = of_reserved_mem_device_init_by_name(emc->dev, np, "derated");
......@@ -2015,8 +1998,7 @@ static int tegra210_emc_probe(struct platform_device *pdev)
tegra210_clk_emc_detach(emc->clk);
release:
of_reserved_mem_device_release(emc->dev);
put_mc:
put_device(emc->mc->dev);
return err;
}
......@@ -2027,7 +2009,6 @@ static int tegra210_emc_remove(struct platform_device *pdev)
debugfs_remove_recursive(emc->debugfs.root);
tegra210_clk_emc_detach(emc->clk);
of_reserved_mem_device_release(emc->dev);
put_device(emc->mc->dev);
return 0;
}
......
......@@ -24,7 +24,7 @@ static const struct tegra_mc_client tegra210_mc_clients[] = {
.reg = 0x2e8,
.shift = 0,
.mask = 0xff,
.def = 0xc2,
.def = 0x1e,
},
}, {
.id = 0x02,
......@@ -38,7 +38,7 @@ static const struct tegra_mc_client tegra210_mc_clients[] = {
.reg = 0x2f4,
.shift = 0,
.mask = 0xff,
.def = 0xc6,
.def = 0x1e,
},
}, {
.id = 0x03,
......@@ -52,7 +52,7 @@ static const struct tegra_mc_client tegra210_mc_clients[] = {
.reg = 0x2e8,
.shift = 16,
.mask = 0xff,
.def = 0x50,
.def = 0x1e,
},
}, {
.id = 0x04,
......@@ -66,7 +66,7 @@ static const struct tegra_mc_client tegra210_mc_clients[] = {
.reg = 0x2f4,
.shift = 16,
.mask = 0xff,
.def = 0x50,
.def = 0x1e,
},
}, {
.id = 0x05,
......@@ -80,7 +80,7 @@ static const struct tegra_mc_client tegra210_mc_clients[] = {
.reg = 0x2ec,
.shift = 0,
.mask = 0xff,
.def = 0x50,
.def = 0x1e,
},
}, {
.id = 0x06,
......@@ -94,7 +94,7 @@ static const struct tegra_mc_client tegra210_mc_clients[] = {
.reg = 0x2f8,
.shift = 0,
.mask = 0xff,
.def = 0x50,
.def = 0x1e,
},
}, {
.id = 0x0e,
......@@ -108,7 +108,7 @@ static const struct tegra_mc_client tegra210_mc_clients[] = {
.reg = 0x2e0,
.shift = 0,
.mask = 0xff,
.def = 0x13,
.def = 0x2e,
},
}, {
.id = 0x0f,
......@@ -136,7 +136,7 @@ static const struct tegra_mc_client tegra210_mc_clients[] = {
.reg = 0x2f0,
.shift = 0,
.mask = 0xff,
.def = 0x50,
.def = 0x1e,
},
}, {
.id = 0x11,
......@@ -150,7 +150,7 @@ static const struct tegra_mc_client tegra210_mc_clients[] = {
.reg = 0x2fc,
.shift = 0,
.mask = 0xff,
.def = 0x50,
.def = 0x1e,
},
}, {
.id = 0x15,
......@@ -380,7 +380,7 @@ static const struct tegra_mc_client tegra210_mc_clients[] = {
.reg = 0x350,
.shift = 16,
.mask = 0xff,
.def = 0x65,
.def = 0x80,
},
}, {
.id = 0x44,
......@@ -620,7 +620,7 @@ static const struct tegra_mc_client tegra210_mc_clients[] = {
.reg = 0x2f0,
.shift = 16,
.mask = 0xff,
.def = 0x50,
.def = 0x1e,
},
}, {
.id = 0x60,
......@@ -648,7 +648,7 @@ static const struct tegra_mc_client tegra210_mc_clients[] = {
.reg = 0x3bc,
.shift = 0,
.mask = 0xff,
.def = 0x49,
.def = 0x5a,
},
}, {
.id = 0x62,
......@@ -676,7 +676,7 @@ static const struct tegra_mc_client tegra210_mc_clients[] = {
.reg = 0x3c4,
.shift = 0,
.mask = 0xff,
.def = 0x49,
.def = 0x5a,
},
}, {
.id = 0x64,
......@@ -897,7 +897,7 @@ static const struct tegra_mc_client tegra210_mc_clients[] = {
.bit = 1,
},
.la = {
.reg = 0xb98,
.reg = 0x3e0,
.shift = 16,
.mask = 0xff,
.def = 0x80,
......@@ -956,7 +956,7 @@ static const struct tegra_mc_client tegra210_mc_clients[] = {
.reg = 0x3ec,
.shift = 16,
.mask = 0xff,
.def = 0xff,
.def = 0x80,
},
}, {
.id = 0x86,
......@@ -1020,35 +1020,45 @@ static const struct tegra_mc_client tegra210_mc_clients[] = {
};
static const struct tegra_smmu_swgroup tegra210_swgroups[] = {
{ .name = "dc", .swgroup = TEGRA_SWGROUP_DC, .reg = 0x240 },
{ .name = "dcb", .swgroup = TEGRA_SWGROUP_DCB, .reg = 0x244 },
{ .name = "afi", .swgroup = TEGRA_SWGROUP_AFI, .reg = 0x238 },
{ .name = "avpc", .swgroup = TEGRA_SWGROUP_AVPC, .reg = 0x23c },
{ .name = "hda", .swgroup = TEGRA_SWGROUP_HDA, .reg = 0x254 },
{ .name = "dc", .swgroup = TEGRA_SWGROUP_DC, .reg = 0x240 },
{ .name = "dcb", .swgroup = TEGRA_SWGROUP_DCB, .reg = 0x244 },
{ .name = "hc", .swgroup = TEGRA_SWGROUP_HC, .reg = 0x250 },
{ .name = "hda", .swgroup = TEGRA_SWGROUP_HDA, .reg = 0x254 },
{ .name = "isp2", .swgroup = TEGRA_SWGROUP_ISP2, .reg = 0x258 },
{ .name = "nvenc", .swgroup = TEGRA_SWGROUP_NVENC, .reg = 0x264 },
{ .name = "nv", .swgroup = TEGRA_SWGROUP_NV, .reg = 0x268 },
{ .name = "nv2", .swgroup = TEGRA_SWGROUP_NV2, .reg = 0x26c },
{ .name = "ppcs", .swgroup = TEGRA_SWGROUP_PPCS, .reg = 0x270 },
{ .name = "sata", .swgroup = TEGRA_SWGROUP_SATA, .reg = 0x274 },
{ .name = "isp2", .swgroup = TEGRA_SWGROUP_ISP2, .reg = 0x258 },
{ .name = "vi", .swgroup = TEGRA_SWGROUP_VI, .reg = 0x280 },
{ .name = "vic", .swgroup = TEGRA_SWGROUP_VIC, .reg = 0x284 },
{ .name = "xusb_host", .swgroup = TEGRA_SWGROUP_XUSB_HOST, .reg = 0x288 },
{ .name = "xusb_dev", .swgroup = TEGRA_SWGROUP_XUSB_DEV, .reg = 0x28c },
{ .name = "isp2b", .swgroup = TEGRA_SWGROUP_ISP2B, .reg = 0xaa4 },
{ .name = "tsec", .swgroup = TEGRA_SWGROUP_TSEC, .reg = 0x294 },
{ .name = "a9avp", .swgroup = TEGRA_SWGROUP_A9AVP, .reg = 0x290 },
{ .name = "gpu", .swgroup = TEGRA_SWGROUP_GPU, .reg = 0xaac },
{ .name = "tsec", .swgroup = TEGRA_SWGROUP_TSEC, .reg = 0x294 },
{ .name = "ppcs1", .swgroup = TEGRA_SWGROUP_PPCS1, .reg = 0x298 },
{ .name = "dc1", .swgroup = TEGRA_SWGROUP_DC1, .reg = 0xa88 },
{ .name = "sdmmc1a", .swgroup = TEGRA_SWGROUP_SDMMC1A, .reg = 0xa94 },
{ .name = "sdmmc2a", .swgroup = TEGRA_SWGROUP_SDMMC2A, .reg = 0xa98 },
{ .name = "sdmmc3a", .swgroup = TEGRA_SWGROUP_SDMMC3A, .reg = 0xa9c },
{ .name = "sdmmc4a", .swgroup = TEGRA_SWGROUP_SDMMC4A, .reg = 0xaa0 },
{ .name = "vic", .swgroup = TEGRA_SWGROUP_VIC, .reg = 0x284 },
{ .name = "vi", .swgroup = TEGRA_SWGROUP_VI, .reg = 0x280 },
{ .name = "isp2b", .swgroup = TEGRA_SWGROUP_ISP2B, .reg = 0xaa4 },
{ .name = "gpu", .swgroup = TEGRA_SWGROUP_GPU, .reg = 0xaac },
{ .name = "ppcs2", .swgroup = TEGRA_SWGROUP_PPCS2, .reg = 0xab0 },
{ .name = "nvdec", .swgroup = TEGRA_SWGROUP_NVDEC, .reg = 0xab4 },
{ .name = "ape", .swgroup = TEGRA_SWGROUP_APE, .reg = 0xab8 },
{ .name = "nvjpg", .swgroup = TEGRA_SWGROUP_NVJPG, .reg = 0xac0 },
{ .name = "se", .swgroup = TEGRA_SWGROUP_SE, .reg = 0xabc },
{ .name = "nvjpg", .swgroup = TEGRA_SWGROUP_NVJPG, .reg = 0xac0 },
{ .name = "hc1", .swgroup = TEGRA_SWGROUP_HC1, .reg = 0xac4 },
{ .name = "se1", .swgroup = TEGRA_SWGROUP_SE1, .reg = 0xac8 },
{ .name = "axiap", .swgroup = TEGRA_SWGROUP_AXIAP, .reg = 0xacc },
{ .name = "etr", .swgroup = TEGRA_SWGROUP_ETR, .reg = 0xad0 },
{ .name = "tsecb", .swgroup = TEGRA_SWGROUP_TSECB, .reg = 0xad4 },
{ .name = "tsec1", .swgroup = TEGRA_SWGROUP_TSEC1, .reg = 0xad8 },
{ .name = "tsecb1", .swgroup = TEGRA_SWGROUP_TSECB1, .reg = 0xadc },
{ .name = "nvdec1", .swgroup = TEGRA_SWGROUP_NVDEC1, .reg = 0xae0 },
};
static const unsigned int tegra210_group_display[] = {
......
......@@ -988,6 +988,11 @@ static struct device_node *emc_find_node_by_ram_code(struct device *dev)
u32 value, ram_code;
int err;
if (of_get_child_count(dev->of_node) == 0) {
dev_info(dev, "device-tree doesn't have memory timings\n");
return NULL;
}
ram_code = tegra_read_ram_code();
for_each_child_of_node(dev->of_node, np) {
......@@ -1057,6 +1062,9 @@ static long emc_round_rate(unsigned long rate,
struct tegra_emc *emc = arg;
unsigned int i;
if (!emc->num_timings)
return clk_get_rate(emc->clk);
min_rate = min(min_rate, emc->timings[emc->num_timings - 1].rate);
for (i = 0; i < emc->num_timings; i++) {
......@@ -1258,49 +1266,28 @@ static void tegra_emc_debugfs_init(struct tegra_emc *emc)
static int tegra_emc_probe(struct platform_device *pdev)
{
struct platform_device *mc;
struct device_node *np;
struct tegra_emc *emc;
int err;
if (of_get_child_count(pdev->dev.of_node) == 0) {
dev_info(&pdev->dev,
"device-tree node doesn't have memory timings\n");
return -ENODEV;
}
np = of_parse_phandle(pdev->dev.of_node, "nvidia,memory-controller", 0);
if (!np) {
dev_err(&pdev->dev, "could not get memory controller node\n");
return -ENOENT;
}
mc = of_find_device_by_node(np);
of_node_put(np);
if (!mc)
return -ENOENT;
np = emc_find_node_by_ram_code(&pdev->dev);
if (!np)
return -EINVAL;
emc = devm_kzalloc(&pdev->dev, sizeof(*emc), GFP_KERNEL);
if (!emc) {
of_node_put(np);
if (!emc)
return -ENOMEM;
}
emc->mc = platform_get_drvdata(mc);
if (!emc->mc)
return -EPROBE_DEFER;
emc->mc = devm_tegra_memory_controller_get(&pdev->dev);
if (IS_ERR(emc->mc))
return PTR_ERR(emc->mc);
emc->clk_nb.notifier_call = emc_clk_change_notify;
emc->dev = &pdev->dev;
err = emc_load_timings_from_dt(emc, np);
of_node_put(np);
if (err)
return err;
np = emc_find_node_by_ram_code(&pdev->dev);
if (np) {
err = emc_load_timings_from_dt(emc, np);
of_node_put(np);
if (err)
return err;
}
emc->regs = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(emc->regs))
......@@ -1311,10 +1298,9 @@ static int tegra_emc_probe(struct platform_device *pdev)
return err;
err = platform_get_irq(pdev, 0);
if (err < 0) {
dev_err(&pdev->dev, "interrupt not specified: %d\n", err);
if (err < 0)
return err;
}
emc->irq = err;
err = devm_request_irq(&pdev->dev, emc->irq, tegra_emc_isr, 0,
......@@ -1343,6 +1329,13 @@ static int tegra_emc_probe(struct platform_device *pdev)
platform_set_drvdata(pdev, emc);
tegra_emc_debugfs_init(emc);
/*
* Don't allow the kernel module to be unloaded. Unloading adds some
* extra complexity which doesn't really worth the effort in a case of
* this driver.
*/
try_module_get(THIS_MODULE);
return 0;
unset_cb:
......@@ -1393,6 +1386,7 @@ static const struct of_device_id tegra_emc_of_match[] = {
{ .compatible = "nvidia,tegra30-emc", },
{},
};
MODULE_DEVICE_TABLE(of, tegra_emc_of_match);
static struct platform_driver tegra_emc_driver = {
.probe = tegra_emc_probe,
......@@ -1403,9 +1397,8 @@ static struct platform_driver tegra_emc_driver = {
.suppress_bind_attrs = true,
},
};
module_platform_driver(tegra_emc_driver);
static int __init tegra_emc_init(void)
{
return platform_driver_register(&tegra_emc_driver);
}
subsys_initcall(tegra_emc_init);
MODULE_AUTHOR("Dmitry Osipenko <digetx@gmail.com>");
MODULE_DESCRIPTION("NVIDIA Tegra30 EMC driver");
MODULE_LICENSE("GPL v2");
This diff is collapsed.
......@@ -3,6 +3,7 @@
* Copyright (c) 2014, NVIDIA CORPORATION. All rights reserved.
*/
#include <linux/export.h>
#include <linux/kernel.h>
#include <linux/of.h>
#include <linux/of_address.h>
......@@ -90,6 +91,7 @@ u32 tegra_read_ram_code(void)
return straps >> PMC_STRAPPING_OPT_A_RAM_CODE_SHIFT;
}
EXPORT_SYMBOL_GPL(tegra_read_ram_code);
static const struct of_device_id apbmisc_match[] __initconst = {
{ .compatible = "nvidia,tegra20-apbmisc", },
......
......@@ -33,6 +33,16 @@
#define TEGRA_SWGROUP_AXIAP 28
#define TEGRA_SWGROUP_ETR 29
#define TEGRA_SWGROUP_TSECB 30
#define TEGRA_SWGROUP_NV 31
#define TEGRA_SWGROUP_NV2 32
#define TEGRA_SWGROUP_PPCS1 33
#define TEGRA_SWGROUP_DC1 34
#define TEGRA_SWGROUP_PPCS2 35
#define TEGRA_SWGROUP_HC1 36
#define TEGRA_SWGROUP_SE1 37
#define TEGRA_SWGROUP_TSEC1 38
#define TEGRA_SWGROUP_TSECB1 39
#define TEGRA_SWGROUP_NVDEC1 40
#define TEGRA210_MC_RESET_AFI 0
#define TEGRA210_MC_RESET_AVPC 1
......
......@@ -56,7 +56,11 @@ u32 tegra_read_straps(void);
u32 tegra_read_ram_code(void);
int tegra_fuse_readl(unsigned long offset, u32 *value);
#ifdef CONFIG_ARCH_TEGRA
extern struct tegra_sku_info tegra_sku_info;
#else
static struct tegra_sku_info tegra_sku_info __maybe_unused;
#endif
struct device *tegra_soc_device_register(void);
......
......@@ -6,7 +6,9 @@
#ifndef __SOC_TEGRA_MC_H__
#define __SOC_TEGRA_MC_H__
#include <linux/bits.h>
#include <linux/err.h>
#include <linux/interconnect-provider.h>
#include <linux/reset-controller.h>
#include <linux/types.h>
......@@ -141,6 +143,17 @@ struct tegra_mc_reset_ops {
const struct tegra_mc_reset *rst);
};
#define TEGRA_MC_ICC_TAG_DEFAULT 0
#define TEGRA_MC_ICC_TAG_ISO BIT(0)
struct tegra_mc_icc_ops {
int (*set)(struct icc_node *src, struct icc_node *dst);
int (*aggregate)(struct icc_node *node, u32 tag, u32 avg_bw,
u32 peak_bw, u32 *agg_avg, u32 *agg_peak);
struct icc_node_data *(*xlate_extended)(struct of_phandle_args *spec,
void *data);
};
struct tegra_mc_soc {
const struct tegra_mc_client *clients;
unsigned int num_clients;
......@@ -160,6 +173,8 @@ struct tegra_mc_soc {
const struct tegra_mc_reset_ops *reset_ops;
const struct tegra_mc_reset *resets;
unsigned int num_resets;
const struct tegra_mc_icc_ops *icc_ops;
};
struct tegra_mc {
......@@ -178,10 +193,22 @@ struct tegra_mc {
struct reset_controller_dev reset;
struct icc_provider provider;
spinlock_t lock;
};
int tegra_mc_write_emem_configuration(struct tegra_mc *mc, unsigned long rate);
unsigned int tegra_mc_get_emem_device_count(struct tegra_mc *mc);
#ifdef CONFIG_TEGRA_MC
struct tegra_mc *devm_tegra_memory_controller_get(struct device *dev);
#else
static inline struct tegra_mc *
devm_tegra_memory_controller_get(struct device *dev)
{
return ERR_PTR(-ENODEV);
}
#endif
#endif /* __SOC_TEGRA_MC_H__ */
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment