213 lines · 5.2 KB · C
RawDownload
// SPDX-License-Identifier: GPL-2.0-only/* * drivers/soc/tegra/flowctrl.c * * Functions and macros to control the flowcontroller * * Copyright (c) 2010-2012, NVIDIA Corporation. All rights reserved. */ #include <linux/cpumask.h>#include <linux/init.h>#include <linux/io.h>#include <linux/kernel.h>#include <linux/of.h>#include <linux/of_address.h>#include <linux/platform_device.h> #include <soc/tegra/common.h>#include <soc/tegra/flowctrl.h>#include <soc/tegra/fuse.h> static u8 flowctrl_offset_halt_cpu[] = {	FLOW_CTRL_HALT_CPU0_EVENTS,	FLOW_CTRL_HALT_CPU1_EVENTS,	FLOW_CTRL_HALT_CPU1_EVENTS + 8,	FLOW_CTRL_HALT_CPU1_EVENTS + 16,}; static u8 flowctrl_offset_cpu_csr[] = {	FLOW_CTRL_CPU0_CSR,	FLOW_CTRL_CPU1_CSR,	FLOW_CTRL_CPU1_CSR + 8,	FLOW_CTRL_CPU1_CSR + 16,}; static void __iomem *tegra_flowctrl_base; static void flowctrl_update(u8 offset, u32 value){	if (WARN_ONCE(IS_ERR_OR_NULL(tegra_flowctrl_base),		      "Tegra flowctrl not initialised!\n"))		return; 	writel(value, tegra_flowctrl_base + offset); 	/* ensure the update has reached the flow controller */	wmb();	readl_relaxed(tegra_flowctrl_base + offset);} u32 flowctrl_read_cpu_csr(unsigned int cpuid){	u8 offset = flowctrl_offset_cpu_csr[cpuid]; 	if (WARN_ONCE(IS_ERR_OR_NULL(tegra_flowctrl_base),		      "Tegra flowctrl not initialised!\n"))		return 0; 	return readl(tegra_flowctrl_base + offset);} void flowctrl_write_cpu_csr(unsigned int cpuid, u32 value){	return flowctrl_update(flowctrl_offset_cpu_csr[cpuid], value);} void flowctrl_write_cpu_halt(unsigned int cpuid, u32 value){	return flowctrl_update(flowctrl_offset_halt_cpu[cpuid], value);} void flowctrl_cpu_suspend_enter(unsigned int cpuid){	unsigned int reg;	int i; 	reg = flowctrl_read_cpu_csr(cpuid);	switch (tegra_get_chip_id()) {	case TEGRA20:		/* clear wfe bitmap */		reg &= ~TEGRA20_FLOW_CTRL_CSR_WFE_BITMAP;		/* clear wfi bitmap */		reg &= ~TEGRA20_FLOW_CTRL_CSR_WFI_BITMAP;		/* pwr gating on wfe */		reg |= TEGRA20_FLOW_CTRL_CSR_WFE_CPU0 << cpuid;		break;	case TEGRA30:	case TEGRA114:	case TEGRA124:		/* clear wfe bitmap */		reg &= ~TEGRA30_FLOW_CTRL_CSR_WFE_BITMAP;		/* clear wfi bitmap */		reg &= ~TEGRA30_FLOW_CTRL_CSR_WFI_BITMAP;		/* pwr gating on wfi */		reg |= TEGRA30_FLOW_CTRL_CSR_WFI_CPU0 << cpuid;		break;	}	reg |= FLOW_CTRL_CSR_INTR_FLAG;			/* clear intr flag */	reg |= FLOW_CTRL_CSR_EVENT_FLAG;		/* clear event flag */	reg |= FLOW_CTRL_CSR_ENABLE;			/* pwr gating */	flowctrl_write_cpu_csr(cpuid, reg); 	for (i = 0; i < num_possible_cpus(); i++) {		if (i == cpuid)			continue;		reg = flowctrl_read_cpu_csr(i);		reg |= FLOW_CTRL_CSR_EVENT_FLAG;		reg |= FLOW_CTRL_CSR_INTR_FLAG;		flowctrl_write_cpu_csr(i, reg);	}} void flowctrl_cpu_suspend_exit(unsigned int cpuid){	unsigned int reg; 	/* Disable powergating via flow controller for CPU0 */	reg = flowctrl_read_cpu_csr(cpuid);	switch (tegra_get_chip_id()) {	case TEGRA20:		/* clear wfe bitmap */		reg &= ~TEGRA20_FLOW_CTRL_CSR_WFE_BITMAP;		/* clear wfi bitmap */		reg &= ~TEGRA20_FLOW_CTRL_CSR_WFI_BITMAP;		break;	case TEGRA30:	case TEGRA114:	case TEGRA124:		/* clear wfe bitmap */		reg &= ~TEGRA30_FLOW_CTRL_CSR_WFE_BITMAP;		/* clear wfi bitmap */		reg &= ~TEGRA30_FLOW_CTRL_CSR_WFI_BITMAP;		break;	}	reg &= ~FLOW_CTRL_CSR_ENABLE;			/* clear enable */	reg |= FLOW_CTRL_CSR_INTR_FLAG;			/* clear intr */	reg |= FLOW_CTRL_CSR_EVENT_FLAG;		/* clear event */	flowctrl_write_cpu_csr(cpuid, reg);} static int tegra_flowctrl_probe(struct platform_device *pdev){	void __iomem *base = tegra_flowctrl_base;	struct resource *res; 	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);	tegra_flowctrl_base = devm_ioremap_resource(&pdev->dev, res);	if (IS_ERR(tegra_flowctrl_base))		return PTR_ERR(tegra_flowctrl_base); 	iounmap(base); 	return 0;} static const struct of_device_id tegra_flowctrl_match[] = {	{ .compatible = "nvidia,tegra210-flowctrl" },	{ .compatible = "nvidia,tegra124-flowctrl" },	{ .compatible = "nvidia,tegra114-flowctrl" },	{ .compatible = "nvidia,tegra30-flowctrl" },	{ .compatible = "nvidia,tegra20-flowctrl" },	{ }}; static struct platform_driver tegra_flowctrl_driver = {	.driver = {		.name = "tegra-flowctrl",		.suppress_bind_attrs = true,		.of_match_table = tegra_flowctrl_match,	},	.probe = tegra_flowctrl_probe,};builtin_platform_driver(tegra_flowctrl_driver); static int __init tegra_flowctrl_init(void){	struct resource res;	struct device_node *np; 	if (!soc_is_tegra())		return 0; 	np = of_find_matching_node(NULL, tegra_flowctrl_match);	if (np) {		if (of_address_to_resource(np, 0, &res) < 0) {			pr_err("failed to get flowctrl register\n");			return -ENXIO;		}		of_node_put(np);	} else if (IS_ENABLED(CONFIG_ARM)) {		/*		 * Hardcoded fallback for 32-bit Tegra		 * devices if device tree node is missing.		 */		res.start = 0x60007000;		res.end = 0x60007fff;		res.flags = IORESOURCE_MEM;	} else {		/*		 * At this point we're running on a Tegra,		 * that doesn't support the flow controller		 * (eg. Tegra186), so just return.		 */		return 0;	} 	tegra_flowctrl_base = ioremap_nocache(res.start, resource_size(&res));	if (!tegra_flowctrl_base)		return -ENXIO; 	return 0;}early_initcall(tegra_flowctrl_init);