From e465007a7a7ca745f018fdcacb0a5a911a5c50d9 Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Tue, 10 May 2016 15:52:20 +0200 Subject: ARM: EXYNOS: Move pm_domains driver to drivers/soc/samsung Exynos PM domains driver does not have mach-specific dependencies so it can be safely moved out of arm/mach-exynos to drivers/soc. This in future will allow re-using it on ARM64 boards. Signed-off-by: Krzysztof Kozlowski Reviewed-by: Javier Martinez Canillas diff --git a/arch/arm/mach-exynos/Makefile b/arch/arm/mach-exynos/Makefile index 34d29df..838249d 100644 --- a/arch/arm/mach-exynos/Makefile +++ b/arch/arm/mach-exynos/Makefile @@ -13,7 +13,6 @@ obj-$(CONFIG_ARCH_EXYNOS) += exynos.o exynos-smc.o firmware.o obj-$(CONFIG_EXYNOS_CPU_SUSPEND) += pm.o sleep.o obj-$(CONFIG_PM_SLEEP) += suspend.o -obj-$(CONFIG_PM_GENERIC_DOMAINS) += pm_domains.o obj-$(CONFIG_SMP) += platsmp.o headsmp.o diff --git a/arch/arm/mach-exynos/pm_domains.c b/arch/arm/mach-exynos/pm_domains.c deleted file mode 100644 index 875a2ba..0000000 --- a/arch/arm/mach-exynos/pm_domains.c +++ /dev/null @@ -1,223 +0,0 @@ -/* - * Exynos Generic power domain support. - * - * Copyright (c) 2012 Samsung Electronics Co., Ltd. - * http://www.samsung.com - * - * Implementation of Exynos specific power domain control which is used in - * conjunction with runtime-pm. Support for both device-tree and non-device-tree - * based power domain support is included. - * - * 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. -*/ - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#define INT_LOCAL_PWR_EN 0x7 -#define MAX_CLK_PER_DOMAIN 4 - -/* - * Exynos specific wrapper around the generic power domain - */ -struct exynos_pm_domain { - void __iomem *base; - char const *name; - bool is_off; - struct generic_pm_domain pd; - struct clk *oscclk; - struct clk *clk[MAX_CLK_PER_DOMAIN]; - struct clk *pclk[MAX_CLK_PER_DOMAIN]; - struct clk *asb_clk[MAX_CLK_PER_DOMAIN]; -}; - -static int exynos_pd_power(struct generic_pm_domain *domain, bool power_on) -{ - struct exynos_pm_domain *pd; - void __iomem *base; - u32 timeout, pwr; - char *op; - int i; - - pd = container_of(domain, struct exynos_pm_domain, pd); - base = pd->base; - - for (i = 0; i < MAX_CLK_PER_DOMAIN; i++) { - if (IS_ERR(pd->asb_clk[i])) - break; - clk_prepare_enable(pd->asb_clk[i]); - } - - /* Set oscclk before powering off a domain*/ - if (!power_on) { - for (i = 0; i < MAX_CLK_PER_DOMAIN; i++) { - if (IS_ERR(pd->clk[i])) - break; - pd->pclk[i] = clk_get_parent(pd->clk[i]); - if (clk_set_parent(pd->clk[i], pd->oscclk)) - pr_err("%s: error setting oscclk as parent to clock %d\n", - pd->name, i); - } - } - - pwr = power_on ? INT_LOCAL_PWR_EN : 0; - __raw_writel(pwr, base); - - /* Wait max 1ms */ - timeout = 10; - - while ((__raw_readl(base + 0x4) & INT_LOCAL_PWR_EN) != pwr) { - if (!timeout) { - op = (power_on) ? "enable" : "disable"; - pr_err("Power domain %s %s failed\n", domain->name, op); - return -ETIMEDOUT; - } - timeout--; - cpu_relax(); - usleep_range(80, 100); - } - - /* Restore clocks after powering on a domain*/ - if (power_on) { - for (i = 0; i < MAX_CLK_PER_DOMAIN; i++) { - if (IS_ERR(pd->clk[i])) - break; - - if (IS_ERR(pd->pclk[i])) - continue; /* Skip on first power up */ - if (clk_set_parent(pd->clk[i], pd->pclk[i])) - pr_err("%s: error setting parent to clock%d\n", - pd->name, i); - } - } - - for (i = 0; i < MAX_CLK_PER_DOMAIN; i++) { - if (IS_ERR(pd->asb_clk[i])) - break; - clk_disable_unprepare(pd->asb_clk[i]); - } - - return 0; -} - -static int exynos_pd_power_on(struct generic_pm_domain *domain) -{ - return exynos_pd_power(domain, true); -} - -static int exynos_pd_power_off(struct generic_pm_domain *domain) -{ - return exynos_pd_power(domain, false); -} - -static __init int exynos4_pm_init_power_domain(void) -{ - struct device_node *np; - - for_each_compatible_node(np, NULL, "samsung,exynos4210-pd") { - struct exynos_pm_domain *pd; - int on, i; - - pd = kzalloc(sizeof(*pd), GFP_KERNEL); - if (!pd) { - pr_err("%s: failed to allocate memory for domain\n", - __func__); - of_node_put(np); - return -ENOMEM; - } - pd->pd.name = kstrdup_const(strrchr(np->full_name, '/') + 1, - GFP_KERNEL); - if (!pd->pd.name) { - kfree(pd); - of_node_put(np); - return -ENOMEM; - } - - pd->name = pd->pd.name; - pd->base = of_iomap(np, 0); - if (!pd->base) { - pr_warn("%s: failed to map memory\n", __func__); - kfree_const(pd->pd.name); - kfree(pd); - continue; - } - - pd->pd.power_off = exynos_pd_power_off; - pd->pd.power_on = exynos_pd_power_on; - - for (i = 0; i < MAX_CLK_PER_DOMAIN; i++) { - char clk_name[8]; - - snprintf(clk_name, sizeof(clk_name), "asb%d", i); - pd->asb_clk[i] = of_clk_get_by_name(np, clk_name); - if (IS_ERR(pd->asb_clk[i])) - break; - } - - pd->oscclk = of_clk_get_by_name(np, "oscclk"); - if (IS_ERR(pd->oscclk)) - goto no_clk; - - for (i = 0; i < MAX_CLK_PER_DOMAIN; i++) { - char clk_name[8]; - - snprintf(clk_name, sizeof(clk_name), "clk%d", i); - pd->clk[i] = of_clk_get_by_name(np, clk_name); - if (IS_ERR(pd->clk[i])) - break; - /* - * Skip setting parent on first power up. - * The parent at this time may not be useful at all. - */ - pd->pclk[i] = ERR_PTR(-EINVAL); - } - - if (IS_ERR(pd->clk[0])) - clk_put(pd->oscclk); - -no_clk: - on = __raw_readl(pd->base + 0x4) & INT_LOCAL_PWR_EN; - - pm_genpd_init(&pd->pd, NULL, !on); - of_genpd_add_provider_simple(np, &pd->pd); - } - - /* Assign the child power domains to their parents */ - for_each_compatible_node(np, NULL, "samsung,exynos4210-pd") { - struct generic_pm_domain *child_domain, *parent_domain; - struct of_phandle_args args; - - args.np = np; - args.args_count = 0; - child_domain = of_genpd_get_from_provider(&args); - if (IS_ERR(child_domain)) - continue; - - if (of_parse_phandle_with_args(np, "power-domains", - "#power-domain-cells", 0, &args) != 0) - continue; - - parent_domain = of_genpd_get_from_provider(&args); - if (IS_ERR(parent_domain)) - continue; - - if (pm_genpd_add_subdomain(parent_domain, child_domain)) - pr_warn("%s failed to add subdomain: %s\n", - parent_domain->name, child_domain->name); - else - pr_info("%s has as child subdomain: %s.\n", - parent_domain->name, child_domain->name); - } - - return 0; -} -core_initcall(exynos4_pm_init_power_domain); diff --git a/drivers/soc/samsung/Makefile b/drivers/soc/samsung/Makefile index f64ac4d..8aead58 100644 --- a/drivers/soc/samsung/Makefile +++ b/drivers/soc/samsung/Makefile @@ -1,2 +1,3 @@ obj-$(CONFIG_EXYNOS_PMU) += exynos-pmu.o exynos3250-pmu.o exynos4-pmu.o \ exynos5250-pmu.o exynos5420-pmu.o +obj-$(CONFIG_PM_GENERIC_DOMAINS) += pm_domains.o diff --git a/drivers/soc/samsung/pm_domains.c b/drivers/soc/samsung/pm_domains.c new file mode 100644 index 0000000..875a2ba --- /dev/null +++ b/drivers/soc/samsung/pm_domains.c @@ -0,0 +1,223 @@ +/* + * Exynos Generic power domain support. + * + * Copyright (c) 2012 Samsung Electronics Co., Ltd. + * http://www.samsung.com + * + * Implementation of Exynos specific power domain control which is used in + * conjunction with runtime-pm. Support for both device-tree and non-device-tree + * based power domain support is included. + * + * 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. +*/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define INT_LOCAL_PWR_EN 0x7 +#define MAX_CLK_PER_DOMAIN 4 + +/* + * Exynos specific wrapper around the generic power domain + */ +struct exynos_pm_domain { + void __iomem *base; + char const *name; + bool is_off; + struct generic_pm_domain pd; + struct clk *oscclk; + struct clk *clk[MAX_CLK_PER_DOMAIN]; + struct clk *pclk[MAX_CLK_PER_DOMAIN]; + struct clk *asb_clk[MAX_CLK_PER_DOMAIN]; +}; + +static int exynos_pd_power(struct generic_pm_domain *domain, bool power_on) +{ + struct exynos_pm_domain *pd; + void __iomem *base; + u32 timeout, pwr; + char *op; + int i; + + pd = container_of(domain, struct exynos_pm_domain, pd); + base = pd->base; + + for (i = 0; i < MAX_CLK_PER_DOMAIN; i++) { + if (IS_ERR(pd->asb_clk[i])) + break; + clk_prepare_enable(pd->asb_clk[i]); + } + + /* Set oscclk before powering off a domain*/ + if (!power_on) { + for (i = 0; i < MAX_CLK_PER_DOMAIN; i++) { + if (IS_ERR(pd->clk[i])) + break; + pd->pclk[i] = clk_get_parent(pd->clk[i]); + if (clk_set_parent(pd->clk[i], pd->oscclk)) + pr_err("%s: error setting oscclk as parent to clock %d\n", + pd->name, i); + } + } + + pwr = power_on ? INT_LOCAL_PWR_EN : 0; + __raw_writel(pwr, base); + + /* Wait max 1ms */ + timeout = 10; + + while ((__raw_readl(base + 0x4) & INT_LOCAL_PWR_EN) != pwr) { + if (!timeout) { + op = (power_on) ? "enable" : "disable"; + pr_err("Power domain %s %s failed\n", domain->name, op); + return -ETIMEDOUT; + } + timeout--; + cpu_relax(); + usleep_range(80, 100); + } + + /* Restore clocks after powering on a domain*/ + if (power_on) { + for (i = 0; i < MAX_CLK_PER_DOMAIN; i++) { + if (IS_ERR(pd->clk[i])) + break; + + if (IS_ERR(pd->pclk[i])) + continue; /* Skip on first power up */ + if (clk_set_parent(pd->clk[i], pd->pclk[i])) + pr_err("%s: error setting parent to clock%d\n", + pd->name, i); + } + } + + for (i = 0; i < MAX_CLK_PER_DOMAIN; i++) { + if (IS_ERR(pd->asb_clk[i])) + break; + clk_disable_unprepare(pd->asb_clk[i]); + } + + return 0; +} + +static int exynos_pd_power_on(struct generic_pm_domain *domain) +{ + return exynos_pd_power(domain, true); +} + +static int exynos_pd_power_off(struct generic_pm_domain *domain) +{ + return exynos_pd_power(domain, false); +} + +static __init int exynos4_pm_init_power_domain(void) +{ + struct device_node *np; + + for_each_compatible_node(np, NULL, "samsung,exynos4210-pd") { + struct exynos_pm_domain *pd; + int on, i; + + pd = kzalloc(sizeof(*pd), GFP_KERNEL); + if (!pd) { + pr_err("%s: failed to allocate memory for domain\n", + __func__); + of_node_put(np); + return -ENOMEM; + } + pd->pd.name = kstrdup_const(strrchr(np->full_name, '/') + 1, + GFP_KERNEL); + if (!pd->pd.name) { + kfree(pd); + of_node_put(np); + return -ENOMEM; + } + + pd->name = pd->pd.name; + pd->base = of_iomap(np, 0); + if (!pd->base) { + pr_warn("%s: failed to map memory\n", __func__); + kfree_const(pd->pd.name); + kfree(pd); + continue; + } + + pd->pd.power_off = exynos_pd_power_off; + pd->pd.power_on = exynos_pd_power_on; + + for (i = 0; i < MAX_CLK_PER_DOMAIN; i++) { + char clk_name[8]; + + snprintf(clk_name, sizeof(clk_name), "asb%d", i); + pd->asb_clk[i] = of_clk_get_by_name(np, clk_name); + if (IS_ERR(pd->asb_clk[i])) + break; + } + + pd->oscclk = of_clk_get_by_name(np, "oscclk"); + if (IS_ERR(pd->oscclk)) + goto no_clk; + + for (i = 0; i < MAX_CLK_PER_DOMAIN; i++) { + char clk_name[8]; + + snprintf(clk_name, sizeof(clk_name), "clk%d", i); + pd->clk[i] = of_clk_get_by_name(np, clk_name); + if (IS_ERR(pd->clk[i])) + break; + /* + * Skip setting parent on first power up. + * The parent at this time may not be useful at all. + */ + pd->pclk[i] = ERR_PTR(-EINVAL); + } + + if (IS_ERR(pd->clk[0])) + clk_put(pd->oscclk); + +no_clk: + on = __raw_readl(pd->base + 0x4) & INT_LOCAL_PWR_EN; + + pm_genpd_init(&pd->pd, NULL, !on); + of_genpd_add_provider_simple(np, &pd->pd); + } + + /* Assign the child power domains to their parents */ + for_each_compatible_node(np, NULL, "samsung,exynos4210-pd") { + struct generic_pm_domain *child_domain, *parent_domain; + struct of_phandle_args args; + + args.np = np; + args.args_count = 0; + child_domain = of_genpd_get_from_provider(&args); + if (IS_ERR(child_domain)) + continue; + + if (of_parse_phandle_with_args(np, "power-domains", + "#power-domain-cells", 0, &args) != 0) + continue; + + parent_domain = of_genpd_get_from_provider(&args); + if (IS_ERR(parent_domain)) + continue; + + if (pm_genpd_add_subdomain(parent_domain, child_domain)) + pr_warn("%s failed to add subdomain: %s\n", + parent_domain->name, child_domain->name); + else + pr_info("%s has as child subdomain: %s.\n", + parent_domain->name, child_domain->name); + } + + return 0; +} +core_initcall(exynos4_pm_init_power_domain); -- cgit v0.10.2 From c028e175713698b88694c2075853963d07c81c13 Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Tue, 10 May 2016 15:52:21 +0200 Subject: soc: samsung: pm_domains: Prepare for supporting ARMv8 Exynos The ARMv8 Exynos family (Exynos5433 and Exynos7420) uses different value (0xf instead of 0x7) for controlling the power domain on/off registers (both for control and for status). Choose the value depending on the compatible. This prepares the driver for supporting ARMv8 SoCs. Signed-off-by: Krzysztof Kozlowski Reviewed-by: Javier Martinez Canillas diff --git a/drivers/soc/samsung/pm_domains.c b/drivers/soc/samsung/pm_domains.c index 875a2ba..f60515e 100644 --- a/drivers/soc/samsung/pm_domains.c +++ b/drivers/soc/samsung/pm_domains.c @@ -23,9 +23,13 @@ #include #include -#define INT_LOCAL_PWR_EN 0x7 #define MAX_CLK_PER_DOMAIN 4 +struct exynos_pm_domain_config { + /* Value for LOCAL_PWR_CFG and STATUS fields for each domain */ + u32 local_pwr_cfg; +}; + /* * Exynos specific wrapper around the generic power domain */ @@ -38,6 +42,7 @@ struct exynos_pm_domain { struct clk *clk[MAX_CLK_PER_DOMAIN]; struct clk *pclk[MAX_CLK_PER_DOMAIN]; struct clk *asb_clk[MAX_CLK_PER_DOMAIN]; + u32 local_pwr_cfg; }; static int exynos_pd_power(struct generic_pm_domain *domain, bool power_on) @@ -69,13 +74,13 @@ static int exynos_pd_power(struct generic_pm_domain *domain, bool power_on) } } - pwr = power_on ? INT_LOCAL_PWR_EN : 0; + pwr = power_on ? pd->local_pwr_cfg : 0; __raw_writel(pwr, base); /* Wait max 1ms */ timeout = 10; - while ((__raw_readl(base + 0x4) & INT_LOCAL_PWR_EN) != pwr) { + while ((__raw_readl(base + 0x4) & pd->local_pwr_cfg) != pwr) { if (!timeout) { op = (power_on) ? "enable" : "disable"; pr_err("Power domain %s %s failed\n", domain->name, op); @@ -119,14 +124,30 @@ static int exynos_pd_power_off(struct generic_pm_domain *domain) return exynos_pd_power(domain, false); } +static const struct exynos_pm_domain_config exynos4210_cfg __initconst = { + .local_pwr_cfg = 0x7, +}; + +static const struct of_device_id exynos_pm_domain_of_match[] __initconst = { + { + .compatible = "samsung,exynos4210-pd", + .data = &exynos4210_cfg, + }, + { }, +}; + static __init int exynos4_pm_init_power_domain(void) { struct device_node *np; + const struct of_device_id *match; - for_each_compatible_node(np, NULL, "samsung,exynos4210-pd") { + for_each_matching_node_and_match(np, exynos_pm_domain_of_match, &match) { + const struct exynos_pm_domain_config *pm_domain_cfg; struct exynos_pm_domain *pd; int on, i; + pm_domain_cfg = match->data; + pd = kzalloc(sizeof(*pd), GFP_KERNEL); if (!pd) { pr_err("%s: failed to allocate memory for domain\n", @@ -153,6 +174,7 @@ static __init int exynos4_pm_init_power_domain(void) pd->pd.power_off = exynos_pd_power_off; pd->pd.power_on = exynos_pd_power_on; + pd->local_pwr_cfg = pm_domain_cfg->local_pwr_cfg; for (i = 0; i < MAX_CLK_PER_DOMAIN; i++) { char clk_name[8]; @@ -185,14 +207,14 @@ static __init int exynos4_pm_init_power_domain(void) clk_put(pd->oscclk); no_clk: - on = __raw_readl(pd->base + 0x4) & INT_LOCAL_PWR_EN; + on = __raw_readl(pd->base + 0x4) & pd->local_pwr_cfg; pm_genpd_init(&pd->pd, NULL, !on); of_genpd_add_provider_simple(np, &pd->pd); } /* Assign the child power domains to their parents */ - for_each_compatible_node(np, NULL, "samsung,exynos4210-pd") { + for_each_matching_node(np, exynos_pm_domain_of_match) { struct generic_pm_domain *child_domain, *parent_domain; struct of_phandle_args args; -- cgit v0.10.2 From 9479f7cc91879c6ba75e70da41c4c9fe7842b342 Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Tue, 10 May 2016 16:31:26 +0200 Subject: soc: samsung: pm_domains: Enable COMPILE_TEST for build coverage Introduce a platform selectable symbol EXYNOS_PM_DOMAINS which can be also toggled on by COMPILE_TEST for some build coverage. Signed-off-by: Krzysztof Kozlowski diff --git a/arch/arm/mach-exynos/Kconfig b/arch/arm/mach-exynos/Kconfig index e65aa7d..58b334f 100644 --- a/arch/arm/mach-exynos/Kconfig +++ b/arch/arm/mach-exynos/Kconfig @@ -19,6 +19,7 @@ menuconfig ARCH_EXYNOS select EXYNOS_THERMAL select EXYNOS_PMU select EXYNOS_SROM + select EXYNOS_PM_DOMAINS if PM_GENERIC_DOMAINS select HAVE_ARM_SCU if SMP select HAVE_S3C2410_I2C if I2C select HAVE_S3C2410_WATCHDOG if WATCHDOG diff --git a/drivers/soc/samsung/Kconfig b/drivers/soc/samsung/Kconfig index d7fc123..2455339 100644 --- a/drivers/soc/samsung/Kconfig +++ b/drivers/soc/samsung/Kconfig @@ -10,4 +10,8 @@ config EXYNOS_PMU bool "Exynos PMU controller driver" if COMPILE_TEST depends on (ARM && ARCH_EXYNOS) || ((ARM || ARM64) && COMPILE_TEST) +config EXYNOS_PM_DOMAINS + bool "Exynos PM domains" if COMPILE_TEST + depends on PM_GENERIC_DOMAINS || COMPILE_TEST + endif diff --git a/drivers/soc/samsung/Makefile b/drivers/soc/samsung/Makefile index 8aead58..3619f2e 100644 --- a/drivers/soc/samsung/Makefile +++ b/drivers/soc/samsung/Makefile @@ -1,3 +1,3 @@ obj-$(CONFIG_EXYNOS_PMU) += exynos-pmu.o exynos3250-pmu.o exynos4-pmu.o \ exynos5250-pmu.o exynos5420-pmu.o -obj-$(CONFIG_PM_GENERIC_DOMAINS) += pm_domains.o +obj-$(CONFIG_EXYNOS_PM_DOMAINS) += pm_domains.o -- cgit v0.10.2 From 8d5b5d5ce58ee1b90110f4e358eefe3c3a6b08a2 Mon Sep 17 00:00:00 2001 From: Masahiro Yamada Date: Sun, 1 May 2016 19:36:57 +0900 Subject: reset: add devm_reset_controller_register API Add a device managed API for reset_controller_register(). This helps in reducing code in .remove callbacks and sometimes dropping .remove callbacks entirely. Signed-off-by: Masahiro Yamada Acked-by: Laxman Dewangan Signed-off-by: Philipp Zabel diff --git a/Documentation/driver-model/devres.txt b/Documentation/driver-model/devres.txt index c63eea0..f5e5223 100644 --- a/Documentation/driver-model/devres.txt +++ b/Documentation/driver-model/devres.txt @@ -352,6 +352,10 @@ REGULATOR devm_regulator_put() devm_regulator_register() +RESET + devm_reset_control_get() + devm_reset_controller_register() + SLAVE DMA ENGINE devm_acpi_dma_controller_register() diff --git a/drivers/reset/core.c b/drivers/reset/core.c index 72b32bd..395dc9c 100644 --- a/drivers/reset/core.c +++ b/drivers/reset/core.c @@ -93,6 +93,43 @@ void reset_controller_unregister(struct reset_controller_dev *rcdev) } EXPORT_SYMBOL_GPL(reset_controller_unregister); +static void devm_reset_controller_release(struct device *dev, void *res) +{ + reset_controller_unregister(*(struct reset_controller_dev **)res); +} + +/** + * devm_reset_controller_register - resource managed reset_controller_register() + * @dev: device that is registering this reset controller + * @rcdev: a pointer to the initialized reset controller device + * + * Managed reset_controller_register(). For reset controllers registered by + * this function, reset_controller_unregister() is automatically called on + * driver detach. See reset_controller_register() for more information. + */ +int devm_reset_controller_register(struct device *dev, + struct reset_controller_dev *rcdev) +{ + struct reset_controller_dev **rcdevp; + int ret; + + rcdevp = devres_alloc(devm_reset_controller_release, sizeof(*rcdevp), + GFP_KERNEL); + if (!rcdevp) + return -ENOMEM; + + ret = reset_controller_register(rcdev); + if (!ret) { + *rcdevp = rcdev; + devres_add(dev, rcdevp); + } else { + devres_free(rcdevp); + } + + return ret; +} +EXPORT_SYMBOL_GPL(devm_reset_controller_register); + /** * reset_control_reset - reset the controlled device * @rstc: reset controller diff --git a/include/linux/reset-controller.h b/include/linux/reset-controller.h index b91ba93..db1fe67 100644 --- a/include/linux/reset-controller.h +++ b/include/linux/reset-controller.h @@ -53,4 +53,8 @@ struct reset_controller_dev { int reset_controller_register(struct reset_controller_dev *rcdev); void reset_controller_unregister(struct reset_controller_dev *rcdev); +struct device; +int devm_reset_controller_register(struct device *dev, + struct reset_controller_dev *rcdev); + #endif -- cgit v0.10.2 From 56865f452a11843e54c215294a436acee31b0c0b Mon Sep 17 00:00:00 2001 From: Masahiro Yamada Date: Sun, 1 May 2016 19:36:58 +0900 Subject: reset: ath79: use devm_reset_controller_register() Use devm_reset_controller_register() for the reset controller registration and remove the unregister call from the .remove callback. Signed-off-by: Masahiro Yamada Signed-off-by: Philipp Zabel diff --git a/drivers/reset/reset-ath79.c b/drivers/reset/reset-ath79.c index ccb940a..16d410c 100644 --- a/drivers/reset/reset-ath79.c +++ b/drivers/reset/reset-ath79.c @@ -112,7 +112,7 @@ static int ath79_reset_probe(struct platform_device *pdev) ath79_reset->rcdev.of_reset_n_cells = 1; ath79_reset->rcdev.nr_resets = 32; - err = reset_controller_register(&ath79_reset->rcdev); + err = devm_reset_controller_register(&pdev->dev, &ath79_reset->rcdev); if (err) return err; @@ -131,7 +131,6 @@ static int ath79_reset_remove(struct platform_device *pdev) struct ath79_reset *ath79_reset = platform_get_drvdata(pdev); unregister_restart_handler(&ath79_reset->restart_nb); - reset_controller_unregister(&ath79_reset->rcdev); return 0; } -- cgit v0.10.2 From 1b1447f49312ac0777fcbc85f0d70b809e458a48 Mon Sep 17 00:00:00 2001 From: Masahiro Yamada Date: Sun, 1 May 2016 19:37:00 +0900 Subject: reset: pistachio: use devm_reset_controller_register() Use devm_reset_controller_register() for the reset controller registration and drop the .remove callback. Signed-off-by: Masahiro Yamada Signed-off-by: Philipp Zabel diff --git a/drivers/reset/reset-pistachio.c b/drivers/reset/reset-pistachio.c index 72a97a1..bbc4c06 100644 --- a/drivers/reset/reset-pistachio.c +++ b/drivers/reset/reset-pistachio.c @@ -121,16 +121,7 @@ static int pistachio_reset_probe(struct platform_device *pdev) rd->rcdev.ops = &pistachio_reset_ops; rd->rcdev.of_node = np; - return reset_controller_register(&rd->rcdev); -} - -static int pistachio_reset_remove(struct platform_device *pdev) -{ - struct pistachio_reset_data *data = platform_get_drvdata(pdev); - - reset_controller_unregister(&data->rcdev); - - return 0; + return devm_reset_controller_register(dev, &rd->rcdev); } static const struct of_device_id pistachio_reset_dt_ids[] = { @@ -141,7 +132,6 @@ MODULE_DEVICE_TABLE(of, pistachio_reset_dt_ids); static struct platform_driver pistachio_reset_driver = { .probe = pistachio_reset_probe, - .remove = pistachio_reset_remove, .driver = { .name = "pistachio-reset", .of_match_table = pistachio_reset_dt_ids, -- cgit v0.10.2 From 2f38a88c7c9b0bf1407eba3b0e8e74abfdbe446a Mon Sep 17 00:00:00 2001 From: Masahiro Yamada Date: Sun, 1 May 2016 19:37:01 +0900 Subject: reset: sunxi: use devm_reset_controller_register() Use devm_reset_controller_register() for the reset controller registration and drop the .remove callback. Signed-off-by: Masahiro Yamada Signed-off-by: Philipp Zabel diff --git a/drivers/reset/reset-sunxi.c b/drivers/reset/reset-sunxi.c index 677f865..3080190 100644 --- a/drivers/reset/reset-sunxi.c +++ b/drivers/reset/reset-sunxi.c @@ -165,21 +165,11 @@ static int sunxi_reset_probe(struct platform_device *pdev) data->rcdev.ops = &sunxi_reset_ops; data->rcdev.of_node = pdev->dev.of_node; - return reset_controller_register(&data->rcdev); -} - -static int sunxi_reset_remove(struct platform_device *pdev) -{ - struct sunxi_reset_data *data = platform_get_drvdata(pdev); - - reset_controller_unregister(&data->rcdev); - - return 0; + return devm_reset_controller_register(&pdev->dev, &data->rcdev); } static struct platform_driver sunxi_reset_driver = { .probe = sunxi_reset_probe, - .remove = sunxi_reset_remove, .driver = { .name = "sunxi-reset", .of_match_table = sunxi_reset_dt_ids, -- cgit v0.10.2 From dc22e08ef71462e56e51e96359105a75097e745c Mon Sep 17 00:00:00 2001 From: Masahiro Yamada Date: Sun, 1 May 2016 19:37:02 +0900 Subject: reset: socfpga: use devm_reset_controller_register() Use devm_reset_controller_register() for the reset controller registration and drop the .remove callback. Signed-off-by: Masahiro Yamada Signed-off-by: Philipp Zabel diff --git a/drivers/reset/reset-socfpga.c b/drivers/reset/reset-socfpga.c index cd05a70..12add9b 100644 --- a/drivers/reset/reset-socfpga.c +++ b/drivers/reset/reset-socfpga.c @@ -134,16 +134,7 @@ static int socfpga_reset_probe(struct platform_device *pdev) data->rcdev.ops = &socfpga_reset_ops; data->rcdev.of_node = pdev->dev.of_node; - return reset_controller_register(&data->rcdev); -} - -static int socfpga_reset_remove(struct platform_device *pdev) -{ - struct socfpga_reset_data *data = platform_get_drvdata(pdev); - - reset_controller_unregister(&data->rcdev); - - return 0; + return devm_reset_controller_register(dev, &data->rcdev); } static const struct of_device_id socfpga_reset_dt_ids[] = { @@ -153,7 +144,6 @@ static const struct of_device_id socfpga_reset_dt_ids[] = { static struct platform_driver socfpga_reset_driver = { .probe = socfpga_reset_probe, - .remove = socfpga_reset_remove, .driver = { .name = "socfpga-reset", .of_match_table = socfpga_reset_dt_ids, -- cgit v0.10.2 From da1fda2a2398960f7f7eb1ee3fb0e50155fde275 Mon Sep 17 00:00:00 2001 From: Masahiro Yamada Date: Sun, 1 May 2016 19:37:03 +0900 Subject: reset: zynq: use devm_reset_controller_register() Use devm_reset_controller_register() for the reset controller registration and drop the .remove callback. Signed-off-by: Masahiro Yamada Signed-off-by: Philipp Zabel diff --git a/drivers/reset/reset-zynq.c b/drivers/reset/reset-zynq.c index a7e87bc..138f2f2 100644 --- a/drivers/reset/reset-zynq.c +++ b/drivers/reset/reset-zynq.c @@ -122,16 +122,7 @@ static int zynq_reset_probe(struct platform_device *pdev) priv->rcdev.ops = &zynq_reset_ops; priv->rcdev.of_node = pdev->dev.of_node; - return reset_controller_register(&priv->rcdev); -} - -static int zynq_reset_remove(struct platform_device *pdev) -{ - struct zynq_reset_data *priv = platform_get_drvdata(pdev); - - reset_controller_unregister(&priv->rcdev); - - return 0; + return devm_reset_controller_register(&pdev->dev, &priv->rcdev); } static const struct of_device_id zynq_reset_dt_ids[] = { @@ -141,7 +132,6 @@ static const struct of_device_id zynq_reset_dt_ids[] = { static struct platform_driver zynq_reset_driver = { .probe = zynq_reset_probe, - .remove = zynq_reset_remove, .driver = { .name = KBUILD_MODNAME, .of_match_table = zynq_reset_dt_ids, -- cgit v0.10.2 From 998cd4637f698b04a464f0fbb25e814651979135 Mon Sep 17 00:00:00 2001 From: Masahiro Yamada Date: Tue, 3 May 2016 15:29:52 +0900 Subject: reset: fix Kconfig menu to include reset drivers in sub-menu In "make menuconfig", reset drivers are currently lined up together with the reset sub-system menu, like this: -*- Reset Controller Support ---- < > Hi6220 Reset Driver (It also means, the menu "Reset Controller Support" is always empty.) "Hi6220 Reset Driver" should go into the sub-menu of the "Reset Controller Support". Signed-off-by: Masahiro Yamada Signed-off-by: Philipp Zabel diff --git a/drivers/reset/Kconfig b/drivers/reset/Kconfig index 0b2733d..ab37f4d 100644 --- a/drivers/reset/Kconfig +++ b/drivers/reset/Kconfig @@ -12,8 +12,12 @@ menuconfig RESET_CONTROLLER If unsure, say no. +if RESET_CONTROLLER + config RESET_OXNAS bool source "drivers/reset/sti/Kconfig" source "drivers/reset/hisilicon/Kconfig" + +endif diff --git a/drivers/reset/sti/Kconfig b/drivers/reset/sti/Kconfig index f8c15a3..6131785 100644 --- a/drivers/reset/sti/Kconfig +++ b/drivers/reset/sti/Kconfig @@ -2,7 +2,6 @@ if ARCH_STI config STI_RESET_SYSCFG bool - select RESET_CONTROLLER config STIH415_RESET bool -- cgit v0.10.2 From 42fa905bd901e55c78ed4a69b9e2cf27a49a886b Mon Sep 17 00:00:00 2001 From: Neil Armstrong Date: Fri, 20 May 2016 14:21:37 +0200 Subject: reset: oxnas: Use devm register API and get rid of platform remove Use the brand new devm_reset_controller_register() API to get rid of the platform driver remove callback. Signed-off-by: Neil Armstrong Signed-off-by: Philipp Zabel diff --git a/drivers/reset/reset-oxnas.c b/drivers/reset/reset-oxnas.c index c60fb2d..9449805 100644 --- a/drivers/reset/reset-oxnas.c +++ b/drivers/reset/reset-oxnas.c @@ -112,21 +112,11 @@ static int oxnas_reset_probe(struct platform_device *pdev) data->rcdev.ops = &oxnas_reset_ops; data->rcdev.of_node = pdev->dev.of_node; - return reset_controller_register(&data->rcdev); -} - -static int oxnas_reset_remove(struct platform_device *pdev) -{ - struct oxnas_reset *data = platform_get_drvdata(pdev); - - reset_controller_unregister(&data->rcdev); - - return 0; + return devm_reset_controller_register(&pdev->dev, &data->rcdev); } static struct platform_driver oxnas_reset_driver = { .probe = oxnas_reset_probe, - .remove = oxnas_reset_remove, .driver = { .name = "oxnas-reset", .of_match_table = oxnas_reset_dt_ids, -- cgit v0.10.2 From 34642650e5bc052674b982433631fcc619237225 Mon Sep 17 00:00:00 2001 From: Florian Fainelli Date: Fri, 20 May 2016 11:35:57 -0700 Subject: soc: Move brcmstb to bcm/brcmstb Unify the different Broadcom SoCs directory and have everybody live under drivers/soc/bcm/*. Acked-by: Scott Branden Signed-off-by: Florian Fainelli diff --git a/drivers/soc/Kconfig b/drivers/soc/Kconfig index cb58ef0..91d5c05 100644 --- a/drivers/soc/Kconfig +++ b/drivers/soc/Kconfig @@ -1,7 +1,6 @@ menu "SOC (System On Chip) specific Drivers" source "drivers/soc/bcm/Kconfig" -source "drivers/soc/brcmstb/Kconfig" source "drivers/soc/fsl/qe/Kconfig" source "drivers/soc/mediatek/Kconfig" source "drivers/soc/qcom/Kconfig" diff --git a/drivers/soc/Makefile b/drivers/soc/Makefile index 380230f..b75e3bd 100644 --- a/drivers/soc/Makefile +++ b/drivers/soc/Makefile @@ -3,7 +3,6 @@ # obj-y += bcm/ -obj-$(CONFIG_SOC_BRCMSTB) += brcmstb/ obj-$(CONFIG_ARCH_DOVE) += dove/ obj-$(CONFIG_MACH_DOVE) += dove/ obj-y += fsl/ diff --git a/drivers/soc/bcm/Kconfig b/drivers/soc/bcm/Kconfig index 3066ede..97156ae 100644 --- a/drivers/soc/bcm/Kconfig +++ b/drivers/soc/bcm/Kconfig @@ -1,3 +1,5 @@ +menu "Broadcom SoC drivers" + config RASPBERRYPI_POWER bool "Raspberry Pi power domain driver" depends on ARCH_BCM2835 || COMPILE_TEST @@ -7,3 +9,16 @@ config RASPBERRYPI_POWER help This enables support for the RPi power domains which can be enabled or disabled via the RPi firmware. + +config SOC_BRCMSTB + bool "Broadcom STB SoC drivers" + depends on ARM + select SOC_BUS + help + Enables drivers for the Broadcom Set-Top Box (STB) series of chips. + This option alone enables only some support code, while the drivers + can be enabled individually within this menu. + + If unsure, say N. + +endmenu diff --git a/drivers/soc/bcm/Makefile b/drivers/soc/bcm/Makefile index 63aa3eb..dc4fced 100644 --- a/drivers/soc/bcm/Makefile +++ b/drivers/soc/bcm/Makefile @@ -1 +1,2 @@ obj-$(CONFIG_RASPBERRYPI_POWER) += raspberrypi-power.o +obj-$(CONFIG_SOC_BRCMSTB) += brcmstb/ diff --git a/drivers/soc/bcm/brcmstb/Makefile b/drivers/soc/bcm/brcmstb/Makefile new file mode 100644 index 0000000..9120b27 --- /dev/null +++ b/drivers/soc/bcm/brcmstb/Makefile @@ -0,0 +1 @@ +obj-y += common.o biuctrl.o diff --git a/drivers/soc/bcm/brcmstb/biuctrl.c b/drivers/soc/bcm/brcmstb/biuctrl.c new file mode 100644 index 0000000..9049c076f --- /dev/null +++ b/drivers/soc/bcm/brcmstb/biuctrl.c @@ -0,0 +1,116 @@ +/* + * Broadcom STB SoCs Bus Unit Interface controls + * + * Copyright (C) 2015, Broadcom Corporation + * + * 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. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#define pr_fmt(fmt) "brcmstb: " KBUILD_MODNAME ": " fmt + +#include +#include +#include +#include + +#define CPU_CREDIT_REG_OFFSET 0x184 +#define CPU_CREDIT_REG_MCPx_WR_PAIRING_EN_MASK 0x70000000 + +static void __iomem *cpubiuctrl_base; +static bool mcp_wr_pairing_en; + +static int __init mcp_write_pairing_set(void) +{ + u32 creds = 0; + + if (!cpubiuctrl_base) + return -1; + + creds = readl_relaxed(cpubiuctrl_base + CPU_CREDIT_REG_OFFSET); + if (mcp_wr_pairing_en) { + pr_info("MCP: Enabling write pairing\n"); + writel_relaxed(creds | CPU_CREDIT_REG_MCPx_WR_PAIRING_EN_MASK, + cpubiuctrl_base + CPU_CREDIT_REG_OFFSET); + } else if (creds & CPU_CREDIT_REG_MCPx_WR_PAIRING_EN_MASK) { + pr_info("MCP: Disabling write pairing\n"); + writel_relaxed(creds & ~CPU_CREDIT_REG_MCPx_WR_PAIRING_EN_MASK, + cpubiuctrl_base + CPU_CREDIT_REG_OFFSET); + } else { + pr_info("MCP: Write pairing already disabled\n"); + } + + return 0; +} + +static int __init setup_hifcpubiuctrl_regs(void) +{ + struct device_node *np; + int ret = 0; + + np = of_find_compatible_node(NULL, NULL, "brcm,brcmstb-cpu-biu-ctrl"); + if (!np) { + pr_err("missing BIU control node\n"); + return -ENODEV; + } + + cpubiuctrl_base = of_iomap(np, 0); + if (!cpubiuctrl_base) { + pr_err("failed to remap BIU control base\n"); + ret = -ENOMEM; + goto out; + } + + mcp_wr_pairing_en = of_property_read_bool(np, "brcm,write-pairing"); +out: + of_node_put(np); + return ret; +} + +#ifdef CONFIG_PM_SLEEP +static u32 cpu_credit_reg_dump; /* for save/restore */ + +static int brcmstb_cpu_credit_reg_suspend(void) +{ + if (cpubiuctrl_base) + cpu_credit_reg_dump = + readl_relaxed(cpubiuctrl_base + CPU_CREDIT_REG_OFFSET); + return 0; +} + +static void brcmstb_cpu_credit_reg_resume(void) +{ + if (cpubiuctrl_base) + writel_relaxed(cpu_credit_reg_dump, + cpubiuctrl_base + CPU_CREDIT_REG_OFFSET); +} + +static struct syscore_ops brcmstb_cpu_credit_syscore_ops = { + .suspend = brcmstb_cpu_credit_reg_suspend, + .resume = brcmstb_cpu_credit_reg_resume, +}; +#endif + + +void __init brcmstb_biuctrl_init(void) +{ + int ret; + + setup_hifcpubiuctrl_regs(); + + ret = mcp_write_pairing_set(); + if (ret) { + pr_err("MCP: Unable to disable write pairing!\n"); + return; + } + +#ifdef CONFIG_PM_SLEEP + register_syscore_ops(&brcmstb_cpu_credit_syscore_ops); +#endif +} diff --git a/drivers/soc/bcm/brcmstb/common.c b/drivers/soc/bcm/brcmstb/common.c new file mode 100644 index 0000000..94e7335 --- /dev/null +++ b/drivers/soc/bcm/brcmstb/common.c @@ -0,0 +1,99 @@ +/* + * Copyright © 2014 NVIDIA Corporation + * Copyright © 2015 Broadcom Corporation + * + * 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. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include +#include +#include +#include +#include + +#include + +static u32 family_id; +static u32 product_id; + +static const struct of_device_id brcmstb_machine_match[] = { + { .compatible = "brcm,brcmstb", }, + { } +}; + +bool soc_is_brcmstb(void) +{ + struct device_node *root; + + root = of_find_node_by_path("/"); + if (!root) + return false; + + return of_match_node(brcmstb_machine_match, root) != NULL; +} + +static const struct of_device_id sun_top_ctrl_match[] = { + { .compatible = "brcm,brcmstb-sun-top-ctrl", }, + { } +}; + +static int __init brcmstb_soc_device_init(void) +{ + struct soc_device_attribute *soc_dev_attr; + struct soc_device *soc_dev; + struct device_node *sun_top_ctrl; + void __iomem *sun_top_ctrl_base; + int ret = 0; + + sun_top_ctrl = of_find_matching_node(NULL, sun_top_ctrl_match); + if (!sun_top_ctrl) + return -ENODEV; + + sun_top_ctrl_base = of_iomap(sun_top_ctrl, 0); + if (!sun_top_ctrl_base) + return -ENODEV; + + family_id = readl(sun_top_ctrl_base); + product_id = readl(sun_top_ctrl_base + 0x4); + + soc_dev_attr = kzalloc(sizeof(*soc_dev_attr), GFP_KERNEL); + if (!soc_dev_attr) { + ret = -ENOMEM; + goto out; + } + + soc_dev_attr->family = kasprintf(GFP_KERNEL, "%x", + family_id >> 28 ? + family_id >> 16 : family_id >> 8); + soc_dev_attr->soc_id = kasprintf(GFP_KERNEL, "%x", + product_id >> 28 ? + product_id >> 16 : product_id >> 8); + soc_dev_attr->revision = kasprintf(GFP_KERNEL, "%c%d", + ((product_id & 0xf0) >> 4) + 'A', + product_id & 0xf); + + soc_dev = soc_device_register(soc_dev_attr); + if (IS_ERR(soc_dev)) { + kfree(soc_dev_attr->family); + kfree(soc_dev_attr->soc_id); + kfree(soc_dev_attr->revision); + kfree(soc_dev_attr); + ret = -ENODEV; + goto out; + } + + return 0; + +out: + iounmap(sun_top_ctrl_base); + return ret; +} +arch_initcall(brcmstb_soc_device_init); diff --git a/drivers/soc/brcmstb/Kconfig b/drivers/soc/brcmstb/Kconfig deleted file mode 100644 index 7fec3b4..0000000 --- a/drivers/soc/brcmstb/Kconfig +++ /dev/null @@ -1,10 +0,0 @@ -menuconfig SOC_BRCMSTB - bool "Broadcom STB SoC drivers" - depends on ARM - select SOC_BUS - help - Enables drivers for the Broadcom Set-Top Box (STB) series of chips. - This option alone enables only some support code, while the drivers - can be enabled individually within this menu. - - If unsure, say N. diff --git a/drivers/soc/brcmstb/Makefile b/drivers/soc/brcmstb/Makefile deleted file mode 100644 index 9120b27..0000000 --- a/drivers/soc/brcmstb/Makefile +++ /dev/null @@ -1 +0,0 @@ -obj-y += common.o biuctrl.o diff --git a/drivers/soc/brcmstb/biuctrl.c b/drivers/soc/brcmstb/biuctrl.c deleted file mode 100644 index 9049c076f..0000000 --- a/drivers/soc/brcmstb/biuctrl.c +++ /dev/null @@ -1,116 +0,0 @@ -/* - * Broadcom STB SoCs Bus Unit Interface controls - * - * Copyright (C) 2015, Broadcom Corporation - * - * 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. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - */ - -#define pr_fmt(fmt) "brcmstb: " KBUILD_MODNAME ": " fmt - -#include -#include -#include -#include - -#define CPU_CREDIT_REG_OFFSET 0x184 -#define CPU_CREDIT_REG_MCPx_WR_PAIRING_EN_MASK 0x70000000 - -static void __iomem *cpubiuctrl_base; -static bool mcp_wr_pairing_en; - -static int __init mcp_write_pairing_set(void) -{ - u32 creds = 0; - - if (!cpubiuctrl_base) - return -1; - - creds = readl_relaxed(cpubiuctrl_base + CPU_CREDIT_REG_OFFSET); - if (mcp_wr_pairing_en) { - pr_info("MCP: Enabling write pairing\n"); - writel_relaxed(creds | CPU_CREDIT_REG_MCPx_WR_PAIRING_EN_MASK, - cpubiuctrl_base + CPU_CREDIT_REG_OFFSET); - } else if (creds & CPU_CREDIT_REG_MCPx_WR_PAIRING_EN_MASK) { - pr_info("MCP: Disabling write pairing\n"); - writel_relaxed(creds & ~CPU_CREDIT_REG_MCPx_WR_PAIRING_EN_MASK, - cpubiuctrl_base + CPU_CREDIT_REG_OFFSET); - } else { - pr_info("MCP: Write pairing already disabled\n"); - } - - return 0; -} - -static int __init setup_hifcpubiuctrl_regs(void) -{ - struct device_node *np; - int ret = 0; - - np = of_find_compatible_node(NULL, NULL, "brcm,brcmstb-cpu-biu-ctrl"); - if (!np) { - pr_err("missing BIU control node\n"); - return -ENODEV; - } - - cpubiuctrl_base = of_iomap(np, 0); - if (!cpubiuctrl_base) { - pr_err("failed to remap BIU control base\n"); - ret = -ENOMEM; - goto out; - } - - mcp_wr_pairing_en = of_property_read_bool(np, "brcm,write-pairing"); -out: - of_node_put(np); - return ret; -} - -#ifdef CONFIG_PM_SLEEP -static u32 cpu_credit_reg_dump; /* for save/restore */ - -static int brcmstb_cpu_credit_reg_suspend(void) -{ - if (cpubiuctrl_base) - cpu_credit_reg_dump = - readl_relaxed(cpubiuctrl_base + CPU_CREDIT_REG_OFFSET); - return 0; -} - -static void brcmstb_cpu_credit_reg_resume(void) -{ - if (cpubiuctrl_base) - writel_relaxed(cpu_credit_reg_dump, - cpubiuctrl_base + CPU_CREDIT_REG_OFFSET); -} - -static struct syscore_ops brcmstb_cpu_credit_syscore_ops = { - .suspend = brcmstb_cpu_credit_reg_suspend, - .resume = brcmstb_cpu_credit_reg_resume, -}; -#endif - - -void __init brcmstb_biuctrl_init(void) -{ - int ret; - - setup_hifcpubiuctrl_regs(); - - ret = mcp_write_pairing_set(); - if (ret) { - pr_err("MCP: Unable to disable write pairing!\n"); - return; - } - -#ifdef CONFIG_PM_SLEEP - register_syscore_ops(&brcmstb_cpu_credit_syscore_ops); -#endif -} diff --git a/drivers/soc/brcmstb/common.c b/drivers/soc/brcmstb/common.c deleted file mode 100644 index 94e7335..0000000 --- a/drivers/soc/brcmstb/common.c +++ /dev/null @@ -1,99 +0,0 @@ -/* - * Copyright © 2014 NVIDIA Corporation - * Copyright © 2015 Broadcom Corporation - * - * 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. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - */ - -#include -#include -#include -#include -#include -#include - -#include - -static u32 family_id; -static u32 product_id; - -static const struct of_device_id brcmstb_machine_match[] = { - { .compatible = "brcm,brcmstb", }, - { } -}; - -bool soc_is_brcmstb(void) -{ - struct device_node *root; - - root = of_find_node_by_path("/"); - if (!root) - return false; - - return of_match_node(brcmstb_machine_match, root) != NULL; -} - -static const struct of_device_id sun_top_ctrl_match[] = { - { .compatible = "brcm,brcmstb-sun-top-ctrl", }, - { } -}; - -static int __init brcmstb_soc_device_init(void) -{ - struct soc_device_attribute *soc_dev_attr; - struct soc_device *soc_dev; - struct device_node *sun_top_ctrl; - void __iomem *sun_top_ctrl_base; - int ret = 0; - - sun_top_ctrl = of_find_matching_node(NULL, sun_top_ctrl_match); - if (!sun_top_ctrl) - return -ENODEV; - - sun_top_ctrl_base = of_iomap(sun_top_ctrl, 0); - if (!sun_top_ctrl_base) - return -ENODEV; - - family_id = readl(sun_top_ctrl_base); - product_id = readl(sun_top_ctrl_base + 0x4); - - soc_dev_attr = kzalloc(sizeof(*soc_dev_attr), GFP_KERNEL); - if (!soc_dev_attr) { - ret = -ENOMEM; - goto out; - } - - soc_dev_attr->family = kasprintf(GFP_KERNEL, "%x", - family_id >> 28 ? - family_id >> 16 : family_id >> 8); - soc_dev_attr->soc_id = kasprintf(GFP_KERNEL, "%x", - product_id >> 28 ? - product_id >> 16 : product_id >> 8); - soc_dev_attr->revision = kasprintf(GFP_KERNEL, "%c%d", - ((product_id & 0xf0) >> 4) + 'A', - product_id & 0xf); - - soc_dev = soc_device_register(soc_dev_attr); - if (IS_ERR(soc_dev)) { - kfree(soc_dev_attr->family); - kfree(soc_dev_attr->soc_id); - kfree(soc_dev_attr->revision); - kfree(soc_dev_attr); - ret = -ENODEV; - goto out; - } - - return 0; - -out: - iounmap(sun_top_ctrl_base); - return ret; -} -arch_initcall(brcmstb_soc_device_init); -- cgit v0.10.2 From 168d7c4e8bb25c076ed8be67fcca84f5dcd0b2c6 Mon Sep 17 00:00:00 2001 From: John Youn Date: Tue, 31 May 2016 16:55:01 -0700 Subject: reset: Return -ENOTSUPP when not configured Prior to commit 6c96f05c8bb8 ("reset: Make [of_]reset_control_get[_foo] functions wrappers"), the "optional" functions returned -ENOTSUPP when CONFIG_RESET_CONTROLLER was not set. Revert back to the old behavior by changing the new __devm_reset_control_get() and __of_reset_control_get() functions to return ERR_PTR(-ENOTSUPP) when compiled without CONFIG_RESET_CONTROLLER. Otherwise they will return -EINVAL causing users to think that an error occurred when CONFIG_RESET_CONTROLLER is not set. Fixes: 6c96f05c8bb8 ("reset: Make [of_]reset_control_get[_foo] functions wrappers") Signed-off-by: John Youn Signed-off-by: Philipp Zabel diff --git a/include/linux/reset.h b/include/linux/reset.h index ec0306ce..067db57 100644 --- a/include/linux/reset.h +++ b/include/linux/reset.h @@ -71,14 +71,14 @@ static inline struct reset_control *__of_reset_control_get( struct device_node *node, const char *id, int index, int shared) { - return ERR_PTR(-EINVAL); + return ERR_PTR(-ENOTSUPP); } static inline struct reset_control *__devm_reset_control_get( struct device *dev, const char *id, int index, int shared) { - return ERR_PTR(-EINVAL); + return ERR_PTR(-ENOTSUPP); } #endif /* CONFIG_RESET_CONTROLLER */ -- cgit v0.10.2 From c7224dc343fda48f469e8337bea32b84f2fac41a Mon Sep 17 00:00:00 2001 From: Neil Armstrong Date: Mon, 30 May 2016 15:27:15 +0200 Subject: reset: Add support for the Amlogic Meson SoC Reset Controller This patch adds the platform driver for the Amlogic Meson SoC Reset Controller. The Meson8b and GXBB SoCs are supported. Signed-off-by: Neil Armstrong Signed-off-by: Philipp Zabel diff --git a/drivers/reset/Makefile b/drivers/reset/Makefile index f173fc3..03dc1bb 100644 --- a/drivers/reset/Makefile +++ b/drivers/reset/Makefile @@ -3,6 +3,7 @@ obj-$(CONFIG_ARCH_LPC18XX) += reset-lpc18xx.o obj-$(CONFIG_ARCH_SOCFPGA) += reset-socfpga.o obj-$(CONFIG_ARCH_BERLIN) += reset-berlin.o obj-$(CONFIG_MACH_PISTACHIO) += reset-pistachio.o +obj-$(CONFIG_ARCH_MESON) += reset-meson.o obj-$(CONFIG_ARCH_SUNXI) += reset-sunxi.o obj-$(CONFIG_ARCH_STI) += sti/ obj-$(CONFIG_ARCH_HISI) += hisilicon/ diff --git a/drivers/reset/reset-meson.c b/drivers/reset/reset-meson.c new file mode 100644 index 0000000..c32f11a --- /dev/null +++ b/drivers/reset/reset-meson.c @@ -0,0 +1,136 @@ +/* + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * GPL LICENSE SUMMARY + * + * Copyright (c) 2016 BayLibre, SAS. + * Author: Neil Armstrong + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see . + * The full GNU General Public License is included in this distribution + * in the file called COPYING. + * + * BSD LICENSE + * + * Copyright (c) 2016 BayLibre, SAS. + * Author: Neil Armstrong + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#include +#include +#include +#include +#include +#include +#include +#include + +#define REG_COUNT 8 +#define BITS_PER_REG 32 + +struct meson_reset { + void __iomem *reg_base; + struct reset_controller_dev rcdev; +}; + +static int meson_reset_reset(struct reset_controller_dev *rcdev, + unsigned long id) +{ + struct meson_reset *data = + container_of(rcdev, struct meson_reset, rcdev); + unsigned int bank = id / BITS_PER_REG; + unsigned int offset = id % BITS_PER_REG; + void __iomem *reg_addr = data->reg_base + (bank << 2); + + if (bank >= REG_COUNT) + return -EINVAL; + + writel(BIT(offset), reg_addr); + + return 0; +} + +static const struct reset_control_ops meson_reset_ops = { + .reset = meson_reset_reset, +}; + +static const struct of_device_id meson_reset_dt_ids[] = { + { .compatible = "amlogic,meson8b-reset", }, + { .compatible = "amlogic,meson-gxbb-reset", }, + { /* sentinel */ }, +}; +MODULE_DEVICE_TABLE(of, meson_reset_dt_ids); + +static int meson_reset_probe(struct platform_device *pdev) +{ + struct meson_reset *data; + struct resource *res; + + data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL); + if (!data) + return -ENOMEM; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + data->reg_base = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(data->reg_base)) + return PTR_ERR(data->reg_base); + + platform_set_drvdata(pdev, data); + + data->rcdev.owner = THIS_MODULE; + data->rcdev.nr_resets = REG_COUNT * BITS_PER_REG; + data->rcdev.ops = &meson_reset_ops; + data->rcdev.of_node = pdev->dev.of_node; + + return devm_reset_controller_register(&pdev->dev, &data->rcdev); +} + +static struct platform_driver meson_reset_driver = { + .probe = meson_reset_probe, + .driver = { + .name = "meson_reset", + .of_match_table = meson_reset_dt_ids, + }, +}; + +module_platform_driver(meson_reset_driver); + +MODULE_AUTHOR("Neil Armstrong "); +MODULE_DESCRIPTION("Amlogic Meson Reset Controller driver"); +MODULE_LICENSE("Dual BSD/GPL"); -- cgit v0.10.2 From 79795e20a184ebabf4ae743d1506cc39783caa46 Mon Sep 17 00:00:00 2001 From: Neil Armstrong Date: Mon, 30 May 2016 15:27:16 +0200 Subject: dt-bindings: reset: Add bindings for the Meson SoC Reset Controller Add DT bindings for the Meson SoC Reset Controller documentation and the associated include file. Acked-by: Rob Herring Signed-off-by: Neil Armstrong Signed-off-by: Philipp Zabel diff --git a/Documentation/devicetree/bindings/reset/amlogic,meson-reset.txt b/Documentation/devicetree/bindings/reset/amlogic,meson-reset.txt new file mode 100644 index 0000000..e746b63 --- /dev/null +++ b/Documentation/devicetree/bindings/reset/amlogic,meson-reset.txt @@ -0,0 +1,18 @@ +Amlogic Meson SoC Reset Controller +======================================= + +Please also refer to reset.txt in this directory for common reset +controller binding usage. + +Required properties: +- compatible: Should be "amlogic,meson8b-reset" or "amlogic,meson-gxbb-reset" +- reg: should contain the register address base +- #reset-cells: 1, see below + +example: + +reset: reset-controller { + compatible = "amlogic,meson-gxbb-reset"; + reg = <0x0 0x04404 0x0 0x20>; + #reset-cells = <1>; +}; diff --git a/include/dt-bindings/reset/amlogic,meson-gxbb-reset.h b/include/dt-bindings/reset/amlogic,meson-gxbb-reset.h new file mode 100644 index 0000000..524d607 --- /dev/null +++ b/include/dt-bindings/reset/amlogic,meson-gxbb-reset.h @@ -0,0 +1,210 @@ +/* + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * GPL LICENSE SUMMARY + * + * Copyright (c) 2016 BayLibre, SAS. + * Author: Neil Armstrong + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see . + * The full GNU General Public License is included in this distribution + * in the file called COPYING. + * + * BSD LICENSE + * + * Copyright (c) 2016 BayLibre, SAS. + * Author: Neil Armstrong + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#ifndef _DT_BINDINGS_AMLOGIC_MESON_GXBB_RESET_H +#define _DT_BINDINGS_AMLOGIC_MESON_GXBB_RESET_H + +/* RESET0 */ +#define RESET_HIU 0 +/* 1 */ +#define RESET_DOS_RESET 2 +#define RESET_DDR_TOP 3 +#define RESET_DCU_RESET 4 +#define RESET_VIU 5 +#define RESET_AIU 6 +#define RESET_VID_PLL_DIV 7 +/* 8 */ +#define RESET_PMUX 9 +#define RESET_VENC 10 +#define RESET_ASSIST 11 +#define RESET_AFIFO2 12 +#define RESET_VCBUS 13 +/* 14 */ +/* 15 */ +#define RESET_GIC 16 +#define RESET_CAPB3_DECODE 17 +#define RESET_NAND_CAPB3 18 +#define RESET_HDMITX_CAPB3 19 +#define RESET_MALI_CAPB3 20 +#define RESET_DOS_CAPB3 21 +#define RESET_SYS_CPU_CAPB3 22 +#define RESET_CBUS_CAPB3 23 +#define RESET_AHB_CNTL 24 +#define RESET_AHB_DATA 25 +#define RESET_VCBUS_CLK81 26 +#define RESET_MMC 27 +#define RESET_MIPI_0 28 +#define RESET_MIPI_1 29 +#define RESET_MIPI_2 30 +#define RESET_MIPI_3 31 +/* RESET1 */ +#define RESET_CPPM 32 +#define RESET_DEMUX 33 +#define RESET_USB_OTG 34 +#define RESET_DDR 35 +#define RESET_AO_RESET 36 +#define RESET_BT656 37 +#define RESET_AHB_SRAM 38 +/* 39 */ +#define RESET_PARSER 40 +#define RESET_BLKMV 41 +#define RESET_ISA 42 +#define RESET_ETHERNET 43 +#define RESET_SD_EMMC_A 44 +#define RESET_SD_EMMC_B 45 +#define RESET_SD_EMMC_C 46 +#define RESET_ROM_BOOT 47 +#define RESET_SYS_CPU_0 48 +#define RESET_SYS_CPU_1 49 +#define RESET_SYS_CPU_2 50 +#define RESET_SYS_CPU_3 51 +#define RESET_SYS_CPU_CORE_0 52 +#define RESET_SYS_CPU_CORE_1 53 +#define RESET_SYS_CPU_CORE_2 54 +#define RESET_SYS_CPU_CORE_3 55 +#define RESET_SYS_PLL_DIV 56 +#define RESET_SYS_CPU_AXI 57 +#define RESET_SYS_CPU_L2 58 +#define RESET_SYS_CPU_P 59 +#define RESET_SYS_CPU_MBIST 60 +/* 61 */ +/* 62 */ +/* 63 */ +/* RESET2 */ +#define RESET_VD_RMEM 64 +#define RESET_AUDIN 65 +#define RESET_HDMI_TX 66 +/* 67 */ +/* 68 */ +/* 69 */ +#define RESET_GE2D 70 +#define RESET_PARSER_REG 71 +#define RESET_PARSER_FETCH 72 +#define RESET_PARSER_CTL 73 +#define RESET_PARSER_TOP 74 +/* 75 */ +/* 76 */ +#define RESET_AO_CPU_RESET 77 +#define RESET_MALI 78 +#define RESET_HDMI_SYSTEM_RESET 79 +/* 80-95 */ +/* RESET3 */ +#define RESET_RING_OSCILLATOR 96 +#define RESET_SYS_CPU 97 +#define RESET_EFUSE 98 +#define RESET_SYS_CPU_BVCI 99 +#define RESET_AIFIFO 100 +#define RESET_TVFE 101 +#define RESET_AHB_BRIDGE_CNTL 102 +/* 103 */ +#define RESET_AUDIO_DAC 104 +#define RESET_DEMUX_TOP 105 +#define RESET_DEMUX_DES 106 +#define RESET_DEMUX_S2P_0 107 +#define RESET_DEMUX_S2P_1 108 +#define RESET_DEMUX_RESET_0 109 +#define RESET_DEMUX_RESET_1 110 +#define RESET_DEMUX_RESET_2 111 +/* 112-127 */ +/* RESET4 */ +/* 128 */ +/* 129 */ +/* 130 */ +/* 131 */ +#define RESET_DVIN_RESET 132 +#define RESET_RDMA 133 +#define RESET_VENCI 134 +#define RESET_VENCP 135 +/* 136 */ +#define RESET_VDAC 137 +#define RESET_RTC 138 +/* 139 */ +#define RESET_VDI6 140 +#define RESET_VENCL 141 +#define RESET_I2C_MASTER_2 142 +#define RESET_I2C_MASTER_1 143 +/* 144-159 */ +/* RESET5 */ +/* 160-191 */ +/* RESET6 */ +#define RESET_PERIPHS_GENERAL 192 +#define RESET_PERIPHS_SPICC 193 +#define RESET_PERIPHS_SMART_CARD 194 +#define RESET_PERIPHS_SAR_ADC 195 +#define RESET_PERIPHS_I2C_MASTER_0 196 +#define RESET_SANA 197 +/* 198 */ +#define RESET_PERIPHS_STREAM_INTERFACE 199 +#define RESET_PERIPHS_SDIO 200 +#define RESET_PERIPHS_UART_0 201 +#define RESET_PERIPHS_UART_1_2 202 +#define RESET_PERIPHS_ASYNC_0 203 +#define RESET_PERIPHS_ASYNC_1 204 +#define RESET_PERIPHS_SPI_0 205 +#define RESET_PERIPHS_SDHC 206 +#define RESET_UART_SLIP 207 +/* 208-223 */ +/* RESET7 */ +#define RESET_USB_DDR_0 224 +#define RESET_USB_DDR_1 225 +#define RESET_USB_DDR_2 226 +#define RESET_USB_DDR_3 227 +/* 228 */ +#define RESET_DEVICE_MMC_ARB 229 +/* 230 */ +#define RESET_VID_LOCK 231 +#define RESET_A9_DMC_PIPEL 232 +/* 233-255 */ + +#endif diff --git a/include/dt-bindings/reset/amlogic,meson8b-reset.h b/include/dt-bindings/reset/amlogic,meson8b-reset.h new file mode 100644 index 0000000..614aff2 --- /dev/null +++ b/include/dt-bindings/reset/amlogic,meson8b-reset.h @@ -0,0 +1,175 @@ +/* + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * GPL LICENSE SUMMARY + * + * Copyright (c) 2016 BayLibre, SAS. + * Author: Neil Armstrong + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see . + * The full GNU General Public License is included in this distribution + * in the file called COPYING. + * + * BSD LICENSE + * + * Copyright (c) 2016 BayLibre, SAS. + * Author: Neil Armstrong + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#ifndef _DT_BINDINGS_AMLOGIC_MESON8B_RESET_H +#define _DT_BINDINGS_AMLOGIC_MESON8B_RESET_H + +/* RESET0 */ +#define RESET_HIU 0 +#define RESET_VLD 1 +#define RESET_IQIDCT 2 +#define RESET_MC 3 +/* 8 */ +#define RESET_VIU 5 +#define RESET_AIU 6 +#define RESET_MCPU 7 +#define RESET_CCPU 8 +#define RESET_PMUX 9 +#define RESET_VENC 10 +#define RESET_ASSIST 11 +#define RESET_AFIFO2 12 +#define RESET_MDEC 13 +#define RESET_VLD_PART 14 +#define RESET_VIFIFO 15 +/* 16-31 */ +/* RESET1 */ +/* 32 */ +#define RESET_DEMUX 33 +#define RESET_USB_OTG 34 +#define RESET_DDR 35 +#define RESET_VDAC_1 36 +#define RESET_BT656 37 +#define RESET_AHB_SRAM 38 +#define RESET_AHB_BRIDGE 39 +#define RESET_PARSER 40 +#define RESET_BLKMV 41 +#define RESET_ISA 42 +#define RESET_ETHERNET 43 +#define RESET_ABUF 44 +#define RESET_AHB_DATA 45 +#define RESET_AHB_CNTL 46 +#define RESET_ROM_BOOT 47 +/* 48-63 */ +/* RESET2 */ +#define RESET_VD_RMEM 64 +#define RESET_AUDIN 65 +#define RESET_DBLK 66 +#define RESET_PIC_DC 66 +#define RESET_PSC 66 +#define RESET_NAND 66 +#define RESET_GE2D 70 +#define RESET_PARSER_REG 71 +#define RESET_PARSER_FETCH 72 +#define RESET_PARSER_CTL 73 +#define RESET_PARSER_TOP 74 +#define RESET_HDMI_APB 75 +#define RESET_AUDIO_APB 76 +#define RESET_MEDIA_CPU 77 +#define RESET_MALI 78 +#define RESET_HDMI_SYSTEM_RESET 79 +/* 80-95 */ +/* RESET3 */ +#define RESET_RING_OSCILLATOR 96 +#define RESET_SYS_CPU_0 97 +#define RESET_EFUSE 98 +#define RESET_SYS_CPU_BVCI 99 +#define RESET_AIFIFO 100 +#define RESET_AUDIO_PLL_MODULATOR 101 +#define RESET_AHB_BRIDGE_CNTL 102 +#define RESET_SYS_CPU_1 103 +#define RESET_AUDIO_DAC 104 +#define RESET_DEMUX_TOP 105 +#define RESET_DEMUX_DES 106 +#define RESET_DEMUX_S2P_0 107 +#define RESET_DEMUX_S2P_1 108 +#define RESET_DEMUX_RESET_0 109 +#define RESET_DEMUX_RESET_1 110 +#define RESET_DEMUX_RESET_2 111 +/* 112-127 */ +/* RESET4 */ +#define RESET_PL310 128 +#define RESET_A5_APB 129 +#define RESET_A5_AXI 130 +#define RESET_A5 131 +#define RESET_DVIN 132 +#define RESET_RDMA 133 +#define RESET_VENCI 134 +#define RESET_VENCP 135 +#define RESET_VENCT 136 +#define RESET_VDAC_4 137 +#define RESET_RTC 138 +#define RESET_A5_DEBUG 139 +#define RESET_VDI6 140 +#define RESET_VENCL 141 +/* 142-159 */ +/* RESET5 */ +#define RESET_DDR_PLL 160 +#define RESET_MISC_PLL 161 +#define RESET_SYS_PLL 162 +#define RESET_HPLL_PLL 163 +#define RESET_AUDIO_PLL 164 +#define RESET_VID2_PLL 165 +/* 166-191 */ +/* RESET6 */ +#define RESET_PERIPHS_GENERAL 192 +#define RESET_PERIPHS_IR_REMOTE 193 +#define RESET_PERIPHS_SMART_CARD 194 +#define RESET_PERIPHS_SAR_ADC 195 +#define RESET_PERIPHS_I2C_MASTER_0 196 +#define RESET_PERIPHS_I2C_MASTER_1 197 +#define RESET_PERIPHS_I2C_SLAVE 198 +#define RESET_PERIPHS_STREAM_INTERFACE 199 +#define RESET_PERIPHS_SDIO 200 +#define RESET_PERIPHS_UART_0 201 +#define RESET_PERIPHS_UART_1 202 +#define RESET_PERIPHS_ASYNC_0 203 +#define RESET_PERIPHS_ASYNC_1 204 +#define RESET_PERIPHS_SPI_0 205 +#define RESET_PERIPHS_SPI_1 206 +#define RESET_PERIPHS_LED_PWM 207 +/* 208-223 */ +/* RESET7 */ +/* 224-255 */ + +#endif -- cgit v0.10.2 From 6a4ec4cd08888b19837d343e52d0b9a986f94db8 Mon Sep 17 00:00:00 2001 From: Boris Brezillon Date: Mon, 23 May 2016 09:44:54 +0200 Subject: memory: add Atmel EBI (External Bus Interface) driver The EBI (External Bus Interface) is used to access external peripherals (NOR, SRAM, NAND, and other specific devices like ethernet controllers). Each device is assigned a CS line and an address range and can have its own configuration (timings, access mode, bus width, ...). This driver provides a generic DT binding to configure a device according to its requirements. For specific device controllers (like the NAND one) the SMC timings should be configured by the controller driver through the matrix and smc syscon regmaps. Signed-off-by: Boris Brezillon Signed-off-by: Alexandre Belloni diff --git a/drivers/memory/Kconfig b/drivers/memory/Kconfig index 81ddb17..1337123 100644 --- a/drivers/memory/Kconfig +++ b/drivers/memory/Kconfig @@ -25,6 +25,17 @@ config ATMEL_SDRAMC Starting with the at91sam9g45, this controller supports SDR, DDR and LP-DDR memories. +config ATMEL_EBI + bool "Atmel EBI driver" + default y + depends on ARCH_AT91 && OF + select MFD_SYSCON + help + Driver for Atmel EBI controller. + Used to configure the EBI (external bus interface) when the device- + tree is used. This bus supports NANDs, external ethernet controller, + SRAMs, ATA devices, etc. + config TI_AEMIF tristate "Texas Instruments AEMIF driver" depends on (ARCH_DAVINCI || ARCH_KEYSTONE) && OF diff --git a/drivers/memory/Makefile b/drivers/memory/Makefile index cb0b7a1..b20ae38 100644 --- a/drivers/memory/Makefile +++ b/drivers/memory/Makefile @@ -7,6 +7,7 @@ obj-$(CONFIG_OF) += of_memory.o endif obj-$(CONFIG_ARM_PL172_MPMC) += pl172.o obj-$(CONFIG_ATMEL_SDRAMC) += atmel-sdramc.o +obj-$(CONFIG_ATMEL_EBI) += atmel-ebi.o obj-$(CONFIG_TI_AEMIF) += ti-aemif.o obj-$(CONFIG_TI_EMIF) += emif.o obj-$(CONFIG_OMAP_GPMC) += omap-gpmc.o diff --git a/drivers/memory/atmel-ebi.c b/drivers/memory/atmel-ebi.c new file mode 100644 index 0000000..17d9d3f --- /dev/null +++ b/drivers/memory/atmel-ebi.c @@ -0,0 +1,771 @@ +/* + * EBI driver for Atmel chips + * inspired by the fsl weim bus driver + * + * Copyright (C) 2013 Jean-Jacques Hiblot + * + * This file is licensed under the terms of the GNU General Public + * License version 2. This program is licensed "as is" without any + * warranty of any kind, whether express or implied. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +struct at91sam9_smc_timings { + u32 ncs_rd_setup_ns; + u32 nrd_setup_ns; + u32 ncs_wr_setup_ns; + u32 nwe_setup_ns; + u32 ncs_rd_pulse_ns; + u32 nrd_pulse_ns; + u32 ncs_wr_pulse_ns; + u32 nwe_pulse_ns; + u32 nrd_cycle_ns; + u32 nwe_cycle_ns; + u32 tdf_ns; +}; + +struct at91sam9_smc_generic_fields { + struct regmap_field *setup; + struct regmap_field *pulse; + struct regmap_field *cycle; + struct regmap_field *mode; +}; + +struct at91sam9_ebi_dev_config { + struct at91sam9_smc_timings timings; + u32 mode; +}; + +struct at91_ebi_dev_config { + int cs; + union { + struct at91sam9_ebi_dev_config sam9; + }; +}; + +struct at91_ebi; + +struct at91_ebi_dev { + struct list_head node; + struct at91_ebi *ebi; + u32 mode; + int numcs; + struct at91_ebi_dev_config configs[]; +}; + +struct at91_ebi_caps { + unsigned int available_cs; + const struct reg_field *ebi_csa; + void (*get_config)(struct at91_ebi_dev *ebid, + struct at91_ebi_dev_config *conf); + int (*xlate_config)(struct at91_ebi_dev *ebid, + struct device_node *configs_np, + struct at91_ebi_dev_config *conf); + int (*apply_config)(struct at91_ebi_dev *ebid, + struct at91_ebi_dev_config *conf); + int (*init)(struct at91_ebi *ebi); +}; + +struct at91_ebi { + struct clk *clk; + struct regmap *smc; + struct regmap *matrix; + + struct regmap_field *ebi_csa; + + struct device *dev; + const struct at91_ebi_caps *caps; + struct list_head devs; + union { + struct at91sam9_smc_generic_fields sam9; + }; +}; + +static void at91sam9_ebi_get_config(struct at91_ebi_dev *ebid, + struct at91_ebi_dev_config *conf) +{ + struct at91sam9_smc_generic_fields *fields = &ebid->ebi->sam9; + unsigned int clk_rate = clk_get_rate(ebid->ebi->clk); + struct at91sam9_ebi_dev_config *config = &conf->sam9; + struct at91sam9_smc_timings *timings = &config->timings; + unsigned int val; + + regmap_fields_read(fields->mode, conf->cs, &val); + config->mode = val & ~AT91_SMC_TDF; + + val = (val & AT91_SMC_TDF) >> 16; + timings->tdf_ns = clk_rate * val; + + regmap_fields_read(fields->setup, conf->cs, &val); + timings->ncs_rd_setup_ns = (val >> 24) & 0x1f; + timings->ncs_rd_setup_ns += ((val >> 29) & 0x1) * 128; + timings->ncs_rd_setup_ns *= clk_rate; + timings->nrd_setup_ns = (val >> 16) & 0x1f; + timings->nrd_setup_ns += ((val >> 21) & 0x1) * 128; + timings->nrd_setup_ns *= clk_rate; + timings->ncs_wr_setup_ns = (val >> 8) & 0x1f; + timings->ncs_wr_setup_ns += ((val >> 13) & 0x1) * 128; + timings->ncs_wr_setup_ns *= clk_rate; + timings->nwe_setup_ns = val & 0x1f; + timings->nwe_setup_ns += ((val >> 5) & 0x1) * 128; + timings->nwe_setup_ns *= clk_rate; + + regmap_fields_read(fields->pulse, conf->cs, &val); + timings->ncs_rd_pulse_ns = (val >> 24) & 0x3f; + timings->ncs_rd_pulse_ns += ((val >> 30) & 0x1) * 256; + timings->ncs_rd_pulse_ns *= clk_rate; + timings->nrd_pulse_ns = (val >> 16) & 0x3f; + timings->nrd_pulse_ns += ((val >> 22) & 0x1) * 256; + timings->nrd_pulse_ns *= clk_rate; + timings->ncs_wr_pulse_ns = (val >> 8) & 0x3f; + timings->ncs_wr_pulse_ns += ((val >> 14) & 0x1) * 256; + timings->ncs_wr_pulse_ns *= clk_rate; + timings->nwe_pulse_ns = val & 0x3f; + timings->nwe_pulse_ns += ((val >> 6) & 0x1) * 256; + timings->nwe_pulse_ns *= clk_rate; + + regmap_fields_read(fields->cycle, conf->cs, &val); + timings->nrd_cycle_ns = (val >> 16) & 0x7f; + timings->nrd_cycle_ns += ((val >> 23) & 0x3) * 256; + timings->nrd_cycle_ns *= clk_rate; + timings->nwe_cycle_ns = val & 0x7f; + timings->nwe_cycle_ns += ((val >> 7) & 0x3) * 256; + timings->nwe_cycle_ns *= clk_rate; +} + +static int at91_xlate_timing(struct device_node *np, const char *prop, + u32 *val, bool *required) +{ + if (!of_property_read_u32(np, prop, val)) { + *required = true; + return 0; + } + + if (*required) + return -EINVAL; + + return 0; +} + +static int at91sam9_smc_xslate_timings(struct at91_ebi_dev *ebid, + struct device_node *np, + struct at91sam9_smc_timings *timings, + bool *required) +{ + int ret; + + ret = at91_xlate_timing(np, "atmel,smc-ncs-rd-setup-ns", + &timings->ncs_rd_setup_ns, required); + if (ret) + goto out; + + ret = at91_xlate_timing(np, "atmel,smc-nrd-setup-ns", + &timings->nrd_setup_ns, required); + if (ret) + goto out; + + ret = at91_xlate_timing(np, "atmel,smc-ncs-wr-setup-ns", + &timings->ncs_wr_setup_ns, required); + if (ret) + goto out; + + ret = at91_xlate_timing(np, "atmel,smc-nwe-setup-ns", + &timings->nwe_setup_ns, required); + if (ret) + goto out; + + ret = at91_xlate_timing(np, "atmel,smc-ncs-rd-pulse-ns", + &timings->ncs_rd_pulse_ns, required); + if (ret) + goto out; + + ret = at91_xlate_timing(np, "atmel,smc-nrd-pulse-ns", + &timings->nrd_pulse_ns, required); + if (ret) + goto out; + + ret = at91_xlate_timing(np, "atmel,smc-ncs-wr-pulse-ns", + &timings->ncs_wr_pulse_ns, required); + if (ret) + goto out; + + ret = at91_xlate_timing(np, "atmel,smc-nwe-pulse-ns", + &timings->nwe_pulse_ns, required); + if (ret) + goto out; + + ret = at91_xlate_timing(np, "atmel,smc-nwe-cycle-ns", + &timings->nwe_cycle_ns, required); + if (ret) + goto out; + + ret = at91_xlate_timing(np, "atmel,smc-nrd-cycle-ns", + &timings->nrd_cycle_ns, required); + if (ret) + goto out; + + ret = at91_xlate_timing(np, "atmel,smc-tdf-ns", + &timings->tdf_ns, required); + +out: + if (ret) + dev_err(ebid->ebi->dev, + "missing or invalid timings definition in %s", + np->full_name); + + return ret; +} + +static int at91sam9_ebi_xslate_config(struct at91_ebi_dev *ebid, + struct device_node *np, + struct at91_ebi_dev_config *conf) +{ + struct at91sam9_ebi_dev_config *config = &conf->sam9; + bool required = false; + const char *tmp_str; + u32 tmp; + int ret; + + ret = of_property_read_u32(np, "atmel,smc-bus-width", &tmp); + if (!ret) { + switch (tmp) { + case 8: + config->mode |= AT91_SMC_DBW_8; + break; + + case 16: + config->mode |= AT91_SMC_DBW_16; + break; + + case 32: + config->mode |= AT91_SMC_DBW_32; + break; + + default: + return -EINVAL; + } + + required = true; + } + + if (of_property_read_bool(np, "atmel,smc-tdf-optimized")) { + config->mode |= AT91_SMC_TDFMODE_OPTIMIZED; + required = true; + } + + tmp_str = NULL; + of_property_read_string(np, "atmel,smc-byte-access-type", &tmp_str); + if (tmp_str && !strcmp(tmp_str, "write")) { + config->mode |= AT91_SMC_BAT_WRITE; + required = true; + } + + tmp_str = NULL; + of_property_read_string(np, "atmel,smc-read-mode", &tmp_str); + if (tmp_str && !strcmp(tmp_str, "nrd")) { + config->mode |= AT91_SMC_READMODE_NRD; + required = true; + } + + tmp_str = NULL; + of_property_read_string(np, "atmel,smc-write-mode", &tmp_str); + if (tmp_str && !strcmp(tmp_str, "nwe")) { + config->mode |= AT91_SMC_WRITEMODE_NWE; + required = true; + } + + tmp_str = NULL; + of_property_read_string(np, "atmel,smc-exnw-mode", &tmp_str); + if (tmp_str) { + if (!strcmp(tmp_str, "frozen")) + config->mode |= AT91_SMC_EXNWMODE_FROZEN; + else if (!strcmp(tmp_str, "ready")) + config->mode |= AT91_SMC_EXNWMODE_READY; + else if (strcmp(tmp_str, "disabled")) + return -EINVAL; + + required = true; + } + + ret = of_property_read_u32(np, "atmel,smc-page-mode", &tmp); + if (!ret) { + switch (tmp) { + case 4: + config->mode |= AT91_SMC_PS_4; + break; + + case 8: + config->mode |= AT91_SMC_PS_8; + break; + + case 16: + config->mode |= AT91_SMC_PS_16; + break; + + case 32: + config->mode |= AT91_SMC_PS_32; + break; + + default: + return -EINVAL; + } + + config->mode |= AT91_SMC_PMEN; + required = true; + } + + ret = at91sam9_smc_xslate_timings(ebid, np, &config->timings, + &required); + if (ret) + return ret; + + return required; +} + +static int at91sam9_ebi_apply_config(struct at91_ebi_dev *ebid, + struct at91_ebi_dev_config *conf) +{ + unsigned int clk_rate = clk_get_rate(ebid->ebi->clk); + struct at91sam9_ebi_dev_config *config = &conf->sam9; + struct at91sam9_smc_timings *timings = &config->timings; + struct at91sam9_smc_generic_fields *fields = &ebid->ebi->sam9; + u32 coded_val; + u32 val; + + coded_val = at91sam9_smc_setup_ns_to_cycles(clk_rate, + timings->ncs_rd_setup_ns); + val = AT91SAM9_SMC_NCS_NRDSETUP(coded_val); + coded_val = at91sam9_smc_setup_ns_to_cycles(clk_rate, + timings->nrd_setup_ns); + val |= AT91SAM9_SMC_NRDSETUP(coded_val); + coded_val = at91sam9_smc_setup_ns_to_cycles(clk_rate, + timings->ncs_wr_setup_ns); + val |= AT91SAM9_SMC_NCS_WRSETUP(coded_val); + coded_val = at91sam9_smc_setup_ns_to_cycles(clk_rate, + timings->nwe_setup_ns); + val |= AT91SAM9_SMC_NWESETUP(coded_val); + regmap_fields_write(fields->setup, conf->cs, val); + + coded_val = at91sam9_smc_pulse_ns_to_cycles(clk_rate, + timings->ncs_rd_pulse_ns); + val = AT91SAM9_SMC_NCS_NRDPULSE(coded_val); + coded_val = at91sam9_smc_pulse_ns_to_cycles(clk_rate, + timings->nrd_pulse_ns); + val |= AT91SAM9_SMC_NRDPULSE(coded_val); + coded_val = at91sam9_smc_pulse_ns_to_cycles(clk_rate, + timings->ncs_wr_pulse_ns); + val |= AT91SAM9_SMC_NCS_WRPULSE(coded_val); + coded_val = at91sam9_smc_pulse_ns_to_cycles(clk_rate, + timings->nwe_pulse_ns); + val |= AT91SAM9_SMC_NWEPULSE(coded_val); + regmap_fields_write(fields->pulse, conf->cs, val); + + coded_val = at91sam9_smc_cycle_ns_to_cycles(clk_rate, + timings->nrd_cycle_ns); + val = AT91SAM9_SMC_NRDCYCLE(coded_val); + coded_val = at91sam9_smc_cycle_ns_to_cycles(clk_rate, + timings->nwe_cycle_ns); + val |= AT91SAM9_SMC_NWECYCLE(coded_val); + regmap_fields_write(fields->cycle, conf->cs, val); + + val = DIV_ROUND_UP(timings->tdf_ns, clk_rate); + if (val > AT91_SMC_TDF_MAX) + val = AT91_SMC_TDF_MAX; + regmap_fields_write(fields->mode, conf->cs, + config->mode | AT91_SMC_TDF_(val)); + + return 0; +} + +static int at91sam9_ebi_init(struct at91_ebi *ebi) +{ + struct at91sam9_smc_generic_fields *fields = &ebi->sam9; + struct reg_field field = REG_FIELD(0, 0, 31); + + field.id_size = fls(ebi->caps->available_cs); + field.id_offset = AT91SAM9_SMC_GENERIC_BLK_SZ; + + field.reg = AT91SAM9_SMC_SETUP(AT91SAM9_SMC_GENERIC); + fields->setup = devm_regmap_field_alloc(ebi->dev, ebi->smc, field); + if (IS_ERR(fields->setup)) + return PTR_ERR(fields->setup); + + field.reg = AT91SAM9_SMC_PULSE(AT91SAM9_SMC_GENERIC); + fields->pulse = devm_regmap_field_alloc(ebi->dev, ebi->smc, field); + if (IS_ERR(fields->pulse)) + return PTR_ERR(fields->pulse); + + field.reg = AT91SAM9_SMC_CYCLE(AT91SAM9_SMC_GENERIC); + fields->cycle = devm_regmap_field_alloc(ebi->dev, ebi->smc, field); + if (IS_ERR(fields->cycle)) + return PTR_ERR(fields->cycle); + + field.reg = AT91SAM9_SMC_MODE(AT91SAM9_SMC_GENERIC); + fields->mode = devm_regmap_field_alloc(ebi->dev, ebi->smc, field); + if (IS_ERR(fields->mode)) + return PTR_ERR(fields->mode); + + return 0; +} + +static int sama5d3_ebi_init(struct at91_ebi *ebi) +{ + struct at91sam9_smc_generic_fields *fields = &ebi->sam9; + struct reg_field field = REG_FIELD(0, 0, 31); + + field.id_size = fls(ebi->caps->available_cs); + field.id_offset = SAMA5_SMC_GENERIC_BLK_SZ; + + field.reg = AT91SAM9_SMC_SETUP(SAMA5_SMC_GENERIC); + fields->setup = devm_regmap_field_alloc(ebi->dev, ebi->smc, field); + if (IS_ERR(fields->setup)) + return PTR_ERR(fields->setup); + + field.reg = AT91SAM9_SMC_PULSE(SAMA5_SMC_GENERIC); + fields->pulse = devm_regmap_field_alloc(ebi->dev, ebi->smc, field); + if (IS_ERR(fields->pulse)) + return PTR_ERR(fields->pulse); + + field.reg = AT91SAM9_SMC_CYCLE(SAMA5_SMC_GENERIC); + fields->cycle = devm_regmap_field_alloc(ebi->dev, ebi->smc, field); + if (IS_ERR(fields->cycle)) + return PTR_ERR(fields->cycle); + + field.reg = SAMA5_SMC_MODE(SAMA5_SMC_GENERIC); + fields->mode = devm_regmap_field_alloc(ebi->dev, ebi->smc, field); + if (IS_ERR(fields->mode)) + return PTR_ERR(fields->mode); + + return 0; +} + +static int at91_ebi_dev_setup(struct at91_ebi *ebi, struct device_node *np, + int reg_cells) +{ + const struct at91_ebi_caps *caps = ebi->caps; + struct at91_ebi_dev_config conf = { }; + struct device *dev = ebi->dev; + struct at91_ebi_dev *ebid; + int ret, numcs = 0, i; + bool apply = false; + + numcs = of_property_count_elems_of_size(np, "reg", + reg_cells * sizeof(u32)); + if (numcs <= 0) { + dev_err(dev, "invalid reg property in %s\n", np->full_name); + return -EINVAL; + } + + ebid = devm_kzalloc(ebi->dev, + sizeof(*ebid) + (numcs * sizeof(*ebid->configs)), + GFP_KERNEL); + if (!ebid) + return -ENOMEM; + + ebid->ebi = ebi; + + ret = caps->xlate_config(ebid, np, &conf); + if (ret < 0) + return ret; + else if (ret) + apply = true; + + for (i = 0; i < numcs; i++) { + u32 cs; + + ret = of_property_read_u32_index(np, "reg", i * reg_cells, + &cs); + if (ret) + return ret; + + if (cs > AT91_MATRIX_EBI_NUM_CS || + !(ebi->caps->available_cs & BIT(cs))) { + dev_err(dev, "invalid reg property in %s\n", + np->full_name); + return -EINVAL; + } + + ebid->configs[i].cs = cs; + + if (apply) { + conf.cs = cs; + ret = caps->apply_config(ebid, &conf); + if (ret) + return ret; + } + + caps->get_config(ebid, &ebid->configs[i]); + + /* + * Attach the EBI device to the generic SMC logic if at least + * one "atmel,smc-" property is present. + */ + if (ebi->ebi_csa && ret) + regmap_field_update_bits(ebi->ebi_csa, + BIT(cs), 0); + } + + list_add_tail(&ebid->node, &ebi->devs); + + return 0; +} + +static const struct reg_field at91sam9260_ebi_csa = + REG_FIELD(AT91SAM9260_MATRIX_EBICSA, 0, + AT91_MATRIX_EBI_NUM_CS - 1); + +static const struct at91_ebi_caps at91sam9260_ebi_caps = { + .available_cs = 0xff, + .ebi_csa = &at91sam9260_ebi_csa, + .get_config = at91sam9_ebi_get_config, + .xlate_config = at91sam9_ebi_xslate_config, + .apply_config = at91sam9_ebi_apply_config, + .init = at91sam9_ebi_init, +}; + +static const struct reg_field at91sam9261_ebi_csa = + REG_FIELD(AT91SAM9261_MATRIX_EBICSA, 0, + AT91_MATRIX_EBI_NUM_CS - 1); + +static const struct at91_ebi_caps at91sam9261_ebi_caps = { + .available_cs = 0xff, + .ebi_csa = &at91sam9261_ebi_csa, + .get_config = at91sam9_ebi_get_config, + .xlate_config = at91sam9_ebi_xslate_config, + .apply_config = at91sam9_ebi_apply_config, + .init = at91sam9_ebi_init, +}; + +static const struct reg_field at91sam9263_ebi0_csa = + REG_FIELD(AT91SAM9263_MATRIX_EBI0CSA, 0, + AT91_MATRIX_EBI_NUM_CS - 1); + +static const struct at91_ebi_caps at91sam9263_ebi0_caps = { + .available_cs = 0x3f, + .ebi_csa = &at91sam9263_ebi0_csa, + .get_config = at91sam9_ebi_get_config, + .xlate_config = at91sam9_ebi_xslate_config, + .apply_config = at91sam9_ebi_apply_config, + .init = at91sam9_ebi_init, +}; + +static const struct reg_field at91sam9263_ebi1_csa = + REG_FIELD(AT91SAM9263_MATRIX_EBI1CSA, 0, + AT91_MATRIX_EBI_NUM_CS - 1); + +static const struct at91_ebi_caps at91sam9263_ebi1_caps = { + .available_cs = 0x7, + .ebi_csa = &at91sam9263_ebi1_csa, + .get_config = at91sam9_ebi_get_config, + .xlate_config = at91sam9_ebi_xslate_config, + .apply_config = at91sam9_ebi_apply_config, + .init = at91sam9_ebi_init, +}; + +static const struct reg_field at91sam9rl_ebi_csa = + REG_FIELD(AT91SAM9RL_MATRIX_EBICSA, 0, + AT91_MATRIX_EBI_NUM_CS - 1); + +static const struct at91_ebi_caps at91sam9rl_ebi_caps = { + .available_cs = 0x3f, + .ebi_csa = &at91sam9rl_ebi_csa, + .get_config = at91sam9_ebi_get_config, + .xlate_config = at91sam9_ebi_xslate_config, + .apply_config = at91sam9_ebi_apply_config, + .init = at91sam9_ebi_init, +}; + +static const struct reg_field at91sam9g45_ebi_csa = + REG_FIELD(AT91SAM9G45_MATRIX_EBICSA, 0, + AT91_MATRIX_EBI_NUM_CS - 1); + +static const struct at91_ebi_caps at91sam9g45_ebi_caps = { + .available_cs = 0x3f, + .ebi_csa = &at91sam9g45_ebi_csa, + .get_config = at91sam9_ebi_get_config, + .xlate_config = at91sam9_ebi_xslate_config, + .apply_config = at91sam9_ebi_apply_config, + .init = at91sam9_ebi_init, +}; + +static const struct at91_ebi_caps at91sam9x5_ebi_caps = { + .available_cs = 0x3f, + .ebi_csa = &at91sam9263_ebi0_csa, + .get_config = at91sam9_ebi_get_config, + .xlate_config = at91sam9_ebi_xslate_config, + .apply_config = at91sam9_ebi_apply_config, + .init = at91sam9_ebi_init, +}; + +static const struct at91_ebi_caps sama5d3_ebi_caps = { + .available_cs = 0xf, + .get_config = at91sam9_ebi_get_config, + .xlate_config = at91sam9_ebi_xslate_config, + .apply_config = at91sam9_ebi_apply_config, + .init = sama5d3_ebi_init, +}; + +static const struct of_device_id at91_ebi_id_table[] = { + { + .compatible = "atmel,at91sam9260-ebi", + .data = &at91sam9260_ebi_caps, + }, + { + .compatible = "atmel,at91sam9261-ebi", + .data = &at91sam9261_ebi_caps, + }, + { + .compatible = "atmel,at91sam9263-ebi0", + .data = &at91sam9263_ebi0_caps, + }, + { + .compatible = "atmel,at91sam9263-ebi1", + .data = &at91sam9263_ebi1_caps, + }, + { + .compatible = "atmel,at91sam9rl-ebi", + .data = &at91sam9rl_ebi_caps, + }, + { + .compatible = "atmel,at91sam9g45-ebi", + .data = &at91sam9g45_ebi_caps, + }, + { + .compatible = "atmel,at91sam9x5-ebi", + .data = &at91sam9x5_ebi_caps, + }, + { + .compatible = "atmel,sama5d3-ebi", + .data = &sama5d3_ebi_caps, + }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, at91_ebi_id_table); + +static int at91_ebi_dev_disable(struct at91_ebi *ebi, struct device_node *np) +{ + struct device *dev = ebi->dev; + struct property *newprop; + + newprop = devm_kzalloc(dev, sizeof(*newprop), GFP_KERNEL); + if (!newprop) + return -ENOMEM; + + newprop->name = devm_kstrdup(dev, "status", GFP_KERNEL); + if (!newprop->name) + return -ENOMEM; + + newprop->value = devm_kstrdup(dev, "disabled", GFP_KERNEL); + if (!newprop->name) + return -ENOMEM; + + newprop->length = sizeof("disabled"); + + return of_update_property(np, newprop); +} + +static int at91_ebi_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct device_node *child, *np = dev->of_node; + const struct of_device_id *match; + struct at91_ebi *ebi; + int ret, reg_cells; + struct clk *clk; + u32 val; + + match = of_match_device(at91_ebi_id_table, dev); + if (!match || !match->data) + return -EINVAL; + + ebi = devm_kzalloc(dev, sizeof(*ebi), GFP_KERNEL); + if (!ebi) + return -ENOMEM; + + INIT_LIST_HEAD(&ebi->devs); + ebi->caps = match->data; + ebi->dev = dev; + + clk = devm_clk_get(dev, NULL); + if (IS_ERR(clk)) + return PTR_ERR(clk); + + ebi->clk = clk; + + ebi->smc = syscon_regmap_lookup_by_phandle(np, "atmel,smc"); + if (IS_ERR(ebi->smc)) + return PTR_ERR(ebi->smc); + + /* + * The sama5d3 does not provide an EBICSA register and thus does need + * to access the matrix registers. + */ + if (ebi->caps->ebi_csa) { + ebi->matrix = + syscon_regmap_lookup_by_phandle(np, "atmel,matrix"); + if (IS_ERR(ebi->matrix)) + return PTR_ERR(ebi->matrix); + + ebi->ebi_csa = regmap_field_alloc(ebi->matrix, + *ebi->caps->ebi_csa); + if (IS_ERR(ebi->ebi_csa)) + return PTR_ERR(ebi->ebi_csa); + } + + ret = ebi->caps->init(ebi); + if (ret) + return ret; + + ret = of_property_read_u32(np, "#address-cells", &val); + if (ret) { + dev_err(dev, "missing #address-cells property\n"); + return ret; + } + + reg_cells = val; + + ret = of_property_read_u32(np, "#size-cells", &val); + if (ret) { + dev_err(dev, "missing #address-cells property\n"); + return ret; + } + + reg_cells += val; + + for_each_available_child_of_node(np, child) { + if (!of_find_property(child, "reg", NULL)) + continue; + + ret = at91_ebi_dev_setup(ebi, child, reg_cells); + if (ret) { + dev_err(dev, "failed to configure EBI bus for %s, disabling the device", + child->full_name); + + ret = at91_ebi_dev_disable(ebi, child); + if (ret) + return ret; + } + } + + return of_platform_populate(np, NULL, NULL, dev); +} + +static struct platform_driver at91_ebi_driver = { + .driver = { + .name = "atmel-ebi", + .of_match_table = at91_ebi_id_table, + }, +}; +module_platform_driver_probe(at91_ebi_driver, at91_ebi_probe); + +MODULE_AUTHOR("Jean-Jacques Hiblot "); +MODULE_DESCRIPTION("Atmel EBI driver"); +MODULE_LICENSE("GPL"); -- cgit v0.10.2 From 1fdab21d1d52a85c31f932844242fec5fb81daac Mon Sep 17 00:00:00 2001 From: Boris Brezillon Date: Mon, 23 May 2016 09:44:55 +0200 Subject: memory: atmel-ebi: add DT bindings documentation The EBI (External Bus Interface) is used to access external peripherals (NOR, SRAM, NAND, and other specific devices like ethernet controllers). Each device is assigned a CS line and an address range and can have its own configuration (timings, access mode, bus width, ...). This driver provides a generic DT binding to configure a device according to its requirements. For specific device controllers (like the NAND one) the SMC timings should be configured by the controller driver through the matrix and smc syscon regmaps. Signed-off-by: Boris Brezillon Acked-by: Rob Herring Signed-off-by: Alexandre Belloni diff --git a/Documentation/devicetree/bindings/memory-controllers/atmel,ebi.txt b/Documentation/devicetree/bindings/memory-controllers/atmel,ebi.txt new file mode 100644 index 0000000..9bb5f57 --- /dev/null +++ b/Documentation/devicetree/bindings/memory-controllers/atmel,ebi.txt @@ -0,0 +1,136 @@ +* Device tree bindings for Atmel EBI + +The External Bus Interface (EBI) controller is a bus where you can connect +asynchronous (NAND, NOR, SRAM, ....) and synchronous memories (SDR/DDR SDRAMs). +The EBI provides a glue-less interface to asynchronous memories through the SMC +(Static Memory Controller). + +Required properties: + +- compatible: "atmel,at91sam9260-ebi" + "atmel,at91sam9261-ebi" + "atmel,at91sam9263-ebi0" + "atmel,at91sam9263-ebi1" + "atmel,at91sam9rl-ebi" + "atmel,at91sam9g45-ebi" + "atmel,at91sam9x5-ebi" + "atmel,sama5d3-ebi" + +- reg: Contains offset/length value for EBI memory mapping. + This property might contain several entries if the EBI + memory range is not contiguous + +- #address-cells: Must be 2. + The first cell encodes the CS. + The second cell encode the offset into the CS memory + range. + +- #size-cells: Must be set to 1. + +- ranges: Encodes CS to memory region association. + +- clocks: Clock feeding the EBI controller. + See clock-bindings.txt + +Children device nodes are representing device connected to the EBI bus. + +Required device node properties: + +- reg: Contains the chip-select id, the offset and the length + of the memory region requested by the device. + +EBI bus configuration will be defined directly in the device subnode. + +Optional EBI/SMC properties: + +- atmel,smc-bus-width: width of the asynchronous device's data bus + 8, 16 or 32. + Default to 8 when undefined. + +- atmel,smc-byte-access-type "write" or "select" (see Atmel datasheet). + Default to "select" when undefined. + +- atmel,smc-read-mode "nrd" or "ncs". + Default to "ncs" when undefined. + +- atmel,smc-write-mode "nwe" or "ncs". + Default to "ncs" when undefined. + +- atmel,smc-exnw-mode "disabled", "frozen" or "ready". + Default to "disabled" when undefined. + +- atmel,smc-page-mode enable page mode if present. The provided value + defines the page size (supported values: 4, 8, + 16 and 32). + +- atmel,smc-tdf-mode: "normal" or "optimized". When set to + "optimized" the data float time is optimized + depending on the next device being accessed + (next device setup time is subtracted to the + current device data float time). + Default to "normal" when undefined. + +If at least one atmel,smc- property is defined the following SMC timing +properties become mandatory. In the other hand, if none of the atmel,smc- +properties are specified, we assume that the EBI bus configuration will be +handled by the sub-device driver, and none of those properties should be +defined. + +All the timings are expressed in nanoseconds (see Atmel datasheet for a full +description). + +- atmel,smc-ncs-rd-setup-ns +- atmel,smc-nrd-setup-ns +- atmel,smc-ncs-wr-setup-ns +- atmel,smc-nwe-setup-ns +- atmel,smc-ncs-rd-pulse-ns +- atmel,smc-nrd-pulse-ns +- atmel,smc-ncs-wr-pulse-ns +- atmel,smc-nwe-pulse-ns +- atmel,smc-nwe-cycle-ns +- atmel,smc-nrd-cycle-ns +- atmel,smc-tdf-ns + +Example: + + ebi: ebi@10000000 { + compatible = "atmel,sama5d3-ebi"; + #address-cells = <2>; + #size-cells = <1>; + atmel,smc = <&hsmc>; + atmel,matrix = <&matrix>; + reg = <0x10000000 0x10000000 + 0x40000000 0x30000000>; + ranges = <0x0 0x0 0x10000000 0x10000000 + 0x1 0x0 0x40000000 0x10000000 + 0x2 0x0 0x50000000 0x10000000 + 0x3 0x0 0x60000000 0x10000000>; + clocks = <&mck>; + + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_ebi_addr>; + + nor: flash@0,0 { + compatible = "cfi-flash"; + #address-cells = <1>; + #size-cells = <1>; + reg = <0x0 0x0 0x1000000>; + bank-width = <2>; + + atmel,smc-read-mode = "nrd"; + atmel,smc-write-mode = "nwe"; + atmel,smc-bus-width = <16>; + atmel,smc-ncs-rd-setup-ns = <0>; + atmel,smc-ncs-wr-setup-ns = <0>; + atmel,smc-nwe-setup-ns = <8>; + atmel,smc-nrd-setup-ns = <16>; + atmel,smc-ncs-rd-pulse-ns = <84>; + atmel,smc-ncs-wr-pulse-ns = <84>; + atmel,smc-nrd-pulse-ns = <76>; + atmel,smc-nwe-pulse-ns = <76>; + atmel,smc-nrd-cycle-ns = <107>; + atmel,smc-nwe-cycle-ns = <84>; + atmel,smc-tdf-ns = <16>; + }; + }; + -- cgit v0.10.2 From b9bacc1e503e6ed893c8642f4de1b27ff98e5365 Mon Sep 17 00:00:00 2001 From: Marek Szyprowski Date: Tue, 24 May 2016 15:31:28 +0200 Subject: ARM: EXYNOS: Remove code for MFC custom reserved memory handling Once MFC driver has been converted to generic reserved memory bindings, there is no need for custom memory reservation code. Signed-off-by: Marek Szyprowski Reviewed-by: Javier Martinez Canillas Signed-off-by: Krzysztof Kozlowski diff --git a/arch/arm/mach-exynos/Makefile b/arch/arm/mach-exynos/Makefile index 34d29df..b91b382 100644 --- a/arch/arm/mach-exynos/Makefile +++ b/arch/arm/mach-exynos/Makefile @@ -23,5 +23,3 @@ AFLAGS_sleep.o :=-Wa,-march=armv7-a$(plus_sec) obj-$(CONFIG_EXYNOS5420_MCPM) += mcpm-exynos.o CFLAGS_mcpm-exynos.o += -march=armv7-a - -obj-$(CONFIG_S5P_DEV_MFC) += s5p-dev-mfc.o diff --git a/arch/arm/mach-exynos/exynos.c b/arch/arm/mach-exynos/exynos.c index 52ccf24..a8620c6 100644 --- a/arch/arm/mach-exynos/exynos.c +++ b/arch/arm/mach-exynos/exynos.c @@ -27,7 +27,6 @@ #include #include "common.h" -#include "mfc.h" static struct map_desc exynos4_iodesc[] __initdata = { { @@ -237,23 +236,6 @@ static char const *const exynos_dt_compat[] __initconst = { NULL }; -static void __init exynos_reserve(void) -{ -#ifdef CONFIG_S5P_DEV_MFC - int i; - char *mfc_mem[] = { - "samsung,mfc-v5", - "samsung,mfc-v6", - "samsung,mfc-v7", - "samsung,mfc-v8", - }; - - for (i = 0; i < ARRAY_SIZE(mfc_mem); i++) - if (of_scan_flat_dt(s5p_fdt_alloc_mfc_mem, mfc_mem[i])) - break; -#endif -} - static void __init exynos_dt_fixup(void) { /* @@ -275,6 +257,5 @@ DT_MACHINE_START(EXYNOS_DT, "SAMSUNG EXYNOS (Flattened Device Tree)") .init_machine = exynos_dt_machine_init, .init_late = exynos_init_late, .dt_compat = exynos_dt_compat, - .reserve = exynos_reserve, .dt_fixup = exynos_dt_fixup, MACHINE_END diff --git a/arch/arm/mach-exynos/mfc.h b/arch/arm/mach-exynos/mfc.h deleted file mode 100644 index dec93cd..0000000 --- a/arch/arm/mach-exynos/mfc.h +++ /dev/null @@ -1,16 +0,0 @@ -/* - * Copyright (C) 2013 Samsung Electronics Co.Ltd - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published by the - * Free Software Foundation; either version 2 of the License, or (at your - * option) any later version. - */ - -#ifndef __MACH_EXYNOS_MFC_H -#define __MACH_EXYNOS_MFC_H __FILE__ - -int __init s5p_fdt_alloc_mfc_mem(unsigned long node, const char *uname, - int depth, void *data); - -#endif /* __MACH_EXYNOS_MFC_H */ diff --git a/arch/arm/mach-exynos/s5p-dev-mfc.c b/arch/arm/mach-exynos/s5p-dev-mfc.c deleted file mode 100644 index 8ef1f3e..0000000 --- a/arch/arm/mach-exynos/s5p-dev-mfc.c +++ /dev/null @@ -1,93 +0,0 @@ -/* - * Copyright (C) 2010-2011 Samsung Electronics Co.Ltd - * - * Base S5P MFC resource and device definitions - * - * 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. - */ - -#include -#include -#include -#include -#include -#include -#include - -static struct platform_device s5p_device_mfc_l; -static struct platform_device s5p_device_mfc_r; - -struct s5p_mfc_dt_meminfo { - unsigned long loff; - unsigned long lsize; - unsigned long roff; - unsigned long rsize; - char *compatible; -}; - -struct s5p_mfc_reserved_mem { - phys_addr_t base; - unsigned long size; - struct device *dev; -}; - -static struct s5p_mfc_reserved_mem s5p_mfc_mem[2] __initdata; - - -static void __init s5p_mfc_reserve_mem(phys_addr_t rbase, unsigned int rsize, - phys_addr_t lbase, unsigned int lsize) -{ - int i; - - s5p_mfc_mem[0].dev = &s5p_device_mfc_r.dev; - s5p_mfc_mem[0].base = rbase; - s5p_mfc_mem[0].size = rsize; - - s5p_mfc_mem[1].dev = &s5p_device_mfc_l.dev; - s5p_mfc_mem[1].base = lbase; - s5p_mfc_mem[1].size = lsize; - - for (i = 0; i < ARRAY_SIZE(s5p_mfc_mem); i++) { - struct s5p_mfc_reserved_mem *area = &s5p_mfc_mem[i]; - if (memblock_remove(area->base, area->size)) { - printk(KERN_ERR "Failed to reserve memory for MFC device (%ld bytes at 0x%08lx)\n", - area->size, (unsigned long) area->base); - area->base = 0; - } - } -} - -int __init s5p_fdt_alloc_mfc_mem(unsigned long node, const char *uname, - int depth, void *data) -{ - const __be32 *prop; - int len; - struct s5p_mfc_dt_meminfo mfc_mem; - - if (!data) - return 0; - - if (!of_flat_dt_is_compatible(node, data)) - return 0; - - prop = of_get_flat_dt_prop(node, "samsung,mfc-l", &len); - if (!prop || (len != 2 * sizeof(unsigned long))) - return 0; - - mfc_mem.loff = be32_to_cpu(prop[0]); - mfc_mem.lsize = be32_to_cpu(prop[1]); - - prop = of_get_flat_dt_prop(node, "samsung,mfc-r", &len); - if (!prop || (len != 2 * sizeof(unsigned long))) - return 0; - - mfc_mem.roff = be32_to_cpu(prop[0]); - mfc_mem.rsize = be32_to_cpu(prop[1]); - - s5p_mfc_reserve_mem(mfc_mem.roff, mfc_mem.rsize, - mfc_mem.loff, mfc_mem.lsize); - - return 1; -} -- cgit v0.10.2 From 8b9ac7e33933c55b90dd77c62971df13d249be1d Mon Sep 17 00:00:00 2001 From: Marek Szyprowski Date: Tue, 24 May 2016 15:31:29 +0200 Subject: ARM: dts: exynos: Convert MFC device to generic reserved memory bindings This patch replaces custom properties for defining reserved memory regions with generic reserved memory bindings for MFC video codec device. Signed-off-by: Marek Szyprowski Reviewed-by: Krzysztof Kozlowski Reviewed-by: Javier Martinez Canillas Tested-by: Javier Martinez Canillas [k.kozlowski: Add Samsung copyrights] Signed-off-by: Krzysztof Kozlowski diff --git a/arch/arm/boot/dts/exynos-mfc-reserved-memory.dtsi b/arch/arm/boot/dts/exynos-mfc-reserved-memory.dtsi new file mode 100644 index 0000000..c4d063a --- /dev/null +++ b/arch/arm/boot/dts/exynos-mfc-reserved-memory.dtsi @@ -0,0 +1,29 @@ +/* + * Samsung's Exynos SoC MFC (Video Codec) reserved memory common definition. + * + * Copyright (c) 2016 Samsung Electronics Co., Ltd + * + * 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. + */ + +/ { + reserved-memory { + #address-cells = <1>; + #size-cells = <1>; + ranges; + + mfc_left: region@51000000 { + compatible = "shared-dma-pool"; + no-map; + reg = <0x51000000 0x800000>; + }; + + mfc_right: region@43000000 { + compatible = "shared-dma-pool"; + no-map; + reg = <0x43000000 0x800000>; + }; + }; +}; diff --git a/arch/arm/boot/dts/exynos4210-origen.dts b/arch/arm/boot/dts/exynos4210-origen.dts index ad7394c..f5e4eb2 100644 --- a/arch/arm/boot/dts/exynos4210-origen.dts +++ b/arch/arm/boot/dts/exynos4210-origen.dts @@ -18,6 +18,7 @@ #include "exynos4210.dtsi" #include #include +#include "exynos-mfc-reserved-memory.dtsi" / { model = "Insignal Origen evaluation board based on Exynos4210"; @@ -288,8 +289,7 @@ }; &mfc { - samsung,mfc-r = <0x43000000 0x800000>; - samsung,mfc-l = <0x51000000 0x800000>; + memory-region = <&mfc_left>, <&mfc_right>; status = "okay"; }; diff --git a/arch/arm/boot/dts/exynos4210-smdkv310.dts b/arch/arm/boot/dts/exynos4210-smdkv310.dts index 94ca7d3..de917f0 100644 --- a/arch/arm/boot/dts/exynos4210-smdkv310.dts +++ b/arch/arm/boot/dts/exynos4210-smdkv310.dts @@ -17,6 +17,7 @@ /dts-v1/; #include "exynos4210.dtsi" #include +#include "exynos-mfc-reserved-memory.dtsi" / { model = "Samsung smdkv310 evaluation board based on Exynos4210"; @@ -133,8 +134,7 @@ }; &mfc { - samsung,mfc-r = <0x43000000 0x800000>; - samsung,mfc-l = <0x51000000 0x800000>; + memory-region = <&mfc_left>, <&mfc_right>; status = "okay"; }; diff --git a/arch/arm/boot/dts/exynos4412-origen.dts b/arch/arm/boot/dts/exynos4412-origen.dts index 8bca699..cd363d7 100644 --- a/arch/arm/boot/dts/exynos4412-origen.dts +++ b/arch/arm/boot/dts/exynos4412-origen.dts @@ -16,6 +16,7 @@ #include "exynos4412.dtsi" #include #include +#include "exynos-mfc-reserved-memory.dtsi" / { model = "Insignal Origen evaluation board based on Exynos4412"; @@ -466,8 +467,7 @@ }; &mfc { - samsung,mfc-r = <0x43000000 0x800000>; - samsung,mfc-l = <0x51000000 0x800000>; + memory-region = <&mfc_left>, <&mfc_right>; status = "okay"; }; diff --git a/arch/arm/boot/dts/exynos4412-smdk4412.dts b/arch/arm/boot/dts/exynos4412-smdk4412.dts index a51069f..9b6d561 100644 --- a/arch/arm/boot/dts/exynos4412-smdk4412.dts +++ b/arch/arm/boot/dts/exynos4412-smdk4412.dts @@ -14,6 +14,7 @@ /dts-v1/; #include "exynos4412.dtsi" +#include "exynos-mfc-reserved-memory.dtsi" / { model = "Samsung SMDK evaluation board based on Exynos4412"; @@ -112,8 +113,7 @@ }; &mfc { - samsung,mfc-r = <0x43000000 0x800000>; - samsung,mfc-l = <0x51000000 0x800000>; + memory-region = <&mfc_left>, <&mfc_right>; status = "okay"; }; diff --git a/arch/arm/boot/dts/exynos5250-arndale.dts b/arch/arm/boot/dts/exynos5250-arndale.dts index 85dad29..39940f4 100644 --- a/arch/arm/boot/dts/exynos5250-arndale.dts +++ b/arch/arm/boot/dts/exynos5250-arndale.dts @@ -14,6 +14,7 @@ #include #include #include "exynos5250.dtsi" +#include "exynos-mfc-reserved-memory.dtsi" / { model = "Insignal Arndale evaluation board based on EXYNOS5250"; @@ -516,8 +517,7 @@ }; &mfc { - samsung,mfc-r = <0x43000000 0x800000>; - samsung,mfc-l = <0x51000000 0x800000>; + memory-region = <&mfc_left>, <&mfc_right>; }; &mmc_0 { diff --git a/arch/arm/boot/dts/exynos5250-smdk5250.dts b/arch/arm/boot/dts/exynos5250-smdk5250.dts index b7992b1..9fac874 100644 --- a/arch/arm/boot/dts/exynos5250-smdk5250.dts +++ b/arch/arm/boot/dts/exynos5250-smdk5250.dts @@ -13,6 +13,7 @@ #include #include #include "exynos5250.dtsi" +#include "exynos-mfc-reserved-memory.dtsi" / { model = "SAMSUNG SMDK5250 board based on EXYNOS5250"; @@ -344,8 +345,7 @@ }; &mfc { - samsung,mfc-r = <0x43000000 0x800000>; - samsung,mfc-l = <0x51000000 0x800000>; + memory-region = <&mfc_left>, <&mfc_right>; }; &mmc_0 { diff --git a/arch/arm/boot/dts/exynos5250-spring.dts b/arch/arm/boot/dts/exynos5250-spring.dts index ac291f5..784130b 100644 --- a/arch/arm/boot/dts/exynos5250-spring.dts +++ b/arch/arm/boot/dts/exynos5250-spring.dts @@ -14,6 +14,7 @@ #include #include #include "exynos5250.dtsi" +#include "exynos-mfc-reserved-memory.dtsi" / { model = "Google Spring"; @@ -425,8 +426,7 @@ }; &mfc { - samsung,mfc-r = <0x43000000 0x800000>; - samsung,mfc-l = <0x51000000 0x800000>; + memory-region = <&mfc_left>, <&mfc_right>; }; &mmc_0 { diff --git a/arch/arm/boot/dts/exynos5420-arndale-octa.dts b/arch/arm/boot/dts/exynos5420-arndale-octa.dts index 60bc861..b8b5f3a 100644 --- a/arch/arm/boot/dts/exynos5420-arndale-octa.dts +++ b/arch/arm/boot/dts/exynos5420-arndale-octa.dts @@ -16,6 +16,7 @@ #include #include #include +#include "exynos-mfc-reserved-memory.dtsi" / { model = "Insignal Arndale Octa evaluation board based on EXYNOS5420"; @@ -347,8 +348,7 @@ }; &mfc { - samsung,mfc-r = <0x43000000 0x800000>; - samsung,mfc-l = <0x51000000 0x800000>; + memory-region = <&mfc_left>, <&mfc_right>; }; &mmc_0 { diff --git a/arch/arm/boot/dts/exynos5420-peach-pit.dts b/arch/arm/boot/dts/exynos5420-peach-pit.dts index f9d2e4f..d530b4f 100644 --- a/arch/arm/boot/dts/exynos5420-peach-pit.dts +++ b/arch/arm/boot/dts/exynos5420-peach-pit.dts @@ -16,6 +16,7 @@ #include #include "exynos5420.dtsi" #include "exynos5420-cpus.dtsi" +#include "exynos-mfc-reserved-memory.dtsi" / { model = "Google Peach Pit Rev 6+"; @@ -695,8 +696,7 @@ }; &mfc { - samsung,mfc-r = <0x43000000 0x800000>; - samsung,mfc-l = <0x51000000 0x800000>; + memory-region = <&mfc_left>, <&mfc_right>; }; &mmc_0 { diff --git a/arch/arm/boot/dts/exynos5420-smdk5420.dts b/arch/arm/boot/dts/exynos5420-smdk5420.dts index 2e748d1..5206f41 100644 --- a/arch/arm/boot/dts/exynos5420-smdk5420.dts +++ b/arch/arm/boot/dts/exynos5420-smdk5420.dts @@ -13,6 +13,7 @@ #include "exynos5420.dtsi" #include "exynos5420-cpus.dtsi" #include +#include "exynos-mfc-reserved-memory.dtsi" / { model = "Samsung SMDK5420 board based on EXYNOS5420"; @@ -355,8 +356,7 @@ }; &mfc { - samsung,mfc-r = <0x43000000 0x800000>; - samsung,mfc-l = <0x51000000 0x800000>; + memory-region = <&mfc_left>, <&mfc_right>; }; &mmc_0 { diff --git a/arch/arm/boot/dts/exynos5422-odroidxu3-common.dtsi b/arch/arm/boot/dts/exynos5422-odroidxu3-common.dtsi index 2a4e10b..7c2335f 100644 --- a/arch/arm/boot/dts/exynos5422-odroidxu3-common.dtsi +++ b/arch/arm/boot/dts/exynos5422-odroidxu3-common.dtsi @@ -17,6 +17,7 @@ #include "exynos5800.dtsi" #include "exynos5422-cpus.dtsi" #include "exynos5422-cpu-thermal.dtsi" +#include "exynos-mfc-reserved-memory.dtsi" / { memory { @@ -406,8 +407,7 @@ }; &mfc { - samsung,mfc-r = <0x43000000 0x800000>; - samsung,mfc-l = <0x51000000 0x800000>; + memory-region = <&mfc_left>, <&mfc_right>; }; &mmc_0 { diff --git a/arch/arm/boot/dts/exynos5800-peach-pi.dts b/arch/arm/boot/dts/exynos5800-peach-pi.dts index 62ceb89..1f73596 100644 --- a/arch/arm/boot/dts/exynos5800-peach-pi.dts +++ b/arch/arm/boot/dts/exynos5800-peach-pi.dts @@ -16,6 +16,7 @@ #include #include "exynos5800.dtsi" #include "exynos5420-cpus.dtsi" +#include "exynos-mfc-reserved-memory.dtsi" / { model = "Google Peach Pi Rev 10+"; @@ -670,8 +671,7 @@ }; &mfc { - samsung,mfc-r = <0x43000000 0x800000>; - samsung,mfc-l = <0x51000000 0x800000>; + memory-region = <&mfc_left>, <&mfc_right>; }; &mmc_0 { -- cgit v0.10.2 From 96167bd37dd604cbba0aed341765dda8556405b2 Mon Sep 17 00:00:00 2001 From: Marek Szyprowski Date: Tue, 24 May 2016 15:31:30 +0200 Subject: ARM: dts: exynos: Enable MFC device on Exynos4412 Odroid boards Enable support for Multimedia Codec (MFC) device for all Exynos4412-based Odroid boards. Signed-off-by: Marek Szyprowski Reviewed-by: Javier Martinez Canillas Signed-off-by: Krzysztof Kozlowski diff --git a/arch/arm/boot/dts/exynos4412-odroid-common.dtsi b/arch/arm/boot/dts/exynos4412-odroid-common.dtsi index ec7619a..276ac9a 100644 --- a/arch/arm/boot/dts/exynos4412-odroid-common.dtsi +++ b/arch/arm/boot/dts/exynos4412-odroid-common.dtsi @@ -13,6 +13,7 @@ #include "exynos4412.dtsi" #include "exynos4412-ppmu-common.dtsi" #include +#include "exynos-mfc-reserved-memory.dtsi" / { chosen { @@ -499,6 +500,11 @@ clock-names = "iis", "i2s_opclk0", "i2s_opclk1"; }; +&mfc { + memory-region = <&mfc_left>, <&mfc_right>; + status = "okay"; +}; + &mixer { status = "okay"; }; -- cgit v0.10.2 From 783cb948d73b46aa336f0f0beb64789a0db35434 Mon Sep 17 00:00:00 2001 From: Chris Brand Date: Wed, 11 May 2016 14:36:17 -0700 Subject: power: Introduce Broadcom kona reset driver This driver supports reset on both BCM21664 and BCM23550. Code is being moved from arch/arm/mach-bcm/board_bcm21664.c Signed-off-by: Chris Brand Acked-By: Sebastian Reichel Signed-off-by: Florian Fainelli diff --git a/MAINTAINERS b/MAINTAINERS index 7304d2e..2e0187d 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -2489,6 +2489,7 @@ F: arch/arm64/boot/dts/broadcom/ F: arch/arm/configs/bcm_defconfig F: drivers/mmc/host/sdhci-bcm-kona.c F: drivers/clocksource/bcm_kona_timer.c +F: drivers/power/reset/brcm-kona-reset.c BROADCOM BCM2835 ARM ARCHITECTURE M: Stephen Warren diff --git a/drivers/power/reset/Kconfig b/drivers/power/reset/Kconfig index 9bb2622..f38ac90 100644 --- a/drivers/power/reset/Kconfig +++ b/drivers/power/reset/Kconfig @@ -46,6 +46,16 @@ config POWER_RESET_AXXIA Say Y if you have an Axxia family SoC. +config POWER_RESET_BRCMKONA + bool "Broadcom Kona reset driver" + depends on ARM || COMPILE_TEST + default ARCH_BCM_MOBILE + help + This driver provides restart support for Broadcom Kona chips. + + Say Y here if you have a Broadcom Kona-based board and you wish + to have restart support. + config POWER_RESET_BRCMSTB bool "Broadcom STB reset driver" depends on ARM || MIPS || COMPILE_TEST diff --git a/drivers/power/reset/Makefile b/drivers/power/reset/Makefile index ab7aa86..6b6eeb3 100644 --- a/drivers/power/reset/Makefile +++ b/drivers/power/reset/Makefile @@ -3,6 +3,7 @@ obj-$(CONFIG_POWER_RESET_AT91_POWEROFF) += at91-poweroff.o obj-$(CONFIG_POWER_RESET_AT91_RESET) += at91-reset.o obj-$(CONFIG_POWER_RESET_AT91_SAMA5D2_SHDWC) += at91-sama5d2_shdwc.o obj-$(CONFIG_POWER_RESET_AXXIA) += axxia-reset.o +obj-$(CONFIG_POWER_RESET_BRCMKONA) += brcm-kona-reset.o obj-$(CONFIG_POWER_RESET_BRCMSTB) += brcmstb-reboot.o obj-$(CONFIG_POWER_RESET_GPIO) += gpio-poweroff.o obj-$(CONFIG_POWER_RESET_GPIO_RESTART) += gpio-restart.o diff --git a/drivers/power/reset/brcm-kona-reset.c b/drivers/power/reset/brcm-kona-reset.c new file mode 100644 index 0000000..8eaa959 --- /dev/null +++ b/drivers/power/reset/brcm-kona-reset.c @@ -0,0 +1,73 @@ +/* + * Copyright (C) 2016 Broadcom + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation version 2. + * + * This program is distributed "as is" WITHOUT ANY WARRANTY of any + * kind, whether express or implied; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include +#include +#include + +#define RSTMGR_REG_WR_ACCESS_OFFSET 0 +#define RSTMGR_REG_CHIP_SOFT_RST_OFFSET 4 + +#define RSTMGR_WR_PASSWORD 0xa5a5 +#define RSTMGR_WR_PASSWORD_SHIFT 8 +#define RSTMGR_WR_ACCESS_ENABLE 1 + +static void __iomem *kona_reset_base; + +static int kona_reset_handler(struct notifier_block *this, + unsigned long mode, void *cmd) +{ + /* + * A soft reset is triggered by writing a 0 to bit 0 of the soft reset + * register. To write to that register we must first write the password + * and the enable bit in the write access enable register. + */ + writel((RSTMGR_WR_PASSWORD << RSTMGR_WR_PASSWORD_SHIFT) | + RSTMGR_WR_ACCESS_ENABLE, + kona_reset_base + RSTMGR_REG_WR_ACCESS_OFFSET); + writel(0, kona_reset_base + RSTMGR_REG_CHIP_SOFT_RST_OFFSET); + + return NOTIFY_DONE; +} + +static struct notifier_block kona_reset_nb = { + .notifier_call = kona_reset_handler, + .priority = 128, +}; + +static int kona_reset_probe(struct platform_device *pdev) +{ + struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + + kona_reset_base = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(kona_reset_base)) + return PTR_ERR(kona_reset_base); + + return register_restart_handler(&kona_reset_nb); +} + +static const struct of_device_id of_match[] = { + { .compatible = "brcm,bcm21664-resetmgr" }, + {}, +}; + +static struct platform_driver bcm_kona_reset_driver = { + .probe = kona_reset_probe, + .driver = { + .name = "brcm-kona-reset", + .of_match_table = of_match, + }, +}; + +builtin_platform_driver(bcm_kona_reset_driver); -- cgit v0.10.2 From a536bcc931ed5c047100c3a28f521283749d3b9a Mon Sep 17 00:00:00 2001 From: Ben Dooks Date: Tue, 7 Jun 2016 18:54:54 +0100 Subject: soc: brcmstb: fix warning from missing include The brcmstb_biuctrl_init() is defined in the soc specific header file, but wasn't included in the driver file. Fix the following warning by including in the driver: drivers/soc/brcmstb/biuctrl.c:101:13: warning: symbol 'brcmstb_biuctrl_init' was not declared. Should it be static? Signed-off-by: Ben Dooks Signed-off-by: Florian Fainelli diff --git a/drivers/soc/bcm/brcmstb/biuctrl.c b/drivers/soc/bcm/brcmstb/biuctrl.c index 9049c076f..3c39415 100644 --- a/drivers/soc/bcm/brcmstb/biuctrl.c +++ b/drivers/soc/bcm/brcmstb/biuctrl.c @@ -19,6 +19,7 @@ #include #include #include +#include #define CPU_CREDIT_REG_OFFSET 0x184 #define CPU_CREDIT_REG_MCPx_WR_PAIRING_EN_MASK 0x70000000 -- cgit v0.10.2 From f5353c60507cb96195cd56499ee5e56a92388645 Mon Sep 17 00:00:00 2001 From: Thierry Reding Date: Wed, 30 Dec 2015 17:13:29 +0100 Subject: soc/tegra: pmc: Use register definitions instead of magic values Use register definitions for the main SoC reset operation instead of hard-coding magic values. Note that the PMC_RST_STATUS register isn't actually accessed, but since it is mentioned in a comment the definitions are added for completeness. Acked-by: Jon Hunter Signed-off-by: Thierry Reding diff --git a/drivers/soc/tegra/pmc.c b/drivers/soc/tegra/pmc.c index bb17345..d135169 100644 --- a/drivers/soc/tegra/pmc.c +++ b/drivers/soc/tegra/pmc.c @@ -51,6 +51,7 @@ #define PMC_CNTRL_CPU_PWRREQ_POLARITY (1 << 15) /* CPU pwr req polarity */ #define PMC_CNTRL_CPU_PWRREQ_OE (1 << 16) /* CPU pwr req enable */ #define PMC_CNTRL_INTR_POLARITY (1 << 17) /* inverts INTR polarity */ +#define PMC_CNTRL_MAIN_RST (1 << 4) #define DPD_SAMPLE 0x020 #define DPD_SAMPLE_ENABLE (1 << 0) @@ -80,6 +81,14 @@ #define PMC_SENSOR_CTRL_SCRATCH_WRITE (1 << 2) #define PMC_SENSOR_CTRL_ENABLE_RST (1 << 1) +#define PMC_RST_STATUS 0x1b4 +#define PMC_RST_STATUS_POR 0 +#define PMC_RST_STATUS_WATCHDOG 1 +#define PMC_RST_STATUS_SENSOR 2 +#define PMC_RST_STATUS_SW_MAIN 3 +#define PMC_RST_STATUS_LP0 4 +#define PMC_RST_STATUS_AOTAG 5 + #define IO_DPD_REQ 0x1b8 #define IO_DPD_REQ_CODE_IDLE (0 << 30) #define IO_DPD_REQ_CODE_OFF (1 << 30) @@ -638,9 +647,10 @@ static int tegra_pmc_restart_notify(struct notifier_block *this, tegra_pmc_writel(value, PMC_SCRATCH0); - value = tegra_pmc_readl(0); - value |= 0x10; - tegra_pmc_writel(value, 0); + /* reset everything but PMC_SCRATCH0 and PMC_RST_STATUS */ + value = tegra_pmc_readl(PMC_CNTRL); + value |= PMC_CNTRL_MAIN_RST; + tegra_pmc_writel(value, PMC_CNTRL); return NOTIFY_DONE; } -- cgit v0.10.2 From f9d91de0ad8c765f5ec6f2e71db89cb2fb99820b Mon Sep 17 00:00:00 2001 From: Sudeep Holla Date: Wed, 20 Apr 2016 13:25:01 +0100 Subject: firmware: arm_scpi: remove dvfs_get packed structure dvfs_get packed structure is used to read the DVFS/OPP index from the firmware. It just contains a single byte that needs no packing making the whole structure defination unnecessary. This patch replaces the unnecessary dvfs_get packed structure with an unsigned byte. Signed-off-by: Sudeep Holla diff --git a/drivers/firmware/arm_scpi.c b/drivers/firmware/arm_scpi.c index 7e3e595..279fb84 100644 --- a/drivers/firmware/arm_scpi.c +++ b/drivers/firmware/arm_scpi.c @@ -210,10 +210,6 @@ struct dvfs_info { } opps[MAX_DVFS_OPPS]; } __packed; -struct dvfs_get { - u8 index; -} __packed; - struct dvfs_set { u8 domain; u8 index; @@ -431,11 +427,11 @@ static int scpi_clk_set_val(u16 clk_id, unsigned long rate) static int scpi_dvfs_get_idx(u8 domain) { int ret; - struct dvfs_get dvfs; + u8 dvfs_idx; ret = scpi_send_message(SCPI_CMD_GET_DVFS, &domain, sizeof(domain), - &dvfs, sizeof(dvfs)); - return ret ? ret : dvfs.index; + &dvfs_idx, sizeof(dvfs_idx)); + return ret ? ret : dvfs_idx; } static int scpi_dvfs_set_idx(u8 domain, u8 index) -- cgit v0.10.2 From 3678b98f86f920f9a2db38a560c34d6bf899adc0 Mon Sep 17 00:00:00 2001 From: Sudeep Holla Date: Tue, 23 Feb 2016 16:21:16 +0000 Subject: firmware: arm_scpi: mark scpi_get_sensor_value as static scpi_get_sensor_value like other scpi operations needs to be static. This patch marks it as static to be consistent with others. Signed-off-by: Sudeep Holla diff --git a/drivers/firmware/arm_scpi.c b/drivers/firmware/arm_scpi.c index 279fb84..51c6db0 100644 --- a/drivers/firmware/arm_scpi.c +++ b/drivers/firmware/arm_scpi.c @@ -522,7 +522,7 @@ static int scpi_sensor_get_info(u16 sensor_id, struct scpi_sensor_info *info) return ret; } -int scpi_sensor_get_value(u16 sensor, u64 *val) +static int scpi_sensor_get_value(u16 sensor, u64 *val) { __le16 id = cpu_to_le16(sensor); struct sensor_value buf; -- cgit v0.10.2 From 8f1498c03d1503c8675a633e9f354041558cc459 Mon Sep 17 00:00:00 2001 From: Sudeep Holla Date: Sun, 5 Jun 2016 18:23:21 +0100 Subject: firmware: arm_scpi: make it depend on MAILBOX instead of ARM_MHU ARM_SCPI_PROTOCOL can be used with any mailbox and not just ARM MHU mailbox controller. This patch drops it's dependency on ARM_MHU and make it depend on just mailbox framework. Signed-off-by: Sudeep Holla diff --git a/drivers/firmware/Kconfig b/drivers/firmware/Kconfig index 6664f11..41abdc5 100644 --- a/drivers/firmware/Kconfig +++ b/drivers/firmware/Kconfig @@ -10,7 +10,7 @@ config ARM_PSCI_FW config ARM_SCPI_PROTOCOL tristate "ARM System Control and Power Interface (SCPI) Message Protocol" - depends on ARM_MHU + depends on MAILBOX help System Control and Power Interface (SCPI) Message Protocol is defined for the purpose of communication between the Application -- cgit v0.10.2 From aafb197f75cec20331c84ade2da9bc94ae0b9aff Mon Sep 17 00:00:00 2001 From: Amitoj Kaur Chawla Date: Mon, 25 Jan 2016 22:57:48 +0530 Subject: memory: tegra: tegra124-emc: Add missing of_node_put() for_each_child_of_node() performs an of_node_get() on each iteration, so to break out of the loop an of_node_put() is required. Found using Coccinelle. The semantic patch used for this is as follows: // @@ expression e; local idexpression n; @@ for_each_child_of_node(..., n) { ... when != of_node_put(n) when != e = n ( return n; | + of_node_put(n); ? return ...; ) ... } // Signed-off-by: Amitoj Kaur Chawla Signed-off-by: Thierry Reding diff --git a/drivers/memory/tegra/tegra124-emc.c b/drivers/memory/tegra/tegra124-emc.c index 3dac7be..02a6317 100644 --- a/drivers/memory/tegra/tegra124-emc.c +++ b/drivers/memory/tegra/tegra124-emc.c @@ -970,8 +970,10 @@ static int tegra_emc_load_timings_from_dt(struct tegra_emc *emc, timing = &emc->timings[i++]; err = load_one_timing_from_dt(emc, timing, child); - if (err) + if (err) { + of_node_put(child); return err; + } } sort(emc->timings, emc->num_timings, sizeof(*timing), cmp_timings, -- cgit v0.10.2 From d1122e4b7639941305166628613a42fdf6e16296 Mon Sep 17 00:00:00 2001 From: Julia Lawall Date: Fri, 9 Oct 2015 19:47:40 +0200 Subject: memory: tegra: Delete unneeded of_node_put() for_each_child_of_node() performs an of_node_put() on each iteration, so putting an of_node_put() before a continue results in a double put. The semantic match that finds this problem is as follows (http://coccinelle.lip6.fr): // @@ expression root,e; local idexpression child; iterator name for_each_child_of_node; @@ for_each_child_of_node(root, child) { ... when != of_node_get(child) * of_node_put(child); ... * continue; } // Signed-off-by: Julia Lawall Signed-off-by: Thierry Reding diff --git a/drivers/memory/tegra/mc.c b/drivers/memory/tegra/mc.c index a1ae0cc..85ce4be 100644 --- a/drivers/memory/tegra/mc.c +++ b/drivers/memory/tegra/mc.c @@ -206,10 +206,8 @@ static int tegra_mc_setup_timings(struct tegra_mc *mc) for_each_child_of_node(mc->dev->of_node, node) { err = of_property_read_u32(node, "nvidia,ram-code", &node_ram_code); - if (err || (node_ram_code != ram_code)) { - of_node_put(node); + if (err || (node_ram_code != ram_code)) continue; - } err = load_timings(mc, node); if (err) diff --git a/drivers/memory/tegra/tegra124-emc.c b/drivers/memory/tegra/tegra124-emc.c index 02a6317..06cc781 100644 --- a/drivers/memory/tegra/tegra124-emc.c +++ b/drivers/memory/tegra/tegra124-emc.c @@ -997,10 +997,8 @@ tegra_emc_find_node_by_ram_code(struct device_node *node, u32 ram_code) u32 value; err = of_property_read_u32(np, "nvidia,ram-code", &value); - if (err || (value != ram_code)) { - of_node_put(np); + if (err || (value != ram_code)) continue; - } return np; } -- cgit v0.10.2 From 55bb1d8355ce91662dc8760f24f578db5a595819 Mon Sep 17 00:00:00 2001 From: Amitoj Kaur Chawla Date: Mon, 25 Jan 2016 22:53:07 +0530 Subject: memory: tegra: mc: Add missing of_node_put() for_each_child_of_node() performs an of_node_get() on each iteration, so to break out of the loop an of_node_put() is required. Found using Coccinelle. The semantic patch used for this is as follows: // @@ expression e; local idexpression n; @@ for_each_child_of_node(..., n) { ... when != of_node_put(n) when != e = n ( return n; | + of_node_put(n); ? return ...; ) ... } // Signed-off-by: Amitoj Kaur Chawla Signed-off-by: Thierry Reding diff --git a/drivers/memory/tegra/mc.c b/drivers/memory/tegra/mc.c index 85ce4be..a4803ac 100644 --- a/drivers/memory/tegra/mc.c +++ b/drivers/memory/tegra/mc.c @@ -186,8 +186,10 @@ static int load_timings(struct tegra_mc *mc, struct device_node *node) timing = &mc->timings[i++]; err = load_one_timing(mc, timing, child); - if (err) + if (err) { + of_node_put(child); return err; + } } return 0; @@ -210,9 +212,9 @@ static int tegra_mc_setup_timings(struct tegra_mc *mc) continue; err = load_timings(mc, node); + of_node_put(node); if (err) return err; - of_node_put(node); break; } -- cgit v0.10.2 From caf21c61a115561553920dd6fd6f888229b7695e Mon Sep 17 00:00:00 2001 From: Paul Gortmaker Date: Thu, 16 Jun 2016 20:37:45 -0400 Subject: memory: omap-gpmc: make it explicitly non-modular The Kconfig currently controlling compilation of this code is: drivers/memory/Kconfig:config OMAP_GPMC drivers/memory/Kconfig: bool ...meaning that it currently is not being built as a module by anyone. Lets remove the modular code that is essentially orphaned, so that when reading the driver there is no doubt it is builtin-only. Since module_init was not in use by this code, the init ordering remains unchanged with this commit. Also note that MODULE_DEVICE_TABLE is a no-op for non-modular code. We don't replace module.h with init.h since the file already has that. Cc: Roger Quadros Cc: Tony Lindgren Cc: linux-omap@vger.kernel.org Signed-off-by: Paul Gortmaker diff --git a/drivers/memory/omap-gpmc.c b/drivers/memory/omap-gpmc.c index af4884b..827832a 100644 --- a/drivers/memory/omap-gpmc.c +++ b/drivers/memory/omap-gpmc.c @@ -20,7 +20,6 @@ #include #include #include -#include #include #include #include @@ -1807,7 +1806,6 @@ static const struct of_device_id gpmc_dt_ids[] = { { .compatible = "ti,am3352-gpmc" }, /* am335x devices */ { } }; -MODULE_DEVICE_TABLE(of, gpmc_dt_ids); /** * gpmc_read_settings_dt - read gpmc settings from device-tree @@ -2437,15 +2435,7 @@ static __init int gpmc_init(void) { return platform_driver_register(&gpmc_driver); } - -static __exit void gpmc_exit(void) -{ - platform_driver_unregister(&gpmc_driver); - -} - postcore_initcall(gpmc_init); -module_exit(gpmc_exit); static struct omap3_gpmc_regs gpmc_context; -- cgit v0.10.2 From 32dd625a9f3541ee6d390f12f810f28531d016c7 Mon Sep 17 00:00:00 2001 From: Roger Quadros Date: Fri, 17 Jun 2016 10:16:50 +0300 Subject: memory: omap-gpmc: Move gpio functions out of #ifdef CONFIG_OF The gpio related functions were mistakenly built only if CONFIG_OF is defined. They are needed even otherwise and will cause build failures if CONFIG_OF is not defined. Move the gpio functions outside #ifdef CONFIG_OF. Fixes: d2d00862dfbb ("memory: omap-gpmc: Support general purpose input for WAITPINs") Signed-off-by: Roger Quadros diff --git a/drivers/memory/omap-gpmc.c b/drivers/memory/omap-gpmc.c index 827832a..2182988 100644 --- a/drivers/memory/omap-gpmc.c +++ b/drivers/memory/omap-gpmc.c @@ -2153,68 +2153,6 @@ err: return ret; } -static int gpmc_gpio_get_direction(struct gpio_chip *chip, unsigned int offset) -{ - return 1; /* we're input only */ -} - -static int gpmc_gpio_direction_input(struct gpio_chip *chip, - unsigned int offset) -{ - return 0; /* we're input only */ -} - -static int gpmc_gpio_direction_output(struct gpio_chip *chip, - unsigned int offset, int value) -{ - return -EINVAL; /* we're input only */ -} - -static void gpmc_gpio_set(struct gpio_chip *chip, unsigned int offset, - int value) -{ -} - -static int gpmc_gpio_get(struct gpio_chip *chip, unsigned int offset) -{ - u32 reg; - - offset += 8; - - reg = gpmc_read_reg(GPMC_STATUS) & BIT(offset); - - return !!reg; -} - -static int gpmc_gpio_init(struct gpmc_device *gpmc) -{ - int ret; - - gpmc->gpio_chip.parent = gpmc->dev; - gpmc->gpio_chip.owner = THIS_MODULE; - gpmc->gpio_chip.label = DEVICE_NAME; - gpmc->gpio_chip.ngpio = gpmc_nr_waitpins; - gpmc->gpio_chip.get_direction = gpmc_gpio_get_direction; - gpmc->gpio_chip.direction_input = gpmc_gpio_direction_input; - gpmc->gpio_chip.direction_output = gpmc_gpio_direction_output; - gpmc->gpio_chip.set = gpmc_gpio_set; - gpmc->gpio_chip.get = gpmc_gpio_get; - gpmc->gpio_chip.base = -1; - - ret = gpiochip_add(&gpmc->gpio_chip); - if (ret < 0) { - dev_err(gpmc->dev, "could not register gpio chip: %d\n", ret); - return ret; - } - - return 0; -} - -static void gpmc_gpio_exit(struct gpmc_device *gpmc) -{ - gpiochip_remove(&gpmc->gpio_chip); -} - static int gpmc_probe_dt(struct platform_device *pdev) { int ret; @@ -2279,7 +2217,69 @@ static int gpmc_probe_dt_children(struct platform_device *pdev) { return 0; } -#endif +#endif /* CONFIG_OF */ + +static int gpmc_gpio_get_direction(struct gpio_chip *chip, unsigned int offset) +{ + return 1; /* we're input only */ +} + +static int gpmc_gpio_direction_input(struct gpio_chip *chip, + unsigned int offset) +{ + return 0; /* we're input only */ +} + +static int gpmc_gpio_direction_output(struct gpio_chip *chip, + unsigned int offset, int value) +{ + return -EINVAL; /* we're input only */ +} + +static void gpmc_gpio_set(struct gpio_chip *chip, unsigned int offset, + int value) +{ +} + +static int gpmc_gpio_get(struct gpio_chip *chip, unsigned int offset) +{ + u32 reg; + + offset += 8; + + reg = gpmc_read_reg(GPMC_STATUS) & BIT(offset); + + return !!reg; +} + +static int gpmc_gpio_init(struct gpmc_device *gpmc) +{ + int ret; + + gpmc->gpio_chip.parent = gpmc->dev; + gpmc->gpio_chip.owner = THIS_MODULE; + gpmc->gpio_chip.label = DEVICE_NAME; + gpmc->gpio_chip.ngpio = gpmc_nr_waitpins; + gpmc->gpio_chip.get_direction = gpmc_gpio_get_direction; + gpmc->gpio_chip.direction_input = gpmc_gpio_direction_input; + gpmc->gpio_chip.direction_output = gpmc_gpio_direction_output; + gpmc->gpio_chip.set = gpmc_gpio_set; + gpmc->gpio_chip.get = gpmc_gpio_get; + gpmc->gpio_chip.base = -1; + + ret = gpiochip_add(&gpmc->gpio_chip); + if (ret < 0) { + dev_err(gpmc->dev, "could not register gpio chip: %d\n", ret); + return ret; + } + + return 0; +} + +static void gpmc_gpio_exit(struct gpmc_device *gpmc) +{ + gpiochip_remove(&gpmc->gpio_chip); +} static int gpmc_probe(struct platform_device *pdev) { -- cgit v0.10.2 From cbf73175ebcee4d705cfcb1a467b6bdc893dea36 Mon Sep 17 00:00:00 2001 From: Ben Dooks Date: Fri, 17 Jun 2016 18:08:10 +0100 Subject: memory: samsung: endian fixes for IO Use the relaxed versions of the IO accessors to avoid any issues if running in big endian. Signed-off-by: Ben Dooks Signed-off-by: Krzysztof Kozlowski diff --git a/drivers/memory/samsung/exynos-srom.c b/drivers/memory/samsung/exynos-srom.c index 96756fb..5714bdf 100644 --- a/drivers/memory/samsung/exynos-srom.c +++ b/drivers/memory/samsung/exynos-srom.c @@ -91,17 +91,17 @@ static int exynos_srom_configure_bank(struct exynos_srom *srom, if (width == 2) cs |= 1 << EXYNOS_SROM_BW__DATAWIDTH__SHIFT; - bw = __raw_readl(srom->reg_base + EXYNOS_SROM_BW); + bw = readl_relaxed(srom->reg_base + EXYNOS_SROM_BW); bw = (bw & ~(EXYNOS_SROM_BW__CS_MASK << bank)) | (cs << bank); - __raw_writel(bw, srom->reg_base + EXYNOS_SROM_BW); - - __raw_writel(pmc | (timing[0] << EXYNOS_SROM_BCX__TACP__SHIFT) | - (timing[1] << EXYNOS_SROM_BCX__TCAH__SHIFT) | - (timing[2] << EXYNOS_SROM_BCX__TCOH__SHIFT) | - (timing[3] << EXYNOS_SROM_BCX__TACC__SHIFT) | - (timing[4] << EXYNOS_SROM_BCX__TCOS__SHIFT) | - (timing[5] << EXYNOS_SROM_BCX__TACS__SHIFT), - srom->reg_base + EXYNOS_SROM_BC0 + bank); + writel_relaxed(bw, srom->reg_base + EXYNOS_SROM_BW); + + writel_relaxed(pmc | (timing[0] << EXYNOS_SROM_BCX__TACP__SHIFT) | + (timing[1] << EXYNOS_SROM_BCX__TCAH__SHIFT) | + (timing[2] << EXYNOS_SROM_BCX__TCOH__SHIFT) | + (timing[3] << EXYNOS_SROM_BCX__TACC__SHIFT) | + (timing[4] << EXYNOS_SROM_BCX__TCOS__SHIFT) | + (timing[5] << EXYNOS_SROM_BCX__TACS__SHIFT), + srom->reg_base + EXYNOS_SROM_BC0 + bank); return 0; } -- cgit v0.10.2 From 58f388bcf011b47040378754cbe25118f7942151 Mon Sep 17 00:00:00 2001 From: Paul Gortmaker Date: Thu, 16 Jun 2016 20:37:47 -0400 Subject: memory: samsung: exynos-srom: make it explicitly non-modular The Kconfig currently controlling compilation of this code is: memory/samsung/Kconfig:config EXYNOS_SROM memory/samsung/Kconfig: bool "Exynos SROM controller driver" if COMPILE_TEST ...meaning that it currently is not being built as a module by anyone. Lets remove the modular code that is essentially orphaned, so that when reading the driver there is no doubt it is builtin-only. We explicitly disallow a driver unbind, since that doesn't have a sensible use case anyway, and it allows us to drop the ".remove" code for non-modular drivers. Since module_platform_driver() uses the same init level priority as builtin_platform_driver() the init ordering remains unchanged with this commit. Also note that MODULE_DEVICE_TABLE is a no-op for non-modular code. We also delete the MODULE_LICENSE tag etc. since all that information is already contained at the top of the file in the comments. Cc: Pankaj Dubey Signed-off-by: Paul Gortmaker Signed-off-by: Krzysztof Kozlowski diff --git a/drivers/memory/samsung/exynos-srom.c b/drivers/memory/samsung/exynos-srom.c index 5714bdf..067f533 100644 --- a/drivers/memory/samsung/exynos-srom.c +++ b/drivers/memory/samsung/exynos-srom.c @@ -11,7 +11,7 @@ */ #include -#include +#include #include #include #include @@ -159,16 +159,6 @@ static int exynos_srom_probe(struct platform_device *pdev) return of_platform_populate(np, NULL, NULL, dev); } -static int exynos_srom_remove(struct platform_device *pdev) -{ - struct exynos_srom *srom = platform_get_drvdata(pdev); - - kfree(srom->reg_offset); - iounmap(srom->reg_base); - - return 0; -} - #ifdef CONFIG_PM_SLEEP static void exynos_srom_save(void __iomem *base, struct exynos_srom_reg_dump *rd, @@ -211,21 +201,16 @@ static const struct of_device_id of_exynos_srom_ids[] = { }, {}, }; -MODULE_DEVICE_TABLE(of, of_exynos_srom_ids); static SIMPLE_DEV_PM_OPS(exynos_srom_pm_ops, exynos_srom_suspend, exynos_srom_resume); static struct platform_driver exynos_srom_driver = { .probe = exynos_srom_probe, - .remove = exynos_srom_remove, .driver = { .name = "exynos-srom", .of_match_table = of_exynos_srom_ids, .pm = &exynos_srom_pm_ops, + .suppress_bind_attrs = true, }, }; -module_platform_driver(exynos_srom_driver); - -MODULE_AUTHOR("Pankaj Dubey "); -MODULE_DESCRIPTION("Exynos SROM Controller Driver"); -MODULE_LICENSE("GPL"); +builtin_platform_driver(exynos_srom_driver); -- cgit v0.10.2 From 37a441dcd5f4db7f769fee50ba7a2c602903a052 Mon Sep 17 00:00:00 2001 From: Sudeep Holla Date: Wed, 20 Apr 2016 14:05:14 +0100 Subject: firmware: arm_scpi: add support for device power state management SCPI protocol supports device power state management. This deals with power states of various peripheral devices in the system other than the core compute subsystem. This patch adds support for the power state management of those peripheral devices. Tested-by: Mathieu Poirier Tested-by: Jon Medhurst Reviewed-by: Jon Medhurst Reviewed-by: Kevin Hilman Reviewed-by: Ulf Hansson Signed-off-by: Sudeep Holla diff --git a/drivers/firmware/arm_scpi.c b/drivers/firmware/arm_scpi.c index 51c6db0..4388937 100644 --- a/drivers/firmware/arm_scpi.c +++ b/drivers/firmware/arm_scpi.c @@ -231,6 +231,11 @@ struct sensor_value { __le32 hi_val; } __packed; +struct dev_pstate_set { + u16 dev_id; + u8 pstate; +} __packed; + static struct scpi_drvinfo *scpi_info; static int scpi_linux_errmap[SCPI_ERR_MAX] = { @@ -537,6 +542,29 @@ static int scpi_sensor_get_value(u16 sensor, u64 *val) return ret; } +static int scpi_device_get_power_state(u16 dev_id) +{ + int ret; + u8 pstate; + __le16 id = cpu_to_le16(dev_id); + + ret = scpi_send_message(SCPI_CMD_GET_DEVICE_PWR_STATE, &id, + sizeof(id), &pstate, sizeof(pstate)); + return ret ? ret : pstate; +} + +static int scpi_device_set_power_state(u16 dev_id, u8 pstate) +{ + int stat; + struct dev_pstate_set dev_set = { + .dev_id = cpu_to_le16(dev_id), + .pstate = pstate, + }; + + return scpi_send_message(SCPI_CMD_SET_DEVICE_PWR_STATE, &dev_set, + sizeof(dev_set), &stat, sizeof(stat)); +} + static struct scpi_ops scpi_ops = { .get_version = scpi_get_version, .clk_get_range = scpi_clk_get_range, @@ -548,6 +576,8 @@ static struct scpi_ops scpi_ops = { .sensor_get_capability = scpi_sensor_get_capability, .sensor_get_info = scpi_sensor_get_info, .sensor_get_value = scpi_sensor_get_value, + .device_get_power_state = scpi_device_get_power_state, + .device_set_power_state = scpi_device_set_power_state, }; struct scpi_ops *get_scpi_ops(void) diff --git a/include/linux/scpi_protocol.h b/include/linux/scpi_protocol.h index 35de50a..dc5f989 100644 --- a/include/linux/scpi_protocol.h +++ b/include/linux/scpi_protocol.h @@ -70,6 +70,8 @@ struct scpi_ops { int (*sensor_get_capability)(u16 *sensors); int (*sensor_get_info)(u16 sensor_id, struct scpi_sensor_info *); int (*sensor_get_value)(u16, u64 *); + int (*device_get_power_state)(u16); + int (*device_set_power_state)(u16, u8); }; #if IS_REACHABLE(CONFIG_ARM_SCPI_PROTOCOL) -- cgit v0.10.2 From 43752b8d7999ac2ebab85f28dc0c92e248b7b5fa Mon Sep 17 00:00:00 2001 From: Sudeep Holla Date: Sun, 5 Jun 2016 18:50:50 +0100 Subject: Documentation: add DT bindings for ARM SCPI power domains The System Control Processor (SCP) provides peripheral devices with power domains that can be enabled and disabled viathe System Control and Power Interface (SCPI) Message Protocol. Add bindings to allow probing of these device power domians. Cc: Rob Herring Tested-by: Mathieu Poirier Tested-by: Jon Medhurst Acked-by: Mark Rutland Reviewed-by: Jon Medhurst Reviewed-by: Kevin Hilman Reviewed-by: Ulf Hansson Signed-off-by: Sudeep Holla diff --git a/Documentation/devicetree/bindings/arm/arm,scpi.txt b/Documentation/devicetree/bindings/arm/arm,scpi.txt index 313dabd..faa4b44 100644 --- a/Documentation/devicetree/bindings/arm/arm,scpi.txt +++ b/Documentation/devicetree/bindings/arm/arm,scpi.txt @@ -87,10 +87,33 @@ Required properties: implementation for the IDs to use. For Juno R0 and Juno R1 refer to [3]. +Power domain bindings for the power domains based on SCPI Message Protocol +------------------------------------------------------------ + +This binding uses the generic power domain binding[4]. + +PM domain providers +=================== + +Required properties: + - #power-domain-cells : Should be 1. Contains the device or the power + domain ID value used by SCPI commands. + - num-domains: Total number of power domains provided by SCPI. This is + needed as the SCPI message protocol lacks a mechanism to + query this information at runtime. + +PM domain consumers +=================== + +Required properties: + - power-domains : A phandle and PM domain specifier as defined by bindings of + the power controller specified by phandle. + [0] http://infocenter.arm.com/help/topic/com.arm.doc.dui0922b/index.html [1] Documentation/devicetree/bindings/clock/clock-bindings.txt [2] Documentation/devicetree/bindings/thermal/thermal.txt [3] http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.dui0922b/apas03s22.html +[4] Documentation/devicetree/bindings/power/power_domain.txt Example: @@ -144,6 +167,12 @@ scpi_protocol: scpi@2e000000 { compatible = "arm,scpi-sensors"; #thermal-sensor-cells = <1>; }; + + scpi_devpd: scpi-power-domains { + compatible = "arm,scpi-power-domains"; + num-domains = <2>; + #power-domain-cells = <1>; + }; }; cpu@0 { @@ -156,6 +185,7 @@ hdlcd@7ff60000 { ... reg = <0 0x7ff60000 0 0x1000>; clocks = <&scpi_clk 4>; + power-domains = <&scpi_devpd 1>; }; thermal-zones { @@ -186,3 +216,7 @@ The thermal-sensors property in the soc_thermal node uses the temperature sensor provided by SCP firmware to setup a thermal zone. The ID "3" is the sensor identifier for the temperature sensor as used by the firmware. + +The num-domains property in scpi-power-domains domain specifies that +SCPI provides 2 power domains. The hdlcd node uses the power domain with +domain ID 1. -- cgit v0.10.2 From 8bec4337ad4023b26de35d3b0c3a3b2735ffc5c7 Mon Sep 17 00:00:00 2001 From: Sudeep Holla Date: Thu, 2 Jun 2016 16:34:03 +0100 Subject: firmware: scpi: add device power domain support using genpd This patch hooks up the support for device power domain provided by SCPI using the Linux generic power domain infrastructure. Cc: "Rafael J. Wysocki" Tested-by: Mathieu Poirier Tested-by: Jon Medhurst Reviewed-by: Jon Medhurst Reviewed-by: Kevin Hilman Reviewed-by: Ulf Hansson Signed-off-by: Sudeep Holla diff --git a/drivers/firmware/Kconfig b/drivers/firmware/Kconfig index 41abdc5..4683bc3 100644 --- a/drivers/firmware/Kconfig +++ b/drivers/firmware/Kconfig @@ -27,6 +27,16 @@ config ARM_SCPI_PROTOCOL This protocol library provides interface for all the client drivers making use of the features offered by the SCP. +config ARM_SCPI_POWER_DOMAIN + tristate "SCPI power domain driver" + depends on ARM_SCPI_PROTOCOL || COMPILE_TEST + default y + select PM_GENERIC_DOMAINS if PM + select PM_GENERIC_DOMAINS_OF if PM + help + This enables support for the SCPI power domains which can be + enabled or disabled via the SCP firmware + config EDD tristate "BIOS Enhanced Disk Drive calls determine boot disk" depends on X86 diff --git a/drivers/firmware/Makefile b/drivers/firmware/Makefile index 474bada..44a59dc 100644 --- a/drivers/firmware/Makefile +++ b/drivers/firmware/Makefile @@ -3,6 +3,7 @@ # obj-$(CONFIG_ARM_PSCI_FW) += psci.o obj-$(CONFIG_ARM_SCPI_PROTOCOL) += arm_scpi.o +obj-$(CONFIG_ARM_SCPI_POWER_DOMAIN) += scpi_pm_domain.o obj-$(CONFIG_DMI) += dmi_scan.o obj-$(CONFIG_DMI_SYSFS) += dmi-sysfs.o obj-$(CONFIG_EDD) += edd.o diff --git a/drivers/firmware/scpi_pm_domain.c b/drivers/firmware/scpi_pm_domain.c new file mode 100644 index 0000000..f395dec --- /dev/null +++ b/drivers/firmware/scpi_pm_domain.c @@ -0,0 +1,163 @@ +/* + * SCPI Generic power domain support. + * + * Copyright (C) 2016 ARM Ltd. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see . + */ + +#include +#include +#include +#include +#include +#include + +struct scpi_pm_domain { + struct generic_pm_domain genpd; + struct scpi_ops *ops; + u32 domain; + char name[30]; +}; + +/* + * These device power state values are not well-defined in the specification. + * In case, different implementations use different values, we can make these + * specific to compatibles rather than getting these values from device tree. + */ +enum scpi_power_domain_state { + SCPI_PD_STATE_ON = 0, + SCPI_PD_STATE_OFF = 3, +}; + +#define to_scpi_pd(gpd) container_of(gpd, struct scpi_pm_domain, genpd) + +static int scpi_pd_power(struct scpi_pm_domain *pd, bool power_on) +{ + int ret; + enum scpi_power_domain_state state; + + if (power_on) + state = SCPI_PD_STATE_ON; + else + state = SCPI_PD_STATE_OFF; + + ret = pd->ops->device_set_power_state(pd->domain, state); + if (ret) + return ret; + + return !(state == pd->ops->device_get_power_state(pd->domain)); +} + +static int scpi_pd_power_on(struct generic_pm_domain *domain) +{ + struct scpi_pm_domain *pd = to_scpi_pd(domain); + + return scpi_pd_power(pd, true); +} + +static int scpi_pd_power_off(struct generic_pm_domain *domain) +{ + struct scpi_pm_domain *pd = to_scpi_pd(domain); + + return scpi_pd_power(pd, false); +} + +static int scpi_pm_domain_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct device_node *np = dev->of_node; + struct scpi_pm_domain *scpi_pd; + struct genpd_onecell_data *scpi_pd_data; + struct generic_pm_domain **domains; + struct scpi_ops *scpi_ops; + int ret, num_domains, i; + + scpi_ops = get_scpi_ops(); + if (!scpi_ops) + return -EPROBE_DEFER; + + if (!np) { + dev_err(dev, "device tree node not found\n"); + return -ENODEV; + } + + if (!scpi_ops->device_set_power_state || + !scpi_ops->device_get_power_state) { + dev_err(dev, "power domains not supported in the firmware\n"); + return -ENODEV; + } + + ret = of_property_read_u32(np, "num-domains", &num_domains); + if (ret) { + dev_err(dev, "number of domains not found\n"); + return -EINVAL; + } + + scpi_pd = devm_kcalloc(dev, num_domains, sizeof(*scpi_pd), GFP_KERNEL); + if (!scpi_pd) + return -ENOMEM; + + scpi_pd_data = devm_kzalloc(dev, sizeof(*scpi_pd_data), GFP_KERNEL); + if (!scpi_pd_data) + return -ENOMEM; + + domains = devm_kcalloc(dev, num_domains, sizeof(*domains), GFP_KERNEL); + if (!domains) + return -ENOMEM; + + for (i = 0; i < num_domains; i++, scpi_pd++) { + domains[i] = &scpi_pd->genpd; + + scpi_pd->domain = i; + scpi_pd->ops = scpi_ops; + sprintf(scpi_pd->name, "%s.%d", np->name, i); + scpi_pd->genpd.name = scpi_pd->name; + scpi_pd->genpd.power_off = scpi_pd_power_off; + scpi_pd->genpd.power_on = scpi_pd_power_on; + + /* + * Treat all power domains as off at boot. + * + * The SCP firmware itself may have switched on some domains, + * but for reference counting purpose, keep it this way. + */ + pm_genpd_init(&scpi_pd->genpd, NULL, true); + } + + scpi_pd_data->domains = domains; + scpi_pd_data->num_domains = num_domains; + + of_genpd_add_provider_onecell(np, scpi_pd_data); + + return 0; +} + +static const struct of_device_id scpi_power_domain_ids[] = { + { .compatible = "arm,scpi-power-domains", }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, scpi_power_domain_ids); + +static struct platform_driver scpi_power_domain_driver = { + .driver = { + .name = "scpi_power_domain", + .of_match_table = scpi_power_domain_ids, + }, + .probe = scpi_pm_domain_probe, +}; +module_platform_driver(scpi_power_domain_driver); + +MODULE_AUTHOR("Sudeep Holla "); +MODULE_DESCRIPTION("ARM SCPI power domain driver"); +MODULE_LICENSE("GPL v2"); -- cgit v0.10.2 From 563b41c983e1ca92ad2aa8600caaa82012718f76 Mon Sep 17 00:00:00 2001 From: Paul Gortmaker Date: Thu, 16 Jun 2016 20:37:43 -0400 Subject: memory: atmel-sdramc: make it explicitly non-modular The Kconfig for this option is currently: config ATMEL_SDRAMC bool "Atmel (Multi-port DDR-)SDRAM Controller" ...meaning that it currently is not being built as a module by anyone. Lets remove the couple traces of modularity, so that when reading the driver there is no doubt it is builtin-only. Since module_init translates to device_initcall in the non-modular case, the init ordering remains unchanged with this commit. An alternate init level might be worth considering at a later date. Also note that MODULE_DEVICE_TABLE is a no-op for non-modular code. We also delete the MODULE_LICENSE tag etc. since all that information was (or is now) contained at the top of the file in the comments. Signed-off-by: Paul Gortmaker Acked-by: Nicolas Ferre Signed-off-by: Alexandre Belloni diff --git a/drivers/memory/atmel-sdramc.c b/drivers/memory/atmel-sdramc.c index a3ebc8a..53a341f 100644 --- a/drivers/memory/atmel-sdramc.c +++ b/drivers/memory/atmel-sdramc.c @@ -1,6 +1,8 @@ /* * Atmel (Multi-port DDR-)SDRAM Controller driver * + * Author: Alexandre Belloni + * * Copyright (C) 2014 Atmel * * This program is free software: you can redistribute it and/or modify @@ -20,7 +22,7 @@ #include #include #include -#include +#include #include #include @@ -48,7 +50,6 @@ static const struct of_device_id atmel_ramc_of_match[] = { { .compatible = "atmel,sama5d3-ddramc", .data = &sama5d3_caps, }, {}, }; -MODULE_DEVICE_TABLE(of, atmel_ramc_of_match); static int atmel_ramc_probe(struct platform_device *pdev) { @@ -90,8 +91,4 @@ static int __init atmel_ramc_init(void) { return platform_driver_register(&atmel_ramc_driver); } -module_init(atmel_ramc_init); - -MODULE_LICENSE("GPL v2"); -MODULE_AUTHOR("Alexandre Belloni "); -MODULE_DESCRIPTION("Atmel (Multi-port DDR-)SDRAM Controller"); +device_initcall(atmel_ramc_init); -- cgit v0.10.2 From 8a86a093ca3b924e6c2d5267cffb809965582a8d Mon Sep 17 00:00:00 2001 From: Paul Gortmaker Date: Thu, 16 Jun 2016 20:37:48 -0400 Subject: memory: atmel-ebi: make it explicitly non-modular The Kconfig currently controlling compilation of this code is: drivers/memory/Kconfig:config ATMEL_EBI drivers/memory/Kconfig: bool "Atmel EBI driver" ...meaning that it currently is not being built as a module by anyone. Lets remove the few remaining modular references, so that when reading the driver there is no doubt it is builtin-only. Since module_platform_driver() uses the same init level priority as builtin_platform_driver() the init ordering remains unchanged with this commit. Also note that MODULE_DEVICE_TABLE is a no-op for non-modular code. We also delete the MODULE_LICENSE tag etc. since all that information is already contained at the top of the file in the comments. Signed-off-by: Paul Gortmaker Acked-by: Nicolas Ferre Acked-by: Boris Brezillon Signed-off-by: Alexandre Belloni diff --git a/drivers/memory/atmel-ebi.c b/drivers/memory/atmel-ebi.c index 17d9d3f..f87ad6f 100644 --- a/drivers/memory/atmel-ebi.c +++ b/drivers/memory/atmel-ebi.c @@ -14,7 +14,7 @@ #include #include #include -#include +#include #include #include @@ -648,7 +648,6 @@ static const struct of_device_id at91_ebi_id_table[] = { }, { /* sentinel */ } }; -MODULE_DEVICE_TABLE(of, at91_ebi_id_table); static int at91_ebi_dev_disable(struct at91_ebi *ebi, struct device_node *np) { @@ -764,8 +763,4 @@ static struct platform_driver at91_ebi_driver = { .of_match_table = at91_ebi_id_table, }, }; -module_platform_driver_probe(at91_ebi_driver, at91_ebi_probe); - -MODULE_AUTHOR("Jean-Jacques Hiblot "); -MODULE_DESCRIPTION("Atmel EBI driver"); -MODULE_LICENSE("GPL"); +builtin_platform_driver_probe(at91_ebi_driver, at91_ebi_probe); -- cgit v0.10.2 From 187364b6fcabb9f4bfefcb62fab4fcda019b5810 Mon Sep 17 00:00:00 2001 From: Ben Dooks Date: Wed, 22 Jun 2016 10:36:37 +0100 Subject: cpufreq: s5pv210: use relaxed IO accesors The use of __raw IO accesors is not endian safe and should be used sparingly. The relaxed variants should be as lightweight and also are endian safe. Signed-off-by: Ben Dooks Acked-by: Viresh Kumar Signed-off-by: Krzysztof Kozlowski diff --git a/drivers/cpufreq/s5pv210-cpufreq.c b/drivers/cpufreq/s5pv210-cpufreq.c index 06d8591..392b29a 100644 --- a/drivers/cpufreq/s5pv210-cpufreq.c +++ b/drivers/cpufreq/s5pv210-cpufreq.c @@ -220,7 +220,7 @@ static void s5pv210_set_refresh(enum s5pv210_dmc_port ch, unsigned long freq) tmp1 /= tmp; - __raw_writel(tmp1, reg); + writel_relaxed(tmp1, reg); } static int s5pv210_target(struct cpufreq_policy *policy, unsigned int index) @@ -301,29 +301,29 @@ static int s5pv210_target(struct cpufreq_policy *policy, unsigned int index) * 1. Temporary Change divider for MFC and G3D * SCLKA2M(200/1=200)->(200/4=50)Mhz */ - reg = __raw_readl(S5P_CLK_DIV2); + reg = readl_relaxed(S5P_CLK_DIV2); reg &= ~(S5P_CLKDIV2_G3D_MASK | S5P_CLKDIV2_MFC_MASK); reg |= (3 << S5P_CLKDIV2_G3D_SHIFT) | (3 << S5P_CLKDIV2_MFC_SHIFT); - __raw_writel(reg, S5P_CLK_DIV2); + writel_relaxed(reg, S5P_CLK_DIV2); /* For MFC, G3D dividing */ do { - reg = __raw_readl(S5P_CLKDIV_STAT0); + reg = readl_relaxed(S5P_CLKDIV_STAT0); } while (reg & ((1 << 16) | (1 << 17))); /* * 2. Change SCLKA2M(200Mhz)to SCLKMPLL in MFC_MUX, G3D MUX * (200/4=50)->(667/4=166)Mhz */ - reg = __raw_readl(S5P_CLK_SRC2); + reg = readl_relaxed(S5P_CLK_SRC2); reg &= ~(S5P_CLKSRC2_G3D_MASK | S5P_CLKSRC2_MFC_MASK); reg |= (1 << S5P_CLKSRC2_G3D_SHIFT) | (1 << S5P_CLKSRC2_MFC_SHIFT); - __raw_writel(reg, S5P_CLK_SRC2); + writel_relaxed(reg, S5P_CLK_SRC2); do { - reg = __raw_readl(S5P_CLKMUX_STAT1); + reg = readl_relaxed(S5P_CLKMUX_STAT1); } while (reg & ((1 << 7) | (1 << 3))); /* @@ -335,19 +335,19 @@ static int s5pv210_target(struct cpufreq_policy *policy, unsigned int index) s5pv210_set_refresh(DMC1, 133000); /* 4. SCLKAPLL -> SCLKMPLL */ - reg = __raw_readl(S5P_CLK_SRC0); + reg = readl_relaxed(S5P_CLK_SRC0); reg &= ~(S5P_CLKSRC0_MUX200_MASK); reg |= (0x1 << S5P_CLKSRC0_MUX200_SHIFT); - __raw_writel(reg, S5P_CLK_SRC0); + writel_relaxed(reg, S5P_CLK_SRC0); do { - reg = __raw_readl(S5P_CLKMUX_STAT0); + reg = readl_relaxed(S5P_CLKMUX_STAT0); } while (reg & (0x1 << 18)); } /* Change divider */ - reg = __raw_readl(S5P_CLK_DIV0); + reg = readl_relaxed(S5P_CLK_DIV0); reg &= ~(S5P_CLKDIV0_APLL_MASK | S5P_CLKDIV0_A2M_MASK | S5P_CLKDIV0_HCLK200_MASK | S5P_CLKDIV0_PCLK100_MASK | @@ -363,25 +363,25 @@ static int s5pv210_target(struct cpufreq_policy *policy, unsigned int index) (clkdiv_val[index][6] << S5P_CLKDIV0_HCLK133_SHIFT) | (clkdiv_val[index][7] << S5P_CLKDIV0_PCLK66_SHIFT)); - __raw_writel(reg, S5P_CLK_DIV0); + writel_relaxed(reg, S5P_CLK_DIV0); do { - reg = __raw_readl(S5P_CLKDIV_STAT0); + reg = readl_relaxed(S5P_CLKDIV_STAT0); } while (reg & 0xff); /* ARM MCS value changed */ - reg = __raw_readl(S5P_ARM_MCS_CON); + reg = readl_relaxed(S5P_ARM_MCS_CON); reg &= ~0x3; if (index >= L3) reg |= 0x3; else reg |= 0x1; - __raw_writel(reg, S5P_ARM_MCS_CON); + writel_relaxed(reg, S5P_ARM_MCS_CON); if (pll_changing) { /* 5. Set Lock time = 30us*24Mhz = 0x2cf */ - __raw_writel(0x2cf, S5P_APLL_LOCK); + writel_relaxed(0x2cf, S5P_APLL_LOCK); /* * 6. Turn on APLL @@ -389,12 +389,12 @@ static int s5pv210_target(struct cpufreq_policy *policy, unsigned int index) * 6-2. Wait untile the PLL is locked */ if (index == L0) - __raw_writel(APLL_VAL_1000, S5P_APLL_CON); + writel_relaxed(APLL_VAL_1000, S5P_APLL_CON); else - __raw_writel(APLL_VAL_800, S5P_APLL_CON); + writel_relaxed(APLL_VAL_800, S5P_APLL_CON); do { - reg = __raw_readl(S5P_APLL_CON); + reg = readl_relaxed(S5P_APLL_CON); } while (!(reg & (0x1 << 29))); /* @@ -402,39 +402,39 @@ static int s5pv210_target(struct cpufreq_policy *policy, unsigned int index) * to SCLKA2M(200Mhz) in MFC_MUX and G3D MUX * (667/4=166)->(200/4=50)Mhz */ - reg = __raw_readl(S5P_CLK_SRC2); + reg = readl_relaxed(S5P_CLK_SRC2); reg &= ~(S5P_CLKSRC2_G3D_MASK | S5P_CLKSRC2_MFC_MASK); reg |= (0 << S5P_CLKSRC2_G3D_SHIFT) | (0 << S5P_CLKSRC2_MFC_SHIFT); - __raw_writel(reg, S5P_CLK_SRC2); + writel_relaxed(reg, S5P_CLK_SRC2); do { - reg = __raw_readl(S5P_CLKMUX_STAT1); + reg = readl_relaxed(S5P_CLKMUX_STAT1); } while (reg & ((1 << 7) | (1 << 3))); /* * 8. Change divider for MFC and G3D * (200/4=50)->(200/1=200)Mhz */ - reg = __raw_readl(S5P_CLK_DIV2); + reg = readl_relaxed(S5P_CLK_DIV2); reg &= ~(S5P_CLKDIV2_G3D_MASK | S5P_CLKDIV2_MFC_MASK); reg |= (clkdiv_val[index][10] << S5P_CLKDIV2_G3D_SHIFT) | (clkdiv_val[index][9] << S5P_CLKDIV2_MFC_SHIFT); - __raw_writel(reg, S5P_CLK_DIV2); + writel_relaxed(reg, S5P_CLK_DIV2); /* For MFC, G3D dividing */ do { - reg = __raw_readl(S5P_CLKDIV_STAT0); + reg = readl_relaxed(S5P_CLKDIV_STAT0); } while (reg & ((1 << 16) | (1 << 17))); /* 9. Change MPLL to APLL in MSYS_MUX */ - reg = __raw_readl(S5P_CLK_SRC0); + reg = readl_relaxed(S5P_CLK_SRC0); reg &= ~(S5P_CLKSRC0_MUX200_MASK); reg |= (0x0 << S5P_CLKSRC0_MUX200_SHIFT); - __raw_writel(reg, S5P_CLK_SRC0); + writel_relaxed(reg, S5P_CLK_SRC0); do { - reg = __raw_readl(S5P_CLKMUX_STAT0); + reg = readl_relaxed(S5P_CLKMUX_STAT0); } while (reg & (0x1 << 18)); /* @@ -451,13 +451,13 @@ static int s5pv210_target(struct cpufreq_policy *policy, unsigned int index) * and memory refresh parameter should be changed */ if (bus_speed_changing) { - reg = __raw_readl(S5P_CLK_DIV6); + reg = readl_relaxed(S5P_CLK_DIV6); reg &= ~S5P_CLKDIV6_ONEDRAM_MASK; reg |= (clkdiv_val[index][8] << S5P_CLKDIV6_ONEDRAM_SHIFT); - __raw_writel(reg, S5P_CLK_DIV6); + writel_relaxed(reg, S5P_CLK_DIV6); do { - reg = __raw_readl(S5P_CLKDIV_STAT1); + reg = readl_relaxed(S5P_CLKDIV_STAT1); } while (reg & (1 << 15)); /* Reconfigure DRAM refresh counter value */ @@ -497,7 +497,7 @@ static int check_mem_type(void __iomem *dmc_reg) { unsigned long val; - val = __raw_readl(dmc_reg + 0x4); + val = readl_relaxed(dmc_reg + 0x4); val = (val & (0xf << 8)); return val >> 8; @@ -542,10 +542,10 @@ static int s5pv210_cpu_init(struct cpufreq_policy *policy) } /* Find current refresh counter and frequency each DMC */ - s5pv210_dram_conf[0].refresh = (__raw_readl(dmc_base[0] + 0x30) * 1000); + s5pv210_dram_conf[0].refresh = (readl_relaxed(dmc_base[0] + 0x30) * 1000); s5pv210_dram_conf[0].freq = clk_get_rate(dmc0_clk); - s5pv210_dram_conf[1].refresh = (__raw_readl(dmc_base[1] + 0x30) * 1000); + s5pv210_dram_conf[1].refresh = (readl_relaxed(dmc_base[1] + 0x30) * 1000); s5pv210_dram_conf[1].freq = clk_get_rate(dmc1_clk); policy->suspend_freq = SLEEP_FREQ; -- cgit v0.10.2 From 0ff50d60dd80e821374ec948c34a386ed5a99bc2 Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Sun, 22 May 2016 11:05:41 +0200 Subject: MAINTAINERS: Add file patterns for qcom device tree bindings Submitters of device tree binding documentation may forget to CC the subsystem maintainer if this is missing. Signed-off-by: Geert Uytterhoeven Cc: Andy Gross Cc: David Brown Cc: linux-arm-msm@vger.kernel.org Cc: linux-soc@vger.kernel.org Signed-off-by: Andy Gross diff --git a/MAINTAINERS b/MAINTAINERS index 7304d2e..79a43ea 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -1521,6 +1521,7 @@ M: David Brown L: linux-arm-msm@vger.kernel.org L: linux-soc@vger.kernel.org S: Maintained +F: Documentation/devicetree/bindings/soc/qcom/ F: arch/arm/boot/dts/qcom-*.dts F: arch/arm/boot/dts/qcom-*.dtsi F: arch/arm/mach-qcom/ -- cgit v0.10.2 From d0f6fa7ba2d6244e0e7ec32f91903ca398e66bb9 Mon Sep 17 00:00:00 2001 From: Andy Gross Date: Fri, 3 Jun 2016 18:25:22 -0500 Subject: firmware: qcom: scm: Convert SCM to platform driver This patch converts the Qualcomm SCM firmware driver into a platform driver. It also adds clock management for firmware calls which require clocks to be enabled during the duration of their execution. Rate setting of the core clock is also in place for higher performance. Signed-off-by: Andy Gross Acked-by: Bjorn Andersson Reviewed-by: Stephen Boyd diff --git a/drivers/firmware/qcom_scm.c b/drivers/firmware/qcom_scm.c index 45c008d..c4ec60d 100644 --- a/drivers/firmware/qcom_scm.c +++ b/drivers/firmware/qcom_scm.c @@ -10,19 +10,61 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA - * 02110-1301, USA. */ - +#include +#include #include #include #include #include +#include +#include +#include #include "qcom_scm.h" +struct qcom_scm { + struct device *dev; + struct clk *core_clk; + struct clk *iface_clk; + struct clk *bus_clk; +}; + +static struct qcom_scm *__scm; + +static int qcom_scm_clk_enable(void) +{ + int ret; + + ret = clk_prepare_enable(__scm->core_clk); + if (ret) + goto bail; + + ret = clk_prepare_enable(__scm->iface_clk); + if (ret) + goto disable_core; + + ret = clk_prepare_enable(__scm->bus_clk); + if (ret) + goto disable_iface; + + return 0; + +disable_iface: + clk_disable_unprepare(__scm->iface_clk); +disable_core: + clk_disable_unprepare(__scm->core_clk); +bail: + return ret; +} + +static void qcom_scm_clk_disable(void) +{ + clk_disable_unprepare(__scm->core_clk); + clk_disable_unprepare(__scm->iface_clk); + clk_disable_unprepare(__scm->bus_clk); +} + /** * qcom_scm_set_cold_boot_addr() - Set the cold boot address for cpus * @entry: Entry point function for the cpus @@ -72,12 +114,17 @@ EXPORT_SYMBOL(qcom_scm_cpu_power_down); */ bool qcom_scm_hdcp_available(void) { - int ret; + int ret = qcom_scm_clk_enable(); + + if (ret) + return ret; ret = __qcom_scm_is_call_available(QCOM_SCM_SVC_HDCP, - QCOM_SCM_CMD_HDCP); + QCOM_SCM_CMD_HDCP); + + qcom_scm_clk_disable(); - return (ret > 0) ? true : false; + return ret > 0 ? true : false; } EXPORT_SYMBOL(qcom_scm_hdcp_available); @@ -91,6 +138,115 @@ EXPORT_SYMBOL(qcom_scm_hdcp_available); */ int qcom_scm_hdcp_req(struct qcom_scm_hdcp_req *req, u32 req_cnt, u32 *resp) { - return __qcom_scm_hdcp_req(req, req_cnt, resp); + int ret = qcom_scm_clk_enable(); + + if (ret) + return ret; + + ret = __qcom_scm_hdcp_req(req, req_cnt, resp); + qcom_scm_clk_disable(); + return ret; } EXPORT_SYMBOL(qcom_scm_hdcp_req); + +static int qcom_scm_probe(struct platform_device *pdev) +{ + struct qcom_scm *scm; + int ret; + + scm = devm_kzalloc(&pdev->dev, sizeof(*scm), GFP_KERNEL); + if (!scm) + return -ENOMEM; + + scm->core_clk = devm_clk_get(&pdev->dev, "core"); + if (IS_ERR(scm->core_clk)) { + if (PTR_ERR(scm->core_clk) == -EPROBE_DEFER) + return PTR_ERR(scm->core_clk); + + scm->core_clk = NULL; + } + + if (of_device_is_compatible(pdev->dev.of_node, "qcom,scm")) { + scm->iface_clk = devm_clk_get(&pdev->dev, "iface"); + if (IS_ERR(scm->iface_clk)) { + if (PTR_ERR(scm->iface_clk) != -EPROBE_DEFER) + dev_err(&pdev->dev, "failed to acquire iface clk\n"); + return PTR_ERR(scm->iface_clk); + } + + scm->bus_clk = devm_clk_get(&pdev->dev, "bus"); + if (IS_ERR(scm->bus_clk)) { + if (PTR_ERR(scm->bus_clk) != -EPROBE_DEFER) + dev_err(&pdev->dev, "failed to acquire bus clk\n"); + return PTR_ERR(scm->bus_clk); + } + } + + /* vote for max clk rate for highest performance */ + ret = clk_set_rate(scm->core_clk, INT_MAX); + if (ret) + return ret; + + __scm = scm; + __scm->dev = &pdev->dev; + + return 0; +} + +static const struct of_device_id qcom_scm_dt_match[] = { + { .compatible = "qcom,scm-apq8064",}, + { .compatible = "qcom,scm-msm8660",}, + { .compatible = "qcom,scm-msm8960",}, + { .compatible = "qcom,scm",}, + {} +}; + +MODULE_DEVICE_TABLE(of, qcom_scm_dt_match); + +static struct platform_driver qcom_scm_driver = { + .driver = { + .name = "qcom_scm", + .of_match_table = qcom_scm_dt_match, + }, + .probe = qcom_scm_probe, +}; + +static int __init qcom_scm_init(void) +{ + struct device_node *np, *fw_np; + int ret; + + fw_np = of_find_node_by_name(NULL, "firmware"); + + if (!fw_np) + return -ENODEV; + + np = of_find_matching_node(fw_np, qcom_scm_dt_match); + + if (!np) { + of_node_put(fw_np); + return -ENODEV; + } + + of_node_put(np); + + ret = of_platform_populate(fw_np, qcom_scm_dt_match, NULL, NULL); + + of_node_put(fw_np); + + if (ret) + return ret; + + return platform_driver_register(&qcom_scm_driver); +} + +arch_initcall(qcom_scm_init); + +static void __exit qcom_scm_exit(void) +{ + platform_driver_unregister(&qcom_scm_driver); +} +module_exit(qcom_scm_exit); + +MODULE_DESCRIPTION("Qualcomm SCM driver"); +MODULE_LICENSE("GPL v2"); -- cgit v0.10.2 From 13e77747800ef71224a12f787ebd26b27c16ff12 Mon Sep 17 00:00:00 2001 From: Andy Gross Date: Fri, 3 Jun 2016 18:25:23 -0500 Subject: firmware: qcom: scm: Use atomic SCM for cold boot This patch changes the cold_set_boot_addr function to use atomic SCM calls. cold_set_boot_addr required adding qcom_scm_call_atomic2 to support the two arguments going to the smc call. Using atomic removes the need for memory allocation and instead places all arguments in registers. Signed-off-by: Andy Gross Reviewed-by: Stephen Boyd Acked-by: Bjorn Andersson diff --git a/drivers/firmware/qcom_scm-32.c b/drivers/firmware/qcom_scm-32.c index 0883292..5be6a12 100644 --- a/drivers/firmware/qcom_scm-32.c +++ b/drivers/firmware/qcom_scm-32.c @@ -342,6 +342,41 @@ static s32 qcom_scm_call_atomic1(u32 svc, u32 cmd, u32 arg1) return r0; } +/** + * qcom_scm_call_atomic2() - Send an atomic SCM command with two arguments + * @svc_id: service identifier + * @cmd_id: command identifier + * @arg1: first argument + * @arg2: second argument + * + * This shall only be used with commands that are guaranteed to be + * uninterruptable, atomic and SMP safe. + */ +static s32 qcom_scm_call_atomic2(u32 svc, u32 cmd, u32 arg1, u32 arg2) +{ + int context_id; + + register u32 r0 asm("r0") = SCM_ATOMIC(svc, cmd, 2); + register u32 r1 asm("r1") = (u32)&context_id; + register u32 r2 asm("r2") = arg1; + register u32 r3 asm("r3") = arg2; + + asm volatile( + __asmeq("%0", "r0") + __asmeq("%1", "r0") + __asmeq("%2", "r1") + __asmeq("%3", "r2") + __asmeq("%4", "r3") +#ifdef REQUIRES_SEC + ".arch_extension sec\n" +#endif + "smc #0 @ switch to secure world\n" + : "=r" (r0) + : "r" (r0), "r" (r1), "r" (r2), "r" (r3) + ); + return r0; +} + u32 qcom_scm_get_version(void) { int context_id; @@ -378,22 +413,6 @@ u32 qcom_scm_get_version(void) } EXPORT_SYMBOL(qcom_scm_get_version); -/* - * Set the cold/warm boot address for one of the CPU cores. - */ -static int qcom_scm_set_boot_addr(u32 addr, int flags) -{ - struct { - __le32 flags; - __le32 addr; - } cmd; - - cmd.addr = cpu_to_le32(addr); - cmd.flags = cpu_to_le32(flags); - return qcom_scm_call(QCOM_SCM_SVC_BOOT, QCOM_SCM_BOOT_ADDR, - &cmd, sizeof(cmd), NULL, 0); -} - /** * qcom_scm_set_cold_boot_addr() - Set the cold boot address for cpus * @entry: Entry point function for the cpus @@ -423,7 +442,8 @@ int __qcom_scm_set_cold_boot_addr(void *entry, const cpumask_t *cpus) set_cpu_present(cpu, false); } - return qcom_scm_set_boot_addr(virt_to_phys(entry), flags); + return qcom_scm_call_atomic2(QCOM_SCM_SVC_BOOT, QCOM_SCM_BOOT_ADDR, + flags, virt_to_phys(entry)); } /** @@ -439,6 +459,10 @@ int __qcom_scm_set_warm_boot_addr(void *entry, const cpumask_t *cpus) int ret; int flags = 0; int cpu; + struct { + __le32 flags; + __le32 addr; + } cmd; /* * Reassign only if we are switching from hotplug entry point @@ -454,7 +478,10 @@ int __qcom_scm_set_warm_boot_addr(void *entry, const cpumask_t *cpus) if (!flags) return 0; - ret = qcom_scm_set_boot_addr(virt_to_phys(entry), flags); + cmd.addr = cpu_to_le32(virt_to_phys(entry)); + cmd.flags = cpu_to_le32(flags); + ret = qcom_scm_call(QCOM_SCM_SVC_BOOT, QCOM_SCM_BOOT_ADDR, + &cmd, sizeof(cmd), NULL, 0); if (!ret) { for_each_cpu(cpu, cpus) qcom_scm_wb[cpu].entry = entry; -- cgit v0.10.2 From 11bdcee4a6b9ff0b09144a81a6b903cbfa599be7 Mon Sep 17 00:00:00 2001 From: Andy Gross Date: Fri, 3 Jun 2016 18:25:24 -0500 Subject: firmware: qcom: scm: Generalize shared error map This patch moves the qcom_scm_remap_error function to the include file where can be used by both the 32 and 64 bit versions of the code. Reviewed-by: Stephen Boyd Acked-by: Bjorn Andersson Signed-off-by: Andy Gross Signed-off-by: Andy Gross diff --git a/drivers/firmware/qcom_scm-32.c b/drivers/firmware/qcom_scm-32.c index 5be6a12..4388d13 100644 --- a/drivers/firmware/qcom_scm-32.c +++ b/drivers/firmware/qcom_scm-32.c @@ -168,23 +168,6 @@ static inline void *qcom_scm_get_response_buffer(const struct qcom_scm_response return (void *)rsp + le32_to_cpu(rsp->buf_offset); } -static int qcom_scm_remap_error(int err) -{ - pr_err("qcom_scm_call failed with error code %d\n", err); - switch (err) { - case QCOM_SCM_ERROR: - return -EIO; - case QCOM_SCM_EINVAL_ADDR: - case QCOM_SCM_EINVAL_ARG: - return -EINVAL; - case QCOM_SCM_EOPNOTSUPP: - return -EOPNOTSUPP; - case QCOM_SCM_ENOMEM: - return -ENOMEM; - } - return -EINVAL; -} - static u32 smc(u32 cmd_addr) { int context_id; diff --git a/drivers/firmware/qcom_scm.h b/drivers/firmware/qcom_scm.h index 2cce75c..7dcc733 100644 --- a/drivers/firmware/qcom_scm.h +++ b/drivers/firmware/qcom_scm.h @@ -44,4 +44,20 @@ extern int __qcom_scm_hdcp_req(struct qcom_scm_hdcp_req *req, u32 req_cnt, #define QCOM_SCM_ERROR -1 #define QCOM_SCM_INTERRUPTED 1 +static inline int qcom_scm_remap_error(int err) +{ + switch (err) { + case QCOM_SCM_ERROR: + return -EIO; + case QCOM_SCM_EINVAL_ADDR: + case QCOM_SCM_EINVAL_ARG: + return -EINVAL; + case QCOM_SCM_EOPNOTSUPP: + return -EOPNOTSUPP; + case QCOM_SCM_ENOMEM: + return -ENOMEM; + } + return -EINVAL; +} + #endif -- cgit v0.10.2 From 16e59467a446514f971cc4669322ab387ca45155 Mon Sep 17 00:00:00 2001 From: Andy Gross Date: Fri, 3 Jun 2016 18:25:25 -0500 Subject: firmware: qcom: scm: Convert to streaming DMA APIS This patch converts the Qualcomm SCM driver to use the streaming DMA APIs for communication buffers. This is being done so that the secure_flush_area call can be removed. Using the DMA APIs will also make the SCM32 symmetric to the coming SCM64 code. Signed-off-by: Andy Gross Reviewed-by: Bjorn Andersson Reviewed-by: Stephen Boyd diff --git a/drivers/firmware/qcom_scm-32.c b/drivers/firmware/qcom_scm-32.c index 4388d13..83a9351 100644 --- a/drivers/firmware/qcom_scm-32.c +++ b/drivers/firmware/qcom_scm-32.c @@ -23,8 +23,7 @@ #include #include #include - -#include +#include #include "qcom_scm.h" @@ -97,44 +96,6 @@ struct qcom_scm_response { }; /** - * alloc_qcom_scm_command() - Allocate an SCM command - * @cmd_size: size of the command buffer - * @resp_size: size of the response buffer - * - * Allocate an SCM command, including enough room for the command - * and response headers as well as the command and response buffers. - * - * Returns a valid &qcom_scm_command on success or %NULL if the allocation fails. - */ -static struct qcom_scm_command *alloc_qcom_scm_command(size_t cmd_size, size_t resp_size) -{ - struct qcom_scm_command *cmd; - size_t len = sizeof(*cmd) + sizeof(struct qcom_scm_response) + cmd_size + - resp_size; - u32 offset; - - cmd = kzalloc(PAGE_ALIGN(len), GFP_KERNEL); - if (cmd) { - cmd->len = cpu_to_le32(len); - offset = offsetof(struct qcom_scm_command, buf); - cmd->buf_offset = cpu_to_le32(offset); - cmd->resp_hdr_offset = cpu_to_le32(offset + cmd_size); - } - return cmd; -} - -/** - * free_qcom_scm_command() - Free an SCM command - * @cmd: command to free - * - * Free an SCM command. - */ -static inline void free_qcom_scm_command(struct qcom_scm_command *cmd) -{ - kfree(cmd); -} - -/** * qcom_scm_command_to_response() - Get a pointer to a qcom_scm_response * @cmd: command * @@ -192,45 +153,9 @@ static u32 smc(u32 cmd_addr) return r0; } -static int __qcom_scm_call(const struct qcom_scm_command *cmd) -{ - int ret; - u32 cmd_addr = virt_to_phys(cmd); - - /* - * Flush the command buffer so that the secure world sees - * the correct data. - */ - secure_flush_area(cmd, cmd->len); - - ret = smc(cmd_addr); - if (ret < 0) - ret = qcom_scm_remap_error(ret); - - return ret; -} - -static void qcom_scm_inv_range(unsigned long start, unsigned long end) -{ - u32 cacheline_size, ctr; - - asm volatile("mrc p15, 0, %0, c0, c0, 1" : "=r" (ctr)); - cacheline_size = 4 << ((ctr >> 16) & 0xf); - - start = round_down(start, cacheline_size); - end = round_up(end, cacheline_size); - outer_inv_range(start, end); - while (start < end) { - asm ("mcr p15, 0, %0, c7, c6, 1" : : "r" (start) - : "memory"); - start += cacheline_size; - } - dsb(); - isb(); -} - /** * qcom_scm_call() - Send an SCM command + * @dev: struct device * @svc_id: service identifier * @cmd_id: command identifier * @cmd_buf: command buffer @@ -247,42 +172,59 @@ static void qcom_scm_inv_range(unsigned long start, unsigned long end) * and response buffers is taken care of by qcom_scm_call; however, callers are * responsible for any other cached buffers passed over to the secure world. */ -static int qcom_scm_call(u32 svc_id, u32 cmd_id, const void *cmd_buf, - size_t cmd_len, void *resp_buf, size_t resp_len) +static int qcom_scm_call(struct device *dev, u32 svc_id, u32 cmd_id, + const void *cmd_buf, size_t cmd_len, void *resp_buf, + size_t resp_len) { int ret; struct qcom_scm_command *cmd; struct qcom_scm_response *rsp; - unsigned long start, end; + size_t alloc_len = sizeof(*cmd) + cmd_len + sizeof(*rsp) + resp_len; + dma_addr_t cmd_phys; - cmd = alloc_qcom_scm_command(cmd_len, resp_len); + cmd = kzalloc(PAGE_ALIGN(alloc_len), GFP_KERNEL); if (!cmd) return -ENOMEM; + cmd->len = cpu_to_le32(alloc_len); + cmd->buf_offset = cpu_to_le32(sizeof(*cmd)); + cmd->resp_hdr_offset = cpu_to_le32(sizeof(*cmd) + cmd_len); + cmd->id = cpu_to_le32((svc_id << 10) | cmd_id); if (cmd_buf) memcpy(qcom_scm_get_command_buffer(cmd), cmd_buf, cmd_len); + rsp = qcom_scm_command_to_response(cmd); + + cmd_phys = dma_map_single(dev, cmd, alloc_len, DMA_TO_DEVICE); + if (dma_mapping_error(dev, cmd_phys)) { + kfree(cmd); + return -ENOMEM; + } + mutex_lock(&qcom_scm_lock); - ret = __qcom_scm_call(cmd); + ret = smc(cmd_phys); + if (ret < 0) + ret = qcom_scm_remap_error(ret); mutex_unlock(&qcom_scm_lock); if (ret) goto out; - rsp = qcom_scm_command_to_response(cmd); - start = (unsigned long)rsp; - do { - qcom_scm_inv_range(start, start + sizeof(*rsp)); + dma_sync_single_for_cpu(dev, cmd_phys + sizeof(*cmd) + cmd_len, + sizeof(*rsp), DMA_FROM_DEVICE); } while (!rsp->is_complete); - end = (unsigned long)qcom_scm_get_response_buffer(rsp) + resp_len; - qcom_scm_inv_range(start, end); - - if (resp_buf) - memcpy(resp_buf, qcom_scm_get_response_buffer(rsp), resp_len); + if (resp_buf) { + dma_sync_single_for_cpu(dev, cmd_phys + sizeof(*cmd) + cmd_len + + le32_to_cpu(rsp->buf_offset), + resp_len, DMA_FROM_DEVICE); + memcpy(resp_buf, qcom_scm_get_response_buffer(rsp), + resp_len); + } out: - free_qcom_scm_command(cmd); + dma_unmap_single(dev, cmd_phys, alloc_len, DMA_TO_DEVICE); + kfree(cmd); return ret; } @@ -437,7 +379,8 @@ int __qcom_scm_set_cold_boot_addr(void *entry, const cpumask_t *cpus) * Set the Linux entry point for the SCM to transfer control to when coming * out of a power down. CPU power down may be executed on cpuidle or hotplug. */ -int __qcom_scm_set_warm_boot_addr(void *entry, const cpumask_t *cpus) +int __qcom_scm_set_warm_boot_addr(struct device *dev, void *entry, + const cpumask_t *cpus) { int ret; int flags = 0; @@ -463,7 +406,7 @@ int __qcom_scm_set_warm_boot_addr(void *entry, const cpumask_t *cpus) cmd.addr = cpu_to_le32(virt_to_phys(entry)); cmd.flags = cpu_to_le32(flags); - ret = qcom_scm_call(QCOM_SCM_SVC_BOOT, QCOM_SCM_BOOT_ADDR, + ret = qcom_scm_call(dev, QCOM_SCM_SVC_BOOT, QCOM_SCM_BOOT_ADDR, &cmd, sizeof(cmd), NULL, 0); if (!ret) { for_each_cpu(cpu, cpus) @@ -487,25 +430,27 @@ void __qcom_scm_cpu_power_down(u32 flags) flags & QCOM_SCM_FLUSH_FLAG_MASK); } -int __qcom_scm_is_call_available(u32 svc_id, u32 cmd_id) +int __qcom_scm_is_call_available(struct device *dev, u32 svc_id, u32 cmd_id) { int ret; __le32 svc_cmd = cpu_to_le32((svc_id << 10) | cmd_id); __le32 ret_val = 0; - ret = qcom_scm_call(QCOM_SCM_SVC_INFO, QCOM_IS_CALL_AVAIL_CMD, &svc_cmd, - sizeof(svc_cmd), &ret_val, sizeof(ret_val)); + ret = qcom_scm_call(dev, QCOM_SCM_SVC_INFO, QCOM_IS_CALL_AVAIL_CMD, + &svc_cmd, sizeof(svc_cmd), &ret_val, + sizeof(ret_val)); if (ret) return ret; return le32_to_cpu(ret_val); } -int __qcom_scm_hdcp_req(struct qcom_scm_hdcp_req *req, u32 req_cnt, u32 *resp) +int __qcom_scm_hdcp_req(struct device *dev, struct qcom_scm_hdcp_req *req, + u32 req_cnt, u32 *resp) { if (req_cnt > QCOM_SCM_HDCP_MAX_REQ_CNT) return -ERANGE; - return qcom_scm_call(QCOM_SCM_SVC_HDCP, QCOM_SCM_CMD_HDCP, + return qcom_scm_call(dev, QCOM_SCM_SVC_HDCP, QCOM_SCM_CMD_HDCP, req, req_cnt * sizeof(*req), resp, sizeof(*resp)); } diff --git a/drivers/firmware/qcom_scm.c b/drivers/firmware/qcom_scm.c index c4ec60d..937c64a 100644 --- a/drivers/firmware/qcom_scm.c +++ b/drivers/firmware/qcom_scm.c @@ -89,7 +89,7 @@ EXPORT_SYMBOL(qcom_scm_set_cold_boot_addr); */ int qcom_scm_set_warm_boot_addr(void *entry, const cpumask_t *cpus) { - return __qcom_scm_set_warm_boot_addr(entry, cpus); + return __qcom_scm_set_warm_boot_addr(__scm->dev, entry, cpus); } EXPORT_SYMBOL(qcom_scm_set_warm_boot_addr); @@ -119,7 +119,7 @@ bool qcom_scm_hdcp_available(void) if (ret) return ret; - ret = __qcom_scm_is_call_available(QCOM_SCM_SVC_HDCP, + ret = __qcom_scm_is_call_available(__scm->dev, QCOM_SCM_SVC_HDCP, QCOM_SCM_CMD_HDCP); qcom_scm_clk_disable(); @@ -143,7 +143,7 @@ int qcom_scm_hdcp_req(struct qcom_scm_hdcp_req *req, u32 req_cnt, u32 *resp) if (ret) return ret; - ret = __qcom_scm_hdcp_req(req, req_cnt, resp); + ret = __qcom_scm_hdcp_req(__scm->dev, req, req_cnt, resp); qcom_scm_clk_disable(); return ret; } diff --git a/drivers/firmware/qcom_scm.h b/drivers/firmware/qcom_scm.h index 7dcc733..afe6676 100644 --- a/drivers/firmware/qcom_scm.h +++ b/drivers/firmware/qcom_scm.h @@ -19,7 +19,8 @@ #define QCOM_SCM_FLAG_HLOS 0x01 #define QCOM_SCM_FLAG_COLDBOOT_MC 0x02 #define QCOM_SCM_FLAG_WARMBOOT_MC 0x04 -extern int __qcom_scm_set_warm_boot_addr(void *entry, const cpumask_t *cpus); +extern int __qcom_scm_set_warm_boot_addr(struct device *dev, void *entry, + const cpumask_t *cpus); extern int __qcom_scm_set_cold_boot_addr(void *entry, const cpumask_t *cpus); #define QCOM_SCM_CMD_TERMINATE_PC 0x2 @@ -29,12 +30,13 @@ extern void __qcom_scm_cpu_power_down(u32 flags); #define QCOM_SCM_SVC_INFO 0x6 #define QCOM_IS_CALL_AVAIL_CMD 0x1 -extern int __qcom_scm_is_call_available(u32 svc_id, u32 cmd_id); +extern int __qcom_scm_is_call_available(struct device *dev, u32 svc_id, + u32 cmd_id); #define QCOM_SCM_SVC_HDCP 0x11 #define QCOM_SCM_CMD_HDCP 0x01 -extern int __qcom_scm_hdcp_req(struct qcom_scm_hdcp_req *req, u32 req_cnt, - u32 *resp); +extern int __qcom_scm_hdcp_req(struct device *dev, + struct qcom_scm_hdcp_req *req, u32 req_cnt, u32 *resp); /* common error codes */ #define QCOM_SCM_ENOMEM -5 -- cgit v0.10.2 From 6b1751a86ce2eb6ebbffa426a703a12f15bcea28 Mon Sep 17 00:00:00 2001 From: Kumar Gala Date: Fri, 3 Jun 2016 18:25:26 -0500 Subject: firmware: qcom: scm: Add support for ARM64 SoCs Add an implementation of the SCM interface that works on ARM64 SoCs. This is used by things like determine if we have HDCP support or not on the system. Signed-off-by: Kumar Gala Signed-off-by: Andy Gross Reviewed-by: Bjorn Andersson Reviewed-by: Stephen Boyd diff --git a/drivers/firmware/qcom_scm-32.c b/drivers/firmware/qcom_scm-32.c index 83a9351..bbf1780 100644 --- a/drivers/firmware/qcom_scm-32.c +++ b/drivers/firmware/qcom_scm-32.c @@ -454,3 +454,7 @@ int __qcom_scm_hdcp_req(struct device *dev, struct qcom_scm_hdcp_req *req, return qcom_scm_call(dev, QCOM_SCM_SVC_HDCP, QCOM_SCM_CMD_HDCP, req, req_cnt * sizeof(*req), resp, sizeof(*resp)); } + +void __qcom_scm_init(void) +{ +} diff --git a/drivers/firmware/qcom_scm-64.c b/drivers/firmware/qcom_scm-64.c index bb6555f..01949f1 100644 --- a/drivers/firmware/qcom_scm-64.c +++ b/drivers/firmware/qcom_scm-64.c @@ -12,7 +12,143 @@ #include #include +#include +#include +#include +#include #include +#include +#include + +#include "qcom_scm.h" + +#define QCOM_SCM_FNID(s, c) ((((s) & 0xFF) << 8) | ((c) & 0xFF)) + +#define MAX_QCOM_SCM_ARGS 10 +#define MAX_QCOM_SCM_RETS 3 + +#define QCOM_SCM_ARGS_IMPL(num, a, b, c, d, e, f, g, h, i, j, ...) (\ + (((a) & 0x3) << 4) | \ + (((b) & 0x3) << 6) | \ + (((c) & 0x3) << 8) | \ + (((d) & 0x3) << 10) | \ + (((e) & 0x3) << 12) | \ + (((f) & 0x3) << 14) | \ + (((g) & 0x3) << 16) | \ + (((h) & 0x3) << 18) | \ + (((i) & 0x3) << 20) | \ + (((j) & 0x3) << 22) | \ + ((num) & 0xf)) + +#define QCOM_SCM_ARGS(...) QCOM_SCM_ARGS_IMPL(__VA_ARGS__, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0) + +/** + * struct qcom_scm_desc + * @arginfo: Metadata describing the arguments in args[] + * @args: The array of arguments for the secure syscall + * @res: The values returned by the secure syscall + */ +struct qcom_scm_desc { + u32 arginfo; + u64 args[MAX_QCOM_SCM_ARGS]; +}; + +static u64 qcom_smccc_convention = -1; +static DEFINE_MUTEX(qcom_scm_lock); + +#define QCOM_SCM_EBUSY_WAIT_MS 30 +#define QCOM_SCM_EBUSY_MAX_RETRY 20 + +#define N_EXT_QCOM_SCM_ARGS 7 +#define FIRST_EXT_ARG_IDX 3 +#define N_REGISTER_ARGS (MAX_QCOM_SCM_ARGS - N_EXT_QCOM_SCM_ARGS + 1) + +/** + * qcom_scm_call() - Invoke a syscall in the secure world + * @dev: device + * @svc_id: service identifier + * @cmd_id: command identifier + * @desc: Descriptor structure containing arguments and return values + * + * Sends a command to the SCM and waits for the command to finish processing. + * This should *only* be called in pre-emptible context. +*/ +static int qcom_scm_call(struct device *dev, u32 svc_id, u32 cmd_id, + const struct qcom_scm_desc *desc, + struct arm_smccc_res *res) +{ + int arglen = desc->arginfo & 0xf; + int retry_count = 0, i; + u32 fn_id = QCOM_SCM_FNID(svc_id, cmd_id); + u64 cmd, x5 = desc->args[FIRST_EXT_ARG_IDX]; + dma_addr_t args_phys = 0; + void *args_virt = NULL; + size_t alloc_len; + + if (unlikely(arglen > N_REGISTER_ARGS)) { + alloc_len = N_EXT_QCOM_SCM_ARGS * sizeof(u64); + args_virt = kzalloc(PAGE_ALIGN(alloc_len), GFP_KERNEL); + + if (!args_virt) + return -ENOMEM; + + if (qcom_smccc_convention == ARM_SMCCC_SMC_32) { + __le32 *args = args_virt; + + for (i = 0; i < N_EXT_QCOM_SCM_ARGS; i++) + args[i] = cpu_to_le32(desc->args[i + + FIRST_EXT_ARG_IDX]); + } else { + __le64 *args = args_virt; + + for (i = 0; i < N_EXT_QCOM_SCM_ARGS; i++) + args[i] = cpu_to_le64(desc->args[i + + FIRST_EXT_ARG_IDX]); + } + + args_phys = dma_map_single(dev, args_virt, alloc_len, + DMA_TO_DEVICE); + + if (dma_mapping_error(dev, args_phys)) { + kfree(args_virt); + return -ENOMEM; + } + + x5 = args_phys; + } + + do { + mutex_lock(&qcom_scm_lock); + + cmd = ARM_SMCCC_CALL_VAL(ARM_SMCCC_STD_CALL, + qcom_smccc_convention, + ARM_SMCCC_OWNER_SIP, fn_id); + + do { + arm_smccc_smc(cmd, desc->arginfo, desc->args[0], + desc->args[1], desc->args[2], x5, 0, 0, + res); + } while (res->a0 == QCOM_SCM_INTERRUPTED); + + mutex_unlock(&qcom_scm_lock); + + if (res->a0 == QCOM_SCM_V2_EBUSY) { + if (retry_count++ > QCOM_SCM_EBUSY_MAX_RETRY) + break; + msleep(QCOM_SCM_EBUSY_WAIT_MS); + } + } while (res->a0 == QCOM_SCM_V2_EBUSY); + + if (args_virt) { + dma_unmap_single(dev, args_phys, alloc_len, DMA_TO_DEVICE); + kfree(args_virt); + } + + if (res->a0 < 0) + return qcom_scm_remap_error(res->a0); + + return 0; +} /** * qcom_scm_set_cold_boot_addr() - Set the cold boot address for cpus @@ -29,13 +165,15 @@ int __qcom_scm_set_cold_boot_addr(void *entry, const cpumask_t *cpus) /** * qcom_scm_set_warm_boot_addr() - Set the warm boot address for cpus + * @dev: Device pointer * @entry: Entry point function for the cpus * @cpus: The cpumask of cpus that will use the entry point * * Set the Linux entry point for the SCM to transfer control to when coming * out of a power down. CPU power down may be executed on cpuidle or hotplug. */ -int __qcom_scm_set_warm_boot_addr(void *entry, const cpumask_t *cpus) +int __qcom_scm_set_warm_boot_addr(struct device *dev, void *entry, + const cpumask_t *cpus) { return -ENOTSUPP; } @@ -52,12 +190,66 @@ void __qcom_scm_cpu_power_down(u32 flags) { } -int __qcom_scm_is_call_available(u32 svc_id, u32 cmd_id) +int __qcom_scm_is_call_available(struct device *dev, u32 svc_id, u32 cmd_id) { - return -ENOTSUPP; + int ret; + struct qcom_scm_desc desc = {0}; + struct arm_smccc_res res; + + desc.arginfo = QCOM_SCM_ARGS(1); + desc.args[0] = QCOM_SCM_FNID(svc_id, cmd_id) | + (ARM_SMCCC_OWNER_SIP << ARM_SMCCC_OWNER_SHIFT); + + ret = qcom_scm_call(dev, QCOM_SCM_SVC_INFO, QCOM_IS_CALL_AVAIL_CMD, + &desc, &res); + + return ret ? : res.a1; } -int __qcom_scm_hdcp_req(struct qcom_scm_hdcp_req *req, u32 req_cnt, u32 *resp) +int __qcom_scm_hdcp_req(struct device *dev, struct qcom_scm_hdcp_req *req, + u32 req_cnt, u32 *resp) { - return -ENOTSUPP; + int ret; + struct qcom_scm_desc desc = {0}; + struct arm_smccc_res res; + + if (req_cnt > QCOM_SCM_HDCP_MAX_REQ_CNT) + return -ERANGE; + + desc.args[0] = req[0].addr; + desc.args[1] = req[0].val; + desc.args[2] = req[1].addr; + desc.args[3] = req[1].val; + desc.args[4] = req[2].addr; + desc.args[5] = req[2].val; + desc.args[6] = req[3].addr; + desc.args[7] = req[3].val; + desc.args[8] = req[4].addr; + desc.args[9] = req[4].val; + desc.arginfo = QCOM_SCM_ARGS(10); + + ret = qcom_scm_call(dev, QCOM_SCM_SVC_HDCP, QCOM_SCM_CMD_HDCP, &desc, + &res); + *resp = res.a1; + + return ret; +} + +void __qcom_scm_init(void) +{ + u64 cmd; + struct arm_smccc_res res; + u32 function = QCOM_SCM_FNID(QCOM_SCM_SVC_INFO, QCOM_IS_CALL_AVAIL_CMD); + + /* First try a SMC64 call */ + cmd = ARM_SMCCC_CALL_VAL(ARM_SMCCC_FAST_CALL, ARM_SMCCC_SMC_64, + ARM_SMCCC_OWNER_SIP, function); + + arm_smccc_smc(cmd, QCOM_SCM_ARGS(1), cmd & (~BIT(ARM_SMCCC_TYPE_SHIFT)), + 0, 0, 0, 0, 0, &res); + + if (!res.a0 && res.a1) + qcom_smccc_convention = ARM_SMCCC_SMC_64; + else + qcom_smccc_convention = ARM_SMCCC_SMC_32; } diff --git a/drivers/firmware/qcom_scm.c b/drivers/firmware/qcom_scm.c index 937c64a..fca0744 100644 --- a/drivers/firmware/qcom_scm.c +++ b/drivers/firmware/qcom_scm.c @@ -190,6 +190,8 @@ static int qcom_scm_probe(struct platform_device *pdev) __scm = scm; __scm->dev = &pdev->dev; + __qcom_scm_init(); + return 0; } diff --git a/drivers/firmware/qcom_scm.h b/drivers/firmware/qcom_scm.h index afe6676..0ea55d7 100644 --- a/drivers/firmware/qcom_scm.h +++ b/drivers/firmware/qcom_scm.h @@ -38,7 +38,10 @@ extern int __qcom_scm_is_call_available(struct device *dev, u32 svc_id, extern int __qcom_scm_hdcp_req(struct device *dev, struct qcom_scm_hdcp_req *req, u32 req_cnt, u32 *resp); +extern void __qcom_scm_init(void); + /* common error codes */ +#define QCOM_SCM_V2_EBUSY -12 #define QCOM_SCM_ENOMEM -5 #define QCOM_SCM_EOPNOTSUPP -4 #define QCOM_SCM_EINVAL_ADDR -3 @@ -58,6 +61,8 @@ static inline int qcom_scm_remap_error(int err) return -EOPNOTSUPP; case QCOM_SCM_ENOMEM: return -ENOMEM; + case QCOM_SCM_V2_EBUSY: + return -EBUSY; } return -EINVAL; } -- cgit v0.10.2 From 6be2b3d0848d1ed3e78e416cc4ae9007e85c7533 Mon Sep 17 00:00:00 2001 From: Bjorn Andersson Date: Mon, 6 Jun 2016 16:58:20 -0700 Subject: soc: qcom: wcnss_ctrl: Make wcnss_ctrl parent the other components We need the signal from wcnss_ctrl indicating that the firmware is up and running before we can communicate with the other components of the chip. So make these other components children of the wcnss_ctrl device, so they can be probed in order. The process seems to take between 1/2-5 seconds, so this is done in a worker, instead of holding up the probe. Signed-off-by: Bjorn Andersson Signed-off-by: Andy Gross diff --git a/drivers/soc/qcom/wcnss_ctrl.c b/drivers/soc/qcom/wcnss_ctrl.c index c544f3d..520aedd 100644 --- a/drivers/soc/qcom/wcnss_ctrl.c +++ b/drivers/soc/qcom/wcnss_ctrl.c @@ -1,4 +1,5 @@ /* + * Copyright (c) 2016, Linaro Ltd. * Copyright (c) 2015, Sony Mobile Communications Inc. * * This program is free software; you can redistribute it and/or modify @@ -14,8 +15,16 @@ #include #include #include +#include +#include +#include +#include #define WCNSS_REQUEST_TIMEOUT (5 * HZ) +#define WCNSS_CBC_TIMEOUT (10 * HZ) + +#define WCNSS_ACK_DONE_BOOTING 1 +#define WCNSS_ACK_COLD_BOOTING 2 #define NV_FRAGMENT_SIZE 3072 #define NVBIN_FILE "wlan/prima/WCNSS_qcom_wlan_nv.bin" @@ -25,17 +34,19 @@ * @dev: device handle * @channel: SMD channel handle * @ack: completion for outstanding requests + * @cbc: completion for cbc complete indication * @ack_status: status of the outstanding request - * @download_nv_work: worker for uploading nv binary + * @probe_work: worker for uploading nv binary */ struct wcnss_ctrl { struct device *dev; struct qcom_smd_channel *channel; struct completion ack; + struct completion cbc; int ack_status; - struct work_struct download_nv_work; + struct work_struct probe_work; }; /* message types */ @@ -48,6 +59,11 @@ enum { WCNSS_UPLOAD_CAL_RESP, WCNSS_DOWNLOAD_CAL_REQ, WCNSS_DOWNLOAD_CAL_RESP, + WCNSS_VBAT_LEVEL_IND, + WCNSS_BUILD_VERSION_REQ, + WCNSS_BUILD_VERSION_RESP, + WCNSS_PM_CONFIG_REQ, + WCNSS_CBC_COMPLETE_IND, }; /** @@ -128,7 +144,7 @@ static int wcnss_ctrl_smd_callback(struct qcom_smd_channel *channel, version->major, version->minor, version->version, version->revision); - schedule_work(&wcnss->download_nv_work); + complete(&wcnss->ack); break; case WCNSS_DOWNLOAD_NV_RESP: if (count != sizeof(*nvresp)) { @@ -141,6 +157,10 @@ static int wcnss_ctrl_smd_callback(struct qcom_smd_channel *channel, wcnss->ack_status = nvresp->status; complete(&wcnss->ack); break; + case WCNSS_CBC_COMPLETE_IND: + dev_dbg(wcnss->dev, "cold boot complete\n"); + complete(&wcnss->cbc); + break; default: dev_info(wcnss->dev, "unknown message type %d\n", hdr->type); break; @@ -156,20 +176,32 @@ static int wcnss_ctrl_smd_callback(struct qcom_smd_channel *channel, static int wcnss_request_version(struct wcnss_ctrl *wcnss) { struct wcnss_msg_hdr msg; + int ret; msg.type = WCNSS_VERSION_REQ; msg.len = sizeof(msg); + ret = qcom_smd_send(wcnss->channel, &msg, sizeof(msg)); + if (ret < 0) + return ret; + + ret = wait_for_completion_timeout(&wcnss->ack, WCNSS_CBC_TIMEOUT); + if (!ret) { + dev_err(wcnss->dev, "timeout waiting for version response\n"); + return -ETIMEDOUT; + } - return qcom_smd_send(wcnss->channel, &msg, sizeof(msg)); + return 0; } /** * wcnss_download_nv() - send nv binary to WCNSS - * @work: work struct to acquire wcnss context + * @wcnss: wcnss_ctrl state handle + * @expect_cbc: indicator to caller that an cbc event is expected + * + * Returns 0 on success. Negative errno on failure. */ -static void wcnss_download_nv(struct work_struct *work) +static int wcnss_download_nv(struct wcnss_ctrl *wcnss, bool *expect_cbc) { - struct wcnss_ctrl *wcnss = container_of(work, struct wcnss_ctrl, download_nv_work); struct wcnss_download_nv_req *req; const struct firmware *fw; const void *data; @@ -178,10 +210,10 @@ static void wcnss_download_nv(struct work_struct *work) req = kzalloc(sizeof(*req) + NV_FRAGMENT_SIZE, GFP_KERNEL); if (!req) - return; + return -ENOMEM; ret = request_firmware(&fw, NVBIN_FILE, wcnss->dev); - if (ret) { + if (ret < 0) { dev_err(wcnss->dev, "Failed to load nv file %s: %d\n", NVBIN_FILE, ret); goto free_req; @@ -207,7 +239,7 @@ static void wcnss_download_nv(struct work_struct *work) memcpy(req->fragment, data, req->frag_size); ret = qcom_smd_send(wcnss->channel, req, req->hdr.len); - if (ret) { + if (ret < 0) { dev_err(wcnss->dev, "failed to send smd packet\n"); goto release_fw; } @@ -220,16 +252,58 @@ static void wcnss_download_nv(struct work_struct *work) } while (left > 0); ret = wait_for_completion_timeout(&wcnss->ack, WCNSS_REQUEST_TIMEOUT); - if (!ret) + if (!ret) { dev_err(wcnss->dev, "timeout waiting for nv upload ack\n"); - else if (wcnss->ack_status != 1) - dev_err(wcnss->dev, "nv upload response failed err: %d\n", - wcnss->ack_status); + ret = -ETIMEDOUT; + } else { + *expect_cbc = wcnss->ack_status == WCNSS_ACK_COLD_BOOTING; + ret = 0; + } release_fw: release_firmware(fw); free_req: kfree(req); + + return ret; +} + +/** + * qcom_wcnss_open_channel() - open additional SMD channel to WCNSS + * @wcnss: wcnss handle, retrieved from drvdata + * @name: SMD channel name + * @cb: callback to handle incoming data on the channel + */ +struct qcom_smd_channel *qcom_wcnss_open_channel(void *wcnss, const char *name, qcom_smd_cb_t cb) +{ + struct wcnss_ctrl *_wcnss = wcnss; + + return qcom_smd_open_channel(_wcnss->channel, name, cb); +} +EXPORT_SYMBOL(qcom_wcnss_open_channel); + +static void wcnss_async_probe(struct work_struct *work) +{ + struct wcnss_ctrl *wcnss = container_of(work, struct wcnss_ctrl, probe_work); + bool expect_cbc; + int ret; + + ret = wcnss_request_version(wcnss); + if (ret < 0) + return; + + ret = wcnss_download_nv(wcnss, &expect_cbc); + if (ret < 0) + return; + + /* Wait for pending cold boot completion if indicated by the nv downloader */ + if (expect_cbc) { + ret = wait_for_completion_timeout(&wcnss->cbc, WCNSS_REQUEST_TIMEOUT); + if (!ret) + dev_err(wcnss->dev, "expected cold boot completion\n"); + } + + of_platform_populate(wcnss->dev->of_node, NULL, NULL, wcnss->dev); } static int wcnss_ctrl_probe(struct qcom_smd_device *sdev) @@ -244,25 +318,38 @@ static int wcnss_ctrl_probe(struct qcom_smd_device *sdev) wcnss->channel = sdev->channel; init_completion(&wcnss->ack); - INIT_WORK(&wcnss->download_nv_work, wcnss_download_nv); + init_completion(&wcnss->cbc); + INIT_WORK(&wcnss->probe_work, wcnss_async_probe); qcom_smd_set_drvdata(sdev->channel, wcnss); + dev_set_drvdata(&sdev->dev, wcnss); + + schedule_work(&wcnss->probe_work); + + return 0; +} + +static void wcnss_ctrl_remove(struct qcom_smd_device *sdev) +{ + struct wcnss_ctrl *wcnss = qcom_smd_get_drvdata(sdev->channel); - return wcnss_request_version(wcnss); + cancel_work_sync(&wcnss->probe_work); + of_platform_depopulate(&sdev->dev); } -static const struct qcom_smd_id wcnss_ctrl_smd_match[] = { - { .name = "WCNSS_CTRL" }, +static const struct of_device_id wcnss_ctrl_of_match[] = { + { .compatible = "qcom,wcnss", }, {} }; static struct qcom_smd_driver wcnss_ctrl_driver = { .probe = wcnss_ctrl_probe, + .remove = wcnss_ctrl_remove, .callback = wcnss_ctrl_smd_callback, - .smd_match_table = wcnss_ctrl_smd_match, .driver = { .name = "qcom_wcnss_ctrl", .owner = THIS_MODULE, + .of_match_table = wcnss_ctrl_of_match, }, }; diff --git a/include/linux/soc/qcom/wcnss_ctrl.h b/include/linux/soc/qcom/wcnss_ctrl.h new file mode 100644 index 0000000..a37bc55 --- /dev/null +++ b/include/linux/soc/qcom/wcnss_ctrl.h @@ -0,0 +1,8 @@ +#ifndef __WCNSS_CTRL_H__ +#define __WCNSS_CTRL_H__ + +#include + +struct qcom_smd_channel *qcom_wcnss_open_channel(void *wcnss, const char *name, qcom_smd_cb_t cb); + +#endif -- cgit v0.10.2 From 63af8e44eaa56ffe613628742a2642c4c2f1a029 Mon Sep 17 00:00:00 2001 From: Bjorn Andersson Date: Thu, 9 Jun 2016 17:22:56 -0700 Subject: soc: qcom: smp2p: Correct addressing of outgoing value The valid_entries index should not be incremented until after we have acquired the pointer to the value, or we will read and write data one item off. Fixes: 50e99641413e ("soc: qcom: smp2p: Qualcomm Shared Memory Point to Point") Cc: stable@vger.kernel.org Signed-off-by: Bjorn Andersson Signed-off-by: Andy Gross diff --git a/drivers/soc/qcom/smp2p.c b/drivers/soc/qcom/smp2p.c index f1eed7f..9c2788b 100644 --- a/drivers/soc/qcom/smp2p.c +++ b/drivers/soc/qcom/smp2p.c @@ -344,11 +344,12 @@ static int qcom_smp2p_outbound_entry(struct qcom_smp2p *smp2p, /* Allocate an entry from the smem item */ strlcpy(buf, entry->name, SMP2P_MAX_ENTRY_NAME); memcpy_toio(out->entries[out->valid_entries].name, buf, SMP2P_MAX_ENTRY_NAME); - out->valid_entries++; /* Make the logical entry reference the physical value */ entry->value = &out->entries[out->valid_entries].value; + out->valid_entries++; + entry->state = qcom_smem_state_register(node, &smp2p_state_ops, entry); if (IS_ERR(entry->state)) { dev_err(smp2p->dev, "failed to register qcom_smem_state\n"); -- cgit v0.10.2 From e7306dd751d9ec58143feb09dd58b92efac72531 Mon Sep 17 00:00:00 2001 From: Bjorn Andersson Date: Thu, 9 Jun 2016 17:22:57 -0700 Subject: soc: qcom: smp2p: Drop io-accessors SMEM is now mapped write-combine and we can use memcpy to access the name of the entires. Signed-off-by: Bjorn Andersson Signed-off-by: Andy Gross diff --git a/drivers/soc/qcom/smp2p.c b/drivers/soc/qcom/smp2p.c index 9c2788b..f51fb2e 100644 --- a/drivers/soc/qcom/smp2p.c +++ b/drivers/soc/qcom/smp2p.c @@ -196,7 +196,7 @@ static irqreturn_t qcom_smp2p_intr(int irq, void *data) /* Match newly created entries */ for (i = smp2p->valid_entries; i < in->valid_entries; i++) { list_for_each_entry(entry, &smp2p->inbound, node) { - memcpy_fromio(buf, in->entries[i].name, sizeof(buf)); + memcpy(buf, in->entries[i].name, sizeof(buf)); if (!strcmp(buf, entry->name)) { entry->value = &in->entries[i].value; break; @@ -343,7 +343,7 @@ static int qcom_smp2p_outbound_entry(struct qcom_smp2p *smp2p, /* Allocate an entry from the smem item */ strlcpy(buf, entry->name, SMP2P_MAX_ENTRY_NAME); - memcpy_toio(out->entries[out->valid_entries].name, buf, SMP2P_MAX_ENTRY_NAME); + memcpy(out->entries[out->valid_entries].name, buf, SMP2P_MAX_ENTRY_NAME); /* Make the logical entry reference the physical value */ entry->value = &out->entries[out->valid_entries].value; -- cgit v0.10.2 From 3680a4a97435496ad22cd0fc9f2ba51751cc4e36 Mon Sep 17 00:00:00 2001 From: Bjorn Andersson Date: Thu, 9 Jun 2016 21:11:22 -0700 Subject: soc: qcom: Update properties for smem state referencing Update the property names to match device tree bindings, the correct values should be qcom,smem-states and qcom,smem-state-names. Also update the #qcom,smem-state-cells for consistency, before we merge any users of these properties. Signed-off-by: Bjorn Andersson Acked-by: Rob Herring Signed-off-by: Andy Gross diff --git a/Documentation/devicetree/bindings/soc/qcom/qcom,smp2p.txt b/Documentation/devicetree/bindings/soc/qcom/qcom,smp2p.txt index 5cc82b8..af9ca37 100644 --- a/Documentation/devicetree/bindings/soc/qcom/qcom,smp2p.txt +++ b/Documentation/devicetree/bindings/soc/qcom/qcom,smp2p.txt @@ -68,7 +68,7 @@ important. Value type: Definition: must be 2 - denoting the bit in the entry and IRQ flags -- #qcom,state-cells: +- #qcom,smem-state-cells: Usage: required for outgoing entries Value type: Definition: must be 1 - denoting the bit in the entry @@ -92,7 +92,7 @@ wcnss-smp2p { wcnss_smp2p_out: master-kernel { qcom,entry-name = "master-kernel"; - #qcom,state-cells = <1>; + #qcom,smem-state-cells = <1>; }; wcnss_smp2p_in: slave-kernel { diff --git a/Documentation/devicetree/bindings/soc/qcom/qcom,smsm.txt b/Documentation/devicetree/bindings/soc/qcom/qcom,smsm.txt index a6634c7..2993b5a 100644 --- a/Documentation/devicetree/bindings/soc/qcom/qcom,smsm.txt +++ b/Documentation/devicetree/bindings/soc/qcom/qcom,smsm.txt @@ -51,7 +51,7 @@ important. Definition: specifies the offset, in words, of the first bit for this entry -- #qcom,state-cells: +- #qcom,smem-state-cells: Usage: required for local entry Value type: Definition: must be 1 - denotes bit number @@ -91,7 +91,7 @@ smsm { apps_smsm: apps@0 { reg = <0>; - #qcom,state-cells = <1>; + #qcom,smem-state-cells = <1>; }; wcnss_smsm: wcnss@7 { diff --git a/drivers/soc/qcom/smem_state.c b/drivers/soc/qcom/smem_state.c index 54261de..d5437ca 100644 --- a/drivers/soc/qcom/smem_state.c +++ b/drivers/soc/qcom/smem_state.c @@ -104,26 +104,26 @@ struct qcom_smem_state *qcom_smem_state_get(struct device *dev, if (con_id) { index = of_property_match_string(dev->of_node, - "qcom,state-names", + "qcom,smem-state-names", con_id); if (index < 0) { - dev_err(dev, "missing qcom,state-names\n"); + dev_err(dev, "missing qcom,smem-state-names\n"); return ERR_PTR(index); } } ret = of_parse_phandle_with_args(dev->of_node, - "qcom,state", - "#qcom,state-cells", + "qcom,smem-states", + "#qcom,smem-state-cells", index, &args); if (ret) { - dev_err(dev, "failed to parse qcom,state property\n"); + dev_err(dev, "failed to parse qcom,smem-states property\n"); return ERR_PTR(ret); } if (args.args_count != 1) { - dev_err(dev, "invalid #qcom,state-cells\n"); + dev_err(dev, "invalid #qcom,smem-state-cells\n"); return ERR_PTR(-EINVAL); } diff --git a/drivers/soc/qcom/smsm.c b/drivers/soc/qcom/smsm.c index 6b777af..d0337b2 100644 --- a/drivers/soc/qcom/smsm.c +++ b/drivers/soc/qcom/smsm.c @@ -495,7 +495,7 @@ static int qcom_smsm_probe(struct platform_device *pdev) if (!smsm->hosts) return -ENOMEM; - local_node = of_find_node_with_property(pdev->dev.of_node, "#qcom,state-cells"); + local_node = of_find_node_with_property(pdev->dev.of_node, "#qcom,smem-state-cells"); if (!local_node) { dev_err(&pdev->dev, "no state entry\n"); return -EINVAL; -- cgit v0.10.2 From f01e90fe34f563a5e189d4070de4a23948105642 Mon Sep 17 00:00:00 2001 From: Bjorn Andersson Date: Wed, 23 Sep 2015 12:56:12 -0700 Subject: firmware: qcom: scm: Peripheral Authentication Service This adds the Peripheral Authentication Service (PAS) interface to the Qualcomm SCM interface. The API is used to authenticate and boot a range of external processors in various Qualcomm platforms. Signed-off-by: Bjorn Andersson Signed-off-by: Andy Gross diff --git a/drivers/firmware/qcom_scm-32.c b/drivers/firmware/qcom_scm-32.c index bbf1780..7cb6a5d 100644 --- a/drivers/firmware/qcom_scm-32.c +++ b/drivers/firmware/qcom_scm-32.c @@ -458,3 +458,92 @@ int __qcom_scm_hdcp_req(struct device *dev, struct qcom_scm_hdcp_req *req, void __qcom_scm_init(void) { } + +bool __qcom_scm_pas_supported(struct device *dev, u32 peripheral) +{ + __le32 out; + __le32 in; + int ret; + + in = cpu_to_le32(peripheral); + ret = qcom_scm_call(dev, QCOM_SCM_SVC_PIL, + QCOM_SCM_PAS_IS_SUPPORTED_CMD, + &in, sizeof(in), + &out, sizeof(out)); + + return ret ? false : !!out; +} + +int __qcom_scm_pas_init_image(struct device *dev, u32 peripheral, + dma_addr_t metadata_phys) +{ + __le32 scm_ret; + int ret; + struct { + __le32 proc; + __le32 image_addr; + } request; + + request.proc = cpu_to_le32(peripheral); + request.image_addr = cpu_to_le32(metadata_phys); + + ret = qcom_scm_call(dev, QCOM_SCM_SVC_PIL, + QCOM_SCM_PAS_INIT_IMAGE_CMD, + &request, sizeof(request), + &scm_ret, sizeof(scm_ret)); + + return ret ? : le32_to_cpu(scm_ret); +} + +int __qcom_scm_pas_mem_setup(struct device *dev, u32 peripheral, + phys_addr_t addr, phys_addr_t size) +{ + __le32 scm_ret; + int ret; + struct { + __le32 proc; + __le32 addr; + __le32 len; + } request; + + request.proc = cpu_to_le32(peripheral); + request.addr = cpu_to_le32(addr); + request.len = cpu_to_le32(size); + + ret = qcom_scm_call(dev, QCOM_SCM_SVC_PIL, + QCOM_SCM_PAS_MEM_SETUP_CMD, + &request, sizeof(request), + &scm_ret, sizeof(scm_ret)); + + return ret ? : le32_to_cpu(scm_ret); +} + +int __qcom_scm_pas_auth_and_reset(struct device *dev, u32 peripheral) +{ + __le32 out; + __le32 in; + int ret; + + in = cpu_to_le32(peripheral); + ret = qcom_scm_call(dev, QCOM_SCM_SVC_PIL, + QCOM_SCM_PAS_AUTH_AND_RESET_CMD, + &in, sizeof(in), + &out, sizeof(out)); + + return ret ? : le32_to_cpu(out); +} + +int __qcom_scm_pas_shutdown(struct device *dev, u32 peripheral) +{ + __le32 out; + __le32 in; + int ret; + + in = cpu_to_le32(peripheral); + ret = qcom_scm_call(dev, QCOM_SCM_SVC_PIL, + QCOM_SCM_PAS_SHUTDOWN_CMD, + &in, sizeof(in), + &out, sizeof(out)); + + return ret ? : le32_to_cpu(out); +} diff --git a/drivers/firmware/qcom_scm-64.c b/drivers/firmware/qcom_scm-64.c index 01949f1..3ac2490 100644 --- a/drivers/firmware/qcom_scm-64.c +++ b/drivers/firmware/qcom_scm-64.c @@ -27,6 +27,13 @@ #define MAX_QCOM_SCM_ARGS 10 #define MAX_QCOM_SCM_RETS 3 +enum qcom_scm_arg_types { + QCOM_SCM_VAL, + QCOM_SCM_RO, + QCOM_SCM_RW, + QCOM_SCM_BUFVAL, +}; + #define QCOM_SCM_ARGS_IMPL(num, a, b, c, d, e, f, g, h, i, j, ...) (\ (((a) & 0x3) << 4) | \ (((b) & 0x3) << 6) | \ @@ -253,3 +260,85 @@ void __qcom_scm_init(void) else qcom_smccc_convention = ARM_SMCCC_SMC_32; } + +bool __qcom_scm_pas_supported(struct device *dev, u32 peripheral) +{ + int ret; + struct qcom_scm_desc desc = {0}; + struct arm_smccc_res res; + + desc.args[0] = peripheral; + desc.arginfo = QCOM_SCM_ARGS(1); + + ret = qcom_scm_call(dev, QCOM_SCM_SVC_PIL, + QCOM_SCM_PAS_IS_SUPPORTED_CMD, + &desc, &res); + + return ret ? false : !!res.a1; +} + +int __qcom_scm_pas_init_image(struct device *dev, u32 peripheral, + dma_addr_t metadata_phys) +{ + int ret; + struct qcom_scm_desc desc = {0}; + struct arm_smccc_res res; + + desc.args[0] = peripheral; + desc.args[1] = metadata_phys; + desc.arginfo = QCOM_SCM_ARGS(2, QCOM_SCM_VAL, QCOM_SCM_RW); + + ret = qcom_scm_call(dev, QCOM_SCM_SVC_PIL, QCOM_SCM_PAS_INIT_IMAGE_CMD, + &desc, &res); + + return ret ? : res.a1; +} + +int __qcom_scm_pas_mem_setup(struct device *dev, u32 peripheral, + phys_addr_t addr, phys_addr_t size) +{ + int ret; + struct qcom_scm_desc desc = {0}; + struct arm_smccc_res res; + + desc.args[0] = peripheral; + desc.args[1] = addr; + desc.args[2] = size; + desc.arginfo = QCOM_SCM_ARGS(3); + + ret = qcom_scm_call(dev, QCOM_SCM_SVC_PIL, QCOM_SCM_PAS_MEM_SETUP_CMD, + &desc, &res); + + return ret ? : res.a1; +} + +int __qcom_scm_pas_auth_and_reset(struct device *dev, u32 peripheral) +{ + int ret; + struct qcom_scm_desc desc = {0}; + struct arm_smccc_res res; + + desc.args[0] = peripheral; + desc.arginfo = QCOM_SCM_ARGS(1); + + ret = qcom_scm_call(dev, QCOM_SCM_SVC_PIL, + QCOM_SCM_PAS_AUTH_AND_RESET_CMD, + &desc, &res); + + return ret ? : res.a1; +} + +int __qcom_scm_pas_shutdown(struct device *dev, u32 peripheral) +{ + int ret; + struct qcom_scm_desc desc = {0}; + struct arm_smccc_res res; + + desc.args[0] = peripheral; + desc.arginfo = QCOM_SCM_ARGS(1); + + ret = qcom_scm_call(dev, QCOM_SCM_SVC_PIL, QCOM_SCM_PAS_SHUTDOWN_CMD, + &desc, &res); + + return ret ? : res.a1; +} diff --git a/drivers/firmware/qcom_scm.c b/drivers/firmware/qcom_scm.c index fca0744..ec46c68 100644 --- a/drivers/firmware/qcom_scm.c +++ b/drivers/firmware/qcom_scm.c @@ -15,6 +15,7 @@ #include #include #include +#include #include #include #include @@ -149,6 +150,139 @@ int qcom_scm_hdcp_req(struct qcom_scm_hdcp_req *req, u32 req_cnt, u32 *resp) } EXPORT_SYMBOL(qcom_scm_hdcp_req); +/** + * qcom_scm_pas_supported() - Check if the peripheral authentication service is + * available for the given peripherial + * @peripheral: peripheral id + * + * Returns true if PAS is supported for this peripheral, otherwise false. + */ +bool qcom_scm_pas_supported(u32 peripheral) +{ + int ret; + + ret = __qcom_scm_is_call_available(__scm->dev, QCOM_SCM_SVC_PIL, + QCOM_SCM_PAS_IS_SUPPORTED_CMD); + if (ret <= 0) + return false; + + return __qcom_scm_pas_supported(__scm->dev, peripheral); +} +EXPORT_SYMBOL(qcom_scm_pas_supported); + +/** + * qcom_scm_pas_init_image() - Initialize peripheral authentication service + * state machine for a given peripheral, using the + * metadata + * @peripheral: peripheral id + * @metadata: pointer to memory containing ELF header, program header table + * and optional blob of data used for authenticating the metadata + * and the rest of the firmware + * @size: size of the metadata + * + * Returns 0 on success. + */ +int qcom_scm_pas_init_image(u32 peripheral, const void *metadata, size_t size) +{ + dma_addr_t mdata_phys; + void *mdata_buf; + int ret; + + /* + * During the scm call memory protection will be enabled for the meta + * data blob, so make sure it's physically contiguous, 4K aligned and + * non-cachable to avoid XPU violations. + */ + mdata_buf = dma_alloc_coherent(__scm->dev, size, &mdata_phys, + GFP_KERNEL); + if (!mdata_buf) { + dev_err(__scm->dev, "Allocation of metadata buffer failed.\n"); + return -ENOMEM; + } + memcpy(mdata_buf, metadata, size); + + ret = qcom_scm_clk_enable(); + if (ret) + goto free_metadata; + + ret = __qcom_scm_pas_init_image(__scm->dev, peripheral, mdata_phys); + + qcom_scm_clk_disable(); + +free_metadata: + dma_free_coherent(__scm->dev, size, mdata_buf, mdata_phys); + + return ret; +} +EXPORT_SYMBOL(qcom_scm_pas_init_image); + +/** + * qcom_scm_pas_mem_setup() - Prepare the memory related to a given peripheral + * for firmware loading + * @peripheral: peripheral id + * @addr: start address of memory area to prepare + * @size: size of the memory area to prepare + * + * Returns 0 on success. + */ +int qcom_scm_pas_mem_setup(u32 peripheral, phys_addr_t addr, phys_addr_t size) +{ + int ret; + + ret = qcom_scm_clk_enable(); + if (ret) + return ret; + + ret = __qcom_scm_pas_mem_setup(__scm->dev, peripheral, addr, size); + qcom_scm_clk_disable(); + + return ret; +} +EXPORT_SYMBOL(qcom_scm_pas_mem_setup); + +/** + * qcom_scm_pas_auth_and_reset() - Authenticate the given peripheral firmware + * and reset the remote processor + * @peripheral: peripheral id + * + * Return 0 on success. + */ +int qcom_scm_pas_auth_and_reset(u32 peripheral) +{ + int ret; + + ret = qcom_scm_clk_enable(); + if (ret) + return ret; + + ret = __qcom_scm_pas_auth_and_reset(__scm->dev, peripheral); + qcom_scm_clk_disable(); + + return ret; +} +EXPORT_SYMBOL(qcom_scm_pas_auth_and_reset); + +/** + * qcom_scm_pas_shutdown() - Shut down the remote processor + * @peripheral: peripheral id + * + * Returns 0 on success. + */ +int qcom_scm_pas_shutdown(u32 peripheral) +{ + int ret; + + ret = qcom_scm_clk_enable(); + if (ret) + return ret; + + ret = __qcom_scm_pas_shutdown(__scm->dev, peripheral); + qcom_scm_clk_disable(); + + return ret; +} +EXPORT_SYMBOL(qcom_scm_pas_shutdown); + static int qcom_scm_probe(struct platform_device *pdev) { struct qcom_scm *scm; diff --git a/drivers/firmware/qcom_scm.h b/drivers/firmware/qcom_scm.h index 0ea55d7..1a16ff9 100644 --- a/drivers/firmware/qcom_scm.h +++ b/drivers/firmware/qcom_scm.h @@ -40,6 +40,20 @@ extern int __qcom_scm_hdcp_req(struct device *dev, extern void __qcom_scm_init(void); +#define QCOM_SCM_SVC_PIL 0x2 +#define QCOM_SCM_PAS_INIT_IMAGE_CMD 0x1 +#define QCOM_SCM_PAS_MEM_SETUP_CMD 0x2 +#define QCOM_SCM_PAS_AUTH_AND_RESET_CMD 0x5 +#define QCOM_SCM_PAS_SHUTDOWN_CMD 0x6 +#define QCOM_SCM_PAS_IS_SUPPORTED_CMD 0x7 +extern bool __qcom_scm_pas_supported(struct device *dev, u32 peripheral); +extern int __qcom_scm_pas_init_image(struct device *dev, u32 peripheral, + dma_addr_t metadata_phys); +extern int __qcom_scm_pas_mem_setup(struct device *dev, u32 peripheral, + phys_addr_t addr, phys_addr_t size); +extern int __qcom_scm_pas_auth_and_reset(struct device *dev, u32 peripheral); +extern int __qcom_scm_pas_shutdown(struct device *dev, u32 peripheral); + /* common error codes */ #define QCOM_SCM_V2_EBUSY -12 #define QCOM_SCM_ENOMEM -5 diff --git a/include/linux/qcom_scm.h b/include/linux/qcom_scm.h index 9e12000..cc32ab8 100644 --- a/include/linux/qcom_scm.h +++ b/include/linux/qcom_scm.h @@ -29,6 +29,14 @@ extern bool qcom_scm_hdcp_available(void); extern int qcom_scm_hdcp_req(struct qcom_scm_hdcp_req *req, u32 req_cnt, u32 *resp); +extern bool qcom_scm_pas_supported(u32 peripheral); +extern int qcom_scm_pas_init_image(u32 peripheral, const void *metadata, + size_t size); +extern int qcom_scm_pas_mem_setup(u32 peripheral, phys_addr_t addr, + phys_addr_t size); +extern int qcom_scm_pas_auth_and_reset(u32 peripheral); +extern int qcom_scm_pas_shutdown(u32 peripheral); + #define QCOM_SCM_CPU_PWR_DOWN_L2_ON 0x0 #define QCOM_SCM_CPU_PWR_DOWN_L2_OFF 0x1 -- cgit v0.10.2 From dd4fe5b292226f2459305965c960d8dc39f36e0f Mon Sep 17 00:00:00 2001 From: Bjorn Andersson Date: Fri, 17 Jun 2016 10:40:43 -0700 Subject: firmware: qcom: scm: Expose PAS command 10 as reset-controller PAS command 10 is used to assert and deassert the MSS reset via TrustZone, expose this as a reset-controller to mimic the direct access case. Cc: Stephen Boyd Acked-by: Rob Herring Signed-off-by: Bjorn Andersson Acked-by: Srinivas Kandagatla Reviewed-by: Stephen Boyd Signed-off-by: Andy Gross diff --git a/drivers/firmware/Kconfig b/drivers/firmware/Kconfig index 6664f11..5e61805 100644 --- a/drivers/firmware/Kconfig +++ b/drivers/firmware/Kconfig @@ -184,6 +184,7 @@ config FW_CFG_SYSFS_CMDLINE config QCOM_SCM bool depends on ARM || ARM64 + select RESET_CONTROLLER config QCOM_SCM_32 def_bool y diff --git a/drivers/firmware/qcom_scm-32.c b/drivers/firmware/qcom_scm-32.c index 7cb6a5d..c6aeedb 100644 --- a/drivers/firmware/qcom_scm-32.c +++ b/drivers/firmware/qcom_scm-32.c @@ -547,3 +547,16 @@ int __qcom_scm_pas_shutdown(struct device *dev, u32 peripheral) return ret ? : le32_to_cpu(out); } + +int __qcom_scm_pas_mss_reset(struct device *dev, bool reset) +{ + __le32 out; + __le32 in = cpu_to_le32(reset); + int ret; + + ret = qcom_scm_call(dev, QCOM_SCM_SVC_PIL, QCOM_SCM_PAS_MSS_RESET, + &in, sizeof(in), + &out, sizeof(out)); + + return ret ? : le32_to_cpu(out); +} diff --git a/drivers/firmware/qcom_scm-64.c b/drivers/firmware/qcom_scm-64.c index 3ac2490..4a0f5ea 100644 --- a/drivers/firmware/qcom_scm-64.c +++ b/drivers/firmware/qcom_scm-64.c @@ -342,3 +342,19 @@ int __qcom_scm_pas_shutdown(struct device *dev, u32 peripheral) return ret ? : res.a1; } + +int __qcom_scm_pas_mss_reset(struct device *dev, bool reset) +{ + struct qcom_scm_desc desc = {0}; + struct arm_smccc_res res; + int ret; + + desc.args[0] = reset; + desc.args[1] = 0; + desc.arginfo = QCOM_SCM_ARGS(2); + + ret = qcom_scm_call(dev, QCOM_SCM_SVC_PIL, QCOM_SCM_PAS_MSS_RESET, &desc, + &res); + + return ret ? : res.a1; +} diff --git a/drivers/firmware/qcom_scm.c b/drivers/firmware/qcom_scm.c index ec46c68..84330c5 100644 --- a/drivers/firmware/qcom_scm.c +++ b/drivers/firmware/qcom_scm.c @@ -21,6 +21,7 @@ #include #include #include +#include #include "qcom_scm.h" @@ -29,6 +30,7 @@ struct qcom_scm { struct clk *core_clk; struct clk *iface_clk; struct clk *bus_clk; + struct reset_controller_dev reset; }; static struct qcom_scm *__scm; @@ -283,6 +285,30 @@ int qcom_scm_pas_shutdown(u32 peripheral) } EXPORT_SYMBOL(qcom_scm_pas_shutdown); +static int qcom_scm_pas_reset_assert(struct reset_controller_dev *rcdev, + unsigned long idx) +{ + if (idx != 0) + return -EINVAL; + + return __qcom_scm_pas_mss_reset(__scm->dev, 1); +} + +static int qcom_scm_pas_reset_deassert(struct reset_controller_dev *rcdev, + unsigned long idx) +{ + if (idx != 0) + return -EINVAL; + + return __qcom_scm_pas_mss_reset(__scm->dev, 0); +} + +static const struct reset_control_ops qcom_scm_pas_reset_ops = { + .assert = qcom_scm_pas_reset_assert, + .deassert = qcom_scm_pas_reset_deassert, +}; + + static int qcom_scm_probe(struct platform_device *pdev) { struct qcom_scm *scm; @@ -316,6 +342,11 @@ static int qcom_scm_probe(struct platform_device *pdev) } } + scm->reset.ops = &qcom_scm_pas_reset_ops; + scm->reset.nr_resets = 1; + scm->reset.of_node = pdev->dev.of_node; + reset_controller_register(&scm->reset); + /* vote for max clk rate for highest performance */ ret = clk_set_rate(scm->core_clk, INT_MAX); if (ret) diff --git a/drivers/firmware/qcom_scm.h b/drivers/firmware/qcom_scm.h index 1a16ff9..3584b00 100644 --- a/drivers/firmware/qcom_scm.h +++ b/drivers/firmware/qcom_scm.h @@ -46,6 +46,7 @@ extern void __qcom_scm_init(void); #define QCOM_SCM_PAS_AUTH_AND_RESET_CMD 0x5 #define QCOM_SCM_PAS_SHUTDOWN_CMD 0x6 #define QCOM_SCM_PAS_IS_SUPPORTED_CMD 0x7 +#define QCOM_SCM_PAS_MSS_RESET 0xa extern bool __qcom_scm_pas_supported(struct device *dev, u32 peripheral); extern int __qcom_scm_pas_init_image(struct device *dev, u32 peripheral, dma_addr_t metadata_phys); @@ -53,6 +54,7 @@ extern int __qcom_scm_pas_mem_setup(struct device *dev, u32 peripheral, phys_addr_t addr, phys_addr_t size); extern int __qcom_scm_pas_auth_and_reset(struct device *dev, u32 peripheral); extern int __qcom_scm_pas_shutdown(struct device *dev, u32 peripheral); +extern int __qcom_scm_pas_mss_reset(struct device *dev, bool reset); /* common error codes */ #define QCOM_SCM_V2_EBUSY -12 -- cgit v0.10.2 From 50f44e894ad06eb8b401308c3cfe5bceaaa26df2 Mon Sep 17 00:00:00 2001 From: Xinliang Liu Date: Mon, 20 Jun 2016 11:50:04 +0800 Subject: reset: hisilicon: Add media reset controller binding Add compatible for media reset controller. Actually, there are two reset controllers in hi6220 SoC: The peripheral reset controller bits are part of sysctrl registers. The media reset controller bits are part of mediactrl registers. So for the compatible part, it should contain "syscon" for both peripheral and media reset controller. Signed-off-by: Xinliang Liu Signed-off-by: Philipp Zabel diff --git a/Documentation/devicetree/bindings/reset/hisilicon,hi6220-reset.txt b/Documentation/devicetree/bindings/reset/hisilicon,hi6220-reset.txt index e0b185a..c25da39 100644 --- a/Documentation/devicetree/bindings/reset/hisilicon,hi6220-reset.txt +++ b/Documentation/devicetree/bindings/reset/hisilicon,hi6220-reset.txt @@ -8,7 +8,9 @@ The reset controller registers are part of the system-ctl block on hi6220 SoC. Required properties: -- compatible: may be "hisilicon,hi6220-sysctrl" +- compatible: should be one of the following: + - "hisilicon,hi6220-sysctrl", "syscon" : For peripheral reset controller. + - "hisilicon,hi6220-mediactrl", "syscon" : For media reset controller. - reg: should be register base and length as documented in the datasheet - #reset-cells: 1, see below -- cgit v0.10.2 From 339d00cb173a5eb283d3b8d617fa97a3a46ada2f Mon Sep 17 00:00:00 2001 From: Xinliang Liu Date: Mon, 20 Jun 2016 11:50:05 +0800 Subject: arm64: dts: hi6220: Add media subsystem reset dts Add media subsystem reset dts support. Signed-off-by: Chen Feng Signed-off-by: Xinliang Liu Signed-off-by: Philipp Zabel diff --git a/arch/arm64/boot/dts/hisilicon/hi6220.dtsi b/arch/arm64/boot/dts/hisilicon/hi6220.dtsi index 189d215..c19b827 100644 --- a/arch/arm64/boot/dts/hisilicon/hi6220.dtsi +++ b/arch/arm64/boot/dts/hisilicon/hi6220.dtsi @@ -5,6 +5,7 @@ */ #include +#include #include #include #include @@ -252,6 +253,7 @@ compatible = "hisilicon,hi6220-mediactrl", "syscon"; reg = <0x0 0xf4410000 0x0 0x1000>; #clock-cells = <1>; + #reset-cells = <1>; }; pm_ctrl: pm_ctrl@f7032000 { diff --git a/include/dt-bindings/reset/hisi,hi6220-resets.h b/include/dt-bindings/reset/hisi,hi6220-resets.h index ca08a7e..322ec53 100644 --- a/include/dt-bindings/reset/hisi,hi6220-resets.h +++ b/include/dt-bindings/reset/hisi,hi6220-resets.h @@ -64,4 +64,12 @@ #define PERIPH_RSDIST9_CARM_SOCDBG 0x507 #define PERIPH_RSDIST9_CARM_ETM 0x508 +#define MEDIA_G3D 0 +#define MEDIA_CODEC_VPU 2 +#define MEDIA_CODEC_JPEG 3 +#define MEDIA_ISP 4 +#define MEDIA_ADE 5 +#define MEDIA_MMU 6 +#define MEDIA_XG2RAM1 7 + #endif /*_DT_BINDINGS_RESET_CONTROLLER_HI6220*/ -- cgit v0.10.2 From 8768a26cea4c54eb8d38bc063fbfba7c60c571f4 Mon Sep 17 00:00:00 2001 From: Chen Feng Date: Mon, 20 Jun 2016 11:50:06 +0800 Subject: reset: hisilicon: Change to syscon register access There are two reset controllers in hi6220 SoC: The peripheral reset controller bits are part of sysctrl registers. The media reset controller bits are part of mediactrl registers. So change register access to syscon way. And rename current reset controller to peripheral one. Signed-off-by: Chen Feng Signed-off-by: Xia Qing Signed-off-by: Xinliang Liu Signed-off-by: Philipp Zabel diff --git a/drivers/reset/hisilicon/hi6220_reset.c b/drivers/reset/hisilicon/hi6220_reset.c index 8f55fd4..686fea9 100644 --- a/drivers/reset/hisilicon/hi6220_reset.c +++ b/drivers/reset/hisilicon/hi6220_reset.c @@ -1,7 +1,8 @@ /* * Hisilicon Hi6220 reset controller driver * - * Copyright (c) 2015 Hisilicon Limited. + * Copyright (c) 2016 Linaro Limited. + * Copyright (c) 2015-2016 Hisilicon Limited. * * Author: Feng Chen * @@ -15,81 +16,85 @@ #include #include #include +#include +#include +#include #include #include #include -#define ASSERT_OFFSET 0x300 -#define DEASSERT_OFFSET 0x304 -#define MAX_INDEX 0x509 +#define PERIPH_ASSERT_OFFSET 0x300 +#define PERIPH_DEASSERT_OFFSET 0x304 +#define PERIPH_MAX_INDEX 0x509 #define to_reset_data(x) container_of(x, struct hi6220_reset_data, rc_dev) struct hi6220_reset_data { - void __iomem *assert_base; - void __iomem *deassert_base; - struct reset_controller_dev rc_dev; + struct reset_controller_dev rc_dev; + struct regmap *regmap; }; -static int hi6220_reset_assert(struct reset_controller_dev *rc_dev, - unsigned long idx) +static int hi6220_peripheral_assert(struct reset_controller_dev *rc_dev, + unsigned long idx) { struct hi6220_reset_data *data = to_reset_data(rc_dev); + struct regmap *regmap = data->regmap; + u32 bank = idx >> 8; + u32 offset = idx & 0xff; + u32 reg = PERIPH_ASSERT_OFFSET + bank * 0x10; - int bank = idx >> 8; - int offset = idx & 0xff; - - writel(BIT(offset), data->assert_base + (bank * 0x10)); - - return 0; + return regmap_write(regmap, reg, BIT(offset)); } -static int hi6220_reset_deassert(struct reset_controller_dev *rc_dev, - unsigned long idx) +static int hi6220_peripheral_deassert(struct reset_controller_dev *rc_dev, + unsigned long idx) { struct hi6220_reset_data *data = to_reset_data(rc_dev); + struct regmap *regmap = data->regmap; + u32 bank = idx >> 8; + u32 offset = idx & 0xff; + u32 reg = PERIPH_DEASSERT_OFFSET + bank * 0x10; - int bank = idx >> 8; - int offset = idx & 0xff; - - writel(BIT(offset), data->deassert_base + (bank * 0x10)); - - return 0; + return regmap_write(regmap, reg, BIT(offset)); } -static const struct reset_control_ops hi6220_reset_ops = { - .assert = hi6220_reset_assert, - .deassert = hi6220_reset_deassert, +static const struct reset_control_ops hi6220_peripheral_reset_ops = { + .assert = hi6220_peripheral_assert, + .deassert = hi6220_peripheral_deassert, }; static int hi6220_reset_probe(struct platform_device *pdev) { + struct device_node *np = pdev->dev.of_node; + struct device *dev = &pdev->dev; struct hi6220_reset_data *data; - struct resource *res; - void __iomem *src_base; + struct regmap *regmap; - data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL); + data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL); if (!data) return -ENOMEM; - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - src_base = devm_ioremap_resource(&pdev->dev, res); - if (IS_ERR(src_base)) - return PTR_ERR(src_base); + regmap = syscon_node_to_regmap(np); + if (IS_ERR(regmap)) { + dev_err(dev, "failed to get reset controller regmap\n"); + return PTR_ERR(regmap); + } - data->assert_base = src_base + ASSERT_OFFSET; - data->deassert_base = src_base + DEASSERT_OFFSET; - data->rc_dev.nr_resets = MAX_INDEX; - data->rc_dev.ops = &hi6220_reset_ops; - data->rc_dev.of_node = pdev->dev.of_node; + data->regmap = regmap; + data->rc_dev.of_node = np; + data->rc_dev.ops = &hi6220_peripheral_reset_ops; + data->rc_dev.nr_resets = PERIPH_MAX_INDEX; return reset_controller_register(&data->rc_dev); } static const struct of_device_id hi6220_reset_match[] = { - { .compatible = "hisilicon,hi6220-sysctrl" }, - { }, + { + .compatible = "hisilicon,hi6220-sysctrl", + }, + { /* sentinel */ }, }; +MODULE_DEVICE_TABLE(of, hi6220_reset_match); static struct platform_driver hi6220_reset_driver = { .probe = hi6220_reset_probe, -- cgit v0.10.2 From ab52b599c197b5e50ad918f8084673d9b4caed83 Mon Sep 17 00:00:00 2001 From: Xinliang Liu Date: Mon, 20 Jun 2016 11:50:07 +0800 Subject: reset: hisilicon: Add hi6220 media subsystem reset support Add hi6220 media subsystem reset controller. Signed-off-by: Chen Feng Signed-off-by: Xia Qing Signed-off-by: Xinliang Liu Signed-off-by: Philipp Zabel diff --git a/drivers/reset/hisilicon/hi6220_reset.c b/drivers/reset/hisilicon/hi6220_reset.c index 686fea9..35ce53e 100644 --- a/drivers/reset/hisilicon/hi6220_reset.c +++ b/drivers/reset/hisilicon/hi6220_reset.c @@ -27,8 +27,17 @@ #define PERIPH_DEASSERT_OFFSET 0x304 #define PERIPH_MAX_INDEX 0x509 +#define SC_MEDIA_RSTEN 0x052C +#define SC_MEDIA_RSTDIS 0x0530 +#define MEDIA_MAX_INDEX 8 + #define to_reset_data(x) container_of(x, struct hi6220_reset_data, rc_dev) +enum hi6220_reset_ctrl_type { + PERIPHERAL, + MEDIA, +}; + struct hi6220_reset_data { struct reset_controller_dev rc_dev; struct regmap *regmap; @@ -63,10 +72,34 @@ static const struct reset_control_ops hi6220_peripheral_reset_ops = { .deassert = hi6220_peripheral_deassert, }; +static int hi6220_media_assert(struct reset_controller_dev *rc_dev, + unsigned long idx) +{ + struct hi6220_reset_data *data = to_reset_data(rc_dev); + struct regmap *regmap = data->regmap; + + return regmap_write(regmap, SC_MEDIA_RSTEN, BIT(idx)); +} + +static int hi6220_media_deassert(struct reset_controller_dev *rc_dev, + unsigned long idx) +{ + struct hi6220_reset_data *data = to_reset_data(rc_dev); + struct regmap *regmap = data->regmap; + + return regmap_write(regmap, SC_MEDIA_RSTDIS, BIT(idx)); +} + +static const struct reset_control_ops hi6220_media_reset_ops = { + .assert = hi6220_media_assert, + .deassert = hi6220_media_deassert, +}; + static int hi6220_reset_probe(struct platform_device *pdev) { struct device_node *np = pdev->dev.of_node; struct device *dev = &pdev->dev; + enum hi6220_reset_ctrl_type type; struct hi6220_reset_data *data; struct regmap *regmap; @@ -74,6 +107,8 @@ static int hi6220_reset_probe(struct platform_device *pdev) if (!data) return -ENOMEM; + type = (enum hi6220_reset_ctrl_type)of_device_get_match_data(dev); + regmap = syscon_node_to_regmap(np); if (IS_ERR(regmap)) { dev_err(dev, "failed to get reset controller regmap\n"); @@ -82,8 +117,13 @@ static int hi6220_reset_probe(struct platform_device *pdev) data->regmap = regmap; data->rc_dev.of_node = np; - data->rc_dev.ops = &hi6220_peripheral_reset_ops; - data->rc_dev.nr_resets = PERIPH_MAX_INDEX; + if (type == MEDIA) { + data->rc_dev.ops = &hi6220_media_reset_ops; + data->rc_dev.nr_resets = MEDIA_MAX_INDEX; + } else { + data->rc_dev.ops = &hi6220_peripheral_reset_ops; + data->rc_dev.nr_resets = PERIPH_MAX_INDEX; + } return reset_controller_register(&data->rc_dev); } @@ -91,6 +131,11 @@ static int hi6220_reset_probe(struct platform_device *pdev) static const struct of_device_id hi6220_reset_match[] = { { .compatible = "hisilicon,hi6220-sysctrl", + .data = (void *)PERIPHERAL, + }, + { + .compatible = "hisilicon,hi6220-mediactrl", + .data = (void *)MEDIA, }, { /* sentinel */ }, }; -- cgit v0.10.2 From a3828519c39aa9c85bae14bad2572d12dc0d1da6 Mon Sep 17 00:00:00 2001 From: "Andrew F. Davis" Date: Mon, 27 Jun 2016 12:12:16 -0500 Subject: Documentation: dt: reset: Add TI syscon reset binding Add TI syscon reset controller binding. This will hook to the reset framework and use syscon/regmap to set reset bits. This allows reset control of individual SoC subsytems and devices with memory-mapped reset registers in a common register memory space. Signed-off-by: Andrew F. Davis Signed-off-by: Suman Anna Acked-by: Rob Herring Signed-off-by: Philipp Zabel diff --git a/Documentation/devicetree/bindings/reset/ti-syscon-reset.txt b/Documentation/devicetree/bindings/reset/ti-syscon-reset.txt new file mode 100644 index 0000000..164c7f3 --- /dev/null +++ b/Documentation/devicetree/bindings/reset/ti-syscon-reset.txt @@ -0,0 +1,91 @@ +TI SysCon Reset Controller +======================= + +Almost all SoCs have hardware modules that require reset control in addition +to clock and power control for their functionality. The reset control is +typically provided by means of memory-mapped I/O registers. These registers are +sometimes a part of a larger register space region implementing various +functionalities. This register range is best represented as a syscon node to +allow multiple entities to access their relevant registers in the common +register space. + +A SysCon Reset Controller node defines a device that uses a syscon node +and provides reset management functionality for various hardware modules +present on the SoC. + +SysCon Reset Controller Node +============================ +Each of the reset provider/controller nodes should be a child of a syscon +node and have the following properties. + +Required properties: +-------------------- + - compatible : Should be, + "ti,k2e-pscrst" + "ti,k2l-pscrst" + "ti,k2hk-pscrst" + "ti,syscon-reset" + - #reset-cells : Should be 1. Please see the reset consumer node below + for usage details + - ti,reset-bits : Contains the reset control register information + Should contain 7 cells for each reset exposed to + consumers, defined as: + Cell #1 : offset of the reset assert control + register from the syscon register base + Cell #2 : bit position of the reset in the reset + assert control register + Cell #3 : offset of the reset deassert control + register from the syscon register base + Cell #4 : bit position of the reset in the reset + deassert control register + Cell #5 : offset of the reset status register + from the syscon register base + Cell #6 : bit position of the reset in the + reset status register + Cell #7 : Flags used to control reset behavior, + availible flags defined in the DT include + file + +SysCon Reset Consumer Nodes +=========================== +Each of the reset consumer nodes should have the following properties, +in addition to their own properties. + +Required properties: +-------------------- + - resets : A phandle to the reset controller node and an index number + to a reset specifier as defined above. + +Please also refer to Documentation/devicetree/bindings/reset/reset.txt for +common reset controller usage by consumers. + +Example: +-------- +The following example demonstrates a syscon node, the reset controller node +using the syscon node, and a consumer (a DSP device) on the TI Keystone 2 +Edison SoC. + +/ { + soc { + psc: power-sleep-controller@02350000 { + compatible = "syscon", "simple-mfd"; + reg = <0x02350000 0x1000>; + + pscrst: psc-reset { + compatible = "ti,k2e-pscrst", "ti,syscon-reset"; + #reset-cells = <1>; + + ti,reset-bits = < + 0xa3c 8 0xa3c 8 0x83c 8 (ASSERT_SET|DEASSERT_CLEAR|STATUS_SET) /* 0: pcrst-dsp0 */ + 0xa40 5 0xa44 3 0 0 (ASSERT_SET|DEASSERT_CLEAR|STATUS_NONE) /* 1: pcrst-example */ + >; + }; + }; + + dsp0: dsp0 { + ... + resets = <&pscrst 0>; + ... + }; + }; +}; diff --git a/include/dt-bindings/reset/ti-syscon.h b/include/dt-bindings/reset/ti-syscon.h new file mode 100644 index 0000000..884fd91 --- /dev/null +++ b/include/dt-bindings/reset/ti-syscon.h @@ -0,0 +1,38 @@ +/* + * TI Syscon Reset definitions + * + * Copyright (C) 2015-2016 Texas Instruments Incorporated - http://www.ti.com/ + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef __DT_BINDINGS_RESET_TI_SYSCON_H__ +#define __DT_BINDINGS_RESET_TI_SYSCON_H__ + +/* + * The reset does not support the feature and corresponding + * values are not valid + */ +#define ASSERT_NONE (1 << 0) +#define DEASSERT_NONE (1 << 1) +#define STATUS_NONE (1 << 2) + +/* When set this function is activated by setting(vs clearing) this bit */ +#define ASSERT_SET (1 << 3) +#define DEASSERT_SET (1 << 4) +#define STATUS_SET (1 << 5) + +/* The following are the inverse of the above and are added for consistency */ +#define ASSERT_CLEAR (0 << 3) +#define DEASSERT_CLEAR (0 << 4) +#define STATUS_CLEAR (0 << 5) + +#endif -- cgit v0.10.2 From cc7c2bb1493c4118d5ae69e350a405faf3ddfb89 Mon Sep 17 00:00:00 2001 From: "Andrew F. Davis" Date: Mon, 27 Jun 2016 12:12:17 -0500 Subject: reset: add TI SYSCON based reset driver Add a reset-controller driver for performing reset management of various devices present on the SoC, with the reset registers shared between devices in a common register memory space. This driver uses the syscon/regmap frameworks to actually implement the various reset functionalities needed by the reset consumer devices. Signed-off-by: Andrew F. Davis [s-anna@ti.com: add documentation, syscon name change] Signed-off-by: Suman Anna Signed-off-by: Philipp Zabel diff --git a/drivers/reset/Kconfig b/drivers/reset/Kconfig index ab37f4d..4be1b8c 100644 --- a/drivers/reset/Kconfig +++ b/drivers/reset/Kconfig @@ -17,6 +17,16 @@ if RESET_CONTROLLER config RESET_OXNAS bool +config TI_SYSCON_RESET + tristate "TI SYSCON Reset Driver" + depends on HAS_IOMEM + select MFD_SYSCON + help + This enables the reset driver support for TI devices with + memory-mapped reset registers as part of a syscon device node. If + you wish to use the reset framework for such memory-mapped devices, + say Y here. Otherwise, say N. + source "drivers/reset/sti/Kconfig" source "drivers/reset/hisilicon/Kconfig" diff --git a/drivers/reset/Makefile b/drivers/reset/Makefile index 03dc1bb..5d65a93 100644 --- a/drivers/reset/Makefile +++ b/drivers/reset/Makefile @@ -10,3 +10,4 @@ obj-$(CONFIG_ARCH_HISI) += hisilicon/ obj-$(CONFIG_ARCH_ZYNQ) += reset-zynq.o obj-$(CONFIG_ATH79) += reset-ath79.o obj-$(CONFIG_RESET_OXNAS) += reset-oxnas.o +obj-$(CONFIG_TI_SYSCON_RESET) += reset-ti-syscon.o diff --git a/drivers/reset/reset-ti-syscon.c b/drivers/reset/reset-ti-syscon.c new file mode 100644 index 0000000..47f0ffd --- /dev/null +++ b/drivers/reset/reset-ti-syscon.c @@ -0,0 +1,237 @@ +/* + * TI SYSCON regmap reset driver + * + * Copyright (C) 2015-2016 Texas Instruments Incorporated - http://www.ti.com/ + * Andrew F. Davis + * Suman Anna + * + * 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. + * + * This program is distributed "as is" WITHOUT ANY WARRANTY of any + * kind, whether express or implied; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include +#include +#include +#include +#include + +#include + +/** + * struct ti_syscon_reset_control - reset control structure + * @assert_offset: reset assert control register offset from syscon base + * @assert_bit: reset assert bit in the reset assert control register + * @deassert_offset: reset deassert control register offset from syscon base + * @deassert_bit: reset deassert bit in the reset deassert control register + * @status_offset: reset status register offset from syscon base + * @status_bit: reset status bit in the reset status register + * @flags: reset flag indicating how the (de)assert and status are handled + */ +struct ti_syscon_reset_control { + unsigned int assert_offset; + unsigned int assert_bit; + unsigned int deassert_offset; + unsigned int deassert_bit; + unsigned int status_offset; + unsigned int status_bit; + u32 flags; +}; + +/** + * struct ti_syscon_reset_data - reset controller information structure + * @rcdev: reset controller entity + * @regmap: regmap handle containing the memory-mapped reset registers + * @controls: array of reset controls + * @nr_controls: number of controls in control array + */ +struct ti_syscon_reset_data { + struct reset_controller_dev rcdev; + struct regmap *regmap; + struct ti_syscon_reset_control *controls; + unsigned int nr_controls; +}; + +#define to_ti_syscon_reset_data(rcdev) \ + container_of(rcdev, struct ti_syscon_reset_data, rcdev) + +/** + * ti_syscon_reset_assert() - assert device reset + * @rcdev: reset controller entity + * @id: ID of the reset to be asserted + * + * This function implements the reset driver op to assert a device's reset. + * This asserts the reset in a manner prescribed by the reset flags. + * + * Return: 0 for successful request, else a corresponding error value + */ +static int ti_syscon_reset_assert(struct reset_controller_dev *rcdev, + unsigned long id) +{ + struct ti_syscon_reset_data *data = to_ti_syscon_reset_data(rcdev); + struct ti_syscon_reset_control *control; + unsigned int mask, value; + + if (id >= data->nr_controls) + return -EINVAL; + + control = &data->controls[id]; + + if (control->flags & ASSERT_NONE) + return -ENOTSUPP; /* assert not supported for this reset */ + + mask = BIT(control->assert_bit); + value = (control->flags & ASSERT_SET) ? mask : 0x0; + + return regmap_update_bits(data->regmap, control->assert_offset, mask, value); +} + +/** + * ti_syscon_reset_deassert() - deassert device reset + * @rcdev: reset controller entity + * @id: ID of reset to be deasserted + * + * This function implements the reset driver op to deassert a device's reset. + * This deasserts the reset in a manner prescribed by the reset flags. + * + * Return: 0 for successful request, else a corresponding error value + */ +static int ti_syscon_reset_deassert(struct reset_controller_dev *rcdev, + unsigned long id) +{ + struct ti_syscon_reset_data *data = to_ti_syscon_reset_data(rcdev); + struct ti_syscon_reset_control *control; + unsigned int mask, value; + + if (id >= data->nr_controls) + return -EINVAL; + + control = &data->controls[id]; + + if (control->flags & DEASSERT_NONE) + return -ENOTSUPP; /* deassert not supported for this reset */ + + mask = BIT(control->deassert_bit); + value = (control->flags & DEASSERT_SET) ? mask : 0x0; + + return regmap_update_bits(data->regmap, control->deassert_offset, mask, value); +} + +/** + * ti_syscon_reset_status() - check device reset status + * @rcdev: reset controller entity + * @id: ID of the reset for which the status is being requested + * + * This function implements the reset driver op to return the status of a + * device's reset. + * + * Return: 0 if reset is deasserted, true if reset is asserted, else a + * corresponding error value + */ +static int ti_syscon_reset_status(struct reset_controller_dev *rcdev, + unsigned long id) +{ + struct ti_syscon_reset_data *data = to_ti_syscon_reset_data(rcdev); + struct ti_syscon_reset_control *control; + unsigned int reset_state; + int ret; + + if (id >= data->nr_controls) + return -EINVAL; + + control = &data->controls[id]; + + if (control->flags & STATUS_NONE) + return -ENOTSUPP; /* status not supported for this reset */ + + ret = regmap_read(data->regmap, control->status_offset, &reset_state); + if (ret) + return ret; + + return (reset_state & BIT(control->status_bit)) && + (control->flags & STATUS_SET); +} + +static struct reset_control_ops ti_syscon_reset_ops = { + .assert = ti_syscon_reset_assert, + .deassert = ti_syscon_reset_deassert, + .status = ti_syscon_reset_status, +}; + +static int ti_syscon_reset_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct device_node *np = dev->of_node; + struct ti_syscon_reset_data *data; + struct regmap *regmap; + const __be32 *list; + struct ti_syscon_reset_control *controls; + int size, nr_controls, i; + + data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL); + if (!data) + return -ENOMEM; + + regmap = syscon_node_to_regmap(np->parent); + if (IS_ERR(regmap)) + return PTR_ERR(regmap); + + list = of_get_property(np, "ti,reset-bits", &size); + if (!list || (size / sizeof(*list)) % 7 != 0) { + dev_err(dev, "invalid DT reset description\n"); + return -EINVAL; + } + + nr_controls = (size / sizeof(*list)) / 7; + controls = devm_kzalloc(dev, nr_controls * sizeof(*controls), GFP_KERNEL); + if (!controls) + return -ENOMEM; + + for (i = 0; i < nr_controls; i++) { + controls[i].assert_offset = be32_to_cpup(list++); + controls[i].assert_bit = be32_to_cpup(list++); + controls[i].deassert_offset = be32_to_cpup(list++); + controls[i].deassert_bit = be32_to_cpup(list++); + controls[i].status_offset = be32_to_cpup(list++); + controls[i].status_bit = be32_to_cpup(list++); + controls[i].flags = be32_to_cpup(list++); + } + + data->rcdev.ops = &ti_syscon_reset_ops; + data->rcdev.owner = THIS_MODULE; + data->rcdev.of_node = np; + data->rcdev.nr_resets = nr_controls; + data->regmap = regmap; + data->controls = controls; + data->nr_controls = nr_controls; + + platform_set_drvdata(pdev, data); + + return devm_reset_controller_register(dev, &data->rcdev); +} + +static const struct of_device_id ti_syscon_reset_of_match[] = { + { .compatible = "ti,syscon-reset", }, + { /* sentinel */ }, +}; +MODULE_DEVICE_TABLE(of, ti_syscon_reset_of_match); + +static struct platform_driver ti_syscon_reset_driver = { + .probe = ti_syscon_reset_probe, + .driver = { + .name = "ti-syscon-reset", + .of_match_table = ti_syscon_reset_of_match, + }, +}; +module_platform_driver(ti_syscon_reset_driver); + +MODULE_AUTHOR("Andrew F. Davis "); +MODULE_AUTHOR("Suman Anna "); +MODULE_DESCRIPTION("TI SYSCON Regmap Reset Driver"); +MODULE_LICENSE("GPL v2"); -- cgit v0.10.2 From 4406d52a0b735b27472846953fd0565302af6f3c Mon Sep 17 00:00:00 2001 From: Ivaylo Dimitrov Date: Wed, 22 Jun 2016 22:22:17 +0300 Subject: ir-rx51: Fix build after multiarch changes broke it MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The ir-rx51 driver for n900 has been disabled since the multiarch changes as plat include directory no longer is SoC specific. Let's fix it with minimal changes to pass the dmtimer calls in pdata. Then the following changes can be done while things can be tested to be working for each change: 1. Change the non-pwm dmtimer to use just hrtimer if possible 2. Change the pwm dmtimer to use Linux PWM API with the new drivers/pwm/pwm-omap-dmtimer.c and remove the direct calls to dmtimer functions 3. Parse configuration from device tree and drop the pdata Cc: Mauro Carvalho Chehab Cc: Neil Armstrong Cc: linux-media@vger.kernel.org Signed-off-by: Tony Lindgren Signed-off-by: Ivaylo Dimitrov Acked-by: Pavel Machek Acked-by: Pali Rohár diff --git a/drivers/media/rc/Kconfig b/drivers/media/rc/Kconfig index bd4d685..370e16e 100644 --- a/drivers/media/rc/Kconfig +++ b/drivers/media/rc/Kconfig @@ -336,7 +336,7 @@ config IR_TTUSBIR config IR_RX51 tristate "Nokia N900 IR transmitter diode" - depends on OMAP_DM_TIMER && ARCH_OMAP2PLUS && LIRC && !ARCH_MULTIPLATFORM + depends on OMAP_DM_TIMER && PWM_OMAP_DMTIMER && ARCH_OMAP2PLUS && LIRC ---help--- Say Y or M here if you want to enable support for the IR transmitter diode built in the Nokia N900 (RX51) device. diff --git a/drivers/media/rc/ir-rx51.c b/drivers/media/rc/ir-rx51.c index 4e1711a..da839c3 100644 --- a/drivers/media/rc/ir-rx51.c +++ b/drivers/media/rc/ir-rx51.c @@ -19,6 +19,7 @@ * */ +#include #include #include #include @@ -26,11 +27,9 @@ #include #include -#include -#include - #include #include +#include #include #define LIRC_RX51_DRIVER_FEATURES (LIRC_CAN_SET_SEND_DUTY_CYCLE | \ @@ -44,8 +43,9 @@ #define TIMER_MAX_VALUE 0xffffffff struct lirc_rx51 { - struct omap_dm_timer *pwm_timer; - struct omap_dm_timer *pulse_timer; + pwm_omap_dmtimer *pwm_timer; + pwm_omap_dmtimer *pulse_timer; + struct pwm_omap_dmtimer_pdata *dmtimer; struct device *dev; struct lirc_rx51_platform_data *pdata; wait_queue_head_t wqueue; @@ -63,14 +63,14 @@ struct lirc_rx51 { static void lirc_rx51_on(struct lirc_rx51 *lirc_rx51) { - omap_dm_timer_set_pwm(lirc_rx51->pwm_timer, 0, 1, - OMAP_TIMER_TRIGGER_OVERFLOW_AND_COMPARE); + lirc_rx51->dmtimer->set_pwm(lirc_rx51->pwm_timer, 0, 1, + PWM_OMAP_DMTIMER_TRIGGER_OVERFLOW_AND_COMPARE); } static void lirc_rx51_off(struct lirc_rx51 *lirc_rx51) { - omap_dm_timer_set_pwm(lirc_rx51->pwm_timer, 0, 1, - OMAP_TIMER_TRIGGER_NONE); + lirc_rx51->dmtimer->set_pwm(lirc_rx51->pwm_timer, 0, 1, + PWM_OMAP_DMTIMER_TRIGGER_NONE); } static int init_timing_params(struct lirc_rx51 *lirc_rx51) @@ -79,12 +79,12 @@ static int init_timing_params(struct lirc_rx51 *lirc_rx51) load = -(lirc_rx51->fclk_khz * 1000 / lirc_rx51->freq); match = -(lirc_rx51->duty_cycle * -load / 100); - omap_dm_timer_set_load(lirc_rx51->pwm_timer, 1, load); - omap_dm_timer_set_match(lirc_rx51->pwm_timer, 1, match); - omap_dm_timer_write_counter(lirc_rx51->pwm_timer, TIMER_MAX_VALUE - 2); - omap_dm_timer_start(lirc_rx51->pwm_timer); - omap_dm_timer_set_int_enable(lirc_rx51->pulse_timer, 0); - omap_dm_timer_start(lirc_rx51->pulse_timer); + lirc_rx51->dmtimer->set_load(lirc_rx51->pwm_timer, 1, load); + lirc_rx51->dmtimer->set_match(lirc_rx51->pwm_timer, 1, match); + lirc_rx51->dmtimer->write_counter(lirc_rx51->pwm_timer, TIMER_MAX_VALUE - 2); + lirc_rx51->dmtimer->start(lirc_rx51->pwm_timer); + lirc_rx51->dmtimer->set_int_enable(lirc_rx51->pulse_timer, 0); + lirc_rx51->dmtimer->start(lirc_rx51->pulse_timer); lirc_rx51->match = 0; @@ -100,15 +100,15 @@ static int pulse_timer_set_timeout(struct lirc_rx51 *lirc_rx51, int usec) BUG_ON(usec < 0); if (lirc_rx51->match == 0) - counter = omap_dm_timer_read_counter(lirc_rx51->pulse_timer); + counter = lirc_rx51->dmtimer->read_counter(lirc_rx51->pulse_timer); else counter = lirc_rx51->match; counter += (u32)(lirc_rx51->fclk_khz * usec / (1000)); - omap_dm_timer_set_match(lirc_rx51->pulse_timer, 1, counter); - omap_dm_timer_set_int_enable(lirc_rx51->pulse_timer, - OMAP_TIMER_INT_MATCH); - if (tics_after(omap_dm_timer_read_counter(lirc_rx51->pulse_timer), + lirc_rx51->dmtimer->set_match(lirc_rx51->pulse_timer, 1, counter); + lirc_rx51->dmtimer->set_int_enable(lirc_rx51->pulse_timer, + PWM_OMAP_DMTIMER_INT_MATCH); + if (tics_after(lirc_rx51->dmtimer->read_counter(lirc_rx51->pulse_timer), counter)) { return 1; } @@ -120,18 +120,18 @@ static irqreturn_t lirc_rx51_interrupt_handler(int irq, void *ptr) unsigned int retval; struct lirc_rx51 *lirc_rx51 = ptr; - retval = omap_dm_timer_read_status(lirc_rx51->pulse_timer); + retval = lirc_rx51->dmtimer->read_status(lirc_rx51->pulse_timer); if (!retval) return IRQ_NONE; - if (retval & ~OMAP_TIMER_INT_MATCH) + if (retval & ~PWM_OMAP_DMTIMER_INT_MATCH) dev_err_ratelimited(lirc_rx51->dev, ": Unexpected interrupt source: %x\n", retval); - omap_dm_timer_write_status(lirc_rx51->pulse_timer, - OMAP_TIMER_INT_MATCH | - OMAP_TIMER_INT_OVERFLOW | - OMAP_TIMER_INT_CAPTURE); + lirc_rx51->dmtimer->write_status(lirc_rx51->pulse_timer, + PWM_OMAP_DMTIMER_INT_MATCH | + PWM_OMAP_DMTIMER_INT_OVERFLOW | + PWM_OMAP_DMTIMER_INT_CAPTURE); if (lirc_rx51->wbuf_index < 0) { dev_err_ratelimited(lirc_rx51->dev, ": BUG wbuf_index has value of %i\n", @@ -165,9 +165,9 @@ end: /* Stop TX here */ lirc_rx51_off(lirc_rx51); lirc_rx51->wbuf_index = -1; - omap_dm_timer_stop(lirc_rx51->pwm_timer); - omap_dm_timer_stop(lirc_rx51->pulse_timer); - omap_dm_timer_set_int_enable(lirc_rx51->pulse_timer, 0); + lirc_rx51->dmtimer->stop(lirc_rx51->pwm_timer); + lirc_rx51->dmtimer->stop(lirc_rx51->pulse_timer); + lirc_rx51->dmtimer->set_int_enable(lirc_rx51->pulse_timer, 0); wake_up_interruptible(&lirc_rx51->wqueue); return IRQ_HANDLED; @@ -178,28 +178,29 @@ static int lirc_rx51_init_port(struct lirc_rx51 *lirc_rx51) struct clk *clk_fclk; int retval, pwm_timer = lirc_rx51->pwm_timer_num; - lirc_rx51->pwm_timer = omap_dm_timer_request_specific(pwm_timer); + lirc_rx51->pwm_timer = lirc_rx51->dmtimer->request_specific(pwm_timer); if (lirc_rx51->pwm_timer == NULL) { dev_err(lirc_rx51->dev, ": Error requesting GPT%d timer\n", pwm_timer); return -EBUSY; } - lirc_rx51->pulse_timer = omap_dm_timer_request(); + lirc_rx51->pulse_timer = lirc_rx51->dmtimer->request(); if (lirc_rx51->pulse_timer == NULL) { dev_err(lirc_rx51->dev, ": Error requesting pulse timer\n"); retval = -EBUSY; goto err1; } - omap_dm_timer_set_source(lirc_rx51->pwm_timer, OMAP_TIMER_SRC_SYS_CLK); - omap_dm_timer_set_source(lirc_rx51->pulse_timer, - OMAP_TIMER_SRC_SYS_CLK); + lirc_rx51->dmtimer->set_source(lirc_rx51->pwm_timer, + PWM_OMAP_DMTIMER_SRC_SYS_CLK); + lirc_rx51->dmtimer->set_source(lirc_rx51->pulse_timer, + PWM_OMAP_DMTIMER_SRC_SYS_CLK); - omap_dm_timer_enable(lirc_rx51->pwm_timer); - omap_dm_timer_enable(lirc_rx51->pulse_timer); + lirc_rx51->dmtimer->enable(lirc_rx51->pwm_timer); + lirc_rx51->dmtimer->enable(lirc_rx51->pulse_timer); - lirc_rx51->irq_num = omap_dm_timer_get_irq(lirc_rx51->pulse_timer); + lirc_rx51->irq_num = lirc_rx51->dmtimer->get_irq(lirc_rx51->pulse_timer); retval = request_irq(lirc_rx51->irq_num, lirc_rx51_interrupt_handler, IRQF_SHARED, "lirc_pulse_timer", lirc_rx51); if (retval) { @@ -207,28 +208,28 @@ static int lirc_rx51_init_port(struct lirc_rx51 *lirc_rx51) goto err2; } - clk_fclk = omap_dm_timer_get_fclk(lirc_rx51->pwm_timer); - lirc_rx51->fclk_khz = clk_fclk->rate / 1000; + clk_fclk = lirc_rx51->dmtimer->get_fclk(lirc_rx51->pwm_timer); + lirc_rx51->fclk_khz = clk_get_rate(clk_fclk) / 1000; return 0; err2: - omap_dm_timer_free(lirc_rx51->pulse_timer); + lirc_rx51->dmtimer->free(lirc_rx51->pulse_timer); err1: - omap_dm_timer_free(lirc_rx51->pwm_timer); + lirc_rx51->dmtimer->free(lirc_rx51->pwm_timer); return retval; } static int lirc_rx51_free_port(struct lirc_rx51 *lirc_rx51) { - omap_dm_timer_set_int_enable(lirc_rx51->pulse_timer, 0); + lirc_rx51->dmtimer->set_int_enable(lirc_rx51->pulse_timer, 0); free_irq(lirc_rx51->irq_num, lirc_rx51); lirc_rx51_off(lirc_rx51); - omap_dm_timer_disable(lirc_rx51->pwm_timer); - omap_dm_timer_disable(lirc_rx51->pulse_timer); - omap_dm_timer_free(lirc_rx51->pwm_timer); - omap_dm_timer_free(lirc_rx51->pulse_timer); + lirc_rx51->dmtimer->disable(lirc_rx51->pwm_timer); + lirc_rx51->dmtimer->disable(lirc_rx51->pulse_timer); + lirc_rx51->dmtimer->free(lirc_rx51->pwm_timer); + lirc_rx51->dmtimer->free(lirc_rx51->pulse_timer); lirc_rx51->wbuf_index = -1; return 0; @@ -446,7 +447,13 @@ static int lirc_rx51_probe(struct platform_device *dev) { lirc_rx51_driver.features = LIRC_RX51_DRIVER_FEATURES; lirc_rx51.pdata = dev->dev.platform_data; + if (!lirc_rx51.pdata->dmtimer) { + dev_err(&dev->dev, "no dmtimer?\n"); + return -ENODEV; + } + lirc_rx51.pwm_timer_num = lirc_rx51.pdata->pwm_timer; + lirc_rx51.dmtimer = lirc_rx51.pdata->dmtimer; lirc_rx51.dev = &dev->dev; lirc_rx51_driver.dev = &dev->dev; lirc_rx51_driver.minor = lirc_register_driver(&lirc_rx51_driver); -- cgit v0.10.2 From a74a198249c473fba092ee549068cea30d54f07e Mon Sep 17 00:00:00 2001 From: Ivaylo Dimitrov Date: Wed, 22 Jun 2016 22:22:18 +0300 Subject: pwm: omap-dmtimer: Allow for setting dmtimer clock source MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit OMAP GP timers can have different input clocks that allow different PWM frequencies. However, there is no other way of setting the clock source but through clocks or clock-names properties of the timer itself. This limits PWM functionality to only the frequencies allowed by the particular clock source. Allowing setting the clock source by PWM rather than by timer allows different PWMs to have different ranges by not hard-wiring the clock source to the timer. Signed-off-by: Ivaylo Dimitrov Acked-by: Rob Herring Acked-by: Thierry Reding Acked-by: Pali Rohár Signed-off-by: Tony Lindgren diff --git a/Documentation/devicetree/bindings/pwm/pwm-omap-dmtimer.txt b/Documentation/devicetree/bindings/pwm/pwm-omap-dmtimer.txt index 5befb53..2e53324 100644 --- a/Documentation/devicetree/bindings/pwm/pwm-omap-dmtimer.txt +++ b/Documentation/devicetree/bindings/pwm/pwm-omap-dmtimer.txt @@ -9,6 +9,10 @@ Required properties: Optional properties: - ti,prescaler: Should be a value between 0 and 7, see the timers datasheet +- ti,clock-source: Set dmtimer parent clock, values between 0 and 2: + - 0x00 - high-frequency system clock (timer_sys_ck) + - 0x01 - 32-kHz always-on clock (timer_32k_ck) + - 0x02 - external clock (timer_ext_ck, OMAP2 only) Example: pwm9: dmtimer-pwm@9 { diff --git a/drivers/pwm/pwm-omap-dmtimer.c b/drivers/pwm/pwm-omap-dmtimer.c index 3e95090..5ad42f3 100644 --- a/drivers/pwm/pwm-omap-dmtimer.c +++ b/drivers/pwm/pwm-omap-dmtimer.c @@ -245,7 +245,7 @@ static int pwm_omap_dmtimer_probe(struct platform_device *pdev) struct pwm_omap_dmtimer_chip *omap; struct pwm_omap_dmtimer_pdata *pdata; pwm_omap_dmtimer *dm_timer; - u32 prescaler; + u32 v; int status; pdata = dev_get_platdata(&pdev->dev); @@ -306,10 +306,12 @@ static int pwm_omap_dmtimer_probe(struct platform_device *pdev) if (pm_runtime_active(&omap->dm_timer_pdev->dev)) omap->pdata->stop(omap->dm_timer); - /* setup dmtimer prescaler */ - if (!of_property_read_u32(pdev->dev.of_node, "ti,prescaler", - &prescaler)) - omap->pdata->set_prescaler(omap->dm_timer, prescaler); + if (!of_property_read_u32(pdev->dev.of_node, "ti,prescaler", &v)) + omap->pdata->set_prescaler(omap->dm_timer, v); + + /* setup dmtimer clock source */ + if (!of_property_read_u32(pdev->dev.of_node, "ti,clock-source", &v)) + omap->pdata->set_source(omap->dm_timer, v); omap->chip.dev = &pdev->dev; omap->chip.ops = &pwm_omap_dmtimer_ops; -- cgit v0.10.2 From 3fdd1526e6c5ceadd4e91906d223e2d382fb72ca Mon Sep 17 00:00:00 2001 From: Ivaylo Dimitrov Date: Wed, 22 Jun 2016 22:22:19 +0300 Subject: ir-rx51: use PWM framework instead of OMAP dmtimer MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Convert driver to use PWM framework instead of calling dmtimer functions directly for PWM timer. Remove paragraph about writing to the Free Software Foundation's mailing address while at it. Signed-off-by: Ivaylo Dimitrov Acked-by: Pali Rohár Signed-off-by: Tony Lindgren diff --git a/arch/arm/mach-omap2/board-rx51-peripherals.c b/arch/arm/mach-omap2/board-rx51-peripherals.c index 9a70739..e487575 100644 --- a/arch/arm/mach-omap2/board-rx51-peripherals.c +++ b/arch/arm/mach-omap2/board-rx51-peripherals.c @@ -1242,7 +1242,6 @@ static struct pwm_omap_dmtimer_pdata __maybe_unused pwm_dmtimer_pdata = { #if defined(CONFIG_IR_RX51) || defined(CONFIG_IR_RX51_MODULE) static struct lirc_rx51_platform_data rx51_lirc_data = { .set_max_mpu_wakeup_lat = omap_pm_set_max_mpu_wakeup_lat, - .pwm_timer = 9, /* Use GPT 9 for CIR */ #if IS_ENABLED(CONFIG_OMAP_DM_TIMER) .dmtimer = &pwm_dmtimer_pdata, #endif diff --git a/arch/arm/mach-omap2/pdata-quirks.c b/arch/arm/mach-omap2/pdata-quirks.c index 6571ad9..278bb8f 100644 --- a/arch/arm/mach-omap2/pdata-quirks.c +++ b/arch/arm/mach-omap2/pdata-quirks.c @@ -491,7 +491,6 @@ static struct pwm_omap_dmtimer_pdata pwm_dmtimer_pdata = { static struct lirc_rx51_platform_data __maybe_unused rx51_lirc_data = { .set_max_mpu_wakeup_lat = omap_pm_set_max_mpu_wakeup_lat, - .pwm_timer = 9, /* Use GPT 9 for CIR */ #if IS_ENABLED(CONFIG_OMAP_DM_TIMER) .dmtimer = &pwm_dmtimer_pdata, #endif diff --git a/drivers/media/rc/ir-rx51.c b/drivers/media/rc/ir-rx51.c index da839c3..5096ef3 100644 --- a/drivers/media/rc/ir-rx51.c +++ b/drivers/media/rc/ir-rx51.c @@ -12,13 +12,7 @@ * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * */ - #include #include #include @@ -26,6 +20,7 @@ #include #include #include +#include #include #include @@ -43,7 +38,7 @@ #define TIMER_MAX_VALUE 0xffffffff struct lirc_rx51 { - pwm_omap_dmtimer *pwm_timer; + struct pwm_device *pwm; pwm_omap_dmtimer *pulse_timer; struct pwm_omap_dmtimer_pdata *dmtimer; struct device *dev; @@ -58,32 +53,28 @@ struct lirc_rx51 { int wbuf[WBUF_LEN]; int wbuf_index; unsigned long device_is_open; - int pwm_timer_num; }; static void lirc_rx51_on(struct lirc_rx51 *lirc_rx51) { - lirc_rx51->dmtimer->set_pwm(lirc_rx51->pwm_timer, 0, 1, - PWM_OMAP_DMTIMER_TRIGGER_OVERFLOW_AND_COMPARE); + pwm_enable(lirc_rx51->pwm); } static void lirc_rx51_off(struct lirc_rx51 *lirc_rx51) { - lirc_rx51->dmtimer->set_pwm(lirc_rx51->pwm_timer, 0, 1, - PWM_OMAP_DMTIMER_TRIGGER_NONE); + pwm_disable(lirc_rx51->pwm); } static int init_timing_params(struct lirc_rx51 *lirc_rx51) { - u32 load, match; - - load = -(lirc_rx51->fclk_khz * 1000 / lirc_rx51->freq); - match = -(lirc_rx51->duty_cycle * -load / 100); - lirc_rx51->dmtimer->set_load(lirc_rx51->pwm_timer, 1, load); - lirc_rx51->dmtimer->set_match(lirc_rx51->pwm_timer, 1, match); - lirc_rx51->dmtimer->write_counter(lirc_rx51->pwm_timer, TIMER_MAX_VALUE - 2); - lirc_rx51->dmtimer->start(lirc_rx51->pwm_timer); + struct pwm_device *pwm = lirc_rx51->pwm; + int duty, period = DIV_ROUND_CLOSEST(NSEC_PER_SEC, lirc_rx51->freq); + + duty = DIV_ROUND_CLOSEST(lirc_rx51->duty_cycle * period, 100); lirc_rx51->dmtimer->set_int_enable(lirc_rx51->pulse_timer, 0); + + pwm_config(pwm, duty, period); + lirc_rx51->dmtimer->start(lirc_rx51->pulse_timer); lirc_rx51->match = 0; @@ -165,7 +156,7 @@ end: /* Stop TX here */ lirc_rx51_off(lirc_rx51); lirc_rx51->wbuf_index = -1; - lirc_rx51->dmtimer->stop(lirc_rx51->pwm_timer); + lirc_rx51->dmtimer->stop(lirc_rx51->pulse_timer); lirc_rx51->dmtimer->set_int_enable(lirc_rx51->pulse_timer, 0); wake_up_interruptible(&lirc_rx51->wqueue); @@ -176,13 +167,13 @@ end: static int lirc_rx51_init_port(struct lirc_rx51 *lirc_rx51) { struct clk *clk_fclk; - int retval, pwm_timer = lirc_rx51->pwm_timer_num; + int retval; - lirc_rx51->pwm_timer = lirc_rx51->dmtimer->request_specific(pwm_timer); - if (lirc_rx51->pwm_timer == NULL) { - dev_err(lirc_rx51->dev, ": Error requesting GPT%d timer\n", - pwm_timer); - return -EBUSY; + lirc_rx51->pwm = pwm_get(lirc_rx51->dev, NULL); + if (IS_ERR(lirc_rx51->pwm)) { + retval = PTR_ERR(lirc_rx51->pwm); + dev_err(lirc_rx51->dev, ": pwm_get failed: %d\n", retval); + return retval; } lirc_rx51->pulse_timer = lirc_rx51->dmtimer->request(); @@ -192,15 +183,11 @@ static int lirc_rx51_init_port(struct lirc_rx51 *lirc_rx51) goto err1; } - lirc_rx51->dmtimer->set_source(lirc_rx51->pwm_timer, - PWM_OMAP_DMTIMER_SRC_SYS_CLK); lirc_rx51->dmtimer->set_source(lirc_rx51->pulse_timer, PWM_OMAP_DMTIMER_SRC_SYS_CLK); - - lirc_rx51->dmtimer->enable(lirc_rx51->pwm_timer); lirc_rx51->dmtimer->enable(lirc_rx51->pulse_timer); - - lirc_rx51->irq_num = lirc_rx51->dmtimer->get_irq(lirc_rx51->pulse_timer); + lirc_rx51->irq_num = + lirc_rx51->dmtimer->get_irq(lirc_rx51->pulse_timer); retval = request_irq(lirc_rx51->irq_num, lirc_rx51_interrupt_handler, IRQF_SHARED, "lirc_pulse_timer", lirc_rx51); if (retval) { @@ -208,7 +195,7 @@ static int lirc_rx51_init_port(struct lirc_rx51 *lirc_rx51) goto err2; } - clk_fclk = lirc_rx51->dmtimer->get_fclk(lirc_rx51->pwm_timer); + clk_fclk = lirc_rx51->dmtimer->get_fclk(lirc_rx51->pulse_timer); lirc_rx51->fclk_khz = clk_get_rate(clk_fclk) / 1000; return 0; @@ -216,7 +203,7 @@ static int lirc_rx51_init_port(struct lirc_rx51 *lirc_rx51) err2: lirc_rx51->dmtimer->free(lirc_rx51->pulse_timer); err1: - lirc_rx51->dmtimer->free(lirc_rx51->pwm_timer); + pwm_put(lirc_rx51->pwm); return retval; } @@ -226,11 +213,10 @@ static int lirc_rx51_free_port(struct lirc_rx51 *lirc_rx51) lirc_rx51->dmtimer->set_int_enable(lirc_rx51->pulse_timer, 0); free_irq(lirc_rx51->irq_num, lirc_rx51); lirc_rx51_off(lirc_rx51); - lirc_rx51->dmtimer->disable(lirc_rx51->pwm_timer); lirc_rx51->dmtimer->disable(lirc_rx51->pulse_timer); - lirc_rx51->dmtimer->free(lirc_rx51->pwm_timer); lirc_rx51->dmtimer->free(lirc_rx51->pulse_timer); lirc_rx51->wbuf_index = -1; + pwm_put(lirc_rx51->pwm); return 0; } @@ -387,7 +373,6 @@ static int lirc_rx51_release(struct inode *inode, struct file *file) } static struct lirc_rx51 lirc_rx51 = { - .freq = 38000, .duty_cycle = 50, .wbuf_index = -1, }; @@ -445,14 +430,34 @@ static int lirc_rx51_resume(struct platform_device *dev) static int lirc_rx51_probe(struct platform_device *dev) { + struct pwm_device *pwm; + lirc_rx51_driver.features = LIRC_RX51_DRIVER_FEATURES; lirc_rx51.pdata = dev->dev.platform_data; + + if (!lirc_rx51.pdata) { + dev_err(&dev->dev, "Platform Data is missing\n"); + return -ENXIO; + } + if (!lirc_rx51.pdata->dmtimer) { dev_err(&dev->dev, "no dmtimer?\n"); return -ENODEV; } - lirc_rx51.pwm_timer_num = lirc_rx51.pdata->pwm_timer; + pwm = pwm_get(&dev->dev, NULL); + if (IS_ERR(pwm)) { + int err = PTR_ERR(pwm); + + if (err != -EPROBE_DEFER) + dev_err(&dev->dev, "pwm_get failed: %d\n", err); + return err; + } + + /* Use default, in case userspace does not set the carrier */ + lirc_rx51.freq = DIV_ROUND_CLOSEST(pwm_get_period(pwm), NSEC_PER_SEC); + pwm_put(pwm); + lirc_rx51.dmtimer = lirc_rx51.pdata->dmtimer; lirc_rx51.dev = &dev->dev; lirc_rx51_driver.dev = &dev->dev; @@ -464,8 +469,6 @@ static int lirc_rx51_probe(struct platform_device *dev) lirc_rx51_driver.minor); return lirc_rx51_driver.minor; } - dev_info(lirc_rx51.dev, "registration ok, minor: %d, pwm: %d\n", - lirc_rx51_driver.minor, lirc_rx51.pwm_timer_num); return 0; } diff --git a/include/linux/platform_data/media/ir-rx51.h b/include/linux/platform_data/media/ir-rx51.h index 3038120..6acf22d 100644 --- a/include/linux/platform_data/media/ir-rx51.h +++ b/include/linux/platform_data/media/ir-rx51.h @@ -2,8 +2,6 @@ #define _LIRC_RX51_H struct lirc_rx51_platform_data { - int pwm_timer; - int(*set_max_mpu_wakeup_lat)(struct device *dev, long t); struct pwm_omap_dmtimer_pdata *dmtimer; }; -- cgit v0.10.2 From b5406176989da601736db862643d3d7ee8335815 Mon Sep 17 00:00:00 2001 From: Ivaylo Dimitrov Date: Wed, 22 Jun 2016 22:22:20 +0300 Subject: ir-rx51: add DT support to driver MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit With the upcoming removal of legacy boot, lets add support to one of the last N900 drivers remaining without it. As the driver still uses omap dmtimer, add auxdata as well. Signed-off-by: Ivaylo Dimitrov Acked-by: Rob Herring Acked-by: Pali Rohár Signed-off-by: Tony Lindgren diff --git a/Documentation/devicetree/bindings/media/nokia,n900-ir b/Documentation/devicetree/bindings/media/nokia,n900-ir new file mode 100644 index 0000000..13a18ce --- /dev/null +++ b/Documentation/devicetree/bindings/media/nokia,n900-ir @@ -0,0 +1,20 @@ +Device-Tree bindings for LIRC TX driver for Nokia N900(RX51) + +Required properties: + - compatible: should be "nokia,n900-ir". + - pwms: specifies PWM used for IR signal transmission. + +Example node: + + pwm9: dmtimer-pwm@9 { + compatible = "ti,omap-dmtimer-pwm"; + ti,timers = <&timer9>; + ti,clock-source = <0x00>; /* timer_sys_ck */ + #pwm-cells = <3>; + }; + + ir: n900-ir { + compatible = "nokia,n900-ir"; + + pwms = <&pwm9 0 26316 0>; /* 38000 Hz */ + }; diff --git a/arch/arm/mach-omap2/pdata-quirks.c b/arch/arm/mach-omap2/pdata-quirks.c index 278bb8f..0d7b05a 100644 --- a/arch/arm/mach-omap2/pdata-quirks.c +++ b/arch/arm/mach-omap2/pdata-quirks.c @@ -273,8 +273,6 @@ static struct platform_device omap3_rom_rng_device = { }, }; -static struct platform_device rx51_lirc_device; - static void __init nokia_n900_legacy_init(void) { hsmmc2_internal_input_clk(); @@ -293,10 +291,7 @@ static void __init nokia_n900_legacy_init(void) pr_info("RX-51: Registering OMAP3 HWRNG device\n"); platform_device_register(&omap3_rom_rng_device); - } - - platform_device_register(&rx51_lirc_device); } static void __init omap3_tao3530_legacy_init(void) @@ -531,6 +526,7 @@ static struct of_dev_auxdata omap_auxdata_lookup[] __initdata = { &omap3_iommu_pdata), OF_DEV_AUXDATA("ti,omap3-hsmmc", 0x4809c000, "4809c000.mmc", &mmc_pdata[0]), OF_DEV_AUXDATA("ti,omap3-hsmmc", 0x480b4000, "480b4000.mmc", &mmc_pdata[1]), + OF_DEV_AUXDATA("nokia,n900-ir", 0, "n900-ir", &rx51_lirc_data), /* Only on am3517 */ OF_DEV_AUXDATA("ti,davinci_mdio", 0x5c030000, "davinci_mdio.0", NULL), OF_DEV_AUXDATA("ti,am3517-emac", 0x5c000000, "davinci_emac.0", diff --git a/drivers/media/rc/ir-rx51.c b/drivers/media/rc/ir-rx51.c index 5096ef3..1cbb43d 100644 --- a/drivers/media/rc/ir-rx51.c +++ b/drivers/media/rc/ir-rx51.c @@ -21,6 +21,7 @@ #include #include #include +#include #include #include @@ -478,6 +479,14 @@ static int lirc_rx51_remove(struct platform_device *dev) return lirc_unregister_driver(lirc_rx51_driver.minor); } +static const struct of_device_id lirc_rx51_match[] = { + { + .compatible = "nokia,n900-ir", + }, + {}, +}; +MODULE_DEVICE_TABLE(of, lirc_rx51_match); + struct platform_driver lirc_rx51_platform_driver = { .probe = lirc_rx51_probe, .remove = lirc_rx51_remove, @@ -485,7 +494,7 @@ struct platform_driver lirc_rx51_platform_driver = { .resume = lirc_rx51_resume, .driver = { .name = DRIVER_NAME, - .owner = THIS_MODULE, + .of_match_table = of_match_ptr(lirc_rx51_match), }, }; module_platform_driver(lirc_rx51_platform_driver); -- cgit v0.10.2 From 79cdad3635b3a253d712aba115fa274ef94a8c6b Mon Sep 17 00:00:00 2001 From: Ivaylo Dimitrov Date: Wed, 22 Jun 2016 22:22:21 +0300 Subject: ir-rx51: use hrtimer instead of dmtimer MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Drop dmtimer usage for pulse timer in favor of hrtimer. That allows removing PWM dmitimer platform data usage. Signed-off-by: Ivaylo Dimitrov Acked-by: Pali Rohár Signed-off-by: Tony Lindgren diff --git a/arch/arm/mach-omap2/board-rx51-peripherals.c b/arch/arm/mach-omap2/board-rx51-peripherals.c index e487575..a5ab712 100644 --- a/arch/arm/mach-omap2/board-rx51-peripherals.c +++ b/arch/arm/mach-omap2/board-rx51-peripherals.c @@ -1242,10 +1242,6 @@ static struct pwm_omap_dmtimer_pdata __maybe_unused pwm_dmtimer_pdata = { #if defined(CONFIG_IR_RX51) || defined(CONFIG_IR_RX51_MODULE) static struct lirc_rx51_platform_data rx51_lirc_data = { .set_max_mpu_wakeup_lat = omap_pm_set_max_mpu_wakeup_lat, -#if IS_ENABLED(CONFIG_OMAP_DM_TIMER) - .dmtimer = &pwm_dmtimer_pdata, -#endif - }; static struct platform_device rx51_lirc_device = { diff --git a/arch/arm/mach-omap2/pdata-quirks.c b/arch/arm/mach-omap2/pdata-quirks.c index 0d7b05a..7cc672b 100644 --- a/arch/arm/mach-omap2/pdata-quirks.c +++ b/arch/arm/mach-omap2/pdata-quirks.c @@ -486,9 +486,6 @@ static struct pwm_omap_dmtimer_pdata pwm_dmtimer_pdata = { static struct lirc_rx51_platform_data __maybe_unused rx51_lirc_data = { .set_max_mpu_wakeup_lat = omap_pm_set_max_mpu_wakeup_lat, -#if IS_ENABLED(CONFIG_OMAP_DM_TIMER) - .dmtimer = &pwm_dmtimer_pdata, -#endif }; static struct platform_device __maybe_unused rx51_lirc_device = { diff --git a/drivers/media/rc/ir-rx51.c b/drivers/media/rc/ir-rx51.c index 1cbb43d..82fb6f2 100644 --- a/drivers/media/rc/ir-rx51.c +++ b/drivers/media/rc/ir-rx51.c @@ -22,10 +22,10 @@ #include #include #include +#include #include #include -#include #include #define LIRC_RX51_DRIVER_FEATURES (LIRC_CAN_SET_SEND_DUTY_CYCLE | \ @@ -36,32 +36,26 @@ #define WBUF_LEN 256 -#define TIMER_MAX_VALUE 0xffffffff - struct lirc_rx51 { struct pwm_device *pwm; - pwm_omap_dmtimer *pulse_timer; - struct pwm_omap_dmtimer_pdata *dmtimer; + struct hrtimer timer; struct device *dev; struct lirc_rx51_platform_data *pdata; wait_queue_head_t wqueue; - unsigned long fclk_khz; unsigned int freq; /* carrier frequency */ unsigned int duty_cycle; /* carrier duty cycle */ - unsigned int irq_num; - unsigned int match; int wbuf[WBUF_LEN]; int wbuf_index; unsigned long device_is_open; }; -static void lirc_rx51_on(struct lirc_rx51 *lirc_rx51) +static inline void lirc_rx51_on(struct lirc_rx51 *lirc_rx51) { pwm_enable(lirc_rx51->pwm); } -static void lirc_rx51_off(struct lirc_rx51 *lirc_rx51) +static inline void lirc_rx51_off(struct lirc_rx51 *lirc_rx51) { pwm_disable(lirc_rx51->pwm); } @@ -72,61 +66,21 @@ static int init_timing_params(struct lirc_rx51 *lirc_rx51) int duty, period = DIV_ROUND_CLOSEST(NSEC_PER_SEC, lirc_rx51->freq); duty = DIV_ROUND_CLOSEST(lirc_rx51->duty_cycle * period, 100); - lirc_rx51->dmtimer->set_int_enable(lirc_rx51->pulse_timer, 0); pwm_config(pwm, duty, period); - lirc_rx51->dmtimer->start(lirc_rx51->pulse_timer); - - lirc_rx51->match = 0; - return 0; } -#define tics_after(a, b) ((long)(b) - (long)(a) < 0) - -static int pulse_timer_set_timeout(struct lirc_rx51 *lirc_rx51, int usec) +static enum hrtimer_restart lirc_rx51_timer_cb(struct hrtimer *timer) { - int counter; - - BUG_ON(usec < 0); - - if (lirc_rx51->match == 0) - counter = lirc_rx51->dmtimer->read_counter(lirc_rx51->pulse_timer); - else - counter = lirc_rx51->match; - - counter += (u32)(lirc_rx51->fclk_khz * usec / (1000)); - lirc_rx51->dmtimer->set_match(lirc_rx51->pulse_timer, 1, counter); - lirc_rx51->dmtimer->set_int_enable(lirc_rx51->pulse_timer, - PWM_OMAP_DMTIMER_INT_MATCH); - if (tics_after(lirc_rx51->dmtimer->read_counter(lirc_rx51->pulse_timer), - counter)) { - return 1; - } - return 0; -} + struct lirc_rx51 *lirc_rx51 = + container_of(timer, struct lirc_rx51, timer); + ktime_t now; -static irqreturn_t lirc_rx51_interrupt_handler(int irq, void *ptr) -{ - unsigned int retval; - struct lirc_rx51 *lirc_rx51 = ptr; - - retval = lirc_rx51->dmtimer->read_status(lirc_rx51->pulse_timer); - if (!retval) - return IRQ_NONE; - - if (retval & ~PWM_OMAP_DMTIMER_INT_MATCH) - dev_err_ratelimited(lirc_rx51->dev, - ": Unexpected interrupt source: %x\n", retval); - - lirc_rx51->dmtimer->write_status(lirc_rx51->pulse_timer, - PWM_OMAP_DMTIMER_INT_MATCH | - PWM_OMAP_DMTIMER_INT_OVERFLOW | - PWM_OMAP_DMTIMER_INT_CAPTURE); if (lirc_rx51->wbuf_index < 0) { dev_err_ratelimited(lirc_rx51->dev, - ": BUG wbuf_index has value of %i\n", + "BUG wbuf_index has value of %i\n", lirc_rx51->wbuf_index); goto end; } @@ -136,6 +90,8 @@ static irqreturn_t lirc_rx51_interrupt_handler(int irq, void *ptr) * pulses until we catch up. */ do { + u64 ns; + if (lirc_rx51->wbuf_index >= WBUF_LEN) goto end; if (lirc_rx51->wbuf[lirc_rx51->wbuf_index] == -1) @@ -146,80 +102,24 @@ static irqreturn_t lirc_rx51_interrupt_handler(int irq, void *ptr) else lirc_rx51_on(lirc_rx51); - retval = pulse_timer_set_timeout(lirc_rx51, - lirc_rx51->wbuf[lirc_rx51->wbuf_index]); + ns = 1000 * lirc_rx51->wbuf[lirc_rx51->wbuf_index]; + hrtimer_add_expires_ns(timer, ns); + lirc_rx51->wbuf_index++; - } while (retval); + now = timer->base->get_time(); + + } while (hrtimer_get_expires_tv64(timer) < now.tv64); - return IRQ_HANDLED; + return HRTIMER_RESTART; end: /* Stop TX here */ lirc_rx51_off(lirc_rx51); lirc_rx51->wbuf_index = -1; - lirc_rx51->dmtimer->stop(lirc_rx51->pulse_timer); - lirc_rx51->dmtimer->set_int_enable(lirc_rx51->pulse_timer, 0); wake_up_interruptible(&lirc_rx51->wqueue); - return IRQ_HANDLED; -} - -static int lirc_rx51_init_port(struct lirc_rx51 *lirc_rx51) -{ - struct clk *clk_fclk; - int retval; - - lirc_rx51->pwm = pwm_get(lirc_rx51->dev, NULL); - if (IS_ERR(lirc_rx51->pwm)) { - retval = PTR_ERR(lirc_rx51->pwm); - dev_err(lirc_rx51->dev, ": pwm_get failed: %d\n", retval); - return retval; - } - - lirc_rx51->pulse_timer = lirc_rx51->dmtimer->request(); - if (lirc_rx51->pulse_timer == NULL) { - dev_err(lirc_rx51->dev, ": Error requesting pulse timer\n"); - retval = -EBUSY; - goto err1; - } - - lirc_rx51->dmtimer->set_source(lirc_rx51->pulse_timer, - PWM_OMAP_DMTIMER_SRC_SYS_CLK); - lirc_rx51->dmtimer->enable(lirc_rx51->pulse_timer); - lirc_rx51->irq_num = - lirc_rx51->dmtimer->get_irq(lirc_rx51->pulse_timer); - retval = request_irq(lirc_rx51->irq_num, lirc_rx51_interrupt_handler, - IRQF_SHARED, "lirc_pulse_timer", lirc_rx51); - if (retval) { - dev_err(lirc_rx51->dev, ": Failed to request interrupt line\n"); - goto err2; - } - - clk_fclk = lirc_rx51->dmtimer->get_fclk(lirc_rx51->pulse_timer); - lirc_rx51->fclk_khz = clk_get_rate(clk_fclk) / 1000; - - return 0; - -err2: - lirc_rx51->dmtimer->free(lirc_rx51->pulse_timer); -err1: - pwm_put(lirc_rx51->pwm); - - return retval; -} - -static int lirc_rx51_free_port(struct lirc_rx51 *lirc_rx51) -{ - lirc_rx51->dmtimer->set_int_enable(lirc_rx51->pulse_timer, 0); - free_irq(lirc_rx51->irq_num, lirc_rx51); - lirc_rx51_off(lirc_rx51); - lirc_rx51->dmtimer->disable(lirc_rx51->pulse_timer); - lirc_rx51->dmtimer->free(lirc_rx51->pulse_timer); - lirc_rx51->wbuf_index = -1; - pwm_put(lirc_rx51->pwm); - - return 0; + return HRTIMER_NORESTART; } static ssize_t lirc_rx51_write(struct file *file, const char *buf, @@ -258,8 +158,9 @@ static ssize_t lirc_rx51_write(struct file *file, const char *buf, lirc_rx51_on(lirc_rx51); lirc_rx51->wbuf_index = 1; - pulse_timer_set_timeout(lirc_rx51, lirc_rx51->wbuf[0]); - + hrtimer_start(&lirc_rx51->timer, + ns_to_ktime(1000 * lirc_rx51->wbuf[0]), + HRTIMER_MODE_REL); /* * Don't return back to the userspace until the transfer has * finished @@ -359,14 +260,24 @@ static int lirc_rx51_open(struct inode *inode, struct file *file) if (test_and_set_bit(1, &lirc_rx51->device_is_open)) return -EBUSY; - return lirc_rx51_init_port(lirc_rx51); + lirc_rx51->pwm = pwm_get(lirc_rx51->dev, NULL); + if (IS_ERR(lirc_rx51->pwm)) { + int res = PTR_ERR(lirc_rx51->pwm); + + dev_err(lirc_rx51->dev, "pwm_get failed: %d\n", res); + return res; + } + + return 0; } static int lirc_rx51_release(struct inode *inode, struct file *file) { struct lirc_rx51 *lirc_rx51 = file->private_data; - lirc_rx51_free_port(lirc_rx51); + hrtimer_cancel(&lirc_rx51->timer); + lirc_rx51_off(lirc_rx51); + pwm_put(lirc_rx51->pwm); clear_bit(1, &lirc_rx51->device_is_open); @@ -441,11 +352,6 @@ static int lirc_rx51_probe(struct platform_device *dev) return -ENXIO; } - if (!lirc_rx51.pdata->dmtimer) { - dev_err(&dev->dev, "no dmtimer?\n"); - return -ENODEV; - } - pwm = pwm_get(&dev->dev, NULL); if (IS_ERR(pwm)) { int err = PTR_ERR(pwm); @@ -459,7 +365,9 @@ static int lirc_rx51_probe(struct platform_device *dev) lirc_rx51.freq = DIV_ROUND_CLOSEST(pwm_get_period(pwm), NSEC_PER_SEC); pwm_put(pwm); - lirc_rx51.dmtimer = lirc_rx51.pdata->dmtimer; + hrtimer_init(&lirc_rx51.timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL); + lirc_rx51.timer.function = lirc_rx51_timer_cb; + lirc_rx51.dev = &dev->dev; lirc_rx51_driver.dev = &dev->dev; lirc_rx51_driver.minor = lirc_register_driver(&lirc_rx51_driver); diff --git a/include/linux/platform_data/media/ir-rx51.h b/include/linux/platform_data/media/ir-rx51.h index 6acf22d..812d873 100644 --- a/include/linux/platform_data/media/ir-rx51.h +++ b/include/linux/platform_data/media/ir-rx51.h @@ -3,7 +3,6 @@ struct lirc_rx51_platform_data { int(*set_max_mpu_wakeup_lat)(struct device *dev, long t); - struct pwm_omap_dmtimer_pdata *dmtimer; }; #endif -- cgit v0.10.2 From 05cfb988a4d08f84abd2f474d8dd771e7528d975 Mon Sep 17 00:00:00 2001 From: Jon Hunter Date: Wed, 29 Jun 2016 10:17:47 +0100 Subject: soc/tegra: pmc: Initialise resets associated with a power partition When registering the Tegra power partitions with the generic PM domain framework, the current state of the each partition is checked and used as the default state for the partition. However, the state of each reset associated with the partition is not initialised and so it is possible that the state of the resets are not in the expected state. For example, if a partition is on, then the resets should be de-asserted and if the partition is off, the resets should be asserted. There have been cases where the bootloader has powered on a partition and only de-asserted some of the resets to some of the devices in the partition. This can cause accesses to these devices to hang the system when the kernel boots and attempts to probe these devices. Ideally, the driver for the device should ensure the reset has been de-asserted when probing, but the resets cannot be shared between the PMC driver (that needs to de-assert/assert the reset when turning the partition on or off) and another driver because we cannot ensure the reset is in the correct state. To ensure the resets are in the correct state, when using the generic PM domain framework, put each reset associated with the partition in the correct state (based upon the partition's current state) when obtaining the resets for a partition. Signed-off-by: Jon Hunter Signed-off-by: Thierry Reding diff --git a/drivers/soc/tegra/pmc.c b/drivers/soc/tegra/pmc.c index d135169..8a421a0 100644 --- a/drivers/soc/tegra/pmc.c +++ b/drivers/soc/tegra/pmc.c @@ -738,7 +738,7 @@ err: } static int tegra_powergate_of_get_resets(struct tegra_powergate *pg, - struct device_node *np) + struct device_node *np, bool off) { struct reset_control *rst; unsigned int i, count; @@ -758,6 +758,16 @@ static int tegra_powergate_of_get_resets(struct tegra_powergate *pg, err = PTR_ERR(pg->resets[i]); goto error; } + + if (off) + err = reset_control_assert(pg->resets[i]); + else + err = reset_control_deassert(pg->resets[i]); + + if (err) { + reset_control_put(pg->resets[i]); + goto error; + } } pg->num_resets = count; @@ -798,14 +808,14 @@ static void tegra_powergate_add(struct tegra_pmc *pmc, struct device_node *np) pg->genpd.power_on = tegra_genpd_power_on; pg->pmc = pmc; + off = !tegra_powergate_is_powered(pg->id); + if (tegra_powergate_of_get_clks(pg, np)) goto set_available; - if (tegra_powergate_of_get_resets(pg, np)) + if (tegra_powergate_of_get_resets(pg, np, off)) goto remove_clks; - off = !tegra_powergate_is_powered(pg->id); - pm_genpd_init(&pg->genpd, NULL, off); if (of_genpd_add_provider_simple(np, &pg->genpd)) -- cgit v0.10.2 From 403db2d21c8f8576d38211e5ec17d149c49e528b Mon Sep 17 00:00:00 2001 From: Jon Hunter Date: Tue, 28 Jun 2016 11:38:23 +0100 Subject: soc/tegra: pmc: Ensure powergate is available when powering on The function tegra_power_sequence_power_up() is a public function used to power on a partition. When this function is called, we do not check to see if the partition being powered up is valid/available. Fix this by checking to see that the partition is valid/available. Signed-off-by: Jon Hunter Signed-off-by: Thierry Reding diff --git a/drivers/soc/tegra/pmc.c b/drivers/soc/tegra/pmc.c index 8a421a0..52a9e97 100644 --- a/drivers/soc/tegra/pmc.c +++ b/drivers/soc/tegra/pmc.c @@ -549,6 +549,9 @@ int tegra_powergate_sequence_power_up(unsigned int id, struct clk *clk, struct tegra_powergate pg; int err; + if (!tegra_powergate_is_available(id)) + return -EINVAL; + pg.id = id; pg.clks = &clk; pg.num_clks = 1; -- cgit v0.10.2 From 11131895cd1ba1cf58a87c6acb574cbeec5818af Mon Sep 17 00:00:00 2001 From: Jon Hunter Date: Tue, 28 Jun 2016 11:38:24 +0100 Subject: soc/tegra: pmc: Fix early initialisation of PMC During early initialisation, the available power partitions for a given device is configured as well as the polarity of the PMC interrupt. Both of which should only be configured if there is a valid device node for the PMC device. This is because the soc data used for configuring the power partitions is only available if a device node for the PMC is found and the code to configure the interrupt polarity uses the device node pointer directly. Some early device-tree images may not have this device node and so fix this by ensuring the device node pointer is valid when configuring these items. Signed-off-by: Jon Hunter Signed-off-by: Thierry Reding diff --git a/drivers/soc/tegra/pmc.c b/drivers/soc/tegra/pmc.c index 52a9e97..2e031c4 100644 --- a/drivers/soc/tegra/pmc.c +++ b/drivers/soc/tegra/pmc.c @@ -1550,27 +1550,29 @@ static int __init tegra_pmc_early_init(void) return -ENXIO; } - /* Create a bit-map of the available and valid partitions */ - for (i = 0; i < pmc->soc->num_powergates; i++) - if (pmc->soc->powergates[i]) - set_bit(i, pmc->powergates_available); - mutex_init(&pmc->powergates_lock); - /* - * Invert the interrupt polarity if a PMC device tree node exists and - * contains the nvidia,invert-interrupt property. - */ - invert = of_property_read_bool(np, "nvidia,invert-interrupt"); + if (np) { + /* Create a bit-map of the available and valid partitions */ + for (i = 0; i < pmc->soc->num_powergates; i++) + if (pmc->soc->powergates[i]) + set_bit(i, pmc->powergates_available); - value = tegra_pmc_readl(PMC_CNTRL); + /* + * Invert the interrupt polarity if a PMC device tree node + * exists and contains the nvidia,invert-interrupt property. + */ + invert = of_property_read_bool(np, "nvidia,invert-interrupt"); - if (invert) - value |= PMC_CNTRL_INTR_POLARITY; - else - value &= ~PMC_CNTRL_INTR_POLARITY; + value = tegra_pmc_readl(PMC_CNTRL); - tegra_pmc_writel(value, PMC_CNTRL); + if (invert) + value |= PMC_CNTRL_INTR_POLARITY; + else + value &= ~PMC_CNTRL_INTR_POLARITY; + + tegra_pmc_writel(value, PMC_CNTRL); + } return 0; } -- cgit v0.10.2 From 718a2426e82076e4eddc2a041c8e944c9f9b3e61 Mon Sep 17 00:00:00 2001 From: Jon Hunter Date: Tue, 28 Jun 2016 11:38:25 +0100 Subject: soc/tegra: pmc: Don't populate SoC data until register space is mapped The public functions exported by the PMC driver use the presence of the SoC data pointer to determine if the PMC device is configured and the registers can be accessed. However, the SoC data is populated before the PMC register space is mapped and this opens a window where the SoC data pointer is valid but the register space has not yet been mapped which could lead to a crash. Furthermore, if the mapping of the PMC register space fails, then the SoC data pointer is not cleared and so would expose a larger window where a crash could occur. Fix this by initialising the SoC data pointer after the PMC register space has been mapped. Signed-off-by: Jon Hunter Signed-off-by: Thierry Reding diff --git a/drivers/soc/tegra/pmc.c b/drivers/soc/tegra/pmc.c index 2e031c4..ed2b2c8 100644 --- a/drivers/soc/tegra/pmc.c +++ b/drivers/soc/tegra/pmc.c @@ -1540,8 +1540,6 @@ static int __init tegra_pmc_early_init(void) pr_err("failed to get PMC registers\n"); return -ENXIO; } - - pmc->soc = match->data; } pmc->base = ioremap_nocache(regs.start, resource_size(®s)); @@ -1553,6 +1551,8 @@ static int __init tegra_pmc_early_init(void) mutex_init(&pmc->powergates_lock); if (np) { + pmc->soc = match->data; + /* Create a bit-map of the available and valid partitions */ for (i = 0; i < pmc->soc->num_powergates; i++) if (pmc->soc->powergates[i]) -- cgit v0.10.2 From 61fd284be8be06db1339ca4c9217f5a13b50074f Mon Sep 17 00:00:00 2001 From: Jon Hunter Date: Tue, 28 Jun 2016 11:38:26 +0100 Subject: soc/tegra: pmc: Ensure mutex is always initialised The mutex used by the PMC driver may not be initialised if early initialisation of the driver fails. If this does happen, then it could be possible for callers of the public PMC functions to still attempt to acquire the mutex. Fix this by initialising the mutex as soon as possible to ensure it will always be initialised. Signed-off-by: Jon Hunter Signed-off-by: Thierry Reding diff --git a/drivers/soc/tegra/pmc.c b/drivers/soc/tegra/pmc.c index ed2b2c8..483d546 100644 --- a/drivers/soc/tegra/pmc.c +++ b/drivers/soc/tegra/pmc.c @@ -1504,6 +1504,8 @@ static int __init tegra_pmc_early_init(void) bool invert; u32 value; + mutex_init(&pmc->powergates_lock); + np = of_find_matching_node_and_match(NULL, tegra_pmc_match, &match); if (!np) { /* @@ -1548,8 +1550,6 @@ static int __init tegra_pmc_early_init(void) return -ENXIO; } - mutex_init(&pmc->powergates_lock); - if (np) { pmc->soc = match->data; -- cgit v0.10.2 From b69a625826ecc563414aea5846e31b05184292f4 Mon Sep 17 00:00:00 2001 From: Jon Hunter Date: Tue, 28 Jun 2016 11:38:27 +0100 Subject: soc/tegra: pmc: Add missing of_node_put() Add missing of_node_put() in PMC early initialisation function to avoid leaking the device nodes. Signed-off-by: Jon Hunter [treding@nvidia.com: squash in a couple more of_node_put() calls] Signed-off-by: Thierry Reding diff --git a/drivers/soc/tegra/pmc.c b/drivers/soc/tegra/pmc.c index 483d546..48e1de2 100644 --- a/drivers/soc/tegra/pmc.c +++ b/drivers/soc/tegra/pmc.c @@ -1540,6 +1540,7 @@ static int __init tegra_pmc_early_init(void) */ if (of_address_to_resource(np, 0, ®s) < 0) { pr_err("failed to get PMC registers\n"); + of_node_put(np); return -ENXIO; } } @@ -1547,6 +1548,7 @@ static int __init tegra_pmc_early_init(void) pmc->base = ioremap_nocache(regs.start, resource_size(®s)); if (!pmc->base) { pr_err("failed to map PMC registers\n"); + of_node_put(np); return -ENXIO; } @@ -1572,6 +1574,8 @@ static int __init tegra_pmc_early_init(void) value &= ~PMC_CNTRL_INTR_POLARITY; tegra_pmc_writel(value, PMC_CNTRL); + + of_node_put(np); } return 0; -- cgit v0.10.2 From a83f1fc3f33930d01e579b9d4de92a045297b402 Mon Sep 17 00:00:00 2001 From: Jon Hunter Date: Tue, 28 Jun 2016 11:38:28 +0100 Subject: soc/tegra: pmc: Don't probe PMC if early initialisation fails Commit 0259f522e04f ('soc/tegra: pmc: Restore base address on probe failure') fixes an issue where the PMC base address pointer is not restored on probe failure. However, this fix creates another problem where if early initialisation of the PMC driver fails and an initial mapping for the PMC address space is not created, then when the PMC device is probed, the PMC base address pointer will not be valid and this will cause a crash when tegra_pmc_init() is called and attempts to access a register. Although the PMC address space is mapped a 2nd time during the probe and so this could be fixed by populating the base address pointer earlier during the probe, this adds more complexity to the code. Moreover, the PMC probe also assumes the the soc data pointer is also initialised when the device is probed and if not will also lead to a crash when calling tegra_pmc_init_tsense_reset(). Given that if the early initialisation does fail then something bad has happen, it seems acceptable to allow the PMC device probe to fail as well. Therefore, if the PMC base address pointer or soc data pointer are not valid when probing the PMC device, WARN and return an error. Fixes: 0259f522e04f ('soc/tegra: pmc: Restore base address on probe failure') Signed-off-by: Jon Hunter Signed-off-by: Thierry Reding diff --git a/drivers/soc/tegra/pmc.c b/drivers/soc/tegra/pmc.c index 48e1de2..1f70253 100644 --- a/drivers/soc/tegra/pmc.c +++ b/drivers/soc/tegra/pmc.c @@ -1228,6 +1228,14 @@ static int tegra_pmc_probe(struct platform_device *pdev) struct resource *res; int err; + /* + * Early initialisation should have configured an initial + * register mapping and setup the soc data pointer. If these + * are not valid then something went badly wrong! + */ + if (WARN_ON(!pmc->base || !pmc->soc)) + return -ENODEV; + err = tegra_pmc_parse_dt(pmc, pdev->dev.of_node); if (err < 0) return err; -- cgit v0.10.2 From da8f4b458944dccc371dba240cfa18c27b7b9802 Mon Sep 17 00:00:00 2001 From: Thierry Reding Date: Thu, 30 Jun 2016 12:12:55 +0200 Subject: soc/tegra: pmc: Use whitespace more consistently Use blank lines after blocks and before labels for consistency with the existing code in the file. Signed-off-by: Thierry Reding diff --git a/drivers/soc/tegra/pmc.c b/drivers/soc/tegra/pmc.c index 1f70253..2ccbdfe 100644 --- a/drivers/soc/tegra/pmc.c +++ b/drivers/soc/tegra/pmc.c @@ -408,6 +408,7 @@ static int tegra_powergate_power_up(struct tegra_powergate *pg, disable_clks: tegra_powergate_disable_clocks(pg); usleep_range(10, 20); + powergate_off: tegra_powergate_set(pg->id, false); @@ -445,6 +446,7 @@ assert_resets: usleep_range(10, 20); tegra_powergate_reset_deassert(pg); usleep_range(10, 20); + disable_clks: tegra_powergate_disable_clocks(pg); @@ -735,6 +737,7 @@ static int tegra_powergate_of_get_clks(struct tegra_powergate *pg, err: while (i--) clk_put(pg->clks[i]); + kfree(pg->clks); return err; @@ -780,6 +783,7 @@ static int tegra_powergate_of_get_resets(struct tegra_powergate *pg, error: while (i--) reset_control_put(pg->resets[i]); + kfree(pg->resets); return err; @@ -831,11 +835,13 @@ static void tegra_powergate_add(struct tegra_pmc *pmc, struct device_node *np) remove_resets: while (pg->num_resets--) reset_control_put(pg->resets[pg->num_resets]); + kfree(pg->resets); remove_clks: while (pg->num_clks--) clk_put(pg->clks[pg->num_clks]); + kfree(pg->clks); set_available: -- cgit v0.10.2 From c2710ac9f5a8dab4f9bb080713452fe6286ca83d Mon Sep 17 00:00:00 2001 From: Jon Hunter Date: Thu, 30 Jun 2016 11:56:24 +0100 Subject: soc/tegra: pmc: Add specific error messages When initialising a powergate, only a single error message is shown if the initialisation fails. Add more error messages to give specific details of what failed if the initialisation failed and remove the generic failure message. Signed-off-by: Jon Hunter Signed-off-by: Thierry Reding diff --git a/drivers/soc/tegra/pmc.c b/drivers/soc/tegra/pmc.c index 2ccbdfe..ecc1ec0 100644 --- a/drivers/soc/tegra/pmc.c +++ b/drivers/soc/tegra/pmc.c @@ -792,16 +792,19 @@ error: static void tegra_powergate_add(struct tegra_pmc *pmc, struct device_node *np) { struct tegra_powergate *pg; + int id, err; bool off; - int id; pg = kzalloc(sizeof(*pg), GFP_KERNEL); if (!pg) - goto error; + return; id = tegra_powergate_lookup(pmc, np->name); - if (id < 0) + if (id < 0) { + dev_err(pmc->dev, "powergate lookup failed for %s: %d\n", + np->name, id); goto free_mem; + } /* * Clear the bit for this powergate so it cannot be managed @@ -817,16 +820,28 @@ static void tegra_powergate_add(struct tegra_pmc *pmc, struct device_node *np) off = !tegra_powergate_is_powered(pg->id); - if (tegra_powergate_of_get_clks(pg, np)) + err = tegra_powergate_of_get_clks(pg, np); + if (err < 0) { + dev_err(pmc->dev, "failed to get clocks for %s: %d\n", + np->name, err); goto set_available; + } - if (tegra_powergate_of_get_resets(pg, np, off)) + err = tegra_powergate_of_get_resets(pg, np, off); + if (err < 0) { + dev_err(pmc->dev, "failed to get resets for %s: %d\n", + np->name, err); goto remove_clks; + } pm_genpd_init(&pg->genpd, NULL, off); - if (of_genpd_add_provider_simple(np, &pg->genpd)) + err = of_genpd_add_provider_simple(np, &pg->genpd); + if (err < 0) { + dev_err(pmc->dev, "failed to add genpd provider for %s: %d\n", + np->name, err); goto remove_resets; + } dev_dbg(pmc->dev, "added power domain %s\n", pg->genpd.name); @@ -849,9 +864,6 @@ set_available: free_mem: kfree(pg); - -error: - dev_err(pmc->dev, "failed to create power domain for %s\n", np->name); } static void tegra_powergate_init(struct tegra_pmc *pmc) -- cgit v0.10.2 From e2d17960532d925a202682ce72379c6055068ca4 Mon Sep 17 00:00:00 2001 From: Jon Hunter Date: Thu, 30 Jun 2016 11:56:25 +0100 Subject: soc/tegra: pmc: Initialise power partitions early If CONFIG_PM_GENERIC_DOMAINS is not enabled, then power partitions associated with a device will not be enabled automatically by the PM core when the device is in use. To avoid situations where a device in a power partition is to be used but the partition is not enabled, initialise the power partitions for Tegra early in the boot process and if CONFIG_PM_GENERIC_DOMAINS is not enabled, then power on all partitions defined in the device-tree blob. Note that if CONFIG_PM_GENERIC_DOMAINS is not enabled, after the partitions are turned on, the clocks and resets used as part of the sequence for turning on the partition are released again as they are no longer needed by the PMC driver. Another benefit of this is that this avoids any issues of sharing resets between the PMC driver and other device drivers that may wish to independently control a particular reset. Signed-off-by: Jon Hunter Signed-off-by: Thierry Reding diff --git a/drivers/soc/tegra/pmc.c b/drivers/soc/tegra/pmc.c index ecc1ec0..7199726 100644 --- a/drivers/soc/tegra/pmc.c +++ b/drivers/soc/tegra/pmc.c @@ -834,6 +834,9 @@ static void tegra_powergate_add(struct tegra_pmc *pmc, struct device_node *np) goto remove_clks; } + if (!IS_ENABLED(CONFIG_PM_GENERIC_DOMAINS)) + goto power_on_cleanup; + pm_genpd_init(&pg->genpd, NULL, off); err = of_genpd_add_provider_simple(np, &pg->genpd); @@ -847,6 +850,10 @@ static void tegra_powergate_add(struct tegra_pmc *pmc, struct device_node *np) return; +power_on_cleanup: + if (off) + WARN_ON(tegra_powergate_power_up(pg, true)); + remove_resets: while (pg->num_resets--) reset_control_put(pg->resets[pg->num_resets]); @@ -866,11 +873,18 @@ free_mem: kfree(pg); } -static void tegra_powergate_init(struct tegra_pmc *pmc) +static void tegra_powergate_init(struct tegra_pmc *pmc, + struct device_node *parent) { struct device_node *np, *child; + unsigned int i; - np = of_get_child_by_name(pmc->dev->of_node, "powergates"); + /* Create a bitmap of the available and valid partitions */ + for (i = 0; i < pmc->soc->num_powergates; i++) + if (pmc->soc->powergates[i]) + set_bit(i, pmc->powergates_available); + + np = of_get_child_by_name(parent, "powergates"); if (!np) return; @@ -1291,8 +1305,6 @@ static int tegra_pmc_probe(struct platform_device *pdev) return err; } - tegra_powergate_init(pmc); - mutex_lock(&pmc->powergates_lock); iounmap(pmc->base); pmc->base = base; @@ -1526,7 +1538,6 @@ static int __init tegra_pmc_early_init(void) const struct of_device_id *match; struct device_node *np; struct resource regs; - unsigned int i; bool invert; u32 value; @@ -1581,10 +1592,7 @@ static int __init tegra_pmc_early_init(void) if (np) { pmc->soc = match->data; - /* Create a bit-map of the available and valid partitions */ - for (i = 0; i < pmc->soc->num_powergates; i++) - if (pmc->soc->powergates[i]) - set_bit(i, pmc->powergates_available); + tegra_powergate_init(pmc, np); /* * Invert the interrupt polarity if a PMC device tree node -- cgit v0.10.2 From 8df127456f29c719eb2b458018298b1980f1218e Mon Sep 17 00:00:00 2001 From: Jon Hunter Date: Thu, 30 Jun 2016 11:56:26 +0100 Subject: soc/tegra: pmc: Enable XUSB partitions on boot The Tegra XHCI driver does not currently manage the Tegra XUSB power partitions and so it these partitions have not been enabled by the bootloader then the system will crash when probing the XHCI device. While proper support for managing the power partitions is being developed to the XHCI driver for Tegra, for now power on all the XUSB partitions for USB host and super-speed on boot if the XHCI driver is enabled. Signed-off-by: Jon Hunter Signed-off-by: Thierry Reding diff --git a/drivers/soc/tegra/pmc.c b/drivers/soc/tegra/pmc.c index 7199726..71c834f 100644 --- a/drivers/soc/tegra/pmc.c +++ b/drivers/soc/tegra/pmc.c @@ -837,6 +837,18 @@ static void tegra_powergate_add(struct tegra_pmc *pmc, struct device_node *np) if (!IS_ENABLED(CONFIG_PM_GENERIC_DOMAINS)) goto power_on_cleanup; + /* + * FIXME: If XHCI is enabled for Tegra, then power-up the XUSB + * host and super-speed partitions. Once the XHCI driver + * manages the partitions itself this code can be removed. Note + * that we don't register these partitions with the genpd core + * to avoid it from powering down the partitions as they appear + * to be unused. + */ + if (IS_ENABLED(CONFIG_USB_XHCI_TEGRA) && + (id == TEGRA_POWERGATE_XUSBA || id == TEGRA_POWERGATE_XUSBC)) + goto power_on_cleanup; + pm_genpd_init(&pg->genpd, NULL, off); err = of_genpd_add_provider_simple(np, &pg->genpd); -- cgit v0.10.2 From 8582f6d158c8824042432f581d49ef478d5cb238 Mon Sep 17 00:00:00 2001 From: Thierry Reding Date: Wed, 29 Jun 2016 16:11:32 +0200 Subject: soc/tegra: Stub out PCIe IRQ workaround on 64-bit ARM The PCIe host controller found on Tegra20 has a hardware bug that causes PCIe interrupts to get lost when LP2 is enabled. Stub out the workaround on 64-bit ARM because none of the more recent Tegra SoC generations seem to have this bug anymore. Signed-off-by: Thierry Reding diff --git a/include/soc/tegra/cpuidle.h b/include/soc/tegra/cpuidle.h index ea04f42..1fae9c7 100644 --- a/include/soc/tegra/cpuidle.h +++ b/include/soc/tegra/cpuidle.h @@ -14,7 +14,7 @@ #ifndef __SOC_TEGRA_CPUIDLE_H__ #define __SOC_TEGRA_CPUIDLE_H__ -#ifdef CONFIG_CPU_IDLE +#if defined(CONFIG_ARM) && defined(CONFIG_CPU_IDLE) void tegra_cpuidle_pcie_irqs_in_use(void); #else static inline void tegra_cpuidle_pcie_irqs_in_use(void) -- cgit v0.10.2 From 894b68e1941fa572a97dc059865e5669ecaa3b4a Mon Sep 17 00:00:00 2001 From: Jon Hunter Date: Fri, 17 Jun 2016 13:40:31 +0100 Subject: dt-bindings: bus: Add documentation for Tegra210 ACONNECT Add binding documentation for the Tegra ACONNECT bus that is part of the Audio Processing Engine (APE) on Tegra210. The ACONNECT bus is used to access devices within the APE subsystem. The APE is located in a separate power domain and so accesses made to the ACONNECT require the power domain to be enabled as well as some platform specific clocks. Signed-off-by: Jon Hunter Acked-by: Rob Herring Signed-off-by: Thierry Reding diff --git a/Documentation/devicetree/bindings/bus/nvidia,tegra210-aconnect.txt b/Documentation/devicetree/bindings/bus/nvidia,tegra210-aconnect.txt new file mode 100644 index 0000000..7ff13be --- /dev/null +++ b/Documentation/devicetree/bindings/bus/nvidia,tegra210-aconnect.txt @@ -0,0 +1,45 @@ +NVIDIA Tegra ACONNECT Bus + +The Tegra ACONNECT bus is an AXI switch which is used to connnect various +components inside the Audio Processing Engine (APE). All CPU accesses to +the APE subsystem go through the ACONNECT via an APB to AXI wrapper. + +Required properties: +- compatible: Must be "nvidia,tegra210-aconnect". +- clocks: Must contain the entries for the APE clock (TEGRA210_CLK_APE), + and APE interface clock (TEGRA210_CLK_APB2APE). +- clock-names: Must contain the names "ape" and "apb2ape" for the corresponding + 'clocks' entries. +- power-domains: Must contain a phandle that points to the audio powergate + (namely 'aud') for Tegra210. +- #address-cells: The number of cells used to represent physical base addresses + in the aconnect address space. Should be 1. +- #size-cells: The number of cells used to represent the size of an address + range in the aconnect address space. Should be 1. +- ranges: Mapping of the aconnect address space to the CPU address space. + +All devices accessed via the ACONNNECT are described by child-nodes. + +Example: + + aconnect@702c0000 { + compatible = "nvidia,tegra210-aconnect"; + clocks = <&tegra_car TEGRA210_CLK_APE>, + <&tegra_car TEGRA210_CLK_APB2APE>; + clock-names = "ape", "apb2ape"; + power-domains = <&pd_audio>; + + #address-cells = <1>; + #size-cells = <1>; + ranges = <0x702c0000 0x0 0x702c0000 0x00040000>; + + status = "disabled"; + + child1 { + ... + }; + + child2 { + ... + }; + }; -- cgit v0.10.2 From 46a88534afb596eb4d9de07ddde778d0e9aa0e3a Mon Sep 17 00:00:00 2001 From: Jon Hunter Date: Fri, 17 Jun 2016 13:40:32 +0100 Subject: bus: Add support for Tegra ACONNECT Add a bus driver for the Tegra ACONNECT which is used to interface to various devices within the Audio Processing Engine (APE). The purpose of the bus driver is to register child devices that are accessed via the ACONNECT bus and through the device parent child relationship, ensure that the appropriate power domain and clocks are enabled for the ACONNECT when any of the child devices are active. Hence, the ACONNECT driver simply enables runtime-pm for the ACONNECT device so that when a child device is resumed, it will enable the power-domain and clocks associated with the ACONNECT. Signed-off-by: Jon Hunter Signed-off-by: Thierry Reding diff --git a/drivers/bus/Kconfig b/drivers/bus/Kconfig index c5a7de9b..3b205e2 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 ccff007..ac84cc4 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/tegra-aconnect.c b/drivers/bus/tegra-aconnect.c new file mode 100644 index 0000000..7e4104b --- /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 +#include +#include +#include +#include +#include + +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 "); +MODULE_LICENSE("GPL v2"); -- cgit v0.10.2 From 72d43419400f31d9eb634466446e3ceddce30be7 Mon Sep 17 00:00:00 2001 From: Andy Gross Date: Wed, 29 Jun 2016 15:28:29 -0500 Subject: firmware: qcom_scm: Add missing is_available API Add back function that was dropped when reworking the SCM code. Signed-off-by: Andy Gross diff --git a/drivers/firmware/qcom_scm.c b/drivers/firmware/qcom_scm.c index 84330c5..89c3775 100644 --- a/drivers/firmware/qcom_scm.c +++ b/drivers/firmware/qcom_scm.c @@ -308,6 +308,14 @@ static const struct reset_control_ops qcom_scm_pas_reset_ops = { .deassert = qcom_scm_pas_reset_deassert, }; +/** + * qcom_scm_is_available() - Checks if SCM is available + */ +bool qcom_scm_is_available(void) +{ + return !!__scm; +} +EXPORT_SYMBOL(qcom_scm_is_available); static int qcom_scm_probe(struct platform_device *pdev) { -- cgit v0.10.2 From 6c8e99d874c96200ce985d19b407d49ed3306226 Mon Sep 17 00:00:00 2001 From: Andy Gross Date: Fri, 1 Jul 2016 23:04:03 -0500 Subject: firmware: qcom: scm: Change initcall to subsys The patch changes the initcall for SCM to use subsys_initcall instead of arch_initcall. This corrects the order so that we don't probe defer when trying to get clks which causes issues later when the spm driver makes calls to qcom_set_warm_boot_addr(). The order became an issue due to the changes to use arch_initcall_sync for of_platform_default_populate_init(). Signed-off-by: Andy Gross diff --git a/drivers/firmware/qcom_scm.c b/drivers/firmware/qcom_scm.c index 89c3775..e64a501 100644 --- a/drivers/firmware/qcom_scm.c +++ b/drivers/firmware/qcom_scm.c @@ -415,7 +415,7 @@ static int __init qcom_scm_init(void) return platform_driver_register(&qcom_scm_driver); } -arch_initcall(qcom_scm_init); +subsys_initcall(qcom_scm_init); static void __exit qcom_scm_exit(void) { -- cgit v0.10.2 From 24c4b47829a16f0f335621b3dee1a437297068d0 Mon Sep 17 00:00:00 2001 From: Seung-Woo Kim Date: Tue, 5 Jul 2016 20:40:17 +0900 Subject: memory: samsung: exynos-srom: Fix wrong count of registers This patch fixes wrong count of array for SROM registers from probe function. Signed-off-by: Seung-Woo Kim Reviewed-by: Chanwoo Choi Signed-off-by: Krzysztof Kozlowski diff --git a/drivers/memory/samsung/exynos-srom.c b/drivers/memory/samsung/exynos-srom.c index 067f533..bf827a6 100644 --- a/drivers/memory/samsung/exynos-srom.c +++ b/drivers/memory/samsung/exynos-srom.c @@ -134,7 +134,7 @@ static int exynos_srom_probe(struct platform_device *pdev) platform_set_drvdata(pdev, srom); srom->reg_offset = exynos_srom_alloc_reg_dump(exynos_srom_offsets, - sizeof(exynos_srom_offsets)); + ARRAY_SIZE(exynos_srom_offsets)); if (!srom->reg_offset) { iounmap(srom->reg_base); return -ENOMEM; -- cgit v0.10.2 From aec6341e2ac76ea8703642e83535f216b8866162 Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Wed, 6 Jul 2016 09:03:46 +0200 Subject: soc: samsung: pmu: Constify arrays with PMU data Arrays storing values for Power Management Unit for given sleep mode can be made const. Signed-off-by: Krzysztof Kozlowski diff --git a/drivers/soc/samsung/exynos3250-pmu.c b/drivers/soc/samsung/exynos3250-pmu.c index 20b3ab8..af2f54e 100644 --- a/drivers/soc/samsung/exynos3250-pmu.c +++ b/drivers/soc/samsung/exynos3250-pmu.c @@ -14,7 +14,7 @@ #include "exynos-pmu.h" -static struct exynos_pmu_conf exynos3250_pmu_config[] = { +static const struct exynos_pmu_conf exynos3250_pmu_config[] = { /* { .offset = offset, .val = { AFTR, W-AFTR, SLEEP } */ { EXYNOS3_ARM_CORE0_SYS_PWR_REG, { 0x0, 0x0, 0x2} }, { EXYNOS3_DIS_IRQ_ARM_CORE0_LOCAL_SYS_PWR_REG, { 0x0, 0x0, 0x0} }, diff --git a/drivers/soc/samsung/exynos5420-pmu.c b/drivers/soc/samsung/exynos5420-pmu.c index b962fb6..3f2c641 100644 --- a/drivers/soc/samsung/exynos5420-pmu.c +++ b/drivers/soc/samsung/exynos5420-pmu.c @@ -17,7 +17,7 @@ #include "exynos-pmu.h" -static struct exynos_pmu_conf exynos5420_pmu_config[] = { +static const struct exynos_pmu_conf exynos5420_pmu_config[] = { /* { .offset = offset, .val = { AFTR, LPA, SLEEP } */ { EXYNOS5_ARM_CORE0_SYS_PWR_REG, { 0x0, 0x0, 0x0} }, { EXYNOS5_DIS_IRQ_ARM_CORE0_LOCAL_SYS_PWR_REG, { 0x0, 0x0, 0x0} }, -- cgit v0.10.2 From 893b77980c84cfcf2e0caff9740080c351433405 Mon Sep 17 00:00:00 2001 From: Alexander Shiyan Date: Sat, 4 Jun 2016 10:09:54 +0300 Subject: clk: clps711x: Changing the compatibility string to match with the smallest supported chip This patch changes the compatibility string to match with the smallest supported chip (EP7209). Since the DT-support for this CPU is not yet announced, this change is safe. Signed-off-by: Alexander Shiyan Signed-off-by: Arnd Bergmann diff --git a/Documentation/devicetree/bindings/clock/clps711x-clock.txt b/Documentation/devicetree/bindings/clock/clps711x-clock.txt index ce5a747..f1bd53f 100644 --- a/Documentation/devicetree/bindings/clock/clps711x-clock.txt +++ b/Documentation/devicetree/bindings/clock/clps711x-clock.txt @@ -1,7 +1,7 @@ * Clock bindings for the Cirrus Logic CLPS711X CPUs Required properties: -- compatible : Shall contain "cirrus,clps711x-clk". +- compatible : Shall contain "cirrus,ep7209-clk". - reg : Address of the internal register set. - startup-frequency: Factory set CPU startup frequency in HZ. - #clock-cells : Should be <1>. @@ -13,7 +13,7 @@ for the full list of CLPS711X clock IDs. Example: clks: clks@80000000 { #clock-cells = <1>; - compatible = "cirrus,ep7312-clk", "cirrus,clps711x-clk"; + compatible = "cirrus,ep7312-clk", "cirrus,ep7209-clk"; reg = <0x80000000 0xc000>; startup-frequency = <73728000>; }; diff --git a/drivers/clk/clk-clps711x.c b/drivers/clk/clk-clps711x.c index 1f60b02..adaf109 100644 --- a/drivers/clk/clk-clps711x.c +++ b/drivers/clk/clk-clps711x.c @@ -184,5 +184,5 @@ static void __init clps711x_clk_init_dt(struct device_node *np) of_clk_add_provider(np, of_clk_src_onecell_get, &clps711x_clk->clk_data); } -CLK_OF_DECLARE(clps711x, "cirrus,clps711x-clk", clps711x_clk_init_dt); +CLK_OF_DECLARE(clps711x, "cirrus,ep7209-clk", clps711x_clk_init_dt); #endif -- cgit v0.10.2 From d64c01058cf769824f71787058aa5211afa81e0d Mon Sep 17 00:00:00 2001 From: Alexander Shiyan Date: Sat, 4 Jun 2016 10:09:55 +0300 Subject: clocksource: clps711x: Changing the compatibility string to match with the smallest supported chip This patch changes the compatibility string to match with the smallest supported chip (EP7209). Since the DT-support for this CPU is not yet announced, this change is safe. Signed-off-by: Alexander Shiyan Acked-by: Daniel Lezcano Signed-off-by: Arnd Bergmann diff --git a/Documentation/devicetree/bindings/timer/cirrus,clps711x-timer.txt b/Documentation/devicetree/bindings/timer/cirrus,clps711x-timer.txt index cd55b52..d4c62e7 100644 --- a/Documentation/devicetree/bindings/timer/cirrus,clps711x-timer.txt +++ b/Documentation/devicetree/bindings/timer/cirrus,clps711x-timer.txt @@ -1,7 +1,7 @@ * Cirrus Logic CLPS711X Timer Counter Required properties: -- compatible: Shall contain "cirrus,clps711x-timer". +- compatible: Shall contain "cirrus,ep7209-timer". - reg : Address and length of the register set. - interrupts: The interrupt number of the timer. - clocks : phandle of timer reference clock. @@ -15,14 +15,14 @@ Example: }; timer1: timer@80000300 { - compatible = "cirrus,ep7312-timer", "cirrus,clps711x-timer"; + compatible = "cirrus,ep7312-timer", "cirrus,ep7209-timer"; reg = <0x80000300 0x4>; interrupts = <8>; clocks = <&clks 5>; }; timer2: timer@80000340 { - compatible = "cirrus,ep7312-timer", "cirrus,clps711x-timer"; + compatible = "cirrus,ep7312-timer", "cirrus,ep7209-timer"; reg = <0x80000340 0x4>; interrupts = <9>; clocks = <&clks 6>; diff --git a/drivers/clocksource/clps711x-timer.c b/drivers/clocksource/clps711x-timer.c index cdd86e3..7c65f9e 100644 --- a/drivers/clocksource/clps711x-timer.c +++ b/drivers/clocksource/clps711x-timer.c @@ -121,5 +121,5 @@ static void __init clps711x_timer_init(struct device_node *np) break; } } -CLOCKSOURCE_OF_DECLARE(clps711x, "cirrus,clps711x-timer", clps711x_timer_init); +CLOCKSOURCE_OF_DECLARE(clps711x, "cirrus,ep7209-timer", clps711x_timer_init); #endif -- cgit v0.10.2 From 4b4d9949580ef18fd3acc38f1419bca522b5b22e Mon Sep 17 00:00:00 2001 From: Alexander Shiyan Date: Sat, 4 Jun 2016 10:09:56 +0300 Subject: irqchip: clps711x: Changing the compatibility string to match with the smallest supported chip This patch changes the compatibility string to match with the smallest supported chip (EP7209). Since the DT-support for this CPU is not yet announced, this change is safe. Signed-off-by: Alexander Shiyan Signed-off-by: Arnd Bergmann diff --git a/Documentation/devicetree/bindings/interrupt-controller/cirrus,clps711x-intc.txt b/Documentation/devicetree/bindings/interrupt-controller/cirrus,clps711x-intc.txt index 759339c..969b458 100644 --- a/Documentation/devicetree/bindings/interrupt-controller/cirrus,clps711x-intc.txt +++ b/Documentation/devicetree/bindings/interrupt-controller/cirrus,clps711x-intc.txt @@ -2,7 +2,7 @@ Cirrus Logic CLPS711X Interrupt Controller Required properties: -- compatible: Should be "cirrus,clps711x-intc". +- compatible: Should be "cirrus,ep7209-intc". - reg: Specifies base physical address of the registers set. - interrupt-controller: Identifies the node as an interrupt controller. - #interrupt-cells: Specifies the number of cells needed to encode an @@ -34,7 +34,7 @@ ID Name Description Example: intc: interrupt-controller { - compatible = "cirrus,clps711x-intc"; + compatible = "cirrus,ep7312-intc", "cirrus,ep7209-intc"; reg = <0x80000000 0x4000>; interrupt-controller; #interrupt-cells = <1>; diff --git a/drivers/irqchip/irq-clps711x.c b/drivers/irqchip/irq-clps711x.c index 2223b3f..f913f4d 100644 --- a/drivers/irqchip/irq-clps711x.c +++ b/drivers/irqchip/irq-clps711x.c @@ -234,5 +234,5 @@ static int __init clps711x_intc_init_dt(struct device_node *np, return _clps711x_intc_init(np, res.start, resource_size(&res)); } -IRQCHIP_DECLARE(clps711x, "cirrus,clps711x-intc", clps711x_intc_init_dt); +IRQCHIP_DECLARE(clps711x, "cirrus,ep7209-intc", clps711x_intc_init_dt); #endif -- cgit v0.10.2 From d305345c9dc7360279b6171b74ea3f1913cf53d5 Mon Sep 17 00:00:00 2001 From: Alexander Shiyan Date: Sat, 4 Jun 2016 10:09:57 +0300 Subject: serial: clps711x: Changing the compatibility string to match with the smallest supported chip This patch changes the compatibility string to match with the smallest supported chip (EP7209). Since the DT-support for this CPU is not yet announced, this change is safe. Signed-off-by: Alexander Shiyan Signed-off-by: Arnd Bergmann diff --git a/Documentation/devicetree/bindings/serial/cirrus,clps711x-uart.txt b/Documentation/devicetree/bindings/serial/cirrus,clps711x-uart.txt index caaeb25..07013fa 100644 --- a/Documentation/devicetree/bindings/serial/cirrus,clps711x-uart.txt +++ b/Documentation/devicetree/bindings/serial/cirrus,clps711x-uart.txt @@ -1,7 +1,7 @@ * Cirrus Logic CLPS711X Universal Asynchronous Receiver/Transmitter (UART) Required properties: -- compatible: Should be "cirrus,clps711x-uart". +- compatible: Should be "cirrus,ep7209-uart". - reg: Address and length of the register set for the device. - interrupts: Should contain UART TX and RX interrupt. - clocks: Should contain UART core clock number. @@ -20,7 +20,7 @@ Example: }; uart1: uart@80000480 { - compatible = "cirrus,clps711x-uart"; + compatible = "cirrus,ep7312-uart","cirrus,ep7209-uart"; reg = <0x80000480 0x80>; interrupts = <12 13>; clocks = <&clks 11>; diff --git a/drivers/tty/serial/clps711x.c b/drivers/tty/serial/clps711x.c index 5beafd2..ac13286 100644 --- a/drivers/tty/serial/clps711x.c +++ b/drivers/tty/serial/clps711x.c @@ -539,7 +539,7 @@ static int uart_clps711x_remove(struct platform_device *pdev) } static const struct of_device_id __maybe_unused clps711x_uart_dt_ids[] = { - { .compatible = "cirrus,clps711x-uart", }, + { .compatible = "cirrus,ep7209-uart", }, { } }; MODULE_DEVICE_TABLE(of, clps711x_uart_dt_ids); -- cgit v0.10.2 From ba60ae1dc091b86d4bc74d23c85fc091a632fba7 Mon Sep 17 00:00:00 2001 From: Alexander Shiyan Date: Sat, 4 Jun 2016 10:09:58 +0300 Subject: pwm: clps711x: Changing the compatibility string to match with the smallest supported chip This patch changes the compatibility string to match with the smallest supported chip (EP7209). Since the DT-support for this CPU is not yet announced, this change is safe. Signed-off-by: Alexander Shiyan Signed-off-by: Arnd Bergmann diff --git a/Documentation/devicetree/bindings/pwm/cirrus,clps711x-pwm.txt b/Documentation/devicetree/bindings/pwm/cirrus,clps711x-pwm.txt index a183db4..c0b2028 100644 --- a/Documentation/devicetree/bindings/pwm/cirrus,clps711x-pwm.txt +++ b/Documentation/devicetree/bindings/pwm/cirrus,clps711x-pwm.txt @@ -1,15 +1,14 @@ * Cirris Logic CLPS711X PWM controller Required properties: -- compatible: Shall contain "cirrus,clps711x-pwm". +- compatible: Shall contain "cirrus,ep7209-pwm". - reg: Physical base address and length of the controller's registers. - clocks: phandle + clock specifier pair of the PWM reference clock. - #pwm-cells: Should be 1. The cell specifies the index of the channel. Example: pwm: pwm@80000400 { - compatible = "cirrus,ep7312-pwm", - "cirrus,clps711x-pwm"; + compatible = "cirrus,ep7312-pwm", "cirrus,ep7209-pwm"; reg = <0x80000400 0x4>; clocks = <&clks 8>; #pwm-cells = <1>; diff --git a/drivers/pwm/pwm-clps711x.c b/drivers/pwm/pwm-clps711x.c index 7d33542..26ec24e 100644 --- a/drivers/pwm/pwm-clps711x.c +++ b/drivers/pwm/pwm-clps711x.c @@ -155,7 +155,7 @@ static int clps711x_pwm_remove(struct platform_device *pdev) } static const struct of_device_id __maybe_unused clps711x_pwm_dt_ids[] = { - { .compatible = "cirrus,clps711x-pwm", }, + { .compatible = "cirrus,ep7209-pwm", }, { } }; MODULE_DEVICE_TABLE(of, clps711x_pwm_dt_ids); -- cgit v0.10.2 From e7c38f221fb256b9ec452531416cca06ebb6d245 Mon Sep 17 00:00:00 2001 From: Alexander Shiyan Date: Sat, 4 Jun 2016 10:10:01 +0300 Subject: input: clps711x-keypad: Changing the compatibility string to match with the smallest supported chip This patch changes the compatibility string to match with the smallest supported chip (EP7209). Since the DT-support for this CPU is not yet announced, this change is safe. Signed-off-by: Alexander Shiyan Signed-off-by: Arnd Bergmann diff --git a/Documentation/devicetree/bindings/input/clps711x-keypad.txt b/Documentation/devicetree/bindings/input/clps711x-keypad.txt index e68d2bb..3eed881 100644 --- a/Documentation/devicetree/bindings/input/clps711x-keypad.txt +++ b/Documentation/devicetree/bindings/input/clps711x-keypad.txt @@ -1,7 +1,7 @@ * Cirrus Logic CLPS711X matrix keypad device tree bindings Required Properties: -- compatible: Shall contain "cirrus,clps711x-keypad". +- compatible: Shall contain "cirrus,ep7209-keypad". - row-gpios: List of GPIOs used as row lines. - poll-interval: Poll interval time in milliseconds. - linux,keymap: The definition can be found at @@ -12,7 +12,7 @@ Optional Properties: Example: keypad { - compatible = "cirrus,ep7312-keypad", "cirrus,clps711x-keypad"; + compatible = "cirrus,ep7312-keypad", "cirrus,ep7209-keypad"; autorepeat; poll-interval = <120>; row-gpios = <&porta 0 0>, diff --git a/drivers/input/keyboard/clps711x-keypad.c b/drivers/input/keyboard/clps711x-keypad.c index b637f1a..997e3e9 100644 --- a/drivers/input/keyboard/clps711x-keypad.c +++ b/drivers/input/keyboard/clps711x-keypad.c @@ -101,7 +101,7 @@ static int clps711x_keypad_probe(struct platform_device *pdev) return -ENOMEM; priv->syscon = - syscon_regmap_lookup_by_compatible("cirrus,clps711x-syscon1"); + syscon_regmap_lookup_by_compatible("cirrus,ep7209-syscon1"); if (IS_ERR(priv->syscon)) return PTR_ERR(priv->syscon); @@ -181,7 +181,7 @@ static int clps711x_keypad_remove(struct platform_device *pdev) } static const struct of_device_id clps711x_keypad_of_match[] = { - { .compatible = "cirrus,clps711x-keypad", }, + { .compatible = "cirrus,ep7209-keypad", }, { } }; MODULE_DEVICE_TABLE(of, clps711x_keypad_of_match); -- cgit v0.10.2 From 564c9741aa508beba23f3b05b7235775a51bae00 Mon Sep 17 00:00:00 2001 From: Alexander Shiyan Date: Sat, 4 Jun 2016 10:10:02 +0300 Subject: video: clps711x-fb: Changing the compatibility string to match with the smallest supported chip This patch changes the compatibility string to match with the smallest supported chip (EP7209). Since the DT-support for this CPU is not yet announced, this change is safe. Signed-off-by: Alexander Shiyan Signed-off-by: Arnd Bergmann diff --git a/Documentation/devicetree/bindings/display/cirrus,clps711x-fb.txt b/Documentation/devicetree/bindings/display/cirrus,clps711x-fb.txt index d685be8..e9c6574 100644 --- a/Documentation/devicetree/bindings/display/cirrus,clps711x-fb.txt +++ b/Documentation/devicetree/bindings/display/cirrus,clps711x-fb.txt @@ -1,7 +1,7 @@ * Currus Logic CLPS711X Framebuffer Required properties: -- compatible: Shall contain "cirrus,clps711x-fb". +- compatible: Shall contain "cirrus,ep7209-fb". - reg : Physical base address and length of the controller's registers + location and size of the framebuffer memory. - clocks : phandle + clock specifier pair of the FB reference clock. @@ -18,7 +18,7 @@ Optional properties: Example: fb: fb@800002c0 { - compatible = "cirrus,ep7312-fb", "cirrus,clps711x-fb"; + compatible = "cirrus,ep7312-fb", "cirrus,ep7209-fb"; reg = <0x800002c0 0xd44>, <0x60000000 0xc000>; clocks = <&clks 2>; lcd-supply = <®5v0>; diff --git a/drivers/video/fbdev/clps711x-fb.c b/drivers/video/fbdev/clps711x-fb.c index 649b32f..ff56107 100644 --- a/drivers/video/fbdev/clps711x-fb.c +++ b/drivers/video/fbdev/clps711x-fb.c @@ -273,7 +273,7 @@ static int clps711x_fb_probe(struct platform_device *pdev) } cfb->syscon = - syscon_regmap_lookup_by_compatible("cirrus,clps711x-syscon1"); + syscon_regmap_lookup_by_compatible("cirrus,ep7209-syscon1"); if (IS_ERR(cfb->syscon)) { ret = PTR_ERR(cfb->syscon); goto out_fb_release; @@ -376,7 +376,7 @@ static int clps711x_fb_remove(struct platform_device *pdev) } static const struct of_device_id clps711x_fb_dt_ids[] = { - { .compatible = "cirrus,clps711x-fb", }, + { .compatible = "cirrus,ep7209-fb", }, { } }; MODULE_DEVICE_TABLE(of, clps711x_fb_dt_ids); -- cgit v0.10.2 From e517dfe67480fcca90831ac02e184859200bbaf6 Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Wed, 6 Jul 2016 14:49:46 +0200 Subject: firmware: scpi: add CONFIG_OF dependency We get a harmless warning if the ARM_SCPI_POWER_DOMAIN driver is enabled without CONFIG_OF during compile testing: warning: (ARM_SCPI_POWER_DOMAIN) selects PM_GENERIC_DOMAINS_OF which has unmet direct dependencies (PM_GENERIC_DOMAINS && OF) There is no need to select PM_GENERIC_DOMAINS_OF if OF is set, so we can replace the 'select' with a dependency. Signed-off-by: Arnd Bergmann Fixes: 8bec4337ad40 ("firmware: scpi: add device power domain support using genpd") Acked-by: Sudeep Holla diff --git a/drivers/firmware/Kconfig b/drivers/firmware/Kconfig index 541d3fb..0e22f24 100644 --- a/drivers/firmware/Kconfig +++ b/drivers/firmware/Kconfig @@ -29,10 +29,9 @@ config ARM_SCPI_PROTOCOL config ARM_SCPI_POWER_DOMAIN tristate "SCPI power domain driver" - depends on ARM_SCPI_PROTOCOL || COMPILE_TEST + depends on ARM_SCPI_PROTOCOL || (COMPILE_TEST && OF) default y select PM_GENERIC_DOMAINS if PM - select PM_GENERIC_DOMAINS_OF if PM help This enables support for the SCPI power domains which can be enabled or disabled via the SCP firmware -- cgit v0.10.2 From 9bb45cd4470607028c4abdaa4063f1674661bb0e Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Wed, 6 Jul 2016 14:49:45 +0200 Subject: soc: raspberrypi-power: add CONFIG_OF dependency We get a harmless warning if the RASPBERRYPI_POWER driver is enabled without CONFIG_OF during compile testing: warning: RASPBERRYPI_POWER selects PM_GENERIC_DOMAINS_OF which has unmet direct dependencies (PM_GENERIC_DOMAINS && OF) There is no need to select PM_GENERIC_DOMAINS_OF if OF is set, so we can replace the 'select' with a dependency. Signed-off-by: Arnd Bergmann Acked-by: Eric Anholt diff --git a/drivers/soc/bcm/Kconfig b/drivers/soc/bcm/Kconfig index 97156ae..a39b0d5 100644 --- a/drivers/soc/bcm/Kconfig +++ b/drivers/soc/bcm/Kconfig @@ -2,10 +2,9 @@ menu "Broadcom SoC drivers" config RASPBERRYPI_POWER bool "Raspberry Pi power domain driver" - depends on ARCH_BCM2835 || COMPILE_TEST + depends on ARCH_BCM2835 || (COMPILE_TEST && OF) depends on RASPBERRYPI_FIRMWARE=y select PM_GENERIC_DOMAINS if PM - select PM_GENERIC_DOMAINS_OF if PM help This enables support for the RPi power domains which can be enabled or disabled via the RPi firmware. -- cgit v0.10.2 From 37dc78d9b17c971479f742d6d08f38d8f2beb688 Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Fri, 18 Mar 2016 16:15:47 +0100 Subject: ARM: ux500: remove unused regulator data Most of the board-mop500-regulators.c file is never referenced and can simply be removed. Cc: Mark Brown Signed-off-by: Arnd Bergmann Signed-off-by: Linus Walleij diff --git a/arch/arm/mach-ux500/board-mop500-regulators.c b/arch/arm/mach-ux500/board-mop500-regulators.c index 32d744e..eff141d 100644 --- a/arch/arm/mach-ux500/board-mop500-regulators.c +++ b/arch/arm/mach-ux500/board-mop500-regulators.c @@ -13,46 +13,6 @@ #include #include #include "board-mop500-regulators.h" -#include "id.h" - -static struct regulator_consumer_supply gpio_en_3v3_consumers[] = { - REGULATOR_SUPPLY("vdd33a", "smsc911x.0"), -}; - -struct regulator_init_data gpio_en_3v3_regulator = { - .constraints = { - .name = "EN-3V3", - .min_uV = 3300000, - .max_uV = 3300000, - .valid_ops_mask = REGULATOR_CHANGE_STATUS, - }, - .num_consumer_supplies = ARRAY_SIZE(gpio_en_3v3_consumers), - .consumer_supplies = gpio_en_3v3_consumers, -}; - -/* - * TPS61052 regulator - */ -static struct regulator_consumer_supply tps61052_vaudio_consumers[] = { - /* - * Boost converter supply to raise voltage on audio speaker, this - * is actually connected to three pins, VInVhfL (left amplifier) - * VInVhfR (right amplifier) and VIntDClassInt - all three must - * be connected to the same voltage. - */ - REGULATOR_SUPPLY("vintdclassint", "ab8500-codec.0"), -}; - -struct regulator_init_data tps61052_regulator = { - .constraints = { - .name = "vaudio-hf", - .min_uV = 4500000, - .max_uV = 4500000, - .valid_ops_mask = REGULATOR_CHANGE_STATUS, - }, - .num_consumer_supplies = ARRAY_SIZE(tps61052_vaudio_consumers), - .consumer_supplies = tps61052_vaudio_consumers, -}; static struct regulator_consumer_supply ab8500_vaux1_consumers[] = { /* Main display, u8500 R3 uib */ @@ -107,27 +67,6 @@ static struct regulator_consumer_supply ab8500_vaux3_consumers[] = { REGULATOR_SUPPLY("vmmc", "sdi0"), }; -static struct regulator_consumer_supply ab8505_vaux4_consumers[] = { -}; - -static struct regulator_consumer_supply ab8505_vaux5_consumers[] = { -}; - -static struct regulator_consumer_supply ab8505_vaux6_consumers[] = { -}; - -static struct regulator_consumer_supply ab8505_vaux8_consumers[] = { - /* AB8500 audio codec device */ - REGULATOR_SUPPLY("v-aux8", NULL), -}; - -static struct regulator_consumer_supply ab8505_vadc_consumers[] = { - /* Internal general-purpose ADC */ - REGULATOR_SUPPLY("vddadc", "ab8500-gpadc.0"), - /* ADC for charger */ - REGULATOR_SUPPLY("vddadc", "ab8500-charger.0"), -}; - static struct regulator_consumer_supply ab8500_vtvout_consumers[] = { /* TV-out DENC supply */ REGULATOR_SUPPLY("vtvout", "ab8500-denc.0"), @@ -168,11 +107,6 @@ static struct regulator_consumer_supply ab8500_vintcore_consumers[] = { REGULATOR_SUPPLY("v-intcore", "abx500-clk.0"), }; -static struct regulator_consumer_supply ab8505_usb_consumers[] = { - /* HS USB OTG physical interface */ - REGULATOR_SUPPLY("v-ape", NULL), -}; - static struct regulator_consumer_supply ab8500_vana_consumers[] = { /* DB8500 DSI */ REGULATOR_SUPPLY("vdddsi1v2", "mcde"), @@ -483,11 +417,6 @@ static struct regulator_consumer_supply ab8500_ext_supply3_consumers[] = { REGULATOR_SUPPLY("vinvsim", "sim-detect.0"), }; -/* extended configuration for VextSupply2, only used for HREFP_V20 boards */ -static struct ab8500_ext_regulator_cfg ab8500_ext_supply2 = { - .hwreq = true, -}; - /* * AB8500 external regulators */ @@ -526,456 +455,6 @@ static struct regulator_init_data ab8500_ext_regulators[] = { }, }; -/* ab8505 regulator register initialization */ -static struct ab8500_regulator_reg_init ab8505_reg_init[] = { - /* - * VarmRequestCtrl - * VsmpsCRequestCtrl - * VsmpsARequestCtrl - * VsmpsBRequestCtrl - */ - INIT_REGULATOR_REGISTER(AB8505_REGUREQUESTCTRL1, 0x00, 0x00), - /* - * VsafeRequestCtrl - * VpllRequestCtrl - * VanaRequestCtrl = HP/LP depending on VxRequest - */ - INIT_REGULATOR_REGISTER(AB8505_REGUREQUESTCTRL2, 0x30, 0x00), - /* - * Vaux1RequestCtrl = HP/LP depending on VxRequest - * Vaux2RequestCtrl = HP/LP depending on VxRequest - */ - INIT_REGULATOR_REGISTER(AB8505_REGUREQUESTCTRL3, 0xf0, 0x00), - /* - * Vaux3RequestCtrl = HP/LP depending on VxRequest - * SwHPReq = Control through SWValid disabled - */ - INIT_REGULATOR_REGISTER(AB8505_REGUREQUESTCTRL4, 0x07, 0x00), - /* - * VsmpsASysClkReq1HPValid - * VsmpsBSysClkReq1HPValid - * VsafeSysClkReq1HPValid - * VanaSysClkReq1HPValid = disabled - * VpllSysClkReq1HPValid - * Vaux1SysClkReq1HPValid = disabled - * Vaux2SysClkReq1HPValid = disabled - * Vaux3SysClkReq1HPValid = disabled - */ - INIT_REGULATOR_REGISTER(AB8505_REGUSYSCLKREQ1HPVALID1, 0xe8, 0x00), - /* - * VsmpsCSysClkReq1HPValid - * VarmSysClkReq1HPValid - * VbbSysClkReq1HPValid - * VsmpsMSysClkReq1HPValid - */ - INIT_REGULATOR_REGISTER(AB8505_REGUSYSCLKREQ1HPVALID2, 0x00, 0x00), - /* - * VsmpsAHwHPReq1Valid - * VsmpsBHwHPReq1Valid - * VsafeHwHPReq1Valid - * VanaHwHPReq1Valid = disabled - * VpllHwHPReq1Valid - * Vaux1HwHPreq1Valid = disabled - * Vaux2HwHPReq1Valid = disabled - * Vaux3HwHPReqValid = disabled - */ - INIT_REGULATOR_REGISTER(AB8505_REGUHWHPREQ1VALID1, 0xe8, 0x00), - /* - * VsmpsMHwHPReq1Valid - */ - INIT_REGULATOR_REGISTER(AB8505_REGUHWHPREQ1VALID2, 0x00, 0x00), - /* - * VsmpsAHwHPReq2Valid - * VsmpsBHwHPReq2Valid - * VsafeHwHPReq2Valid - * VanaHwHPReq2Valid = disabled - * VpllHwHPReq2Valid - * Vaux1HwHPReq2Valid = disabled - * Vaux2HwHPReq2Valid = disabled - * Vaux3HwHPReq2Valid = disabled - */ - INIT_REGULATOR_REGISTER(AB8505_REGUHWHPREQ2VALID1, 0xe8, 0x00), - /* - * VsmpsMHwHPReq2Valid - */ - INIT_REGULATOR_REGISTER(AB8505_REGUHWHPREQ2VALID2, 0x00, 0x00), - /** - * VsmpsCSwHPReqValid - * VarmSwHPReqValid - * VsmpsASwHPReqValid - * VsmpsBSwHPReqValid - * VsafeSwHPReqValid - * VanaSwHPReqValid - * VanaSwHPReqValid = disabled - * VpllSwHPReqValid - * Vaux1SwHPReqValid = disabled - */ - INIT_REGULATOR_REGISTER(AB8505_REGUSWHPREQVALID1, 0xa0, 0x00), - /* - * Vaux2SwHPReqValid = disabled - * Vaux3SwHPReqValid = disabled - * VsmpsMSwHPReqValid - */ - INIT_REGULATOR_REGISTER(AB8505_REGUSWHPREQVALID2, 0x03, 0x00), - /* - * SysClkReq2Valid1 = SysClkReq2 controlled - * SysClkReq3Valid1 = disabled - * SysClkReq4Valid1 = SysClkReq4 controlled - */ - INIT_REGULATOR_REGISTER(AB8505_REGUSYSCLKREQVALID1, 0x0e, 0x0a), - /* - * SysClkReq2Valid2 = disabled - * SysClkReq3Valid2 = disabled - * SysClkReq4Valid2 = disabled - */ - INIT_REGULATOR_REGISTER(AB8505_REGUSYSCLKREQVALID2, 0x0e, 0x00), - /* - * Vaux4SwHPReqValid - * Vaux4HwHPReq2Valid - * Vaux4HwHPReq1Valid - * Vaux4SysClkReq1HPValid - */ - INIT_REGULATOR_REGISTER(AB8505_REGUVAUX4REQVALID, 0x00, 0x00), - /* - * VadcEna = disabled - * VintCore12Ena = disabled - * VintCore12Sel = 1.25 V - * VintCore12LP = inactive (HP) - * VadcLP = inactive (HP) - */ - INIT_REGULATOR_REGISTER(AB8505_REGUMISC1, 0xfe, 0x10), - /* - * VaudioEna = disabled - * Vaux8Ena = disabled - * Vamic1Ena = disabled - * Vamic2Ena = disabled - */ - INIT_REGULATOR_REGISTER(AB8505_VAUDIOSUPPLY, 0x1e, 0x00), - /* - * Vamic1_dzout = high-Z when Vamic1 is disabled - * Vamic2_dzout = high-Z when Vamic2 is disabled - */ - INIT_REGULATOR_REGISTER(AB8505_REGUCTRL1VAMIC, 0x03, 0x00), - /* - * VsmpsARegu - * VsmpsASelCtrl - * VsmpsAAutoMode - * VsmpsAPWMMode - */ - INIT_REGULATOR_REGISTER(AB8505_VSMPSAREGU, 0x00, 0x00), - /* - * VsmpsBRegu - * VsmpsBSelCtrl - * VsmpsBAutoMode - * VsmpsBPWMMode - */ - INIT_REGULATOR_REGISTER(AB8505_VSMPSBREGU, 0x00, 0x00), - /* - * VsafeRegu - * VsafeSelCtrl - * VsafeAutoMode - * VsafePWMMode - */ - INIT_REGULATOR_REGISTER(AB8505_VSAFEREGU, 0x00, 0x00), - /* - * VPll = Hw controlled (NOTE! PRCMU bits) - * VanaRegu = force off - */ - INIT_REGULATOR_REGISTER(AB8505_VPLLVANAREGU, 0x0f, 0x02), - /* - * VextSupply1Regu = force OFF (OTP_ExtSupply12LPnPolarity 1) - * VextSupply2Regu = force OFF (OTP_ExtSupply12LPnPolarity 1) - * VextSupply3Regu = force OFF (OTP_ExtSupply3LPnPolarity 0) - * ExtSupply2Bypass = ExtSupply12LPn ball is 0 when Ena is 0 - * ExtSupply3Bypass = ExtSupply3LPn ball is 0 when Ena is 0 - */ - INIT_REGULATOR_REGISTER(AB8505_EXTSUPPLYREGU, 0xff, 0x30), - /* - * Vaux1Regu = force HP - * Vaux2Regu = force off - */ - INIT_REGULATOR_REGISTER(AB8505_VAUX12REGU, 0x0f, 0x01), - /* - * Vaux3Regu = force off - */ - INIT_REGULATOR_REGISTER(AB8505_VRF1VAUX3REGU, 0x03, 0x00), - /* - * VsmpsASel1 - */ - INIT_REGULATOR_REGISTER(AB8505_VSMPSASEL1, 0x00, 0x00), - /* - * VsmpsASel2 - */ - INIT_REGULATOR_REGISTER(AB8505_VSMPSASEL2, 0x00, 0x00), - /* - * VsmpsASel3 - */ - INIT_REGULATOR_REGISTER(AB8505_VSMPSASEL3, 0x00, 0x00), - /* - * VsmpsBSel1 - */ - INIT_REGULATOR_REGISTER(AB8505_VSMPSBSEL1, 0x00, 0x00), - /* - * VsmpsBSel2 - */ - INIT_REGULATOR_REGISTER(AB8505_VSMPSBSEL2, 0x00, 0x00), - /* - * VsmpsBSel3 - */ - INIT_REGULATOR_REGISTER(AB8505_VSMPSBSEL3, 0x00, 0x00), - /* - * VsafeSel1 - */ - INIT_REGULATOR_REGISTER(AB8505_VSAFESEL1, 0x00, 0x00), - /* - * VsafeSel2 - */ - INIT_REGULATOR_REGISTER(AB8505_VSAFESEL2, 0x00, 0x00), - /* - * VsafeSel3 - */ - INIT_REGULATOR_REGISTER(AB8505_VSAFESEL3, 0x00, 0x00), - /* - * Vaux1Sel = 2.8 V - */ - INIT_REGULATOR_REGISTER(AB8505_VAUX1SEL, 0x0f, 0x0C), - /* - * Vaux2Sel = 2.9 V - */ - INIT_REGULATOR_REGISTER(AB8505_VAUX2SEL, 0x0f, 0x0d), - /* - * Vaux3Sel = 2.91 V - */ - INIT_REGULATOR_REGISTER(AB8505_VRF1VAUX3SEL, 0x07, 0x07), - /* - * Vaux4RequestCtrl - */ - INIT_REGULATOR_REGISTER(AB8505_VAUX4REQCTRL, 0x00, 0x00), - /* - * Vaux4Regu - */ - INIT_REGULATOR_REGISTER(AB8505_VAUX4REGU, 0x00, 0x00), - /* - * Vaux4Sel - */ - INIT_REGULATOR_REGISTER(AB8505_VAUX4SEL, 0x00, 0x00), - /* - * Vaux1Disch = short discharge time - * Vaux2Disch = short discharge time - * Vaux3Disch = short discharge time - * Vintcore12Disch = short discharge time - * VTVoutDisch = short discharge time - * VaudioDisch = short discharge time - */ - INIT_REGULATOR_REGISTER(AB8505_REGUCTRLDISCH, 0xfc, 0x00), - /* - * VanaDisch = short discharge time - * Vaux8PullDownEna = pulldown disabled when Vaux8 is disabled - * Vaux8Disch = short discharge time - */ - INIT_REGULATOR_REGISTER(AB8505_REGUCTRLDISCH2, 0x16, 0x00), - /* - * Vaux4Disch = short discharge time - */ - INIT_REGULATOR_REGISTER(AB8505_REGUCTRLDISCH3, 0x01, 0x00), - /* - * Vaux5Sel - * Vaux5LP - * Vaux5Ena - * Vaux5Disch - * Vaux5DisSfst - * Vaux5DisPulld - */ - INIT_REGULATOR_REGISTER(AB8505_CTRLVAUX5, 0x00, 0x00), - /* - * Vaux6Sel - * Vaux6LP - * Vaux6Ena - * Vaux6DisPulld - */ - INIT_REGULATOR_REGISTER(AB8505_CTRLVAUX6, 0x00, 0x00), -}; - -static struct regulator_init_data ab8505_regulators[AB8505_NUM_REGULATORS] = { - /* supplies to the display/camera */ - [AB8505_LDO_AUX1] = { - .constraints = { - .name = "V-DISPLAY", - .min_uV = 2800000, - .max_uV = 3300000, - .valid_ops_mask = REGULATOR_CHANGE_VOLTAGE | - REGULATOR_CHANGE_STATUS, - .boot_on = 1, /* display is on at boot */ - }, - .num_consumer_supplies = ARRAY_SIZE(ab8500_vaux1_consumers), - .consumer_supplies = ab8500_vaux1_consumers, - }, - /* supplies to the on-board eMMC */ - [AB8505_LDO_AUX2] = { - .constraints = { - .name = "V-eMMC1", - .min_uV = 1100000, - .max_uV = 3300000, - .valid_ops_mask = REGULATOR_CHANGE_VOLTAGE | - REGULATOR_CHANGE_STATUS | - REGULATOR_CHANGE_MODE, - .valid_modes_mask = REGULATOR_MODE_NORMAL | - REGULATOR_MODE_IDLE, - }, - .num_consumer_supplies = ARRAY_SIZE(ab8500_vaux2_consumers), - .consumer_supplies = ab8500_vaux2_consumers, - }, - /* supply for VAUX3, supplies to SDcard slots */ - [AB8505_LDO_AUX3] = { - .constraints = { - .name = "V-MMC-SD", - .min_uV = 1100000, - .max_uV = 3300000, - .valid_ops_mask = REGULATOR_CHANGE_VOLTAGE | - REGULATOR_CHANGE_STATUS | - REGULATOR_CHANGE_MODE, - .valid_modes_mask = REGULATOR_MODE_NORMAL | - REGULATOR_MODE_IDLE, - }, - .num_consumer_supplies = ARRAY_SIZE(ab8500_vaux3_consumers), - .consumer_supplies = ab8500_vaux3_consumers, - }, - /* supply for VAUX4, supplies to NFC and standalone secure element */ - [AB8505_LDO_AUX4] = { - .constraints = { - .name = "V-NFC-SE", - .min_uV = 1100000, - .max_uV = 3300000, - .valid_ops_mask = REGULATOR_CHANGE_VOLTAGE | - REGULATOR_CHANGE_STATUS | - REGULATOR_CHANGE_MODE, - .valid_modes_mask = REGULATOR_MODE_NORMAL | - REGULATOR_MODE_IDLE, - }, - .num_consumer_supplies = ARRAY_SIZE(ab8505_vaux4_consumers), - .consumer_supplies = ab8505_vaux4_consumers, - }, - /* supply for VAUX5, supplies to TBD */ - [AB8505_LDO_AUX5] = { - .constraints = { - .name = "V-AUX5", - .min_uV = 1050000, - .max_uV = 2790000, - .valid_ops_mask = REGULATOR_CHANGE_VOLTAGE | - REGULATOR_CHANGE_STATUS | - REGULATOR_CHANGE_MODE, - .valid_modes_mask = REGULATOR_MODE_NORMAL | - REGULATOR_MODE_IDLE, - }, - .num_consumer_supplies = ARRAY_SIZE(ab8505_vaux5_consumers), - .consumer_supplies = ab8505_vaux5_consumers, - }, - /* supply for VAUX6, supplies to TBD */ - [AB8505_LDO_AUX6] = { - .constraints = { - .name = "V-AUX6", - .min_uV = 1050000, - .max_uV = 2790000, - .valid_ops_mask = REGULATOR_CHANGE_VOLTAGE | - REGULATOR_CHANGE_STATUS | - REGULATOR_CHANGE_MODE, - .valid_modes_mask = REGULATOR_MODE_NORMAL | - REGULATOR_MODE_IDLE, - }, - .num_consumer_supplies = ARRAY_SIZE(ab8505_vaux6_consumers), - .consumer_supplies = ab8505_vaux6_consumers, - }, - /* supply for gpadc, ADC LDO */ - [AB8505_LDO_ADC] = { - .constraints = { - .name = "V-ADC", - .valid_ops_mask = REGULATOR_CHANGE_STATUS, - }, - .num_consumer_supplies = ARRAY_SIZE(ab8505_vadc_consumers), - .consumer_supplies = ab8505_vadc_consumers, - }, - /* supply for ab8500-vaudio, VAUDIO LDO */ - [AB8505_LDO_AUDIO] = { - .constraints = { - .name = "V-AUD", - .valid_ops_mask = REGULATOR_CHANGE_STATUS, - }, - .num_consumer_supplies = ARRAY_SIZE(ab8500_vaud_consumers), - .consumer_supplies = ab8500_vaud_consumers, - }, - /* supply for v-anamic1 VAMic1-LDO */ - [AB8505_LDO_ANAMIC1] = { - .constraints = { - .name = "V-AMIC1", - .valid_ops_mask = REGULATOR_CHANGE_STATUS | - REGULATOR_CHANGE_MODE, - .valid_modes_mask = REGULATOR_MODE_NORMAL | - REGULATOR_MODE_IDLE, - }, - .num_consumer_supplies = ARRAY_SIZE(ab8500_vamic1_consumers), - .consumer_supplies = ab8500_vamic1_consumers, - }, - /* supply for v-amic2, VAMIC2 LDO, reuse constants for AMIC1 */ - [AB8505_LDO_ANAMIC2] = { - .constraints = { - .name = "V-AMIC2", - .valid_ops_mask = REGULATOR_CHANGE_STATUS | - REGULATOR_CHANGE_MODE, - .valid_modes_mask = REGULATOR_MODE_NORMAL | - REGULATOR_MODE_IDLE, - }, - .num_consumer_supplies = ARRAY_SIZE(ab8500_vamic2_consumers), - .consumer_supplies = ab8500_vamic2_consumers, - }, - /* supply for v-aux8, VAUX8 LDO */ - [AB8505_LDO_AUX8] = { - .constraints = { - .name = "V-AUX8", - .valid_ops_mask = REGULATOR_CHANGE_STATUS, - }, - .num_consumer_supplies = ARRAY_SIZE(ab8505_vaux8_consumers), - .consumer_supplies = ab8505_vaux8_consumers, - }, - /* supply for v-intcore12, VINTCORE12 LDO */ - [AB8505_LDO_INTCORE] = { - .constraints = { - .name = "V-INTCORE", - .min_uV = 1250000, - .max_uV = 1350000, - .input_uV = 1800000, - .valid_ops_mask = REGULATOR_CHANGE_VOLTAGE | - REGULATOR_CHANGE_STATUS | - REGULATOR_CHANGE_MODE | - REGULATOR_CHANGE_DRMS, - .valid_modes_mask = REGULATOR_MODE_NORMAL | - REGULATOR_MODE_IDLE, - }, - .num_consumer_supplies = ARRAY_SIZE(ab8500_vintcore_consumers), - .consumer_supplies = ab8500_vintcore_consumers, - }, - /* supply for LDO USB */ - [AB8505_LDO_USB] = { - .constraints = { - .name = "V-USB", - .valid_ops_mask = REGULATOR_CHANGE_STATUS | - REGULATOR_CHANGE_MODE, - .valid_modes_mask = REGULATOR_MODE_NORMAL | - REGULATOR_MODE_IDLE, - }, - .num_consumer_supplies = ARRAY_SIZE(ab8505_usb_consumers), - .consumer_supplies = ab8505_usb_consumers, - }, - /* supply for U8500 CSI-DSI, VANA LDO */ - [AB8505_LDO_ANA] = { - .constraints = { - .name = "V-CSI-DSI", - .valid_ops_mask = REGULATOR_CHANGE_STATUS, - }, - .num_consumer_supplies = ARRAY_SIZE(ab8500_vana_consumers), - .consumer_supplies = ab8500_vana_consumers, - }, -}; - struct ab8500_regulator_platform_data ab8500_regulator_plat_data = { .reg_init = ab8500_reg_init, .num_reg_init = ARRAY_SIZE(ab8500_reg_init), @@ -984,82 +463,3 @@ struct ab8500_regulator_platform_data ab8500_regulator_plat_data = { .ext_regulator = ab8500_ext_regulators, .num_ext_regulator = ARRAY_SIZE(ab8500_ext_regulators), }; - -struct ab8500_regulator_platform_data ab8505_regulator_plat_data = { - .reg_init = ab8505_reg_init, - .num_reg_init = ARRAY_SIZE(ab8505_reg_init), - .regulator = ab8505_regulators, - .num_regulator = ARRAY_SIZE(ab8505_regulators), -}; - -static void ab8500_modify_reg_init(int id, u8 mask, u8 value) -{ - int i; - - if (cpu_is_u8520()) { - for (i = ARRAY_SIZE(ab8505_reg_init) - 1; i >= 0; i--) { - if (ab8505_reg_init[i].id == id) { - u8 initval = ab8505_reg_init[i].value; - initval = (initval & ~mask) | (value & mask); - ab8505_reg_init[i].value = initval; - - BUG_ON(mask & ~ab8505_reg_init[i].mask); - return; - } - } - } else { - for (i = ARRAY_SIZE(ab8500_reg_init) - 1; i >= 0; i--) { - if (ab8500_reg_init[i].id == id) { - u8 initval = ab8500_reg_init[i].value; - initval = (initval & ~mask) | (value & mask); - ab8500_reg_init[i].value = initval; - - BUG_ON(mask & ~ab8500_reg_init[i].mask); - return; - } - } - } - - BUG_ON(1); -} - -void mop500_regulator_init(void) -{ - struct regulator_init_data *regulator; - - /* - * Temporarily turn on Vaux2 on 8520 machine - */ - if (cpu_is_u8520()) { - /* Vaux2 initialized to be on */ - ab8500_modify_reg_init(AB8505_VAUX12REGU, 0x0f, 0x05); - } - - /* - * Handle AB8500_EXT_SUPPLY2 on HREFP_V20_V50 boards (do it for - * all HREFP_V20 boards) - */ - if (cpu_is_u8500v20()) { - /* VextSupply2RequestCtrl = HP/OFF depending on VxRequest */ - ab8500_modify_reg_init(AB8500_REGUREQUESTCTRL3, 0x01, 0x01); - - /* VextSupply2SysClkReq1HPValid = SysClkReq1 controlled */ - ab8500_modify_reg_init(AB8500_REGUSYSCLKREQ1HPVALID2, - 0x20, 0x20); - - /* VextSupply2 = force HP at initialization */ - ab8500_modify_reg_init(AB8500_EXTSUPPLYREGU, 0x0c, 0x04); - - /* enable VextSupply2 during platform active */ - regulator = &ab8500_ext_regulators[AB8500_EXT_SUPPLY2]; - regulator->constraints.always_on = 1; - - /* disable VextSupply2 in suspend */ - regulator = &ab8500_ext_regulators[AB8500_EXT_SUPPLY2]; - regulator->constraints.state_mem.disabled = 1; - regulator->constraints.state_standby.disabled = 1; - - /* enable VextSupply2 HW control (used in suspend) */ - regulator->driver_data = (void *)&ab8500_ext_supply2; - } -} diff --git a/arch/arm/mach-ux500/board-mop500-regulators.h b/arch/arm/mach-ux500/board-mop500-regulators.h index 9bece38..3bbb083 100644 --- a/arch/arm/mach-ux500/board-mop500-regulators.h +++ b/arch/arm/mach-ux500/board-mop500-regulators.h @@ -11,14 +11,6 @@ #ifndef __BOARD_MOP500_REGULATORS_H #define __BOARD_MOP500_REGULATORS_H -#include -#include - extern struct ab8500_regulator_platform_data ab8500_regulator_plat_data; -extern struct ab8500_regulator_platform_data ab8505_regulator_plat_data; -extern struct regulator_init_data tps61052_regulator; -extern struct regulator_init_data gpio_en_3v3_regulator; - -void mop500_regulator_init(void); #endif -- cgit v0.10.2 From 79886be09751c543155608036134b9948288bfc3 Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Fri, 18 Mar 2016 16:21:36 +0100 Subject: ARM: ux500: move ab8500_regulator_plat_data into driver There is only one instance of ab8500_regulator_platform_data, and it's safe to assume we won't ever merge another one, so it's rather pointless to pass it through multiple levels of platform data pointers. This moves the structure and everything referenced by it into the driver that uses it. Signed-off-by: Arnd Bergmann Acked-by: Mark Brown Signed-off-by: Linus Walleij diff --git a/arch/arm/mach-ux500/Makefile b/arch/arm/mach-ux500/Makefile index edfff1a..ea8893f 100644 --- a/arch/arm/mach-ux500/Makefile +++ b/arch/arm/mach-ux500/Makefile @@ -5,8 +5,7 @@ obj-y := cpu.o id.o pm.o obj-$(CONFIG_CACHE_L2X0) += cache-l2x0.o obj-$(CONFIG_UX500_SOC_DB8500) += cpu-db8500.o -obj-$(CONFIG_MACH_MOP500) += board-mop500-regulators.o \ - board-mop500-audio.o +obj-$(CONFIG_MACH_MOP500) += board-mop500-audio.o obj-$(CONFIG_SMP) += platsmp.o obj-$(CONFIG_HOTPLUG_CPU) += hotplug.o obj-$(CONFIG_PM_GENERIC_DOMAINS) += pm_domains.o diff --git a/arch/arm/mach-ux500/board-mop500-regulators.c b/arch/arm/mach-ux500/board-mop500-regulators.c deleted file mode 100644 index eff141d..0000000 --- a/arch/arm/mach-ux500/board-mop500-regulators.c +++ /dev/null @@ -1,465 +0,0 @@ -/* - * Copyright (C) ST-Ericsson SA 2010 - * - * License Terms: GNU General Public License v2 - * - * Authors: Sundar Iyer - * Bengt Jonsson - * Daniel Willerud - * - * MOP500 board specific initialization for regulators - */ -#include -#include -#include -#include "board-mop500-regulators.h" - -static struct regulator_consumer_supply ab8500_vaux1_consumers[] = { - /* Main display, u8500 R3 uib */ - REGULATOR_SUPPLY("vddi", "mcde_disp_sony_acx424akp.0"), - /* Main display, u8500 uib and ST uib */ - REGULATOR_SUPPLY("vdd1", "samsung_s6d16d0.0"), - /* Secondary display, ST uib */ - REGULATOR_SUPPLY("vdd1", "samsung_s6d16d0.1"), - /* SFH7741 proximity sensor */ - REGULATOR_SUPPLY("vcc", "gpio-keys.0"), - /* BH1780GLS ambient light sensor */ - REGULATOR_SUPPLY("vcc", "2-0029"), - /* lsm303dlh accelerometer */ - REGULATOR_SUPPLY("vdd", "2-0018"), - /* lsm303dlhc accelerometer */ - REGULATOR_SUPPLY("vdd", "2-0019"), - /* lsm303dlh magnetometer */ - REGULATOR_SUPPLY("vdd", "2-001e"), - /* Rohm BU21013 Touchscreen devices */ - REGULATOR_SUPPLY("avdd", "3-005c"), - REGULATOR_SUPPLY("avdd", "3-005d"), - /* Synaptics RMI4 Touchscreen device */ - REGULATOR_SUPPLY("vdd", "3-004b"), - /* L3G4200D Gyroscope device */ - REGULATOR_SUPPLY("vdd", "2-0068"), - /* Ambient light sensor device */ - REGULATOR_SUPPLY("vdd", "3-0029"), - /* Pressure sensor device */ - REGULATOR_SUPPLY("vdd", "2-005c"), - /* Cypress TrueTouch Touchscreen device */ - REGULATOR_SUPPLY("vcpin", "spi8.0"), - /* Camera device */ - REGULATOR_SUPPLY("vaux12v5", "mmio_camera"), -}; - -static struct regulator_consumer_supply ab8500_vaux2_consumers[] = { - /* On-board eMMC power */ - REGULATOR_SUPPLY("vmmc", "sdi4"), - /* AB8500 audio codec */ - REGULATOR_SUPPLY("vcc-N2158", "ab8500-codec.0"), - /* AB8500 accessory detect 1 */ - REGULATOR_SUPPLY("vcc-N2158", "ab8500-acc-det.0"), - /* AB8500 Tv-out device */ - REGULATOR_SUPPLY("vcc-N2158", "mcde_tv_ab8500.4"), - /* AV8100 HDMI device */ - REGULATOR_SUPPLY("vcc-N2158", "av8100_hdmi.3"), -}; - -static struct regulator_consumer_supply ab8500_vaux3_consumers[] = { - REGULATOR_SUPPLY("v-SD-STM", "stm"), - /* External MMC slot power */ - REGULATOR_SUPPLY("vmmc", "sdi0"), -}; - -static struct regulator_consumer_supply ab8500_vtvout_consumers[] = { - /* TV-out DENC supply */ - REGULATOR_SUPPLY("vtvout", "ab8500-denc.0"), - /* Internal general-purpose ADC */ - REGULATOR_SUPPLY("vddadc", "ab8500-gpadc.0"), - /* ADC for charger */ - REGULATOR_SUPPLY("vddadc", "ab8500-charger.0"), - /* AB8500 Tv-out device */ - REGULATOR_SUPPLY("vtvout", "mcde_tv_ab8500.4"), -}; - -static struct regulator_consumer_supply ab8500_vaud_consumers[] = { - /* AB8500 audio-codec main supply */ - REGULATOR_SUPPLY("vaud", "ab8500-codec.0"), -}; - -static struct regulator_consumer_supply ab8500_vamic1_consumers[] = { - /* AB8500 audio-codec Mic1 supply */ - REGULATOR_SUPPLY("vamic1", "ab8500-codec.0"), -}; - -static struct regulator_consumer_supply ab8500_vamic2_consumers[] = { - /* AB8500 audio-codec Mic2 supply */ - REGULATOR_SUPPLY("vamic2", "ab8500-codec.0"), -}; - -static struct regulator_consumer_supply ab8500_vdmic_consumers[] = { - /* AB8500 audio-codec DMic supply */ - REGULATOR_SUPPLY("vdmic", "ab8500-codec.0"), -}; - -static struct regulator_consumer_supply ab8500_vintcore_consumers[] = { - /* SoC core supply, no device */ - REGULATOR_SUPPLY("v-intcore", NULL), - /* USB Transceiver */ - REGULATOR_SUPPLY("vddulpivio18", "ab8500-usb.0"), - /* Handled by abx500 clk driver */ - REGULATOR_SUPPLY("v-intcore", "abx500-clk.0"), -}; - -static struct regulator_consumer_supply ab8500_vana_consumers[] = { - /* DB8500 DSI */ - REGULATOR_SUPPLY("vdddsi1v2", "mcde"), - REGULATOR_SUPPLY("vdddsi1v2", "b2r2_core"), - REGULATOR_SUPPLY("vdddsi1v2", "b2r2_1_core"), - REGULATOR_SUPPLY("vdddsi1v2", "dsilink.0"), - REGULATOR_SUPPLY("vdddsi1v2", "dsilink.1"), - REGULATOR_SUPPLY("vdddsi1v2", "dsilink.2"), - /* DB8500 CSI */ - REGULATOR_SUPPLY("vddcsi1v2", "mmio_camera"), -}; - -/* ab8500 regulator register initialization */ -static struct ab8500_regulator_reg_init ab8500_reg_init[] = { - /* - * VanaRequestCtrl = HP/LP depending on VxRequest - * VextSupply1RequestCtrl = HP/LP depending on VxRequest - */ - INIT_REGULATOR_REGISTER(AB8500_REGUREQUESTCTRL2, 0xf0, 0x00), - /* - * VextSupply2RequestCtrl = HP/LP depending on VxRequest - * VextSupply3RequestCtrl = HP/LP depending on VxRequest - * Vaux1RequestCtrl = HP/LP depending on VxRequest - * Vaux2RequestCtrl = HP/LP depending on VxRequest - */ - INIT_REGULATOR_REGISTER(AB8500_REGUREQUESTCTRL3, 0xff, 0x00), - /* - * Vaux3RequestCtrl = HP/LP depending on VxRequest - * SwHPReq = Control through SWValid disabled - */ - INIT_REGULATOR_REGISTER(AB8500_REGUREQUESTCTRL4, 0x07, 0x00), - /* - * VanaSysClkReq1HPValid = disabled - * Vaux1SysClkReq1HPValid = disabled - * Vaux2SysClkReq1HPValid = disabled - * Vaux3SysClkReq1HPValid = disabled - */ - INIT_REGULATOR_REGISTER(AB8500_REGUSYSCLKREQ1HPVALID1, 0xe8, 0x00), - /* - * VextSupply1SysClkReq1HPValid = disabled - * VextSupply2SysClkReq1HPValid = disabled - * VextSupply3SysClkReq1HPValid = SysClkReq1 controlled - */ - INIT_REGULATOR_REGISTER(AB8500_REGUSYSCLKREQ1HPVALID2, 0x70, 0x40), - /* - * VanaHwHPReq1Valid = disabled - * Vaux1HwHPreq1Valid = disabled - * Vaux2HwHPReq1Valid = disabled - * Vaux3HwHPReqValid = disabled - */ - INIT_REGULATOR_REGISTER(AB8500_REGUHWHPREQ1VALID1, 0xe8, 0x00), - /* - * VextSupply1HwHPReq1Valid = disabled - * VextSupply2HwHPReq1Valid = disabled - * VextSupply3HwHPReq1Valid = disabled - */ - INIT_REGULATOR_REGISTER(AB8500_REGUHWHPREQ1VALID2, 0x07, 0x00), - /* - * VanaHwHPReq2Valid = disabled - * Vaux1HwHPReq2Valid = disabled - * Vaux2HwHPReq2Valid = disabled - * Vaux3HwHPReq2Valid = disabled - */ - INIT_REGULATOR_REGISTER(AB8500_REGUHWHPREQ2VALID1, 0xe8, 0x00), - /* - * VextSupply1HwHPReq2Valid = disabled - * VextSupply2HwHPReq2Valid = disabled - * VextSupply3HwHPReq2Valid = HWReq2 controlled - */ - INIT_REGULATOR_REGISTER(AB8500_REGUHWHPREQ2VALID2, 0x07, 0x04), - /* - * VanaSwHPReqValid = disabled - * Vaux1SwHPReqValid = disabled - */ - INIT_REGULATOR_REGISTER(AB8500_REGUSWHPREQVALID1, 0xa0, 0x00), - /* - * Vaux2SwHPReqValid = disabled - * Vaux3SwHPReqValid = disabled - * VextSupply1SwHPReqValid = disabled - * VextSupply2SwHPReqValid = disabled - * VextSupply3SwHPReqValid = disabled - */ - INIT_REGULATOR_REGISTER(AB8500_REGUSWHPREQVALID2, 0x1f, 0x00), - /* - * SysClkReq2Valid1 = SysClkReq2 controlled - * SysClkReq3Valid1 = disabled - * SysClkReq4Valid1 = SysClkReq4 controlled - * SysClkReq5Valid1 = disabled - * SysClkReq6Valid1 = SysClkReq6 controlled - * SysClkReq7Valid1 = disabled - * SysClkReq8Valid1 = disabled - */ - INIT_REGULATOR_REGISTER(AB8500_REGUSYSCLKREQVALID1, 0xfe, 0x2a), - /* - * SysClkReq2Valid2 = disabled - * SysClkReq3Valid2 = disabled - * SysClkReq4Valid2 = disabled - * SysClkReq5Valid2 = disabled - * SysClkReq6Valid2 = SysClkReq6 controlled - * SysClkReq7Valid2 = disabled - * SysClkReq8Valid2 = disabled - */ - INIT_REGULATOR_REGISTER(AB8500_REGUSYSCLKREQVALID2, 0xfe, 0x20), - /* - * VTVoutEna = disabled - * Vintcore12Ena = disabled - * Vintcore12Sel = 1.25 V - * Vintcore12LP = inactive (HP) - * VTVoutLP = inactive (HP) - */ - INIT_REGULATOR_REGISTER(AB8500_REGUMISC1, 0xfe, 0x10), - /* - * VaudioEna = disabled - * VdmicEna = disabled - * Vamic1Ena = disabled - * Vamic2Ena = disabled - */ - INIT_REGULATOR_REGISTER(AB8500_VAUDIOSUPPLY, 0x1e, 0x00), - /* - * Vamic1_dzout = high-Z when Vamic1 is disabled - * Vamic2_dzout = high-Z when Vamic2 is disabled - */ - INIT_REGULATOR_REGISTER(AB8500_REGUCTRL1VAMIC, 0x03, 0x00), - /* - * VPll = Hw controlled (NOTE! PRCMU bits) - * VanaRegu = force off - */ - INIT_REGULATOR_REGISTER(AB8500_VPLLVANAREGU, 0x0f, 0x02), - /* - * VrefDDREna = disabled - * VrefDDRSleepMode = inactive (no pulldown) - */ - INIT_REGULATOR_REGISTER(AB8500_VREFDDR, 0x03, 0x00), - /* - * VextSupply1Regu = force LP - * VextSupply2Regu = force OFF - * VextSupply3Regu = force HP (-> STBB2=LP and TPS=LP) - * ExtSupply2Bypass = ExtSupply12LPn ball is 0 when Ena is 0 - * ExtSupply3Bypass = ExtSupply3LPn ball is 0 when Ena is 0 - */ - INIT_REGULATOR_REGISTER(AB8500_EXTSUPPLYREGU, 0xff, 0x13), - /* - * Vaux1Regu = force HP - * Vaux2Regu = force off - */ - INIT_REGULATOR_REGISTER(AB8500_VAUX12REGU, 0x0f, 0x01), - /* - * Vaux3Regu = force off - */ - INIT_REGULATOR_REGISTER(AB8500_VRF1VAUX3REGU, 0x03, 0x00), - /* - * Vaux1Sel = 2.8 V - */ - INIT_REGULATOR_REGISTER(AB8500_VAUX1SEL, 0x0f, 0x0C), - /* - * Vaux2Sel = 2.9 V - */ - INIT_REGULATOR_REGISTER(AB8500_VAUX2SEL, 0x0f, 0x0d), - /* - * Vaux3Sel = 2.91 V - */ - INIT_REGULATOR_REGISTER(AB8500_VRF1VAUX3SEL, 0x07, 0x07), - /* - * VextSupply12LP = disabled (no LP) - */ - INIT_REGULATOR_REGISTER(AB8500_REGUCTRL2SPARE, 0x01, 0x00), - /* - * Vaux1Disch = short discharge time - * Vaux2Disch = short discharge time - * Vaux3Disch = short discharge time - * Vintcore12Disch = short discharge time - * VTVoutDisch = short discharge time - * VaudioDisch = short discharge time - */ - INIT_REGULATOR_REGISTER(AB8500_REGUCTRLDISCH, 0xfc, 0x00), - /* - * VanaDisch = short discharge time - * VdmicPullDownEna = pulldown disabled when Vdmic is disabled - * VdmicDisch = short discharge time - */ - INIT_REGULATOR_REGISTER(AB8500_REGUCTRLDISCH2, 0x16, 0x00), -}; - -/* AB8500 regulators */ -static struct regulator_init_data ab8500_regulators[AB8500_NUM_REGULATORS] = { - /* supplies to the display/camera */ - [AB8500_LDO_AUX1] = { - .supply_regulator = "ab8500-ext-supply3", - .constraints = { - .name = "V-DISPLAY", - .min_uV = 2800000, - .max_uV = 3300000, - .valid_ops_mask = REGULATOR_CHANGE_VOLTAGE | - REGULATOR_CHANGE_STATUS, - .boot_on = 1, /* display is on at boot */ - }, - .num_consumer_supplies = ARRAY_SIZE(ab8500_vaux1_consumers), - .consumer_supplies = ab8500_vaux1_consumers, - }, - /* supplies to the on-board eMMC */ - [AB8500_LDO_AUX2] = { - .supply_regulator = "ab8500-ext-supply3", - .constraints = { - .name = "V-eMMC1", - .min_uV = 1100000, - .max_uV = 3300000, - .valid_ops_mask = REGULATOR_CHANGE_VOLTAGE | - REGULATOR_CHANGE_STATUS | - REGULATOR_CHANGE_MODE, - .valid_modes_mask = REGULATOR_MODE_NORMAL | - REGULATOR_MODE_IDLE, - }, - .num_consumer_supplies = ARRAY_SIZE(ab8500_vaux2_consumers), - .consumer_supplies = ab8500_vaux2_consumers, - }, - /* supply for VAUX3, supplies to SDcard slots */ - [AB8500_LDO_AUX3] = { - .supply_regulator = "ab8500-ext-supply3", - .constraints = { - .name = "V-MMC-SD", - .min_uV = 1100000, - .max_uV = 3300000, - .valid_ops_mask = REGULATOR_CHANGE_VOLTAGE | - REGULATOR_CHANGE_STATUS | - REGULATOR_CHANGE_MODE, - .valid_modes_mask = REGULATOR_MODE_NORMAL | - REGULATOR_MODE_IDLE, - }, - .num_consumer_supplies = ARRAY_SIZE(ab8500_vaux3_consumers), - .consumer_supplies = ab8500_vaux3_consumers, - }, - /* supply for tvout, gpadc, TVOUT LDO */ - [AB8500_LDO_TVOUT] = { - .constraints = { - .name = "V-TVOUT", - .valid_ops_mask = REGULATOR_CHANGE_STATUS, - }, - .num_consumer_supplies = ARRAY_SIZE(ab8500_vtvout_consumers), - .consumer_supplies = ab8500_vtvout_consumers, - }, - /* supply for ab8500-vaudio, VAUDIO LDO */ - [AB8500_LDO_AUDIO] = { - .constraints = { - .name = "V-AUD", - .valid_ops_mask = REGULATOR_CHANGE_STATUS, - }, - .num_consumer_supplies = ARRAY_SIZE(ab8500_vaud_consumers), - .consumer_supplies = ab8500_vaud_consumers, - }, - /* supply for v-anamic1 VAMic1-LDO */ - [AB8500_LDO_ANAMIC1] = { - .constraints = { - .name = "V-AMIC1", - .valid_ops_mask = REGULATOR_CHANGE_STATUS, - }, - .num_consumer_supplies = ARRAY_SIZE(ab8500_vamic1_consumers), - .consumer_supplies = ab8500_vamic1_consumers, - }, - /* supply for v-amic2, VAMIC2 LDO, reuse constants for AMIC1 */ - [AB8500_LDO_ANAMIC2] = { - .constraints = { - .name = "V-AMIC2", - .valid_ops_mask = REGULATOR_CHANGE_STATUS, - }, - .num_consumer_supplies = ARRAY_SIZE(ab8500_vamic2_consumers), - .consumer_supplies = ab8500_vamic2_consumers, - }, - /* supply for v-dmic, VDMIC LDO */ - [AB8500_LDO_DMIC] = { - .constraints = { - .name = "V-DMIC", - .valid_ops_mask = REGULATOR_CHANGE_STATUS, - }, - .num_consumer_supplies = ARRAY_SIZE(ab8500_vdmic_consumers), - .consumer_supplies = ab8500_vdmic_consumers, - }, - /* supply for v-intcore12, VINTCORE12 LDO */ - [AB8500_LDO_INTCORE] = { - .constraints = { - .name = "V-INTCORE", - .min_uV = 1250000, - .max_uV = 1350000, - .input_uV = 1800000, - .valid_ops_mask = REGULATOR_CHANGE_VOLTAGE | - REGULATOR_CHANGE_STATUS | - REGULATOR_CHANGE_MODE | - REGULATOR_CHANGE_DRMS, - .valid_modes_mask = REGULATOR_MODE_NORMAL | - REGULATOR_MODE_IDLE, - }, - .num_consumer_supplies = ARRAY_SIZE(ab8500_vintcore_consumers), - .consumer_supplies = ab8500_vintcore_consumers, - }, - /* supply for U8500 CSI-DSI, VANA LDO */ - [AB8500_LDO_ANA] = { - .constraints = { - .name = "V-CSI-DSI", - .valid_ops_mask = REGULATOR_CHANGE_STATUS, - }, - .num_consumer_supplies = ARRAY_SIZE(ab8500_vana_consumers), - .consumer_supplies = ab8500_vana_consumers, - }, -}; - -/* supply for VextSupply3 */ -static struct regulator_consumer_supply ab8500_ext_supply3_consumers[] = { - /* SIM supply for 3 V SIM cards */ - REGULATOR_SUPPLY("vinvsim", "sim-detect.0"), -}; - -/* - * AB8500 external regulators - */ -static struct regulator_init_data ab8500_ext_regulators[] = { - /* fixed Vbat supplies VSMPS1_EXT_1V8 */ - [AB8500_EXT_SUPPLY1] = { - .constraints = { - .name = "ab8500-ext-supply1", - .min_uV = 1800000, - .max_uV = 1800000, - .initial_mode = REGULATOR_MODE_IDLE, - .boot_on = 1, - .always_on = 1, - }, - }, - /* fixed Vbat supplies VSMPS2_EXT_1V36 and VSMPS5_EXT_1V15 */ - [AB8500_EXT_SUPPLY2] = { - .constraints = { - .name = "ab8500-ext-supply2", - .min_uV = 1360000, - .max_uV = 1360000, - }, - }, - /* fixed Vbat supplies VSMPS3_EXT_3V4 and VSMPS4_EXT_3V4 */ - [AB8500_EXT_SUPPLY3] = { - .constraints = { - .name = "ab8500-ext-supply3", - .min_uV = 3400000, - .max_uV = 3400000, - .valid_ops_mask = REGULATOR_CHANGE_STATUS, - .boot_on = 1, - }, - .num_consumer_supplies = - ARRAY_SIZE(ab8500_ext_supply3_consumers), - .consumer_supplies = ab8500_ext_supply3_consumers, - }, -}; - -struct ab8500_regulator_platform_data ab8500_regulator_plat_data = { - .reg_init = ab8500_reg_init, - .num_reg_init = ARRAY_SIZE(ab8500_reg_init), - .regulator = ab8500_regulators, - .num_regulator = ARRAY_SIZE(ab8500_regulators), - .ext_regulator = ab8500_ext_regulators, - .num_ext_regulator = ARRAY_SIZE(ab8500_ext_regulators), -}; diff --git a/arch/arm/mach-ux500/board-mop500-regulators.h b/arch/arm/mach-ux500/board-mop500-regulators.h deleted file mode 100644 index 3bbb083..0000000 --- a/arch/arm/mach-ux500/board-mop500-regulators.h +++ /dev/null @@ -1,16 +0,0 @@ -/* - * Copyright (C) ST-Ericsson SA 2010 - * - * License Terms: GNU General Public License v2 - * - * Author: Bengt Jonsson for ST-Ericsson - * - * MOP500 board specific initialization for regulators - */ - -#ifndef __BOARD_MOP500_REGULATORS_H -#define __BOARD_MOP500_REGULATORS_H - -extern struct ab8500_regulator_platform_data ab8500_regulator_plat_data; - -#endif diff --git a/arch/arm/mach-ux500/cpu-db8500.c b/arch/arm/mach-ux500/cpu-db8500.c index a557955..c015ef8 100644 --- a/arch/arm/mach-ux500/cpu-db8500.c +++ b/arch/arm/mach-ux500/cpu-db8500.c @@ -28,13 +28,11 @@ #include "setup.h" -#include "board-mop500-regulators.h" #include "board-mop500.h" #include "db8500-regs.h" #include "id.h" static struct ab8500_platform_data ab8500_platdata = { - .regulator = &ab8500_regulator_plat_data, }; static struct prcmu_pdata db8500_prcmu_pdata = { diff --git a/drivers/regulator/ab8500-ext.c b/drivers/regulator/ab8500-ext.c index 84c1ee3..2ca0004 100644 --- a/drivers/regulator/ab8500-ext.c +++ b/drivers/regulator/ab8500-ext.c @@ -25,6 +25,456 @@ #include #include +static struct regulator_consumer_supply ab8500_vaux1_consumers[] = { + /* Main display, u8500 R3 uib */ + REGULATOR_SUPPLY("vddi", "mcde_disp_sony_acx424akp.0"), + /* Main display, u8500 uib and ST uib */ + REGULATOR_SUPPLY("vdd1", "samsung_s6d16d0.0"), + /* Secondary display, ST uib */ + REGULATOR_SUPPLY("vdd1", "samsung_s6d16d0.1"), + /* SFH7741 proximity sensor */ + REGULATOR_SUPPLY("vcc", "gpio-keys.0"), + /* BH1780GLS ambient light sensor */ + REGULATOR_SUPPLY("vcc", "2-0029"), + /* lsm303dlh accelerometer */ + REGULATOR_SUPPLY("vdd", "2-0018"), + /* lsm303dlhc accelerometer */ + REGULATOR_SUPPLY("vdd", "2-0019"), + /* lsm303dlh magnetometer */ + REGULATOR_SUPPLY("vdd", "2-001e"), + /* Rohm BU21013 Touchscreen devices */ + REGULATOR_SUPPLY("avdd", "3-005c"), + REGULATOR_SUPPLY("avdd", "3-005d"), + /* Synaptics RMI4 Touchscreen device */ + REGULATOR_SUPPLY("vdd", "3-004b"), + /* L3G4200D Gyroscope device */ + REGULATOR_SUPPLY("vdd", "2-0068"), + /* Ambient light sensor device */ + REGULATOR_SUPPLY("vdd", "3-0029"), + /* Pressure sensor device */ + REGULATOR_SUPPLY("vdd", "2-005c"), + /* Cypress TrueTouch Touchscreen device */ + REGULATOR_SUPPLY("vcpin", "spi8.0"), + /* Camera device */ + REGULATOR_SUPPLY("vaux12v5", "mmio_camera"), +}; + +static struct regulator_consumer_supply ab8500_vaux2_consumers[] = { + /* On-board eMMC power */ + REGULATOR_SUPPLY("vmmc", "sdi4"), + /* AB8500 audio codec */ + REGULATOR_SUPPLY("vcc-N2158", "ab8500-codec.0"), + /* AB8500 accessory detect 1 */ + REGULATOR_SUPPLY("vcc-N2158", "ab8500-acc-det.0"), + /* AB8500 Tv-out device */ + REGULATOR_SUPPLY("vcc-N2158", "mcde_tv_ab8500.4"), + /* AV8100 HDMI device */ + REGULATOR_SUPPLY("vcc-N2158", "av8100_hdmi.3"), +}; + +static struct regulator_consumer_supply ab8500_vaux3_consumers[] = { + REGULATOR_SUPPLY("v-SD-STM", "stm"), + /* External MMC slot power */ + REGULATOR_SUPPLY("vmmc", "sdi0"), +}; + +static struct regulator_consumer_supply ab8500_vtvout_consumers[] = { + /* TV-out DENC supply */ + REGULATOR_SUPPLY("vtvout", "ab8500-denc.0"), + /* Internal general-purpose ADC */ + REGULATOR_SUPPLY("vddadc", "ab8500-gpadc.0"), + /* ADC for charger */ + REGULATOR_SUPPLY("vddadc", "ab8500-charger.0"), + /* AB8500 Tv-out device */ + REGULATOR_SUPPLY("vtvout", "mcde_tv_ab8500.4"), +}; + +static struct regulator_consumer_supply ab8500_vaud_consumers[] = { + /* AB8500 audio-codec main supply */ + REGULATOR_SUPPLY("vaud", "ab8500-codec.0"), +}; + +static struct regulator_consumer_supply ab8500_vamic1_consumers[] = { + /* AB8500 audio-codec Mic1 supply */ + REGULATOR_SUPPLY("vamic1", "ab8500-codec.0"), +}; + +static struct regulator_consumer_supply ab8500_vamic2_consumers[] = { + /* AB8500 audio-codec Mic2 supply */ + REGULATOR_SUPPLY("vamic2", "ab8500-codec.0"), +}; + +static struct regulator_consumer_supply ab8500_vdmic_consumers[] = { + /* AB8500 audio-codec DMic supply */ + REGULATOR_SUPPLY("vdmic", "ab8500-codec.0"), +}; + +static struct regulator_consumer_supply ab8500_vintcore_consumers[] = { + /* SoC core supply, no device */ + REGULATOR_SUPPLY("v-intcore", NULL), + /* USB Transceiver */ + REGULATOR_SUPPLY("vddulpivio18", "ab8500-usb.0"), + /* Handled by abx500 clk driver */ + REGULATOR_SUPPLY("v-intcore", "abx500-clk.0"), +}; + +static struct regulator_consumer_supply ab8500_vana_consumers[] = { + /* DB8500 DSI */ + REGULATOR_SUPPLY("vdddsi1v2", "mcde"), + REGULATOR_SUPPLY("vdddsi1v2", "b2r2_core"), + REGULATOR_SUPPLY("vdddsi1v2", "b2r2_1_core"), + REGULATOR_SUPPLY("vdddsi1v2", "dsilink.0"), + REGULATOR_SUPPLY("vdddsi1v2", "dsilink.1"), + REGULATOR_SUPPLY("vdddsi1v2", "dsilink.2"), + /* DB8500 CSI */ + REGULATOR_SUPPLY("vddcsi1v2", "mmio_camera"), +}; + +/* ab8500 regulator register initialization */ +static struct ab8500_regulator_reg_init ab8500_reg_init[] = { + /* + * VanaRequestCtrl = HP/LP depending on VxRequest + * VextSupply1RequestCtrl = HP/LP depending on VxRequest + */ + INIT_REGULATOR_REGISTER(AB8500_REGUREQUESTCTRL2, 0xf0, 0x00), + /* + * VextSupply2RequestCtrl = HP/LP depending on VxRequest + * VextSupply3RequestCtrl = HP/LP depending on VxRequest + * Vaux1RequestCtrl = HP/LP depending on VxRequest + * Vaux2RequestCtrl = HP/LP depending on VxRequest + */ + INIT_REGULATOR_REGISTER(AB8500_REGUREQUESTCTRL3, 0xff, 0x00), + /* + * Vaux3RequestCtrl = HP/LP depending on VxRequest + * SwHPReq = Control through SWValid disabled + */ + INIT_REGULATOR_REGISTER(AB8500_REGUREQUESTCTRL4, 0x07, 0x00), + /* + * VanaSysClkReq1HPValid = disabled + * Vaux1SysClkReq1HPValid = disabled + * Vaux2SysClkReq1HPValid = disabled + * Vaux3SysClkReq1HPValid = disabled + */ + INIT_REGULATOR_REGISTER(AB8500_REGUSYSCLKREQ1HPVALID1, 0xe8, 0x00), + /* + * VextSupply1SysClkReq1HPValid = disabled + * VextSupply2SysClkReq1HPValid = disabled + * VextSupply3SysClkReq1HPValid = SysClkReq1 controlled + */ + INIT_REGULATOR_REGISTER(AB8500_REGUSYSCLKREQ1HPVALID2, 0x70, 0x40), + /* + * VanaHwHPReq1Valid = disabled + * Vaux1HwHPreq1Valid = disabled + * Vaux2HwHPReq1Valid = disabled + * Vaux3HwHPReqValid = disabled + */ + INIT_REGULATOR_REGISTER(AB8500_REGUHWHPREQ1VALID1, 0xe8, 0x00), + /* + * VextSupply1HwHPReq1Valid = disabled + * VextSupply2HwHPReq1Valid = disabled + * VextSupply3HwHPReq1Valid = disabled + */ + INIT_REGULATOR_REGISTER(AB8500_REGUHWHPREQ1VALID2, 0x07, 0x00), + /* + * VanaHwHPReq2Valid = disabled + * Vaux1HwHPReq2Valid = disabled + * Vaux2HwHPReq2Valid = disabled + * Vaux3HwHPReq2Valid = disabled + */ + INIT_REGULATOR_REGISTER(AB8500_REGUHWHPREQ2VALID1, 0xe8, 0x00), + /* + * VextSupply1HwHPReq2Valid = disabled + * VextSupply2HwHPReq2Valid = disabled + * VextSupply3HwHPReq2Valid = HWReq2 controlled + */ + INIT_REGULATOR_REGISTER(AB8500_REGUHWHPREQ2VALID2, 0x07, 0x04), + /* + * VanaSwHPReqValid = disabled + * Vaux1SwHPReqValid = disabled + */ + INIT_REGULATOR_REGISTER(AB8500_REGUSWHPREQVALID1, 0xa0, 0x00), + /* + * Vaux2SwHPReqValid = disabled + * Vaux3SwHPReqValid = disabled + * VextSupply1SwHPReqValid = disabled + * VextSupply2SwHPReqValid = disabled + * VextSupply3SwHPReqValid = disabled + */ + INIT_REGULATOR_REGISTER(AB8500_REGUSWHPREQVALID2, 0x1f, 0x00), + /* + * SysClkReq2Valid1 = SysClkReq2 controlled + * SysClkReq3Valid1 = disabled + * SysClkReq4Valid1 = SysClkReq4 controlled + * SysClkReq5Valid1 = disabled + * SysClkReq6Valid1 = SysClkReq6 controlled + * SysClkReq7Valid1 = disabled + * SysClkReq8Valid1 = disabled + */ + INIT_REGULATOR_REGISTER(AB8500_REGUSYSCLKREQVALID1, 0xfe, 0x2a), + /* + * SysClkReq2Valid2 = disabled + * SysClkReq3Valid2 = disabled + * SysClkReq4Valid2 = disabled + * SysClkReq5Valid2 = disabled + * SysClkReq6Valid2 = SysClkReq6 controlled + * SysClkReq7Valid2 = disabled + * SysClkReq8Valid2 = disabled + */ + INIT_REGULATOR_REGISTER(AB8500_REGUSYSCLKREQVALID2, 0xfe, 0x20), + /* + * VTVoutEna = disabled + * Vintcore12Ena = disabled + * Vintcore12Sel = 1.25 V + * Vintcore12LP = inactive (HP) + * VTVoutLP = inactive (HP) + */ + INIT_REGULATOR_REGISTER(AB8500_REGUMISC1, 0xfe, 0x10), + /* + * VaudioEna = disabled + * VdmicEna = disabled + * Vamic1Ena = disabled + * Vamic2Ena = disabled + */ + INIT_REGULATOR_REGISTER(AB8500_VAUDIOSUPPLY, 0x1e, 0x00), + /* + * Vamic1_dzout = high-Z when Vamic1 is disabled + * Vamic2_dzout = high-Z when Vamic2 is disabled + */ + INIT_REGULATOR_REGISTER(AB8500_REGUCTRL1VAMIC, 0x03, 0x00), + /* + * VPll = Hw controlled (NOTE! PRCMU bits) + * VanaRegu = force off + */ + INIT_REGULATOR_REGISTER(AB8500_VPLLVANAREGU, 0x0f, 0x02), + /* + * VrefDDREna = disabled + * VrefDDRSleepMode = inactive (no pulldown) + */ + INIT_REGULATOR_REGISTER(AB8500_VREFDDR, 0x03, 0x00), + /* + * VextSupply1Regu = force LP + * VextSupply2Regu = force OFF + * VextSupply3Regu = force HP (-> STBB2=LP and TPS=LP) + * ExtSupply2Bypass = ExtSupply12LPn ball is 0 when Ena is 0 + * ExtSupply3Bypass = ExtSupply3LPn ball is 0 when Ena is 0 + */ + INIT_REGULATOR_REGISTER(AB8500_EXTSUPPLYREGU, 0xff, 0x13), + /* + * Vaux1Regu = force HP + * Vaux2Regu = force off + */ + INIT_REGULATOR_REGISTER(AB8500_VAUX12REGU, 0x0f, 0x01), + /* + * Vaux3Regu = force off + */ + INIT_REGULATOR_REGISTER(AB8500_VRF1VAUX3REGU, 0x03, 0x00), + /* + * Vaux1Sel = 2.8 V + */ + INIT_REGULATOR_REGISTER(AB8500_VAUX1SEL, 0x0f, 0x0C), + /* + * Vaux2Sel = 2.9 V + */ + INIT_REGULATOR_REGISTER(AB8500_VAUX2SEL, 0x0f, 0x0d), + /* + * Vaux3Sel = 2.91 V + */ + INIT_REGULATOR_REGISTER(AB8500_VRF1VAUX3SEL, 0x07, 0x07), + /* + * VextSupply12LP = disabled (no LP) + */ + INIT_REGULATOR_REGISTER(AB8500_REGUCTRL2SPARE, 0x01, 0x00), + /* + * Vaux1Disch = short discharge time + * Vaux2Disch = short discharge time + * Vaux3Disch = short discharge time + * Vintcore12Disch = short discharge time + * VTVoutDisch = short discharge time + * VaudioDisch = short discharge time + */ + INIT_REGULATOR_REGISTER(AB8500_REGUCTRLDISCH, 0xfc, 0x00), + /* + * VanaDisch = short discharge time + * VdmicPullDownEna = pulldown disabled when Vdmic is disabled + * VdmicDisch = short discharge time + */ + INIT_REGULATOR_REGISTER(AB8500_REGUCTRLDISCH2, 0x16, 0x00), +}; + +/* AB8500 regulators */ +static struct regulator_init_data ab8500_regulators[AB8500_NUM_REGULATORS] = { + /* supplies to the display/camera */ + [AB8500_LDO_AUX1] = { + .supply_regulator = "ab8500-ext-supply3", + .constraints = { + .name = "V-DISPLAY", + .min_uV = 2800000, + .max_uV = 3300000, + .valid_ops_mask = REGULATOR_CHANGE_VOLTAGE | + REGULATOR_CHANGE_STATUS, + .boot_on = 1, /* display is on at boot */ + }, + .num_consumer_supplies = ARRAY_SIZE(ab8500_vaux1_consumers), + .consumer_supplies = ab8500_vaux1_consumers, + }, + /* supplies to the on-board eMMC */ + [AB8500_LDO_AUX2] = { + .supply_regulator = "ab8500-ext-supply3", + .constraints = { + .name = "V-eMMC1", + .min_uV = 1100000, + .max_uV = 3300000, + .valid_ops_mask = REGULATOR_CHANGE_VOLTAGE | + REGULATOR_CHANGE_STATUS | + REGULATOR_CHANGE_MODE, + .valid_modes_mask = REGULATOR_MODE_NORMAL | + REGULATOR_MODE_IDLE, + }, + .num_consumer_supplies = ARRAY_SIZE(ab8500_vaux2_consumers), + .consumer_supplies = ab8500_vaux2_consumers, + }, + /* supply for VAUX3, supplies to SDcard slots */ + [AB8500_LDO_AUX3] = { + .supply_regulator = "ab8500-ext-supply3", + .constraints = { + .name = "V-MMC-SD", + .min_uV = 1100000, + .max_uV = 3300000, + .valid_ops_mask = REGULATOR_CHANGE_VOLTAGE | + REGULATOR_CHANGE_STATUS | + REGULATOR_CHANGE_MODE, + .valid_modes_mask = REGULATOR_MODE_NORMAL | + REGULATOR_MODE_IDLE, + }, + .num_consumer_supplies = ARRAY_SIZE(ab8500_vaux3_consumers), + .consumer_supplies = ab8500_vaux3_consumers, + }, + /* supply for tvout, gpadc, TVOUT LDO */ + [AB8500_LDO_TVOUT] = { + .constraints = { + .name = "V-TVOUT", + .valid_ops_mask = REGULATOR_CHANGE_STATUS, + }, + .num_consumer_supplies = ARRAY_SIZE(ab8500_vtvout_consumers), + .consumer_supplies = ab8500_vtvout_consumers, + }, + /* supply for ab8500-vaudio, VAUDIO LDO */ + [AB8500_LDO_AUDIO] = { + .constraints = { + .name = "V-AUD", + .valid_ops_mask = REGULATOR_CHANGE_STATUS, + }, + .num_consumer_supplies = ARRAY_SIZE(ab8500_vaud_consumers), + .consumer_supplies = ab8500_vaud_consumers, + }, + /* supply for v-anamic1 VAMic1-LDO */ + [AB8500_LDO_ANAMIC1] = { + .constraints = { + .name = "V-AMIC1", + .valid_ops_mask = REGULATOR_CHANGE_STATUS, + }, + .num_consumer_supplies = ARRAY_SIZE(ab8500_vamic1_consumers), + .consumer_supplies = ab8500_vamic1_consumers, + }, + /* supply for v-amic2, VAMIC2 LDO, reuse constants for AMIC1 */ + [AB8500_LDO_ANAMIC2] = { + .constraints = { + .name = "V-AMIC2", + .valid_ops_mask = REGULATOR_CHANGE_STATUS, + }, + .num_consumer_supplies = ARRAY_SIZE(ab8500_vamic2_consumers), + .consumer_supplies = ab8500_vamic2_consumers, + }, + /* supply for v-dmic, VDMIC LDO */ + [AB8500_LDO_DMIC] = { + .constraints = { + .name = "V-DMIC", + .valid_ops_mask = REGULATOR_CHANGE_STATUS, + }, + .num_consumer_supplies = ARRAY_SIZE(ab8500_vdmic_consumers), + .consumer_supplies = ab8500_vdmic_consumers, + }, + /* supply for v-intcore12, VINTCORE12 LDO */ + [AB8500_LDO_INTCORE] = { + .constraints = { + .name = "V-INTCORE", + .min_uV = 1250000, + .max_uV = 1350000, + .input_uV = 1800000, + .valid_ops_mask = REGULATOR_CHANGE_VOLTAGE | + REGULATOR_CHANGE_STATUS | + REGULATOR_CHANGE_MODE | + REGULATOR_CHANGE_DRMS, + .valid_modes_mask = REGULATOR_MODE_NORMAL | + REGULATOR_MODE_IDLE, + }, + .num_consumer_supplies = ARRAY_SIZE(ab8500_vintcore_consumers), + .consumer_supplies = ab8500_vintcore_consumers, + }, + /* supply for U8500 CSI-DSI, VANA LDO */ + [AB8500_LDO_ANA] = { + .constraints = { + .name = "V-CSI-DSI", + .valid_ops_mask = REGULATOR_CHANGE_STATUS, + }, + .num_consumer_supplies = ARRAY_SIZE(ab8500_vana_consumers), + .consumer_supplies = ab8500_vana_consumers, + }, +}; + +/* supply for VextSupply3 */ +static struct regulator_consumer_supply ab8500_ext_supply3_consumers[] = { + /* SIM supply for 3 V SIM cards */ + REGULATOR_SUPPLY("vinvsim", "sim-detect.0"), +}; + +/* + * AB8500 external regulators + */ +static struct regulator_init_data ab8500_ext_regulators[] = { + /* fixed Vbat supplies VSMPS1_EXT_1V8 */ + [AB8500_EXT_SUPPLY1] = { + .constraints = { + .name = "ab8500-ext-supply1", + .min_uV = 1800000, + .max_uV = 1800000, + .initial_mode = REGULATOR_MODE_IDLE, + .boot_on = 1, + .always_on = 1, + }, + }, + /* fixed Vbat supplies VSMPS2_EXT_1V36 and VSMPS5_EXT_1V15 */ + [AB8500_EXT_SUPPLY2] = { + .constraints = { + .name = "ab8500-ext-supply2", + .min_uV = 1360000, + .max_uV = 1360000, + }, + }, + /* fixed Vbat supplies VSMPS3_EXT_3V4 and VSMPS4_EXT_3V4 */ + [AB8500_EXT_SUPPLY3] = { + .constraints = { + .name = "ab8500-ext-supply3", + .min_uV = 3400000, + .max_uV = 3400000, + .valid_ops_mask = REGULATOR_CHANGE_STATUS, + .boot_on = 1, + }, + .num_consumer_supplies = + ARRAY_SIZE(ab8500_ext_supply3_consumers), + .consumer_supplies = ab8500_ext_supply3_consumers, + }, +}; + +static struct ab8500_regulator_platform_data ab8500_regulator_plat_data = { + .reg_init = ab8500_reg_init, + .num_reg_init = ARRAY_SIZE(ab8500_reg_init), + .regulator = ab8500_regulators, + .num_regulator = ARRAY_SIZE(ab8500_regulators), + .ext_regulator = ab8500_ext_regulators, + .num_ext_regulator = ARRAY_SIZE(ab8500_ext_regulators), +}; + /** * struct ab8500_ext_regulator_info - ab8500 regulator information * @dev: device pointer @@ -344,8 +794,7 @@ static struct of_regulator_match ab8500_ext_regulator_match[] = { static int ab8500_ext_regulator_probe(struct platform_device *pdev) { struct ab8500 *ab8500 = dev_get_drvdata(pdev->dev.parent); - struct ab8500_platform_data *ppdata; - struct ab8500_regulator_platform_data *pdata; + struct ab8500_regulator_platform_data *pdata = &ab8500_regulator_plat_data; struct device_node *np = pdev->dev.of_node; struct regulator_config config = { }; int i, err; @@ -366,18 +815,6 @@ static int ab8500_ext_regulator_probe(struct platform_device *pdev) return -EINVAL; } - ppdata = dev_get_platdata(ab8500->dev); - if (!ppdata) { - dev_err(&pdev->dev, "null parent pdata\n"); - return -EINVAL; - } - - pdata = ppdata->regulator; - if (!pdata) { - dev_err(&pdev->dev, "null pdata\n"); - return -EINVAL; - } - /* make sure the platform data has the correct size */ if (pdata->num_ext_regulator != ARRAY_SIZE(ab8500_ext_regulator_info)) { dev_err(&pdev->dev, "Configuration error: size mismatch.\n"); -- cgit v0.10.2 From cf4129ea212d71f7860bded25d42388265172bee Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Fri, 18 Mar 2016 16:50:33 +0100 Subject: ASoC: ab8500-codec: remove platform data based probe The ux500 platform hasn't used board files for a long time, and nothing defines a ab8500_codec_platform_data, so we can just remove the probing based on that and always use device tree properties directly. Signed-off-by: Arnd Bergmann Acked-by: Mark Brown Signed-off-by: Linus Walleij diff --git a/sound/soc/codecs/ab8500-codec.c b/sound/soc/codecs/ab8500-codec.c index 8b1d0c1..2fc8915 100644 --- a/sound/soc/codecs/ab8500-codec.c +++ b/sound/soc/codecs/ab8500-codec.c @@ -2464,45 +2464,20 @@ static int ab8500_codec_probe(struct snd_soc_codec *codec) struct device *dev = codec->dev; struct device_node *np = dev->of_node; struct ab8500_codec_drvdata *drvdata = dev_get_drvdata(dev); - struct ab8500_platform_data *pdata; + struct ab8500_codec_platform_data codec_pdata; struct filter_control *fc; int status; dev_dbg(dev, "%s: Enter.\n", __func__); - /* Setup AB8500 according to board-settings */ - pdata = dev_get_platdata(dev->parent); + ab8500_codec_of_probe(dev, np, &codec_pdata); - if (np) { - if (!pdata) - pdata = devm_kzalloc(dev, - sizeof(struct ab8500_platform_data), - GFP_KERNEL); - - if (pdata && !pdata->codec) - pdata->codec - = devm_kzalloc(dev, - sizeof(struct ab8500_codec_platform_data), - GFP_KERNEL); - - if (!(pdata && pdata->codec)) - return -ENOMEM; - - ab8500_codec_of_probe(dev, np, pdata->codec); - - } else { - if (!(pdata && pdata->codec)) { - dev_err(dev, "No codec platform data or DT found\n"); - return -EINVAL; - } - } - - status = ab8500_audio_setup_mics(codec, &pdata->codec->amics); + status = ab8500_audio_setup_mics(codec, &codec_pdata.amics); if (status < 0) { pr_err("%s: Failed to setup mics (%d)!\n", __func__, status); return status; } - status = ab8500_audio_set_ear_cmv(codec, pdata->codec->ear_cmv); + status = ab8500_audio_set_ear_cmv(codec, codec_pdata.ear_cmv); if (status < 0) { pr_err("%s: Failed to set earpiece CM-voltage (%d)!\n", __func__, status); -- cgit v0.10.2 From 4e657946cbf68998b51b331375a1209e90792fe4 Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Fri, 18 Mar 2016 16:53:31 +0100 Subject: mfd: db8500 stop passing around platform data Except for the constant DB8500_PRCMU_FW_VERSION_OFFSET number, nothing is ever passed through the platform data and used in a driver, so we can simply stop passing it around. Acked-by: Lee Jones Signed-off-by: Arnd Bergmann Signed-off-by: Linus Walleij diff --git a/arch/arm/mach-ux500/cpu-db8500.c b/arch/arm/mach-ux500/cpu-db8500.c index c015ef8..bbd1b4b 100644 --- a/arch/arm/mach-ux500/cpu-db8500.c +++ b/arch/arm/mach-ux500/cpu-db8500.c @@ -16,8 +16,6 @@ #include #include #include -#include -#include #include #include #include @@ -32,15 +30,6 @@ #include "db8500-regs.h" #include "id.h" -static struct ab8500_platform_data ab8500_platdata = { -}; - -static struct prcmu_pdata db8500_prcmu_pdata = { - .ab_platdata = &ab8500_platdata, - .version_offset = DB8500_PRCMU_FW_VERSION_OFFSET, - .legacy_offset = DB8500_PRCMU_LEGACY_OFFSET, -}; - static void __init u8500_map_io(void) { debug_ll_io_init(); @@ -109,8 +98,7 @@ static struct of_dev_auxdata u8500_auxdata_lookup[] __initdata = { OF_DEV_AUXDATA("stericsson,ux500-msp-i2s", 0x80125000, "ux500-msp-i2s.3", &msp3_platform_data), /* Requires non-DT:able platform data. */ - OF_DEV_AUXDATA("stericsson,db8500-prcmu", 0x80157000, "db8500-prcmu", - &db8500_prcmu_pdata), + OF_DEV_AUXDATA("stericsson,db8500-prcmu", 0x80157000, "db8500-prcmu", NULL), OF_DEV_AUXDATA("stericsson,ux500-cryp", 0xa03cb000, "cryp1", NULL), OF_DEV_AUXDATA("stericsson,ux500-hash", 0xa03c2000, "hash1", NULL), OF_DEV_AUXDATA("stericsson,snd-soc-mop500", 0, "snd-soc-mop500.0", @@ -119,8 +107,7 @@ static struct of_dev_auxdata u8500_auxdata_lookup[] __initdata = { }; static struct of_dev_auxdata u8540_auxdata_lookup[] __initdata = { - OF_DEV_AUXDATA("stericsson,db8500-prcmu", 0x80157000, "db8500-prcmu", - &db8500_prcmu_pdata), + OF_DEV_AUXDATA("stericsson,db8500-prcmu", 0x80157000, "db8500-prcmu", NULL), {}, }; diff --git a/arch/arm/mach-ux500/setup.h b/arch/arm/mach-ux500/setup.h index c704254..e606847 100644 --- a/arch/arm/mach-ux500/setup.h +++ b/arch/arm/mach-ux500/setup.h @@ -13,7 +13,6 @@ #include #include -#include void ux500_restart(enum reboot_mode mode, const char *cmd); diff --git a/drivers/mfd/ab8500-core.c b/drivers/mfd/ab8500-core.c index f3d6891..589eebf 100644 --- a/drivers/mfd/ab8500-core.c +++ b/drivers/mfd/ab8500-core.c @@ -1087,7 +1087,6 @@ static int ab8500_probe(struct platform_device *pdev) "Vbus Detect (USB)", "USB ID Detect", "UART Factory Mode Detect"}; - struct ab8500_platform_data *plat = dev_get_platdata(&pdev->dev); const struct platform_device_id *platid = platform_get_device_id(pdev); enum ab8500_version version = AB8500_VERSION_UNDEFINED; struct device_node *np = pdev->dev.of_node; @@ -1219,9 +1218,6 @@ static int ab8500_probe(struct platform_device *pdev) pr_cont("None\n"); } - if (plat && plat->init) - plat->init(ab8500); - if (is_ab9540(ab8500)) { ret = get_register_interruptible(ab8500, AB8500_CHARGER, AB8500_CH_USBCH_STAT1_REG, &value); diff --git a/drivers/mfd/ab8500-sysctrl.c b/drivers/mfd/ab8500-sysctrl.c index b9f0010..207cc49 100644 --- a/drivers/mfd/ab8500-sysctrl.c +++ b/drivers/mfd/ab8500-sysctrl.c @@ -127,45 +127,11 @@ EXPORT_SYMBOL(ab8500_sysctrl_write); static int ab8500_sysctrl_probe(struct platform_device *pdev) { - struct ab8500 *ab8500 = dev_get_drvdata(pdev->dev.parent); - struct ab8500_platform_data *plat; - struct ab8500_sysctrl_platform_data *pdata; - - plat = dev_get_platdata(pdev->dev.parent); - - if (!plat) - return -EINVAL; - sysctrl_dev = &pdev->dev; if (!pm_power_off) pm_power_off = ab8500_power_off; - pdata = plat->sysctrl; - if (pdata) { - int last, ret, i, j; - - if (is_ab8505(ab8500)) - last = AB8500_SYSCLKREQ4RFCLKBUF; - else - last = AB8500_SYSCLKREQ8RFCLKBUF; - - for (i = AB8500_SYSCLKREQ1RFCLKBUF; i <= last; i++) { - j = i - AB8500_SYSCLKREQ1RFCLKBUF; - ret = ab8500_sysctrl_write(i, 0xff, - pdata->initial_req_buf_config[j]); - dev_dbg(&pdev->dev, - "Setting SysClkReq%dRfClkBuf 0x%X\n", - j + 1, - pdata->initial_req_buf_config[j]); - if (ret < 0) { - dev_err(&pdev->dev, - "Can't set sysClkReq%dRfClkBuf: %d\n", - j + 1, ret); - } - } - } - return 0; } diff --git a/drivers/mfd/db8500-prcmu.c b/drivers/mfd/db8500-prcmu.c index c0a86ae..388e268 100644 --- a/drivers/mfd/db8500-prcmu.c +++ b/drivers/mfd/db8500-prcmu.c @@ -3094,8 +3094,7 @@ static void db8500_prcmu_update_cpufreq(void) } } -static int db8500_prcmu_register_ab8500(struct device *parent, - struct ab8500_platform_data *pdata) +static int db8500_prcmu_register_ab8500(struct device *parent) { struct device_node *np; struct resource ab8500_resource; @@ -3103,8 +3102,6 @@ static int db8500_prcmu_register_ab8500(struct device *parent, .name = "ab8500-core", .of_compatible = "stericsson,ab8500", .id = AB8500_VERSION_AB8500, - .platform_data = pdata, - .pdata_size = sizeof(struct ab8500_platform_data), .resources = &ab8500_resource, .num_resources = 1, }; @@ -3133,7 +3130,6 @@ static int db8500_prcmu_register_ab8500(struct device *parent, static int db8500_prcmu_probe(struct platform_device *pdev) { struct device_node *np = pdev->dev.of_node; - struct prcmu_pdata *pdata = dev_get_platdata(&pdev->dev); int irq = 0, err = 0; struct resource *res; @@ -3149,7 +3145,7 @@ static int db8500_prcmu_probe(struct platform_device *pdev) return -ENOMEM; } init_prcm_registers(); - dbx500_fw_version_init(pdev, pdata->version_offset); + dbx500_fw_version_init(pdev, DB8500_PRCMU_FW_VERSION_OFFSET); res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "prcmu-tcdm"); if (!res) { dev_err(&pdev->dev, "no prcmu tcdm region provided\n"); @@ -3204,7 +3200,7 @@ static int db8500_prcmu_probe(struct platform_device *pdev) } } - err = db8500_prcmu_register_ab8500(&pdev->dev, pdata->ab_platdata); + err = db8500_prcmu_register_ab8500(&pdev->dev); if (err) { mfd_remove_devices(&pdev->dev); pr_err("prcmu: Failed to add ab8500 subdevice\n"); diff --git a/include/linux/mfd/abx500/ab8500-sysctrl.h b/include/linux/mfd/abx500/ab8500-sysctrl.h index 6893127..01024d1 100644 --- a/include/linux/mfd/abx500/ab8500-sysctrl.h +++ b/include/linux/mfd/abx500/ab8500-sysctrl.h @@ -37,12 +37,6 @@ static inline int ab8500_sysctrl_clear(u16 reg, u8 bits) return ab8500_sysctrl_write(reg, bits, 0); } -/* Configuration data for SysClkReq1RfClkBuf - SysClkReq8RfClkBuf */ -struct ab8500_sysctrl_platform_data { - u8 initial_req_buf_config[8]; - u16 (*reboot_reason_code)(const char *cmd); -}; - /* Registers */ #define AB8500_TURNONSTATUS 0x100 #define AB8500_RESETSTATUS 0x101 diff --git a/include/linux/mfd/dbx500-prcmu.h b/include/linux/mfd/dbx500-prcmu.h index bf5109d..5d37460 100644 --- a/include/linux/mfd/dbx500-prcmu.h +++ b/include/linux/mfd/dbx500-prcmu.h @@ -178,16 +178,6 @@ enum ddr_pwrst { #define DB8500_PRCMU_LEGACY_OFFSET 0xDD4 -struct prcmu_pdata -{ - bool enable_set_ddr_opp; - bool enable_ape_opp_100_voltage; - struct ab8500_platform_data *ab_platdata; - u32 version_offset; - u32 legacy_offset; - u32 adt_offset; -}; - #define PRCMU_FW_PROJECT_U8500 2 #define PRCMU_FW_PROJECT_U8400 3 #define PRCMU_FW_PROJECT_U9500 4 /* Customer specific */ -- cgit v0.10.2 From 1e6cbc0691abc6b1a053f98d8ce1d692c8c71501 Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Mon, 20 Jun 2016 22:27:23 +0200 Subject: ARM: ux500: move l2x0 init to .init_irq The generic IRQ init function also enables the l2 cache implicitly when the machine descriptor sets an .l2c_aux_mask. Let's use that on ux500 and remove the ux500_l2x0_init() along with the cpu_is_u8500_family checks. Signed-off-by: Arnd Bergmann Signed-off-by: Linus Walleij diff --git a/arch/arm/mach-ux500/cache-l2x0.c b/arch/arm/mach-ux500/cache-l2x0.c index 780bd13..d7abc11 100644 --- a/arch/arm/mach-ux500/cache-l2x0.c +++ b/arch/arm/mach-ux500/cache-l2x0.c @@ -51,17 +51,11 @@ static void ux500_l2c310_write_sec(unsigned long val, unsigned reg) */ } -static int __init ux500_l2x0_init(void) +void __init ux500_l2x0_init(void) { - /* Multiplatform guard */ - if (!((cpu_is_u8500_family() || cpu_is_ux540_family()))) - return -ENODEV; - /* Unlock before init */ ux500_l2x0_unlock(); outer_cache.write_sec = ux500_l2c310_write_sec; - l2x0_of_init(0, ~0); return 0; } -early_initcall(ux500_l2x0_init); diff --git a/arch/arm/mach-ux500/cpu-db8500.c b/arch/arm/mach-ux500/cpu-db8500.c index bbd1b4b..3874e9c 100644 --- a/arch/arm/mach-ux500/cpu-db8500.c +++ b/arch/arm/mach-ux500/cpu-db8500.c @@ -141,10 +141,11 @@ static const char * stericsson_dt_platform_compat[] = { }; DT_MACHINE_START(U8500_DT, "ST-Ericsson Ux5x0 platform (Device Tree Support)") + .l2c_aux_val = 0, + .l2c_aux_mask = ~0, .map_io = u8500_map_io, .init_irq = ux500_init_irq, .init_machine = u8500_init_machine, - .init_late = NULL, .dt_compat = stericsson_dt_platform_compat, .restart = ux500_restart, MACHINE_END diff --git a/arch/arm/mach-ux500/cpu.c b/arch/arm/mach-ux500/cpu.c index 82156cb..f8c2d6f 100644 --- a/arch/arm/mach-ux500/cpu.c +++ b/arch/arm/mach-ux500/cpu.c @@ -65,6 +65,7 @@ void __init ux500_init_irq(void) } prcmu_early_init(r.start, r.end-r.start); ux500_pm_init(r.start, r.end-r.start); + ux500_l2x0_init(); /* * Init clocks here so that they are available for system timer diff --git a/arch/arm/mach-ux500/setup.h b/arch/arm/mach-ux500/setup.h index e606847..8b44b64 100644 --- a/arch/arm/mach-ux500/setup.h +++ b/arch/arm/mach-ux500/setup.h @@ -14,6 +14,9 @@ #include #include + +void ux500_l2x0_init(void); + void ux500_restart(enum reboot_mode mode, const char *cmd); void __init ux500_setup_id(void); -- cgit v0.10.2 From 269f1aac1410d27959bd073a089eb85eed55c8a1 Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Mon, 20 Jun 2016 22:47:53 +0200 Subject: ARM: ux500: use CLK_OF_DECLARE() The ux500 DT support predates the CLK_OF_DECLARE macro and calls directly into the clk driver from platform code. Converting this to CLK_OF_DECLARE makes the code much nicer and similar to how modern platforms do it today. It also removes the last user of cpu_is_u8500_family() etc. Signed-off-by: Arnd Bergmann Signed-off-by: Linus Walleij diff --git a/MAINTAINERS b/MAINTAINERS index 7304d2e..1c75f3f 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -1821,7 +1821,6 @@ L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers) T: git git://git.linaro.org/people/ulfh/clk.git S: Maintained F: drivers/clk/ux500/ -F: include/linux/platform_data/clk-ux500.h ARM/VERSATILE EXPRESS PLATFORM M: Liviu Dudau diff --git a/arch/arm/mach-ux500/cpu.c b/arch/arm/mach-ux500/cpu.c index f8c2d6f..63853e7 100644 --- a/arch/arm/mach-ux500/cpu.c +++ b/arch/arm/mach-ux500/cpu.c @@ -19,7 +19,6 @@ #include #include #include -#include #include #include @@ -66,17 +65,6 @@ void __init ux500_init_irq(void) prcmu_early_init(r.start, r.end-r.start); ux500_pm_init(r.start, r.end-r.start); ux500_l2x0_init(); - - /* - * Init clocks here so that they are available for system timer - * initialization. - */ - if (cpu_is_u8500_family()) - u8500_clk_init(); - else if (cpu_is_u9540()) - u9540_clk_init(); - else if (cpu_is_u8540()) - u8540_clk_init(); } static const char * __init ux500_get_machine(void) diff --git a/drivers/clk/ux500/u8500_of_clk.c b/drivers/clk/ux500/u8500_of_clk.c index 9a736d9..e960d68 100644 --- a/drivers/clk/ux500/u8500_of_clk.c +++ b/drivers/clk/ux500/u8500_of_clk.c @@ -11,7 +11,6 @@ #include #include #include -#include #include "clk.h" #define PRCC_NUM_PERIPH_CLUSTERS 6 @@ -48,11 +47,6 @@ static struct clk *ux500_twocell_get(struct of_phandle_args *clkspec, return PRCC_SHOW(clk_data, base, bit); } -static const struct of_device_id u8500_clk_of_match[] = { - { .compatible = "stericsson,u8500-clks", }, - { }, -}; - /* CLKRST4 is missing making it hard to index things */ enum clkrst_index { CLKRST1_INDEX = 0, @@ -63,22 +57,15 @@ enum clkrst_index { CLKRST_MAX, }; -void u8500_clk_init(void) +static void u8500_clk_init(struct device_node *np) { struct prcmu_fw_version *fw_version; - struct device_node *np = NULL; struct device_node *child = NULL; const char *sgaclk_parent = NULL; struct clk *clk, *rtc_clk, *twd_clk; u32 bases[CLKRST_MAX]; int i; - if (of_have_populated_dt()) - np = of_find_matching_node(NULL, u8500_clk_of_match); - if (!np) { - pr_err("Either DT or U8500 Clock node not found\n"); - return; - } for (i = 0; i < ARRAY_SIZE(bases); i++) { struct resource r; @@ -573,3 +560,4 @@ void u8500_clk_init(void) of_clk_add_provider(child, of_clk_src_simple_get, twd_clk); } } +CLK_OF_DECLARE(u8500_clks, "stericsson,u8500-clks", u8500_clk_init); diff --git a/drivers/clk/ux500/u8540_clk.c b/drivers/clk/ux500/u8540_clk.c index 86549e5..133859f 100644 --- a/drivers/clk/ux500/u8540_clk.c +++ b/drivers/clk/ux500/u8540_clk.c @@ -12,14 +12,8 @@ #include #include #include -#include #include "clk.h" -static const struct of_device_id u8540_clk_of_match[] = { - { .compatible = "stericsson,u8540-clks", }, - { } -}; - /* CLKRST4 is missing making it hard to index things */ enum clkrst_index { CLKRST1_INDEX = 0, @@ -30,19 +24,12 @@ enum clkrst_index { CLKRST_MAX, }; -void u8540_clk_init(void) +static void u8540_clk_init(struct device_node *np) { struct clk *clk; - struct device_node *np = NULL; u32 bases[CLKRST_MAX]; int i; - if (of_have_populated_dt()) - np = of_find_matching_node(NULL, u8540_clk_of_match); - if (!np) { - pr_err("Either DT or U8540 Clock node not found\n"); - return; - } for (i = 0; i < ARRAY_SIZE(bases); i++) { struct resource r; @@ -607,3 +594,4 @@ void u8540_clk_init(void) bases[CLKRST6_INDEX], BIT(0), CLK_SET_RATE_GATE); clk_register_clkdev(clk, NULL, "rng"); } +CLK_OF_DECLARE(u8540_clks, "stericsson,u8540-clks", u8540_clk_init); diff --git a/drivers/clk/ux500/u9540_clk.c b/drivers/clk/ux500/u9540_clk.c index 2138a4c..7b6bca4 100644 --- a/drivers/clk/ux500/u9540_clk.c +++ b/drivers/clk/ux500/u9540_clk.c @@ -9,10 +9,10 @@ #include #include -#include #include "clk.h" -void u9540_clk_init(void) +static void u9540_clk_init(struct device_node *np) { /* register clocks here */ } +CLK_OF_DECLARE(u9540_clks, "stericsson,u9540-clks", u9540_clk_init); diff --git a/include/linux/platform_data/clk-ux500.h b/include/linux/platform_data/clk-ux500.h deleted file mode 100644 index 3af0da1..0000000 --- a/include/linux/platform_data/clk-ux500.h +++ /dev/null @@ -1,17 +0,0 @@ -/* - * Clock definitions for ux500 platforms - * - * Copyright (C) 2012 ST-Ericsson SA - * Author: Ulf Hansson - * - * License terms: GNU General Public License (GPL) version 2 - */ - -#ifndef __CLK_UX500_H -#define __CLK_UX500_H - -void u8500_clk_init(void); -void u9540_clk_init(void); -void u8540_clk_init(void); - -#endif /* __CLK_UX500_H */ -- cgit v0.10.2 From 4cf124f9a9d0d3505b6335d6061da54f83125b8e Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Mon, 20 Jun 2016 22:52:25 +0200 Subject: ARM: ux500: remove cpu_is_u* helpers These functions are all unused now and can be removed. Signed-off-by: Arnd Bergmann Signed-off-by: Linus Walleij diff --git a/arch/arm/mach-ux500/id.h b/arch/arm/mach-ux500/id.h index bcc58a8..929f78e 100644 --- a/arch/arm/mach-ux500/id.h +++ b/arch/arm/mach-ux500/id.h @@ -32,113 +32,6 @@ static inline unsigned int __attribute_const__ dbx500_revision(void) return dbx500_id.revision; } -/* - * SOCs - */ - -static inline bool __attribute_const__ cpu_is_u8500(void) -{ - return dbx500_partnumber() == 0x8500; -} - -static inline bool __attribute_const__ cpu_is_u8520(void) -{ - return dbx500_partnumber() == 0x8520; -} - -static inline bool cpu_is_u8500_family(void) -{ - return cpu_is_u8500() || cpu_is_u8520(); -} - -static inline bool __attribute_const__ cpu_is_u9540(void) -{ - return dbx500_partnumber() == 0x9540; -} - -static inline bool __attribute_const__ cpu_is_u8540(void) -{ - return dbx500_partnumber() == 0x8540; -} - -static inline bool __attribute_const__ cpu_is_u8580(void) -{ - return dbx500_partnumber() == 0x8580; -} - -static inline bool cpu_is_ux540_family(void) -{ - return cpu_is_u9540() || cpu_is_u8540() || cpu_is_u8580(); -} - -/* - * 8500 revisions - */ - -static inline bool __attribute_const__ cpu_is_u8500ed(void) -{ - return cpu_is_u8500() && dbx500_revision() == 0x00; -} - -static inline bool __attribute_const__ cpu_is_u8500v1(void) -{ - return cpu_is_u8500() && (dbx500_revision() & 0xf0) == 0xA0; -} - -static inline bool __attribute_const__ cpu_is_u8500v10(void) -{ - return cpu_is_u8500() && dbx500_revision() == 0xA0; -} - -static inline bool __attribute_const__ cpu_is_u8500v11(void) -{ - return cpu_is_u8500() && dbx500_revision() == 0xA1; -} - -static inline bool __attribute_const__ cpu_is_u8500v2(void) -{ - return cpu_is_u8500() && ((dbx500_revision() & 0xf0) == 0xB0); -} - -static inline bool cpu_is_u8500v20(void) -{ - return cpu_is_u8500() && (dbx500_revision() == 0xB0); -} - -static inline bool cpu_is_u8500v21(void) -{ - return cpu_is_u8500() && (dbx500_revision() == 0xB1); -} - -static inline bool cpu_is_u8500v22(void) -{ - return cpu_is_u8500() && (dbx500_revision() == 0xB2); -} - -static inline bool cpu_is_u8500v20_or_later(void) -{ - return (cpu_is_u8500() && !cpu_is_u8500v10() && !cpu_is_u8500v11()); -} - -/* - * 8540 revisions - */ - -static inline bool __attribute_const__ cpu_is_u8540v10(void) -{ - return cpu_is_u8540() && dbx500_revision() == 0xA0; -} - -static inline bool __attribute_const__ cpu_is_u8580v10(void) -{ - return cpu_is_u8580() && dbx500_revision() == 0xA0; -} - -static inline bool ux500_is_svp(void) -{ - return false; -} - #define ux500_unknown_soc() BUG() #endif -- cgit v0.10.2 From cd1dc431d02afda521bed53b7c87a678a3ac246f Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Mon, 20 Jun 2016 23:01:26 +0200 Subject: ARM: ux500: consolidate soc_device code in id.c Nothing else uses the global dbx500_asic_id structure, so we can merge the two small files that reference it into one. Signed-off-by: Arnd Bergmann Signed-off-by: Linus Walleij diff --git a/arch/arm/mach-ux500/cache-l2x0.c b/arch/arm/mach-ux500/cache-l2x0.c index d7abc11..adec59c 100644 --- a/arch/arm/mach-ux500/cache-l2x0.c +++ b/arch/arm/mach-ux500/cache-l2x0.c @@ -12,7 +12,6 @@ #include #include "db8500-regs.h" -#include "id.h" static int __init ux500_l2x0_unlock(void) { diff --git a/arch/arm/mach-ux500/cpu-db8500.c b/arch/arm/mach-ux500/cpu-db8500.c index 3874e9c..e7eba47 100644 --- a/arch/arm/mach-ux500/cpu-db8500.c +++ b/arch/arm/mach-ux500/cpu-db8500.c @@ -20,7 +20,6 @@ #include #include #include -#include #include @@ -60,31 +59,6 @@ static struct arm_pmu_platdata db8500_pmu_platdata = { .handle_irq = db8500_pmu_handler, }; -static const char *db8500_read_soc_id(void) -{ - void __iomem *uid; - const char *retstr; - - uid = ioremap(U8500_BB_UID_BASE, 0x20); - if (!uid) - return NULL; - /* Throw these device-specific numbers into the entropy pool */ - add_device_randomness(uid, 0x14); - retstr = kasprintf(GFP_KERNEL, "%08x%08x%08x%08x%08x", - readl((u32 *)uid+0), - readl((u32 *)uid+1), readl((u32 *)uid+2), - readl((u32 *)uid+3), readl((u32 *)uid+4)); - iounmap(uid); - return retstr; -} - -static struct device * __init db8500_soc_device_init(void) -{ - const char *soc_id = db8500_read_soc_id(); - - return ux500_soc_device_init(soc_id); -} - static struct of_dev_auxdata u8500_auxdata_lookup[] __initdata = { /* Requires call-back bindings. */ OF_DEV_AUXDATA("arm,cortex-a9-pmu", 0, "arm-pmu", &db8500_pmu_platdata), @@ -121,7 +95,7 @@ static const struct of_device_id u8500_local_bus_nodes[] = { static void __init u8500_init_machine(void) { - struct device *parent = db8500_soc_device_init(); + struct device *parent = ux500_soc_device_init(); /* automatically probe child nodes of dbx5x0 devices */ if (of_machine_is_compatible("st-ericsson,u8540")) diff --git a/arch/arm/mach-ux500/cpu.c b/arch/arm/mach-ux500/cpu.c index 63853e7..a34af28 100644 --- a/arch/arm/mach-ux500/cpu.c +++ b/arch/arm/mach-ux500/cpu.c @@ -27,7 +27,6 @@ #include "board-mop500.h" #include "db8500-regs.h" -#include "id.h" void ux500_restart(enum reboot_mode mode, const char *cmd) { @@ -66,72 +65,3 @@ void __init ux500_init_irq(void) ux500_pm_init(r.start, r.end-r.start); ux500_l2x0_init(); } - -static const char * __init ux500_get_machine(void) -{ - return kasprintf(GFP_KERNEL, "DB%4x", dbx500_partnumber()); -} - -static const char * __init ux500_get_family(void) -{ - return kasprintf(GFP_KERNEL, "ux500"); -} - -static const char * __init ux500_get_revision(void) -{ - unsigned int rev = dbx500_revision(); - - if (rev == 0x01) - return kasprintf(GFP_KERNEL, "%s", "ED"); - else if (rev >= 0xA0) - return kasprintf(GFP_KERNEL, "%d.%d", - (rev >> 4) - 0xA + 1, rev & 0xf); - - return kasprintf(GFP_KERNEL, "%s", "Unknown"); -} - -static ssize_t ux500_get_process(struct device *dev, - struct device_attribute *attr, - char *buf) -{ - if (dbx500_id.process == 0x00) - return sprintf(buf, "Standard\n"); - - return sprintf(buf, "%02xnm\n", dbx500_id.process); -} - -static void __init soc_info_populate(struct soc_device_attribute *soc_dev_attr, - const char *soc_id) -{ - soc_dev_attr->soc_id = soc_id; - soc_dev_attr->machine = ux500_get_machine(); - soc_dev_attr->family = ux500_get_family(); - soc_dev_attr->revision = ux500_get_revision(); -} - -static const struct device_attribute ux500_soc_attr = - __ATTR(process, S_IRUGO, ux500_get_process, NULL); - -struct device * __init ux500_soc_device_init(const char *soc_id) -{ - struct device *parent; - struct soc_device *soc_dev; - struct soc_device_attribute *soc_dev_attr; - - soc_dev_attr = kzalloc(sizeof(*soc_dev_attr), GFP_KERNEL); - if (!soc_dev_attr) - return ERR_PTR(-ENOMEM); - - soc_info_populate(soc_dev_attr, soc_id); - - soc_dev = soc_device_register(soc_dev_attr); - if (IS_ERR(soc_dev)) { - kfree(soc_dev_attr); - return NULL; - } - - parent = soc_device_to_device(soc_dev); - device_create_file(parent, &ux500_soc_attr); - - return parent; -} diff --git a/arch/arm/mach-ux500/id.c b/arch/arm/mach-ux500/id.c index 1e81e99..17f80ef 100644 --- a/arch/arm/mach-ux500/id.c +++ b/arch/arm/mach-ux500/id.c @@ -8,6 +8,9 @@ #include #include #include +#include +#include +#include #include #include @@ -17,9 +20,20 @@ #include "setup.h" #include "db8500-regs.h" -#include "id.h" -struct dbx500_asic_id dbx500_id; +/** + * struct dbx500_asic_id - fields of the ASIC ID + * @process: the manufacturing process, 0x40 is 40 nm 0x00 is "standard" + * @partnumber: hithereto 0x8500 for DB8500 + * @revision: version code in the series + */ +struct dbx500_asic_id { + u16 partnumber; + u8 revision; + u8 process; +}; + +static struct dbx500_asic_id dbx500_id; static unsigned int __init ux500_read_asicid(phys_addr_t addr) { @@ -42,9 +56,9 @@ static unsigned int __init ux500_read_asicid(phys_addr_t addr) static void ux500_print_soc_info(unsigned int asicid) { - unsigned int rev = dbx500_revision(); + unsigned int rev = dbx500_id.revision; - pr_info("DB%4x ", dbx500_partnumber()); + pr_info("DB%4x ", dbx500_id.partnumber); if (rev == 0x01) pr_cont("Early Drop"); @@ -105,7 +119,7 @@ void __init ux500_setup_id(void) if (!asicid) { pr_err("Unable to identify SoC\n"); - ux500_unknown_soc(); + BUG(); } dbx500_id.process = asicid >> 24; @@ -114,3 +128,89 @@ void __init ux500_setup_id(void) ux500_print_soc_info(asicid); } + +static const char * __init ux500_get_machine(void) +{ + return kasprintf(GFP_KERNEL, "DB%4x", dbx500_id.partnumber); +} + +static const char * __init ux500_get_family(void) +{ + return kasprintf(GFP_KERNEL, "ux500"); +} + +static const char * __init ux500_get_revision(void) +{ + unsigned int rev = dbx500_id.revision; + + if (rev == 0x01) + return kasprintf(GFP_KERNEL, "%s", "ED"); + else if (rev >= 0xA0) + return kasprintf(GFP_KERNEL, "%d.%d", + (rev >> 4) - 0xA + 1, rev & 0xf); + + return kasprintf(GFP_KERNEL, "%s", "Unknown"); +} + +static ssize_t ux500_get_process(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + if (dbx500_id.process == 0x00) + return sprintf(buf, "Standard\n"); + + return sprintf(buf, "%02xnm\n", dbx500_id.process); +} + +static const char *db8500_read_soc_id(void) +{ + void __iomem *uid; + const char *retstr; + + uid = ioremap(U8500_BB_UID_BASE, 0x20); + if (!uid) + return NULL; + /* Throw these device-specific numbers into the entropy pool */ + add_device_randomness(uid, 0x14); + retstr = kasprintf(GFP_KERNEL, "%08x%08x%08x%08x%08x", + readl((u32 *)uid+0), + readl((u32 *)uid+1), readl((u32 *)uid+2), + readl((u32 *)uid+3), readl((u32 *)uid+4)); + iounmap(uid); + return retstr; +} + +static void __init soc_info_populate(struct soc_device_attribute *soc_dev_attr) +{ + soc_dev_attr->soc_id = db8500_read_soc_id(); + soc_dev_attr->machine = ux500_get_machine(); + soc_dev_attr->family = ux500_get_family(); + soc_dev_attr->revision = ux500_get_revision(); +} + +static const struct device_attribute ux500_soc_attr = + __ATTR(process, S_IRUGO, ux500_get_process, NULL); + +struct device * __init ux500_soc_device_init(void) +{ + struct device *parent; + struct soc_device *soc_dev; + struct soc_device_attribute *soc_dev_attr; + + soc_dev_attr = kzalloc(sizeof(*soc_dev_attr), GFP_KERNEL); + if (!soc_dev_attr) + return ERR_PTR(-ENOMEM); + + soc_info_populate(soc_dev_attr); + + soc_dev = soc_device_register(soc_dev_attr); + if (IS_ERR(soc_dev)) { + kfree(soc_dev_attr); + return NULL; + } + + parent = soc_device_to_device(soc_dev); + device_create_file(parent, &ux500_soc_attr); + + return parent; +} diff --git a/arch/arm/mach-ux500/id.h b/arch/arm/mach-ux500/id.h deleted file mode 100644 index 929f78e..0000000 --- a/arch/arm/mach-ux500/id.h +++ /dev/null @@ -1,37 +0,0 @@ -/* - * Copyright (C) ST-Ericsson SA 2010 - * - * Author: Rabin Vincent for ST-Ericsson - * License terms: GNU General Public License (GPL) version 2 - */ - -#ifndef __MACH_UX500_ID -#define __MACH_UX500_ID - -/** - * struct dbx500_asic_id - fields of the ASIC ID - * @process: the manufacturing process, 0x40 is 40 nm 0x00 is "standard" - * @partnumber: hithereto 0x8500 for DB8500 - * @revision: version code in the series - */ -struct dbx500_asic_id { - u16 partnumber; - u8 revision; - u8 process; -}; - -extern struct dbx500_asic_id dbx500_id; - -static inline unsigned int __attribute_const__ dbx500_partnumber(void) -{ - return dbx500_id.partnumber; -} - -static inline unsigned int __attribute_const__ dbx500_revision(void) -{ - return dbx500_id.revision; -} - -#define ux500_unknown_soc() BUG() - -#endif diff --git a/arch/arm/mach-ux500/platsmp.c b/arch/arm/mach-ux500/platsmp.c index 88b8ab4..8f2f615 100644 --- a/arch/arm/mach-ux500/platsmp.c +++ b/arch/arm/mach-ux500/platsmp.c @@ -26,7 +26,6 @@ #include "setup.h" #include "db8500-regs.h" -#include "id.h" /* Magic triggers in backup RAM */ #define UX500_CPU1_JUMPADDR_OFFSET 0x1FF4 diff --git a/arch/arm/mach-ux500/setup.h b/arch/arm/mach-ux500/setup.h index 8b44b64..1e9e7c5 100644 --- a/arch/arm/mach-ux500/setup.h +++ b/arch/arm/mach-ux500/setup.h @@ -19,11 +19,9 @@ void ux500_l2x0_init(void); void ux500_restart(enum reboot_mode mode, const char *cmd); -void __init ux500_setup_id(void); - extern void __init ux500_init_irq(void); -extern struct device *ux500_soc_device_init(const char *soc_id); +extern struct device *ux500_soc_device_init(void); extern void ux500_cpu_die(unsigned int cpu); -- cgit v0.10.2 From f15601d62bf1348c1dc9fe3455b277a652db1c6e Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Mon, 20 Jun 2016 23:21:20 +0200 Subject: ARM: ux500: call ux500_setup_id later ux500_setup_id is currently called from u8500_map_io(), which is really early, but nothing relies on the ID any more, other than a printk message that is not really all that important to have early during boot. If we move the call to ux500_setup_id() into ux500_soc_device_init(), that file becomes usuable almost entirely standalone, and we can kill off the u8500_map_io() callback as it just does the default debug_ll_io_init() now. Signed-off-by: Arnd Bergmann Signed-off-by: Linus Walleij diff --git a/arch/arm/mach-ux500/cpu-db8500.c b/arch/arm/mach-ux500/cpu-db8500.c index e7eba47..881cafb 100644 --- a/arch/arm/mach-ux500/cpu-db8500.c +++ b/arch/arm/mach-ux500/cpu-db8500.c @@ -27,13 +27,6 @@ #include "board-mop500.h" #include "db8500-regs.h" -#include "id.h" - -static void __init u8500_map_io(void) -{ - debug_ll_io_init(); - ux500_setup_id(); -} /* * The PMU IRQ lines of two cores are wired together into a single interrupt. @@ -117,7 +110,6 @@ static const char * stericsson_dt_platform_compat[] = { DT_MACHINE_START(U8500_DT, "ST-Ericsson Ux5x0 platform (Device Tree Support)") .l2c_aux_val = 0, .l2c_aux_mask = ~0, - .map_io = u8500_map_io, .init_irq = ux500_init_irq, .init_machine = u8500_init_machine, .dt_compat = stericsson_dt_platform_compat, diff --git a/arch/arm/mach-ux500/id.c b/arch/arm/mach-ux500/id.c index 17f80ef..983004d 100644 --- a/arch/arm/mach-ux500/id.c +++ b/arch/arm/mach-ux500/id.c @@ -37,21 +37,16 @@ static struct dbx500_asic_id dbx500_id; static unsigned int __init ux500_read_asicid(phys_addr_t addr) { - phys_addr_t base = addr & ~0xfff; - struct map_desc desc = { - .virtual = (unsigned long)UX500_VIRT_ROM, - .pfn = __phys_to_pfn(base), - .length = SZ_16K, - .type = MT_DEVICE, - }; + void __iomem *virt = ioremap(addr, 4); + unsigned int asicid; - iotable_init(&desc, 1); + if (!virt) + return 0; - /* As in devicemaps_init() */ - local_flush_tlb_all(); - flush_cache_all(); + asicid = readl(virt); + iounmap(virt); - return readl(UX500_VIRT_ROM + (addr & 0xfff)); + return asicid; } static void ux500_print_soc_info(unsigned int asicid) @@ -86,7 +81,7 @@ static unsigned int partnumber(unsigned int asicid) * DB9540 0x413fc090 0xFFFFDBF4 0x009540xx */ -void __init ux500_setup_id(void) +static void __init ux500_setup_id(void) { unsigned int cpuid = read_cpuid_id(); unsigned int asicid = 0; @@ -197,6 +192,8 @@ struct device * __init ux500_soc_device_init(void) struct soc_device *soc_dev; struct soc_device_attribute *soc_dev_attr; + ux500_setup_id(); + soc_dev_attr = kzalloc(sizeof(*soc_dev_attr), GFP_KERNEL); if (!soc_dev_attr) return ERR_PTR(-ENOMEM); -- cgit v0.10.2 From 18a992787896fe822b2ea750e68d69cac0726739 Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Wed, 22 Jun 2016 16:32:36 +0200 Subject: ARM: ux500: move soc_id driver to drivers/soc As the ux500 id code is basically a standalone driver, we can move it out of the arch code into drivers/soc/ux500. This is a user-visible change, as it moves all the devices in sysfs from /sys/devices/soc0/ to /sys/devices/ and leaves the soc0 node as a separate device. Originally the idea was to put all on-chip devices under the soc node, and ux500 was the first platform to have this device, but later platforms almost all didn't follow that pattern, so this makes the platform do the same thing as everyone else. Since the platform is really obsolete now, I am optimistic that nothing will break after moving the devices around. As the SoC driver no longer has access to the private header files, I'm changing the code to instead look up the address of the backupram from devicetree, which is a good idea anyway. Finally, having a separate Kconfig symbol means the driver is now optional and could even be a loadable module rather than always being built-in if we allowed that for soc_device. Signed-off-by: Arnd Bergmann [Fixup mising Makefile, fixup BB_UID_BASE to fc0] Signed-off-by: Linus Walleij diff --git a/arch/arm/mach-ux500/Makefile b/arch/arm/mach-ux500/Makefile index ea8893f..753d3ee 100644 --- a/arch/arm/mach-ux500/Makefile +++ b/arch/arm/mach-ux500/Makefile @@ -2,7 +2,7 @@ # Makefile for the linux kernel, U8500 machine. # -obj-y := cpu.o id.o pm.o +obj-y := cpu.o pm.o obj-$(CONFIG_CACHE_L2X0) += cache-l2x0.o obj-$(CONFIG_UX500_SOC_DB8500) += cpu-db8500.o obj-$(CONFIG_MACH_MOP500) += board-mop500-audio.o diff --git a/arch/arm/mach-ux500/cpu-db8500.c b/arch/arm/mach-ux500/cpu-db8500.c index 881cafb..c9c9832 100644 --- a/arch/arm/mach-ux500/cpu-db8500.c +++ b/arch/arm/mach-ux500/cpu-db8500.c @@ -88,15 +88,13 @@ static const struct of_device_id u8500_local_bus_nodes[] = { static void __init u8500_init_machine(void) { - struct device *parent = ux500_soc_device_init(); - /* automatically probe child nodes of dbx5x0 devices */ if (of_machine_is_compatible("st-ericsson,u8540")) of_platform_populate(NULL, u8500_local_bus_nodes, - u8540_auxdata_lookup, parent); + u8540_auxdata_lookup, NULL); else of_platform_populate(NULL, u8500_local_bus_nodes, - u8500_auxdata_lookup, parent); + u8500_auxdata_lookup, NULL); } static const char * stericsson_dt_platform_compat[] = { diff --git a/arch/arm/mach-ux500/id.c b/arch/arm/mach-ux500/id.c deleted file mode 100644 index 983004d..0000000 --- a/arch/arm/mach-ux500/id.c +++ /dev/null @@ -1,213 +0,0 @@ -/* - * Copyright (C) ST-Ericsson SA 2010 - * - * Author: Rabin Vincent for ST-Ericsson - * License terms: GNU General Public License (GPL) version 2 - */ - -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include - -#include "setup.h" - -#include "db8500-regs.h" - -/** - * struct dbx500_asic_id - fields of the ASIC ID - * @process: the manufacturing process, 0x40 is 40 nm 0x00 is "standard" - * @partnumber: hithereto 0x8500 for DB8500 - * @revision: version code in the series - */ -struct dbx500_asic_id { - u16 partnumber; - u8 revision; - u8 process; -}; - -static struct dbx500_asic_id dbx500_id; - -static unsigned int __init ux500_read_asicid(phys_addr_t addr) -{ - void __iomem *virt = ioremap(addr, 4); - unsigned int asicid; - - if (!virt) - return 0; - - asicid = readl(virt); - iounmap(virt); - - return asicid; -} - -static void ux500_print_soc_info(unsigned int asicid) -{ - unsigned int rev = dbx500_id.revision; - - pr_info("DB%4x ", dbx500_id.partnumber); - - if (rev == 0x01) - pr_cont("Early Drop"); - else if (rev >= 0xA0) - pr_cont("v%d.%d" , (rev >> 4) - 0xA + 1, rev & 0xf); - else - pr_cont("Unknown"); - - pr_cont(" [%#010x]\n", asicid); -} - -static unsigned int partnumber(unsigned int asicid) -{ - return (asicid >> 8) & 0xffff; -} - -/* - * SOC MIDR ASICID ADDRESS ASICID VALUE - * DB8500ed 0x410fc090 0x9001FFF4 0x00850001 - * DB8500v1 0x411fc091 0x9001FFF4 0x008500A0 - * DB8500v1.1 0x411fc091 0x9001FFF4 0x008500A1 - * DB8500v2 0x412fc091 0x9001DBF4 0x008500B0 - * DB8520v2.2 0x412fc091 0x9001DBF4 0x008500B2 - * DB5500v1 0x412fc091 0x9001FFF4 0x005500A0 - * DB9540 0x413fc090 0xFFFFDBF4 0x009540xx - */ - -static void __init ux500_setup_id(void) -{ - unsigned int cpuid = read_cpuid_id(); - unsigned int asicid = 0; - phys_addr_t addr = 0; - - switch (cpuid) { - case 0x410fc090: /* DB8500ed */ - case 0x411fc091: /* DB8500v1 */ - addr = 0x9001FFF4; - break; - - case 0x412fc091: /* DB8520 / DB8500v2 / DB5500v1 */ - asicid = ux500_read_asicid(0x9001DBF4); - if (partnumber(asicid) == 0x8500 || - partnumber(asicid) == 0x8520) - /* DB8500v2 */ - break; - - /* DB5500v1 */ - addr = 0x9001FFF4; - break; - - case 0x413fc090: /* DB9540 */ - addr = 0xFFFFDBF4; - break; - } - - if (addr) - asicid = ux500_read_asicid(addr); - - if (!asicid) { - pr_err("Unable to identify SoC\n"); - BUG(); - } - - dbx500_id.process = asicid >> 24; - dbx500_id.partnumber = partnumber(asicid); - dbx500_id.revision = asicid & 0xff; - - ux500_print_soc_info(asicid); -} - -static const char * __init ux500_get_machine(void) -{ - return kasprintf(GFP_KERNEL, "DB%4x", dbx500_id.partnumber); -} - -static const char * __init ux500_get_family(void) -{ - return kasprintf(GFP_KERNEL, "ux500"); -} - -static const char * __init ux500_get_revision(void) -{ - unsigned int rev = dbx500_id.revision; - - if (rev == 0x01) - return kasprintf(GFP_KERNEL, "%s", "ED"); - else if (rev >= 0xA0) - return kasprintf(GFP_KERNEL, "%d.%d", - (rev >> 4) - 0xA + 1, rev & 0xf); - - return kasprintf(GFP_KERNEL, "%s", "Unknown"); -} - -static ssize_t ux500_get_process(struct device *dev, - struct device_attribute *attr, - char *buf) -{ - if (dbx500_id.process == 0x00) - return sprintf(buf, "Standard\n"); - - return sprintf(buf, "%02xnm\n", dbx500_id.process); -} - -static const char *db8500_read_soc_id(void) -{ - void __iomem *uid; - const char *retstr; - - uid = ioremap(U8500_BB_UID_BASE, 0x20); - if (!uid) - return NULL; - /* Throw these device-specific numbers into the entropy pool */ - add_device_randomness(uid, 0x14); - retstr = kasprintf(GFP_KERNEL, "%08x%08x%08x%08x%08x", - readl((u32 *)uid+0), - readl((u32 *)uid+1), readl((u32 *)uid+2), - readl((u32 *)uid+3), readl((u32 *)uid+4)); - iounmap(uid); - return retstr; -} - -static void __init soc_info_populate(struct soc_device_attribute *soc_dev_attr) -{ - soc_dev_attr->soc_id = db8500_read_soc_id(); - soc_dev_attr->machine = ux500_get_machine(); - soc_dev_attr->family = ux500_get_family(); - soc_dev_attr->revision = ux500_get_revision(); -} - -static const struct device_attribute ux500_soc_attr = - __ATTR(process, S_IRUGO, ux500_get_process, NULL); - -struct device * __init ux500_soc_device_init(void) -{ - struct device *parent; - struct soc_device *soc_dev; - struct soc_device_attribute *soc_dev_attr; - - ux500_setup_id(); - - soc_dev_attr = kzalloc(sizeof(*soc_dev_attr), GFP_KERNEL); - if (!soc_dev_attr) - return ERR_PTR(-ENOMEM); - - soc_info_populate(soc_dev_attr); - - soc_dev = soc_device_register(soc_dev_attr); - if (IS_ERR(soc_dev)) { - kfree(soc_dev_attr); - return NULL; - } - - parent = soc_device_to_device(soc_dev); - device_create_file(parent, &ux500_soc_attr); - - return parent; -} diff --git a/arch/arm/mach-ux500/setup.h b/arch/arm/mach-ux500/setup.h index 1e9e7c5..85b7819 100644 --- a/arch/arm/mach-ux500/setup.h +++ b/arch/arm/mach-ux500/setup.h @@ -21,8 +21,6 @@ void ux500_restart(enum reboot_mode mode, const char *cmd); extern void __init ux500_init_irq(void); -extern struct device *ux500_soc_device_init(void); - extern void ux500_cpu_die(unsigned int cpu); #endif /* __ASM_ARCH_SETUP_H */ diff --git a/drivers/soc/Kconfig b/drivers/soc/Kconfig index cb58ef0..b9c1bf4 100644 --- a/drivers/soc/Kconfig +++ b/drivers/soc/Kconfig @@ -10,6 +10,7 @@ source "drivers/soc/samsung/Kconfig" source "drivers/soc/sunxi/Kconfig" source "drivers/soc/tegra/Kconfig" source "drivers/soc/ti/Kconfig" +source "drivers/soc/ux500/Kconfig" source "drivers/soc/versatile/Kconfig" endmenu diff --git a/drivers/soc/Makefile b/drivers/soc/Makefile index 380230f..02359c9 100644 --- a/drivers/soc/Makefile +++ b/drivers/soc/Makefile @@ -15,4 +15,5 @@ obj-$(CONFIG_SOC_SAMSUNG) += samsung/ obj-$(CONFIG_ARCH_SUNXI) += sunxi/ obj-$(CONFIG_ARCH_TEGRA) += tegra/ obj-$(CONFIG_SOC_TI) += ti/ +obj-$(CONFIG_ARCH_U8500) += ux500/ obj-$(CONFIG_PLAT_VERSATILE) += versatile/ diff --git a/drivers/soc/ux500/Kconfig b/drivers/soc/ux500/Kconfig new file mode 100644 index 0000000..025a44a --- /dev/null +++ b/drivers/soc/ux500/Kconfig @@ -0,0 +1,7 @@ +config UX500_SOC_ID + bool "SoC bus for ST-Ericsson ux500" + depends on ARCH_U8500 || COMPILE_TEST + default ARCH_U8500 + help + Include support for the SoC bus on the ARM RealView platforms + providing some sysfs information about the ASIC variant. diff --git a/drivers/soc/ux500/Makefile b/drivers/soc/ux500/Makefile new file mode 100644 index 0000000..0b87ad0 --- /dev/null +++ b/drivers/soc/ux500/Makefile @@ -0,0 +1 @@ +obj-$(CONFIG_UX500_SOC_ID) += ux500-soc-id.o diff --git a/drivers/soc/ux500/ux500-soc-id.c b/drivers/soc/ux500/ux500-soc-id.c new file mode 100644 index 0000000..6c1be74 --- /dev/null +++ b/drivers/soc/ux500/ux500-soc-id.c @@ -0,0 +1,222 @@ +/* + * Copyright (C) ST-Ericsson SA 2010 + * + * Author: Rabin Vincent for ST-Ericsson + * License terms: GNU General Public License (GPL) version 2 + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +/** + * struct dbx500_asic_id - fields of the ASIC ID + * @process: the manufacturing process, 0x40 is 40 nm 0x00 is "standard" + * @partnumber: hithereto 0x8500 for DB8500 + * @revision: version code in the series + */ +struct dbx500_asic_id { + u16 partnumber; + u8 revision; + u8 process; +}; + +static struct dbx500_asic_id dbx500_id; + +static unsigned int __init ux500_read_asicid(phys_addr_t addr) +{ + void __iomem *virt = ioremap(addr, 4); + unsigned int asicid; + + if (!virt) + return 0; + + asicid = readl(virt); + iounmap(virt); + + return asicid; +} + +static void ux500_print_soc_info(unsigned int asicid) +{ + unsigned int rev = dbx500_id.revision; + + pr_info("DB%4x ", dbx500_id.partnumber); + + if (rev == 0x01) + pr_cont("Early Drop"); + else if (rev >= 0xA0) + pr_cont("v%d.%d" , (rev >> 4) - 0xA + 1, rev & 0xf); + else + pr_cont("Unknown"); + + pr_cont(" [%#010x]\n", asicid); +} + +static unsigned int partnumber(unsigned int asicid) +{ + return (asicid >> 8) & 0xffff; +} + +/* + * SOC MIDR ASICID ADDRESS ASICID VALUE + * DB8500ed 0x410fc090 0x9001FFF4 0x00850001 + * DB8500v1 0x411fc091 0x9001FFF4 0x008500A0 + * DB8500v1.1 0x411fc091 0x9001FFF4 0x008500A1 + * DB8500v2 0x412fc091 0x9001DBF4 0x008500B0 + * DB8520v2.2 0x412fc091 0x9001DBF4 0x008500B2 + * DB5500v1 0x412fc091 0x9001FFF4 0x005500A0 + * DB9540 0x413fc090 0xFFFFDBF4 0x009540xx + */ + +static void __init ux500_setup_id(void) +{ + unsigned int cpuid = read_cpuid_id(); + unsigned int asicid = 0; + phys_addr_t addr = 0; + + switch (cpuid) { + case 0x410fc090: /* DB8500ed */ + case 0x411fc091: /* DB8500v1 */ + addr = 0x9001FFF4; + break; + + case 0x412fc091: /* DB8520 / DB8500v2 / DB5500v1 */ + asicid = ux500_read_asicid(0x9001DBF4); + if (partnumber(asicid) == 0x8500 || + partnumber(asicid) == 0x8520) + /* DB8500v2 */ + break; + + /* DB5500v1 */ + addr = 0x9001FFF4; + break; + + case 0x413fc090: /* DB9540 */ + addr = 0xFFFFDBF4; + break; + } + + if (addr) + asicid = ux500_read_asicid(addr); + + if (!asicid) { + pr_err("Unable to identify SoC\n"); + BUG(); + } + + dbx500_id.process = asicid >> 24; + dbx500_id.partnumber = partnumber(asicid); + dbx500_id.revision = asicid & 0xff; + + ux500_print_soc_info(asicid); +} + +static const char * __init ux500_get_machine(void) +{ + return kasprintf(GFP_KERNEL, "DB%4x", dbx500_id.partnumber); +} + +static const char * __init ux500_get_family(void) +{ + return kasprintf(GFP_KERNEL, "ux500"); +} + +static const char * __init ux500_get_revision(void) +{ + unsigned int rev = dbx500_id.revision; + + if (rev == 0x01) + return kasprintf(GFP_KERNEL, "%s", "ED"); + else if (rev >= 0xA0) + return kasprintf(GFP_KERNEL, "%d.%d", + (rev >> 4) - 0xA + 1, rev & 0xf); + + return kasprintf(GFP_KERNEL, "%s", "Unknown"); +} + +static ssize_t ux500_get_process(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + if (dbx500_id.process == 0x00) + return sprintf(buf, "Standard\n"); + + return sprintf(buf, "%02xnm\n", dbx500_id.process); +} + +static const char *db8500_read_soc_id(struct device_node *backupram) +{ + void __iomem *base; + void __iomem *uid; + const char *retstr; + + base = of_iomap(backupram, 0); + if (!base) + return NULL; + uid = base + 0x1fc0; + + /* Throw these device-specific numbers into the entropy pool */ + add_device_randomness(uid, 0x14); + retstr = kasprintf(GFP_KERNEL, "%08x%08x%08x%08x%08x", + readl((u32 *)uid+0), + readl((u32 *)uid+1), readl((u32 *)uid+2), + readl((u32 *)uid+3), readl((u32 *)uid+4)); + iounmap(base); + return retstr; +} + +static void __init soc_info_populate(struct soc_device_attribute *soc_dev_attr, + struct device_node *backupram) +{ + soc_dev_attr->soc_id = db8500_read_soc_id(backupram); + soc_dev_attr->machine = ux500_get_machine(); + soc_dev_attr->family = ux500_get_family(); + soc_dev_attr->revision = ux500_get_revision(); +} + +static const struct device_attribute ux500_soc_attr = + __ATTR(process, S_IRUGO, ux500_get_process, NULL); + +static int __init ux500_soc_device_init(void) +{ + struct device *parent; + struct soc_device *soc_dev; + struct soc_device_attribute *soc_dev_attr; + struct device_node *backupram; + + backupram = of_find_compatible_node(NULL, NULL, "ste,dbx500-backupram"); + if (!backupram) + return 0; + + ux500_setup_id(); + + soc_dev_attr = kzalloc(sizeof(*soc_dev_attr), GFP_KERNEL); + if (!soc_dev_attr) + return -ENOMEM; + + soc_info_populate(soc_dev_attr, backupram); + + soc_dev = soc_device_register(soc_dev_attr); + if (IS_ERR(soc_dev)) { + kfree(soc_dev_attr); + return PTR_ERR(soc_dev); + } + + parent = soc_device_to_device(soc_dev); + device_create_file(parent, &ux500_soc_attr); + + return 0; +} +subsys_initcall(ux500_soc_device_init); -- cgit v0.10.2 From a16729ea822806a6f87415bcb523eaeb7f4b4ebd Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Mon, 20 Jun 2016 23:40:55 +0200 Subject: ARM: ux500: consolidate base platform files The cpu.c and cache-l2x0.c files hold only two or three simple functions each, and they are all called from the machine descriptors, so we can just move them all into the same file for simplicity and consistency. Signed-off-by: Arnd Bergmann Signed-off-by: Linus Walleij diff --git a/arch/arm/mach-ux500/Makefile b/arch/arm/mach-ux500/Makefile index 753d3ee..56d0eb6 100644 --- a/arch/arm/mach-ux500/Makefile +++ b/arch/arm/mach-ux500/Makefile @@ -2,8 +2,7 @@ # Makefile for the linux kernel, U8500 machine. # -obj-y := cpu.o pm.o -obj-$(CONFIG_CACHE_L2X0) += cache-l2x0.o +obj-y := pm.o obj-$(CONFIG_UX500_SOC_DB8500) += cpu-db8500.o obj-$(CONFIG_MACH_MOP500) += board-mop500-audio.o obj-$(CONFIG_SMP) += platsmp.o diff --git a/arch/arm/mach-ux500/cache-l2x0.c b/arch/arm/mach-ux500/cache-l2x0.c deleted file mode 100644 index adec59c..0000000 --- a/arch/arm/mach-ux500/cache-l2x0.c +++ /dev/null @@ -1,60 +0,0 @@ -/* - * Copyright (C) ST-Ericsson SA 2011 - * - * License terms: GNU General Public License (GPL) version 2 - */ - -#include -#include -#include - -#include -#include - -#include "db8500-regs.h" - -static int __init ux500_l2x0_unlock(void) -{ - int i; - struct device_node *np; - void __iomem *l2x0_base; - - np = of_find_compatible_node(NULL, NULL, "arm,pl310-cache"); - l2x0_base = of_iomap(np, 0); - of_node_put(np); - if (!l2x0_base) - return -ENODEV; - - /* - * Unlock Data and Instruction Lock if locked. Ux500 U-Boot versions - * apparently locks both caches before jumping to the kernel. The - * l2x0 core will not touch the unlock registers if the l2x0 is - * already enabled, so we do it right here instead. The PL310 has - * 8 sets of registers, one per possible CPU. - */ - for (i = 0; i < 8; i++) { - writel_relaxed(0x0, l2x0_base + L2X0_LOCKDOWN_WAY_D_BASE + - i * L2X0_LOCKDOWN_STRIDE); - writel_relaxed(0x0, l2x0_base + L2X0_LOCKDOWN_WAY_I_BASE + - i * L2X0_LOCKDOWN_STRIDE); - } - iounmap(l2x0_base); - return 0; -} - -static void ux500_l2c310_write_sec(unsigned long val, unsigned reg) -{ - /* - * We can't write to secure registers as we are in non-secure - * mode, until we have some SMI service available. - */ -} - -void __init ux500_l2x0_init(void) -{ - /* Unlock before init */ - ux500_l2x0_unlock(); - outer_cache.write_sec = ux500_l2c310_write_sec; - - return 0; -} diff --git a/arch/arm/mach-ux500/cpu-db8500.c b/arch/arm/mach-ux500/cpu-db8500.c index c9c9832..46b1da1 100644 --- a/arch/arm/mach-ux500/cpu-db8500.c +++ b/arch/arm/mach-ux500/cpu-db8500.c @@ -12,22 +12,109 @@ #include #include #include +#include #include #include +#include +#include +#include +#include #include #include #include +#include #include #include #include +#include +#include #include +#include #include "setup.h" #include "board-mop500.h" #include "db8500-regs.h" +static int __init ux500_l2x0_unlock(void) +{ + int i; + struct device_node *np; + void __iomem *l2x0_base; + + np = of_find_compatible_node(NULL, NULL, "arm,pl310-cache"); + l2x0_base = of_iomap(np, 0); + of_node_put(np); + if (!l2x0_base) + return -ENODEV; + + /* + * Unlock Data and Instruction Lock if locked. Ux500 U-Boot versions + * apparently locks both caches before jumping to the kernel. The + * l2x0 core will not touch the unlock registers if the l2x0 is + * already enabled, so we do it right here instead. The PL310 has + * 8 sets of registers, one per possible CPU. + */ + for (i = 0; i < 8; i++) { + writel_relaxed(0x0, l2x0_base + L2X0_LOCKDOWN_WAY_D_BASE + + i * L2X0_LOCKDOWN_STRIDE); + writel_relaxed(0x0, l2x0_base + L2X0_LOCKDOWN_WAY_I_BASE + + i * L2X0_LOCKDOWN_STRIDE); + } + iounmap(l2x0_base); + return 0; +} + +static void ux500_l2c310_write_sec(unsigned long val, unsigned reg) +{ + /* + * We can't write to secure registers as we are in non-secure + * mode, until we have some SMI service available. + */ +} + +/* + * FIXME: Should we set up the GPIO domain here? + * + * The problem is that we cannot put the interrupt resources into the platform + * device until the irqdomain has been added. Right now, we set the GIC interrupt + * domain from init_irq(), then load the gpio driver from + * core_initcall(nmk_gpio_init) and add the platform devices from + * arch_initcall(customize_machine). + * + * This feels fragile because it depends on the gpio device getting probed + * _before_ any device uses the gpio interrupts. +*/ +static void __init ux500_init_irq(void) +{ + struct device_node *np; + struct resource r; + + irqchip_init(); + np = of_find_compatible_node(NULL, NULL, "stericsson,db8500-prcmu"); + of_address_to_resource(np, 0, &r); + of_node_put(np); + if (!r.start) { + pr_err("could not find PRCMU base resource\n"); + return; + } + prcmu_early_init(r.start, r.end-r.start); + ux500_pm_init(r.start, r.end-r.start); + + /* Unlock before init */ + ux500_l2x0_unlock(); + outer_cache.write_sec = ux500_l2c310_write_sec; +} + +static void ux500_restart(enum reboot_mode mode, const char *cmd) +{ + local_irq_disable(); + local_fiq_disable(); + + prcmu_system_reset(0); +} + /* * The PMU IRQ lines of two cores are wired together into a single interrupt. * Bounce the interrupt to the other core if it's not ours. diff --git a/arch/arm/mach-ux500/cpu.c b/arch/arm/mach-ux500/cpu.c deleted file mode 100644 index a34af28..0000000 --- a/arch/arm/mach-ux500/cpu.c +++ /dev/null @@ -1,67 +0,0 @@ -/* - * Copyright (C) ST-Ericsson SA 2010 - * - * Author: Rabin Vincent for ST-Ericsson - * Author: Lee Jones for ST-Ericsson - * License terms: GNU General Public License (GPL) version 2 - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include - -#include "setup.h" - -#include "board-mop500.h" -#include "db8500-regs.h" - -void ux500_restart(enum reboot_mode mode, const char *cmd) -{ - local_irq_disable(); - local_fiq_disable(); - - prcmu_system_reset(0); -} - -/* - * FIXME: Should we set up the GPIO domain here? - * - * The problem is that we cannot put the interrupt resources into the platform - * device until the irqdomain has been added. Right now, we set the GIC interrupt - * domain from init_irq(), then load the gpio driver from - * core_initcall(nmk_gpio_init) and add the platform devices from - * arch_initcall(customize_machine). - * - * This feels fragile because it depends on the gpio device getting probed - * _before_ any device uses the gpio interrupts. -*/ -void __init ux500_init_irq(void) -{ - struct device_node *np; - struct resource r; - - irqchip_init(); - np = of_find_compatible_node(NULL, NULL, "stericsson,db8500-prcmu"); - of_address_to_resource(np, 0, &r); - of_node_put(np); - if (!r.start) { - pr_err("could not find PRCMU base resource\n"); - return; - } - prcmu_early_init(r.start, r.end-r.start); - ux500_pm_init(r.start, r.end-r.start); - ux500_l2x0_init(); -} diff --git a/arch/arm/mach-ux500/setup.h b/arch/arm/mach-ux500/setup.h index 85b7819..988e7c7 100644 --- a/arch/arm/mach-ux500/setup.h +++ b/arch/arm/mach-ux500/setup.h @@ -11,16 +11,6 @@ #ifndef __ASM_ARCH_SETUP_H #define __ASM_ARCH_SETUP_H -#include -#include - - -void ux500_l2x0_init(void); - -void ux500_restart(enum reboot_mode mode, const char *cmd); - -extern void __init ux500_init_irq(void); - extern void ux500_cpu_die(unsigned int cpu); #endif /* __ASM_ARCH_SETUP_H */ -- cgit v0.10.2