diff options
Diffstat (limited to 'drivers/bus')
-rw-r--r-- | drivers/bus/Kconfig | 13 | ||||
-rw-r--r-- | drivers/bus/Makefile | 1 | ||||
-rw-r--r-- | drivers/bus/arm-cci.c | 53 | ||||
-rw-r--r-- | drivers/bus/arm-ccn.c | 57 | ||||
-rw-r--r-- | drivers/bus/imx-weim.c | 5 | ||||
-rw-r--r-- | drivers/bus/mvebu-mbus.c | 10 | ||||
-rw-r--r-- | drivers/bus/tegra-aconnect.c | 112 | ||||
-rw-r--r-- | drivers/bus/uniphier-system-bus.c | 3 |
8 files changed, 185 insertions, 69 deletions
diff --git a/drivers/bus/Kconfig b/drivers/bus/Kconfig index c5a7de9bc783..3b205e212337 100644 --- a/drivers/bus/Kconfig +++ b/drivers/bus/Kconfig @@ -132,6 +132,19 @@ config SUNXI_RSB with various RSB based devices, such as AXP223, AXP8XX PMICs, and AC100/AC200 ICs. +# TODO: This uses pm_clk_*() symbols that aren't exported in v4.7 and hence +# the driver will fail to build as a module. However there are patches to +# address that queued for v4.8, so this can be turned into a tristate symbol +# after v4.8-rc1. +config TEGRA_ACONNECT + bool "Tegra ACONNECT Bus Driver" + depends on ARCH_TEGRA_210_SOC + depends on OF && PM + select PM_CLK + help + Driver for the Tegra ACONNECT bus which is used to interface with + the devices inside the Audio Processing Engine (APE) for Tegra210. + config UNIPHIER_SYSTEM_BUS tristate "UniPhier System Bus driver" depends on ARCH_UNIPHIER && OF diff --git a/drivers/bus/Makefile b/drivers/bus/Makefile index ccff007ee7e8..ac84cc4348e3 100644 --- a/drivers/bus/Makefile +++ b/drivers/bus/Makefile @@ -17,5 +17,6 @@ obj-$(CONFIG_OMAP_INTERCONNECT) += omap_l3_smx.o omap_l3_noc.o obj-$(CONFIG_OMAP_OCP2SCP) += omap-ocp2scp.o obj-$(CONFIG_SUNXI_RSB) += sunxi-rsb.o obj-$(CONFIG_SIMPLE_PM_BUS) += simple-pm-bus.o +obj-$(CONFIG_TEGRA_ACONNECT) += tegra-aconnect.o obj-$(CONFIG_UNIPHIER_SYSTEM_BUS) += uniphier-system-bus.o obj-$(CONFIG_VEXPRESS_CONFIG) += vexpress-config.o diff --git a/drivers/bus/arm-cci.c b/drivers/bus/arm-cci.c index a49b28378d59..5755907f836f 100644 --- a/drivers/bus/arm-cci.c +++ b/drivers/bus/arm-cci.c @@ -144,12 +144,15 @@ struct cci_pmu { int num_cntrs; atomic_t active_events; struct mutex reserve_mutex; - struct notifier_block cpu_nb; + struct list_head entry; cpumask_t cpus; }; #define to_cci_pmu(c) (container_of(c, struct cci_pmu, pmu)) +static DEFINE_MUTEX(cci_pmu_mutex); +static LIST_HEAD(cci_pmu_list); + enum cci_models { #ifdef CONFIG_ARM_CCI400_PMU CCI400_R0, @@ -1503,31 +1506,26 @@ static int cci_pmu_init(struct cci_pmu *cci_pmu, struct platform_device *pdev) return perf_pmu_register(&cci_pmu->pmu, name, -1); } -static int cci_pmu_cpu_notifier(struct notifier_block *self, - unsigned long action, void *hcpu) +static int cci_pmu_offline_cpu(unsigned int cpu) { - struct cci_pmu *cci_pmu = container_of(self, - struct cci_pmu, cpu_nb); - unsigned int cpu = (long)hcpu; + struct cci_pmu *cci_pmu; unsigned int target; - switch (action & ~CPU_TASKS_FROZEN) { - case CPU_DOWN_PREPARE: + mutex_lock(&cci_pmu_mutex); + list_for_each_entry(cci_pmu, &cci_pmu_list, entry) { if (!cpumask_test_and_clear_cpu(cpu, &cci_pmu->cpus)) - break; + continue; target = cpumask_any_but(cpu_online_mask, cpu); - if (target >= nr_cpu_ids) // UP, last CPU - break; + if (target >= nr_cpu_ids) + continue; /* * TODO: migrate context once core races on event->ctx have * been fixed. */ cpumask_set_cpu(target, &cci_pmu->cpus); - default: - break; } - - return NOTIFY_OK; + mutex_unlock(&cci_pmu_mutex); + return 0; } static struct cci_pmu_model cci_pmu_models[] = { @@ -1766,24 +1764,13 @@ static int cci_pmu_probe(struct platform_device *pdev) atomic_set(&cci_pmu->active_events, 0); cpumask_set_cpu(smp_processor_id(), &cci_pmu->cpus); - cci_pmu->cpu_nb = (struct notifier_block) { - .notifier_call = cci_pmu_cpu_notifier, - /* - * to migrate uncore events, our notifier should be executed - * before perf core's notifier. - */ - .priority = CPU_PRI_PERF + 1, - }; - - ret = register_cpu_notifier(&cci_pmu->cpu_nb); + ret = cci_pmu_init(cci_pmu, pdev); if (ret) return ret; - ret = cci_pmu_init(cci_pmu, pdev); - if (ret) { - unregister_cpu_notifier(&cci_pmu->cpu_nb); - return ret; - } + mutex_lock(&cci_pmu_mutex); + list_add(&cci_pmu->entry, &cci_pmu_list); + mutex_unlock(&cci_pmu_mutex); pr_info("ARM %s PMU driver probed", cci_pmu->model->name); return 0; @@ -1817,6 +1804,12 @@ static int __init cci_platform_init(void) { int ret; + ret = cpuhp_setup_state_nocalls(CPUHP_AP_PERF_ARM_CCI_ONLINE, + "AP_PERF_ARM_CCI_ONLINE", NULL, + cci_pmu_offline_cpu); + if (ret) + return ret; + ret = platform_driver_register(&cci_pmu_driver); if (ret) return ret; diff --git a/drivers/bus/arm-ccn.c b/drivers/bus/arm-ccn.c index acc3eb542c74..97a9185af433 100644 --- a/drivers/bus/arm-ccn.c +++ b/drivers/bus/arm-ccn.c @@ -167,7 +167,7 @@ struct arm_ccn_dt { struct hrtimer hrtimer; cpumask_t cpu; - struct notifier_block cpu_nb; + struct list_head entry; struct pmu pmu; }; @@ -189,6 +189,8 @@ struct arm_ccn { struct arm_ccn_dt dt; }; +static DEFINE_MUTEX(arm_ccn_mutex); +static LIST_HEAD(arm_ccn_list); static int arm_ccn_node_to_xp(int node) { @@ -1171,30 +1173,27 @@ static enum hrtimer_restart arm_ccn_pmu_timer_handler(struct hrtimer *hrtimer) } -static int arm_ccn_pmu_cpu_notifier(struct notifier_block *nb, - unsigned long action, void *hcpu) +static int arm_ccn_pmu_offline_cpu(unsigned int cpu) { - struct arm_ccn_dt *dt = container_of(nb, struct arm_ccn_dt, cpu_nb); - struct arm_ccn *ccn = container_of(dt, struct arm_ccn, dt); - unsigned int cpu = (long)hcpu; /* for (long) see kernel/cpu.c */ + struct arm_ccn_dt *dt; unsigned int target; - switch (action & ~CPU_TASKS_FROZEN) { - case CPU_DOWN_PREPARE: + mutex_lock(&arm_ccn_mutex); + list_for_each_entry(dt, &arm_ccn_list, entry) { + struct arm_ccn *ccn = container_of(dt, struct arm_ccn, dt); + if (!cpumask_test_and_clear_cpu(cpu, &dt->cpu)) - break; + continue; target = cpumask_any_but(cpu_online_mask, cpu); if (target >= nr_cpu_ids) - break; + continue; perf_pmu_migrate_context(&dt->pmu, cpu, target); cpumask_set_cpu(target, &dt->cpu); if (ccn->irq) WARN_ON(irq_set_affinity_hint(ccn->irq, &dt->cpu) != 0); - default: - break; } - - return NOTIFY_OK; + mutex_unlock(&arm_ccn_mutex); + return 0; } @@ -1266,16 +1265,6 @@ static int arm_ccn_pmu_init(struct arm_ccn *ccn) /* Pick one CPU which we will use to collect data from CCN... */ cpumask_set_cpu(smp_processor_id(), &ccn->dt.cpu); - /* - * ... and change the selection when it goes offline. Priority is - * picked to have a chance to migrate events before perf is notified. - */ - ccn->dt.cpu_nb.notifier_call = arm_ccn_pmu_cpu_notifier; - ccn->dt.cpu_nb.priority = CPU_PRI_PERF + 1, - err = register_cpu_notifier(&ccn->dt.cpu_nb); - if (err) - goto error_cpu_notifier; - /* Also make sure that the overflow interrupt is handled by this CPU */ if (ccn->irq) { err = irq_set_affinity_hint(ccn->irq, &ccn->dt.cpu); @@ -1289,12 +1278,13 @@ static int arm_ccn_pmu_init(struct arm_ccn *ccn) if (err) goto error_pmu_register; + mutex_lock(&arm_ccn_mutex); + list_add(&ccn->dt.entry, &arm_ccn_list); + mutex_unlock(&arm_ccn_mutex); return 0; error_pmu_register: error_set_affinity: - unregister_cpu_notifier(&ccn->dt.cpu_nb); -error_cpu_notifier: ida_simple_remove(&arm_ccn_pmu_ida, ccn->dt.id); for (i = 0; i < ccn->num_xps; i++) writel(0, ccn->xp[i].base + CCN_XP_DT_CONTROL); @@ -1306,9 +1296,12 @@ static void arm_ccn_pmu_cleanup(struct arm_ccn *ccn) { int i; + mutex_lock(&arm_ccn_mutex); + list_del(&ccn->dt.entry); + mutex_unlock(&arm_ccn_mutex); + if (ccn->irq) irq_set_affinity_hint(ccn->irq, NULL); - unregister_cpu_notifier(&ccn->dt.cpu_nb); for (i = 0; i < ccn->num_xps; i++) writel(0, ccn->xp[i].base + CCN_XP_DT_CONTROL); writel(0, ccn->dt.base + CCN_DT_PMCR); @@ -1316,7 +1309,6 @@ static void arm_ccn_pmu_cleanup(struct arm_ccn *ccn) ida_simple_remove(&arm_ccn_pmu_ida, ccn->dt.id); } - static int arm_ccn_for_each_valid_region(struct arm_ccn *ccn, int (*callback)(struct arm_ccn *ccn, int region, void __iomem *base, u32 type, u32 id)) @@ -1533,7 +1525,13 @@ static struct platform_driver arm_ccn_driver = { static int __init arm_ccn_init(void) { - int i; + int i, ret; + + ret = cpuhp_setup_state_nocalls(CPUHP_AP_PERF_ARM_CCN_ONLINE, + "AP_PERF_ARM_CCN_ONLINE", NULL, + arm_ccn_pmu_offline_cpu); + if (ret) + return ret; for (i = 0; i < ARRAY_SIZE(arm_ccn_pmu_events); i++) arm_ccn_pmu_events_attrs[i] = &arm_ccn_pmu_events[i].attr.attr; @@ -1543,6 +1541,7 @@ static int __init arm_ccn_init(void) static void __exit arm_ccn_exit(void) { + cpuhp_remove_state_nocalls(CPUHP_AP_PERF_ARM_CCN_ONLINE); platform_driver_unregister(&arm_ccn_driver); } diff --git a/drivers/bus/imx-weim.c b/drivers/bus/imx-weim.c index 1827fc4d15c1..4bd361d64270 100644 --- a/drivers/bus/imx-weim.c +++ b/drivers/bus/imx-weim.c @@ -163,9 +163,8 @@ static int __init weim_parse_dt(struct platform_device *pdev, } if (have_child) - ret = of_platform_populate(pdev->dev.of_node, - of_default_bus_match_table, - NULL, &pdev->dev); + ret = of_platform_default_populate(pdev->dev.of_node, + NULL, &pdev->dev); if (ret) dev_err(&pdev->dev, "%s fail to create devices.\n", pdev->dev.of_node->full_name); diff --git a/drivers/bus/mvebu-mbus.c b/drivers/bus/mvebu-mbus.c index ce54a0160faa..c7f396903184 100644 --- a/drivers/bus/mvebu-mbus.c +++ b/drivers/bus/mvebu-mbus.c @@ -117,7 +117,7 @@ struct mvebu_mbus_soc_data { unsigned int (*win_remap_offset)(const int win); void (*setup_cpu_target)(struct mvebu_mbus_state *s); int (*save_cpu_target)(struct mvebu_mbus_state *s, - u32 *store_addr); + u32 __iomem *store_addr); int (*show_cpu_target)(struct mvebu_mbus_state *s, struct seq_file *seq, void *v); }; @@ -728,7 +728,7 @@ mvebu_mbus_default_setup_cpu_target(struct mvebu_mbus_state *mbus) static int mvebu_mbus_default_save_cpu_target(struct mvebu_mbus_state *mbus, - u32 *store_addr) + u32 __iomem *store_addr) { int i; @@ -780,7 +780,7 @@ mvebu_mbus_dove_setup_cpu_target(struct mvebu_mbus_state *mbus) static int mvebu_mbus_dove_save_cpu_target(struct mvebu_mbus_state *mbus, - u32 *store_addr) + u32 __iomem *store_addr) { int i; @@ -796,7 +796,7 @@ mvebu_mbus_dove_save_cpu_target(struct mvebu_mbus_state *mbus, return 4; } -int mvebu_mbus_save_cpu_target(u32 *store_addr) +int mvebu_mbus_save_cpu_target(u32 __iomem *store_addr) { return mbus_state.soc->save_cpu_target(&mbus_state, store_addr); } @@ -1089,7 +1089,7 @@ static void mvebu_mbus_resume(void) } } -struct syscore_ops mvebu_mbus_syscore_ops = { +static struct syscore_ops mvebu_mbus_syscore_ops = { .suspend = mvebu_mbus_suspend, .resume = mvebu_mbus_resume, }; diff --git a/drivers/bus/tegra-aconnect.c b/drivers/bus/tegra-aconnect.c new file mode 100644 index 000000000000..7e4104b74fa8 --- /dev/null +++ b/drivers/bus/tegra-aconnect.c @@ -0,0 +1,112 @@ +/* + * Tegra ACONNECT Bus Driver + * + * Copyright (C) 2016, NVIDIA CORPORATION. All rights reserved. + * + * This file is subject to the terms and conditions of the GNU General Public + * License. See the file "COPYING" in the main directory of this archive + * for more details. + */ + +#include <linux/clk.h> +#include <linux/module.h> +#include <linux/of_platform.h> +#include <linux/platform_device.h> +#include <linux/pm_clock.h> +#include <linux/pm_runtime.h> + +static int tegra_aconnect_add_clock(struct device *dev, char *name) +{ + struct clk *clk; + int ret; + + clk = clk_get(dev, name); + if (IS_ERR(clk)) { + dev_err(dev, "%s clock not found\n", name); + return PTR_ERR(clk); + } + + ret = pm_clk_add_clk(dev, clk); + if (ret) + clk_put(clk); + + return ret; +} + +static int tegra_aconnect_probe(struct platform_device *pdev) +{ + int ret; + + if (!pdev->dev.of_node) + return -EINVAL; + + ret = pm_clk_create(&pdev->dev); + if (ret) + return ret; + + ret = tegra_aconnect_add_clock(&pdev->dev, "ape"); + if (ret) + goto clk_destroy; + + ret = tegra_aconnect_add_clock(&pdev->dev, "apb2ape"); + if (ret) + goto clk_destroy; + + pm_runtime_enable(&pdev->dev); + + of_platform_populate(pdev->dev.of_node, NULL, NULL, &pdev->dev); + + dev_info(&pdev->dev, "Tegra ACONNECT bus registered\n"); + + return 0; + +clk_destroy: + pm_clk_destroy(&pdev->dev); + + return ret; +} + +static int tegra_aconnect_remove(struct platform_device *pdev) +{ + pm_runtime_disable(&pdev->dev); + + pm_clk_destroy(&pdev->dev); + + return 0; +} + +static int tegra_aconnect_runtime_resume(struct device *dev) +{ + return pm_clk_resume(dev); +} + +static int tegra_aconnect_runtime_suspend(struct device *dev) +{ + return pm_clk_suspend(dev); +} + +static const struct dev_pm_ops tegra_aconnect_pm_ops = { + SET_RUNTIME_PM_OPS(tegra_aconnect_runtime_suspend, + tegra_aconnect_runtime_resume, NULL) +}; + +static const struct of_device_id tegra_aconnect_of_match[] = { + { .compatible = "nvidia,tegra210-aconnect", }, + { } +}; +MODULE_DEVICE_TABLE(of, tegra_aconnect_of_match); + +static struct platform_driver tegra_aconnect_driver = { + .probe = tegra_aconnect_probe, + .remove = tegra_aconnect_remove, + .driver = { + .name = "tegra-aconnect", + .of_match_table = tegra_aconnect_of_match, + .pm = &tegra_aconnect_pm_ops, + }, +}; +module_platform_driver(tegra_aconnect_driver); + +MODULE_DESCRIPTION("NVIDIA Tegra ACONNECT Bus Driver"); +MODULE_AUTHOR("Jon Hunter <jonathanh@nvidia.com>"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/bus/uniphier-system-bus.c b/drivers/bus/uniphier-system-bus.c index 350b7309c26d..1e6e0269edcc 100644 --- a/drivers/bus/uniphier-system-bus.c +++ b/drivers/bus/uniphier-system-bus.c @@ -257,8 +257,7 @@ static int uniphier_system_bus_probe(struct platform_device *pdev) uniphier_system_bus_set_reg(priv); /* Now, the bus is configured. Populate platform_devices below it */ - return of_platform_populate(dev->of_node, of_default_bus_match_table, - NULL, dev); + return of_platform_default_populate(dev->of_node, NULL, dev); } static const struct of_device_id uniphier_system_bus_match[] = { |