From 017b5522d5e31a0b2c2f54f566aa8887838bccc7 Mon Sep 17 00:00:00 2001 From: Alexandre Belloni Date: Tue, 8 Jul 2014 18:21:11 +0200 Subject: ARM: at91: Add new binding for sama5d3-ddramc The IP for the SDRAM controller found on sama5d3 SoCs is different from the g45 one. Introduce a new compatible string to reflect that. Signed-off-by: Alexandre Belloni Signed-off-by: Maxime Ripard Acked-by: Nicolas Ferre --- Documentation/devicetree/bindings/arm/atmel-at91.txt | 1 + arch/arm/mach-at91/setup.c | 1 + 2 files changed, 2 insertions(+) diff --git a/Documentation/devicetree/bindings/arm/atmel-at91.txt b/Documentation/devicetree/bindings/arm/atmel-at91.txt index 16f60b41c147..33bcf8b57798 100644 --- a/Documentation/devicetree/bindings/arm/atmel-at91.txt +++ b/Documentation/devicetree/bindings/arm/atmel-at91.txt @@ -61,6 +61,7 @@ RAMC SDRAM/DDR Controller required properties: - compatible: Should be "atmel,at91rm9200-sdramc", "atmel,at91sam9260-sdramc", "atmel,at91sam9g45-ddramc", + "atmel,sama5d3-ddramc", - reg: Should contain registers location and length For at91sam9263 and at91sam9g45 you must specify 2 entries. diff --git a/arch/arm/mach-at91/setup.c b/arch/arm/mach-at91/setup.c index f7a07a58ebb6..faff1eae3e65 100644 --- a/arch/arm/mach-at91/setup.c +++ b/arch/arm/mach-at91/setup.c @@ -384,6 +384,7 @@ static struct of_device_id ramc_ids[] = { { .compatible = "atmel,at91rm9200-sdramc", .data = at91rm9200_standby }, { .compatible = "atmel,at91sam9260-sdramc", .data = at91sam9_sdram_standby }, { .compatible = "atmel,at91sam9g45-ddramc", .data = at91_ddr_standby }, + { .compatible = "atmel,sama5d3-ddramc", .data = at91_ddr_standby }, { /*sentinel*/ } }; -- cgit v1.2.1 From e81b6abebc87ec40a434ada4ca8d1f2aa16cea9d Mon Sep 17 00:00:00 2001 From: Alexandre Belloni Date: Tue, 8 Jul 2014 18:21:12 +0200 Subject: memory: add a driver for atmel ram controllers Atmel SoCs have one or multiple RAM controllers that need one or multiple clocks to run. This driver handle those clocks. Signed-off-by: Alexandre Belloni Signed-off-by: Maxime Ripard Acked-by: Nicolas Ferre --- drivers/memory/Kconfig | 10 +++++ drivers/memory/Makefile | 1 + drivers/memory/atmel-sdramc.c | 98 +++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 109 insertions(+) create mode 100644 drivers/memory/atmel-sdramc.c diff --git a/drivers/memory/Kconfig b/drivers/memory/Kconfig index c59e9c96e86d..257061c3c816 100644 --- a/drivers/memory/Kconfig +++ b/drivers/memory/Kconfig @@ -7,6 +7,16 @@ menuconfig MEMORY if MEMORY +config ATMEL_SDRAMC + bool "Atmel (Multi-port DDR-)SDRAM Controller" + default y + depends on ARCH_AT91 && OF + help + This driver is for Atmel SDRAM Controller or Atmel Multi-port + DDR-SDRAM Controller available on Atmel AT91SAM9 and SAMA5 SoCs. + Starting with the at91sam9g45, this controller supports SDR, DDR and + LP-DDR memories. + 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 71160a2b7313..b8637261e1a1 100644 --- a/drivers/memory/Makefile +++ b/drivers/memory/Makefile @@ -5,6 +5,7 @@ ifeq ($(CONFIG_DDR),y) obj-$(CONFIG_OF) += of_memory.o endif +obj-$(CONFIG_ATMEL_SDRAMC) += atmel-sdramc.o obj-$(CONFIG_TI_AEMIF) += ti-aemif.o obj-$(CONFIG_TI_EMIF) += emif.o obj-$(CONFIG_FSL_IFC) += fsl_ifc.o diff --git a/drivers/memory/atmel-sdramc.c b/drivers/memory/atmel-sdramc.c new file mode 100644 index 000000000000..fed04e8efe75 --- /dev/null +++ b/drivers/memory/atmel-sdramc.c @@ -0,0 +1,98 @@ +/* + * Atmel (Multi-port DDR-)SDRAM Controller driver + * + * Copyright (C) 2014 Atmel + * + * 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 of the License. + * + * 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 . + * + */ + +#include +#include +#include +#include +#include +#include + +struct at91_ramc_caps { + bool has_ddrck; + bool has_mpddr_clk; +}; + +static const struct at91_ramc_caps at91rm9200_caps = { }; + +static const struct at91_ramc_caps at91sam9g45_caps = { + .has_ddrck = 1, + .has_mpddr_clk = 0, +}; + +static const struct at91_ramc_caps sama5d3_caps = { + .has_ddrck = 1, + .has_mpddr_clk = 1, +}; + +static const struct of_device_id atmel_ramc_of_match[] = { + { .compatible = "atmel,at91rm9200-sdramc", .data = &at91rm9200_caps, }, + { .compatible = "atmel,at91sam9260-sdramc", .data = &at91rm9200_caps, }, + { .compatible = "atmel,at91sam9g45-ddramc", .data = &at91sam9g45_caps, }, + { .compatible = "atmel,sama5d3-ddramc", .data = &sama5d3_caps, }, + {}, +}; +MODULE_DEVICE_TABLE(of, atmel_ramc_of_match); + +static int atmel_ramc_probe(struct platform_device *pdev) +{ + const struct of_device_id *match; + const struct at91_ramc_caps *caps; + struct clk *clk; + + match = of_match_device(atmel_ramc_of_match, &pdev->dev); + caps = match->data; + + if (caps->has_ddrck) { + clk = devm_clk_get(&pdev->dev, "ddrck"); + if (IS_ERR(clk)) + return PTR_ERR(clk); + clk_prepare_enable(clk); + } + + if (caps->has_mpddr_clk) { + clk = devm_clk_get(&pdev->dev, "mpddr"); + if (IS_ERR(clk)) { + pr_err("AT91 RAMC: couldn't get mpddr clock\n"); + return PTR_ERR(clk); + } + clk_prepare_enable(clk); + } + + return 0; +} + +static struct platform_driver atmel_ramc_driver = { + .probe = atmel_ramc_probe, + .driver = { + .name = "atmel-ramc", + .owner = THIS_MODULE, + .of_match_table = atmel_ramc_of_match, + }, +}; + +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"); -- cgit v1.2.1 From 63e60368956ea076278dd3d70dc80b366e0ec6f3 Mon Sep 17 00:00:00 2001 From: Alexandre Belloni Date: Tue, 8 Jul 2014 18:21:13 +0200 Subject: ARM: at91: select ATMEL_SDRAMC when using OF When using device tree, select the Atmel RAM controller driver to handle its clocks. Signed-off-by: Alexandre Belloni Signed-off-by: Maxime Ripard Acked-by: Nicolas Ferre --- arch/arm/mach-at91/Kconfig | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/arch/arm/mach-at91/Kconfig b/arch/arm/mach-at91/Kconfig index 45b55e0f0db6..7a69ffd93b6b 100644 --- a/arch/arm/mach-at91/Kconfig +++ b/arch/arm/mach-at91/Kconfig @@ -49,6 +49,8 @@ config SOC_AT91SAM9 select GENERIC_CLOCKEVENTS select MULTI_IRQ_HANDLER select SPARSE_IRQ + select MEMORY if USE_OF + select ATMEL_SDRAMC if USE_OF config SOC_SAMA5 bool @@ -58,6 +60,8 @@ config SOC_SAMA5 select MULTI_IRQ_HANDLER select SPARSE_IRQ select USE_OF + select MEMORY + select ATMEL_SDRAMC menu "Atmel AT91 System-on-Chip" -- cgit v1.2.1 From b736bcb3d8fe6182d12feb0412106802d80b4527 Mon Sep 17 00:00:00 2001 From: Alexandre Belloni Date: Tue, 8 Jul 2014 18:21:16 +0200 Subject: clk: at91: remove the useless CLK_IGNORE_UNUSED flag The CLK_IGNORE_UNUSED flag was added on all the system clocks because of the ddrck. Now that it is handled by the ram controller driver, we can drop it. Signed-off-by: Alexandre Belloni Signed-off-by: Maxime Ripard Acked-by: Nicolas Ferre --- drivers/clk/at91/clk-system.c | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/drivers/clk/at91/clk-system.c b/drivers/clk/at91/clk-system.c index 8c96307d7363..a76d03fd577b 100644 --- a/drivers/clk/at91/clk-system.c +++ b/drivers/clk/at91/clk-system.c @@ -119,13 +119,7 @@ at91_clk_register_system(struct at91_pmc *pmc, const char *name, init.ops = &system_ops; init.parent_names = &parent_name; init.num_parents = 1; - /* - * CLK_IGNORE_UNUSED is used to avoid ddrck switch off. - * TODO : we should implement a driver supporting at91 ddr controller - * (see drivers/memory) which would request and enable the ddrck clock. - * When this is done we will be able to remove CLK_IGNORE_UNUSED flag. - */ - init.flags = CLK_SET_RATE_PARENT | CLK_IGNORE_UNUSED; + init.flags = CLK_SET_RATE_PARENT; sys->id = id; sys->hw.init = &init; -- cgit v1.2.1 From 6ca4f46005dd7934308588d8e4d73637bbaef7ad Mon Sep 17 00:00:00 2001 From: Maxime Ripard Date: Tue, 1 Jul 2014 16:58:40 +0200 Subject: power: reset: Add if statement instead of multiple depends on All the config option so far are depending on the POWER_RESET symbol Signed-off-by: Maxime Ripard Acked-by: Nicolas Ferre Acked-by: Alexandre Belloni --- drivers/power/reset/Kconfig | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/drivers/power/reset/Kconfig b/drivers/power/reset/Kconfig index bdcf5173e377..19d546c5edfa 100644 --- a/drivers/power/reset/Kconfig +++ b/drivers/power/reset/Kconfig @@ -6,15 +6,17 @@ menuconfig POWER_RESET Say Y here to enable board reset and power off +if POWER_RESET + config POWER_RESET_AS3722 bool "ams AS3722 power-off driver" - depends on MFD_AS3722 && POWER_RESET + depends on MFD_AS3722 help This driver supports turning off board via a ams AS3722 power-off. config POWER_RESET_AXXIA bool "LSI Axxia reset driver" - depends on POWER_RESET && ARCH_AXXIA + depends on ARCH_AXXIA help This driver supports restart for Axxia SoC. @@ -22,7 +24,7 @@ config POWER_RESET_AXXIA config POWER_RESET_GPIO bool "GPIO power-off driver" - depends on OF_GPIO && POWER_RESET + depends on OF_GPIO help This driver supports turning off your board via a GPIO line. If your board needs a GPIO high/low to power down, say Y and @@ -30,13 +32,13 @@ config POWER_RESET_GPIO config POWER_RESET_MSM bool "Qualcomm MSM power-off driver" - depends on POWER_RESET && ARCH_QCOM + depends on ARCH_QCOM help Power off and restart support for Qualcomm boards. config POWER_RESET_QNAP bool "QNAP power-off driver" - depends on OF_GPIO && POWER_RESET && PLAT_ORION + depends on OF_GPIO && PLAT_ORION help This driver supports turning off QNAP NAS devices by sending commands to the microcontroller which controls the main power. @@ -54,14 +56,13 @@ config POWER_RESET_RESTART config POWER_RESET_SUN6I bool "Allwinner A31 SoC reset driver" depends on ARCH_SUNXI - depends on POWER_RESET help Reboot support for the Allwinner A31 SoCs. config POWER_RESET_VEXPRESS bool "ARM Versatile Express power-off and reset driver" depends on ARM || ARM64 - depends on POWER_RESET && VEXPRESS_CONFIG + depends on VEXPRESS_CONFIG help Power off and reset support for the ARM Ltd. Versatile Express boards. @@ -69,7 +70,6 @@ config POWER_RESET_VEXPRESS config POWER_RESET_XGENE bool "APM SoC X-Gene reset driver" depends on ARM64 - depends on POWER_RESET help Reboot support for the APM SoC X-Gene Eval boards. @@ -80,3 +80,4 @@ config POWER_RESET_KEYSTONE help Reboot support for the KEYSTONE SoCs. +endif -- cgit v1.2.1 From ecfe64d8c55f8f210a609cd2eabfcc03f03672a9 Mon Sep 17 00:00:00 2001 From: Maxime Ripard Date: Wed, 2 Jul 2014 17:46:58 +0200 Subject: power: reset: Add AT91 reset driver Implement the reset behaviour of the various AT91 SoCS in drivers/power/reset. It used to be (and still is) located in arch/arm/mach-at91, and in order to preserve bisectability is not removed yet, but every board should be converted to use this driver instead. Signed-off-by: Maxime Ripard Acked-by: Nicolas Ferre Acked-by: Alexandre Belloni --- drivers/power/reset/Kconfig | 8 ++ drivers/power/reset/Makefile | 1 + drivers/power/reset/at91-reset.c | 252 +++++++++++++++++++++++++++++++++++++++ 3 files changed, 261 insertions(+) create mode 100644 drivers/power/reset/at91-reset.c diff --git a/drivers/power/reset/Kconfig b/drivers/power/reset/Kconfig index 19d546c5edfa..360bac75a3da 100644 --- a/drivers/power/reset/Kconfig +++ b/drivers/power/reset/Kconfig @@ -14,6 +14,14 @@ config POWER_RESET_AS3722 help This driver supports turning off board via a ams AS3722 power-off. +config POWER_RESET_AT91_RESET + bool "Atmel AT91 reset driver" + depends on MACH_AT91 + default SOC_AT91SAM9 || SOC_SAMA5 + help + This driver supports restart for Atmel AT91SAM9 and SAMA5 + SoCs + config POWER_RESET_AXXIA bool "LSI Axxia reset driver" depends on ARCH_AXXIA diff --git a/drivers/power/reset/Makefile b/drivers/power/reset/Makefile index dde2e8bbac53..1599214b91d9 100644 --- a/drivers/power/reset/Makefile +++ b/drivers/power/reset/Makefile @@ -1,4 +1,5 @@ obj-$(CONFIG_POWER_RESET_AS3722) += as3722-poweroff.o +obj-$(CONFIG_POWER_RESET_AT91_RESET) += at91-reset.o obj-$(CONFIG_POWER_RESET_AXXIA) += axxia-reset.o obj-$(CONFIG_POWER_RESET_GPIO) += gpio-poweroff.o obj-$(CONFIG_POWER_RESET_MSM) += msm-poweroff.o diff --git a/drivers/power/reset/at91-reset.c b/drivers/power/reset/at91-reset.c new file mode 100644 index 000000000000..3611806c9cfd --- /dev/null +++ b/drivers/power/reset/at91-reset.c @@ -0,0 +1,252 @@ +/* + * Atmel AT91 SAM9 SoCs reset code + * + * Copyright (C) 2007 Atmel Corporation. + * Copyright (C) BitBox Ltd 2010 + * Copyright (C) 2011 Jean-Christophe PLAGNIOL-VILLARD + * Copyright (C) 2014 Free Electrons + * + * 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 + +#define AT91_RSTC_CR 0x00 /* Reset Controller Control Register */ +#define AT91_RSTC_PROCRST BIT(0) /* Processor Reset */ +#define AT91_RSTC_PERRST BIT(2) /* Peripheral Reset */ +#define AT91_RSTC_EXTRST BIT(3) /* External Reset */ +#define AT91_RSTC_KEY (0xa5 << 24) /* KEY Password */ + +#define AT91_RSTC_SR 0x04 /* Reset Controller Status Register */ +#define AT91_RSTC_URSTS BIT(0) /* User Reset Status */ +#define AT91_RSTC_RSTTYP GENMASK(10, 8) /* Reset Type */ +#define AT91_RSTC_NRSTL BIT(16) /* NRST Pin Level */ +#define AT91_RSTC_SRCMP BIT(17) /* Software Reset Command in Progress */ + +#define AT91_RSTC_MR 0x08 /* Reset Controller Mode Register */ +#define AT91_RSTC_URSTEN BIT(0) /* User Reset Enable */ +#define AT91_RSTC_URSTIEN BIT(4) /* User Reset Interrupt Enable */ +#define AT91_RSTC_ERSTL GENMASK(11, 8) /* External Reset Length */ + +enum reset_type { + RESET_TYPE_GENERAL = 0, + RESET_TYPE_WAKEUP = 1, + RESET_TYPE_WATCHDOG = 2, + RESET_TYPE_SOFTWARE = 3, + RESET_TYPE_USER = 4, +}; + +static void __iomem *at91_ramc_base[2], *at91_rstc_base; + +/* +* unless the SDRAM is cleanly shutdown before we hit the +* reset register it can be left driving the data bus and +* killing the chance of a subsequent boot from NAND +*/ +static void at91sam9260_restart(enum reboot_mode mode, const char *cmd) +{ + asm volatile( + /* Align to cache lines */ + ".balign 32\n\t" + + /* Disable SDRAM accesses */ + "str %2, [%0, #" __stringify(AT91_SDRAMC_TR) "]\n\t" + + /* Power down SDRAM */ + "str %3, [%0, #" __stringify(AT91_SDRAMC_LPR) "]\n\t" + + /* Reset CPU */ + "str %4, [%1, #" __stringify(AT91_RSTC_CR) "]\n\t" + + "b .\n\t" + : + : "r" (at91_ramc_base[0]), + "r" (at91_rstc_base), + "r" (1), + "r" (AT91_SDRAMC_LPCB_POWER_DOWN), + "r" (AT91_RSTC_KEY | AT91_RSTC_PERRST | AT91_RSTC_PROCRST)); +} + +static void at91sam9g45_restart(enum reboot_mode mode, const char *cmd) +{ + asm volatile( + /* + * Test wether we have a second RAM controller to care + * about. + * + * First, test that we can dereference the virtual address. + */ + "cmp %1, #0\n\t" + "beq 1f\n\t" + + /* Then, test that the RAM controller is enabled */ + "ldr r0, [%1]\n\t" + "cmp r0, #0\n\t" + + /* Align to cache lines */ + ".balign 32\n\t" + + /* Disable SDRAM0 accesses */ + "1: str %3, [%0, #" __stringify(AT91_DDRSDRC_RTR) "]\n\t" + /* Power down SDRAM0 */ + " str %4, [%0, #" __stringify(AT91_DDRSDRC_RTR) "]\n\t" + /* Disable SDRAM1 accesses */ + " strne %3, [%1, #" __stringify(AT91_DDRSDRC_RTR) "]\n\t" + /* Power down SDRAM1 */ + " strne %4, [%1, #" __stringify(AT91_DDRSDRC_RTR) "]\n\t" + /* Reset CPU */ + " str %5, [%2, #" __stringify(AT91_RSTC_CR) "]\n\t" + + " b .\n\t" + : + : "r" (at91_ramc_base[0]), + "r" (at91_ramc_base[1]), + "r" (at91_rstc_base), + "r" (1), + "r" (AT91_DDRSDRC_LPCB_POWER_DOWN), + "r" (AT91_RSTC_KEY | AT91_RSTC_PERRST | AT91_RSTC_PROCRST) + : "r0"); +} + +static void __init at91_reset_status(struct platform_device *pdev) +{ + u32 reg = readl(at91_rstc_base + AT91_RSTC_SR); + char *reason; + + switch ((reg & AT91_RSTC_RSTTYP) >> 8) { + case RESET_TYPE_GENERAL: + reason = "general reset"; + break; + case RESET_TYPE_WAKEUP: + reason = "wakeup"; + break; + case RESET_TYPE_WATCHDOG: + reason = "watchdog reset"; + break; + case RESET_TYPE_SOFTWARE: + reason = "software reset"; + break; + case RESET_TYPE_USER: + reason = "user reset"; + break; + default: + reason = "unknown reset"; + break; + } + + pr_info("AT91: Starting after %s\n", reason); +} + +static struct of_device_id at91_ramc_of_match[] = { + { .compatible = "atmel,at91sam9260-sdramc", }, + { .compatible = "atmel,at91sam9g45-ddramc", }, + { .compatible = "atmel,sama5d3-ddramc", }, + { /* sentinel */ } +}; + +static struct of_device_id at91_reset_of_match[] = { + { .compatible = "atmel,at91sam9260-rstc", .data = at91sam9260_restart }, + { .compatible = "atmel,at91sam9g45-rstc", .data = at91sam9g45_restart }, + { /* sentinel */ } +}; + +static int at91_reset_of_probe(struct platform_device *pdev) +{ + const struct of_device_id *match; + struct device_node *np; + int idx = 0; + + at91_rstc_base = of_iomap(pdev->dev.of_node, 0); + if (!at91_rstc_base) { + dev_err(&pdev->dev, "Could not map reset controller address\n"); + return -ENODEV; + } + + for_each_matching_node(np, at91_ramc_of_match) { + at91_ramc_base[idx] = of_iomap(np, 0); + if (!at91_ramc_base[idx]) { + dev_err(&pdev->dev, "Could not map ram controller address\n"); + return -ENODEV; + } + idx++; + } + + match = of_match_node(at91_reset_of_match, pdev->dev.of_node); + arm_pm_restart = match->data; + + return 0; +} + +static int at91_reset_platform_probe(struct platform_device *pdev) +{ + const struct platform_device_id *match; + struct resource *res; + int idx = 0; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + at91_rstc_base = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(at91_rstc_base)) { + dev_err(&pdev->dev, "Could not map reset controller address\n"); + return PTR_ERR(at91_rstc_base); + } + + for (idx = 0; idx < 2; idx++) { + res = platform_get_resource(pdev, IORESOURCE_MEM, idx + 1 ); + at91_ramc_base[idx] = devm_ioremap(&pdev->dev, res->start, + resource_size(res)); + if (IS_ERR(at91_ramc_base[idx])) { + dev_err(&pdev->dev, "Could not map ram controller address\n"); + return PTR_ERR(at91_ramc_base[idx]); + } + } + + match = platform_get_device_id(pdev); + arm_pm_restart = (void (*)(enum reboot_mode, const char*)) + match->driver_data; + + return 0; +} + +static int at91_reset_probe(struct platform_device *pdev) +{ + int ret; + + if (pdev->dev.of_node) + ret = at91_reset_of_probe(pdev); + else + ret = at91_reset_platform_probe(pdev); + + if (ret) + return ret; + + at91_reset_status(pdev); + + return 0; +} + +static struct platform_device_id at91_reset_plat_match[] = { + { "at91-sam9260-reset", (unsigned long)at91sam9260_restart }, + { "at91-sam9g45-reset", (unsigned long)at91sam9g45_restart }, + { /* sentinel */ } +}; + +static struct platform_driver at91_reset_driver = { + .probe = at91_reset_probe, + .driver = { + .name = "at91-reset", + .of_match_table = at91_reset_of_match, + }, + .id_table = at91_reset_plat_match, +}; +module_platform_driver(at91_reset_driver); -- cgit v1.2.1 From ae499f0fadaf28bf3138676fa2d3f6cf7d57556a Mon Sep 17 00:00:00 2001 From: Maxime Ripard Date: Thu, 3 Jul 2014 14:07:18 +0200 Subject: power: reset: Add AT91 poweroff driver Add a driver to handle the shutdown of the Atmel SoCs. This code used to be (and still is) in arch/arm/mach-at91. We didn't remove it yet so that we can convert all the boards to using this driver, before removing it entirely in a separate patch. Signed-off-by: Maxime Ripard Acked-by: Nicolas Ferre Acked-by: Alexandre Belloni --- drivers/power/reset/Kconfig | 8 ++ drivers/power/reset/Makefile | 1 + drivers/power/reset/at91-poweroff.c | 156 ++++++++++++++++++++++++++++++++++++ 3 files changed, 165 insertions(+) create mode 100644 drivers/power/reset/at91-poweroff.c diff --git a/drivers/power/reset/Kconfig b/drivers/power/reset/Kconfig index 360bac75a3da..1f9d0c53fc14 100644 --- a/drivers/power/reset/Kconfig +++ b/drivers/power/reset/Kconfig @@ -14,6 +14,14 @@ config POWER_RESET_AS3722 help This driver supports turning off board via a ams AS3722 power-off. +config POWER_RESET_AT91_POWEROFF + bool "Atmel AT91 poweroff driver" + depends on MACH_AT91 + default SOC_AT91SAM9 || SOC_SAMA5 + help + This driver supports poweroff for Atmel AT91SAM9 and SAMA5 + SoCs + config POWER_RESET_AT91_RESET bool "Atmel AT91 reset driver" depends on MACH_AT91 diff --git a/drivers/power/reset/Makefile b/drivers/power/reset/Makefile index 1599214b91d9..8bf941bac3da 100644 --- a/drivers/power/reset/Makefile +++ b/drivers/power/reset/Makefile @@ -1,4 +1,5 @@ obj-$(CONFIG_POWER_RESET_AS3722) += as3722-poweroff.o +obj-$(CONFIG_POWER_RESET_AT91_POWEROFF) += at91-poweroff.o obj-$(CONFIG_POWER_RESET_AT91_RESET) += at91-reset.o obj-$(CONFIG_POWER_RESET_AXXIA) += axxia-reset.o obj-$(CONFIG_POWER_RESET_GPIO) += gpio-poweroff.o diff --git a/drivers/power/reset/at91-poweroff.c b/drivers/power/reset/at91-poweroff.c new file mode 100644 index 000000000000..40bf42d146f1 --- /dev/null +++ b/drivers/power/reset/at91-poweroff.c @@ -0,0 +1,156 @@ +/* + * Atmel AT91 SAM9 SoCs reset code + * + * Copyright (C) 2007 Atmel Corporation. + * Copyright (C) 2011 Jean-Christophe PLAGNIOL-VILLARD + * Copyright (C) 2014 Free Electrons + * + * 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 + +#define AT91_SHDW_CR 0x00 /* Shut Down Control Register */ +#define AT91_SHDW_SHDW BIT(0) /* Shut Down command */ +#define AT91_SHDW_KEY (0xa5 << 24) /* KEY Password */ + +#define AT91_SHDW_MR 0x04 /* Shut Down Mode Register */ +#define AT91_SHDW_WKMODE0 GENMASK(2, 0) /* Wake-up 0 Mode Selection */ +#define AT91_SHDW_CPTWK0_MAX 0xf /* Maximum Counter On Wake Up 0 */ +#define AT91_SHDW_CPTWK0 (AT91_SHDW_CPTWK0_MAX << 4) /* Counter On Wake Up 0 */ +#define AT91_SHDW_CPTWK0_(x) ((x) << 4) +#define AT91_SHDW_RTTWKEN BIT(16) /* Real Time Timer Wake-up Enable */ +#define AT91_SHDW_RTCWKEN BIT(17) /* Real Time Clock Wake-up Enable */ + +#define AT91_SHDW_SR 0x08 /* Shut Down Status Register */ +#define AT91_SHDW_WAKEUP0 BIT(0) /* Wake-up 0 Status */ +#define AT91_SHDW_RTTWK BIT(16) /* Real-time Timer Wake-up */ +#define AT91_SHDW_RTCWK BIT(17) /* Real-time Clock Wake-up [SAM9RL] */ + +enum wakeup_type { + AT91_SHDW_WKMODE0_NONE = 0, + AT91_SHDW_WKMODE0_HIGH = 1, + AT91_SHDW_WKMODE0_LOW = 2, + AT91_SHDW_WKMODE0_ANYLEVEL = 3, +}; + +static const char *shdwc_wakeup_modes[] = { + [AT91_SHDW_WKMODE0_NONE] = "none", + [AT91_SHDW_WKMODE0_HIGH] = "high", + [AT91_SHDW_WKMODE0_LOW] = "low", + [AT91_SHDW_WKMODE0_ANYLEVEL] = "any", +}; + +static void __iomem *at91_shdwc_base; + +static void __init at91_wakeup_status(void) +{ + u32 reg = readl(at91_shdwc_base); + char *reason = "unknown"; + + /* Simple power-on, just bail out */ + if (!reg) + return; + + if (reg & AT91_SHDW_RTTWK) + reason = "RTT"; + else if (reg & AT91_SHDW_RTCWK) + reason = "RTC"; + + pr_info("AT91: Wake-Up source: %s\n", reason); +} + +static void at91_poweroff(void) +{ + writel(AT91_SHDW_KEY | AT91_SHDW_SHDW, at91_shdwc_base + AT91_SHDW_CR); +} + +const enum wakeup_type at91_poweroff_get_wakeup_mode(struct device_node *np) +{ + const char *pm; + int err, i; + + err = of_property_read_string(np, "atmel,wakeup-mode", &pm); + if (err < 0) + return AT91_SHDW_WKMODE0_ANYLEVEL; + + for (i = 0; i < ARRAY_SIZE(shdwc_wakeup_modes); i++) + if (!strcasecmp(pm, shdwc_wakeup_modes[i])) + return i; + + return -ENODEV; +} + +static void at91_poweroff_dt_set_wakeup_mode(struct platform_device *pdev) +{ + struct device_node *np = pdev->dev.of_node; + enum wakeup_type wakeup_mode; + u32 mode = 0, tmp; + + wakeup_mode = at91_poweroff_get_wakeup_mode(np); + if (wakeup_mode < 0) { + dev_warn(&pdev->dev, "shdwc unknown wakeup mode\n"); + return; + } + + if (!of_property_read_u32(np, "atmel,wakeup-counter", &tmp)) { + if (tmp > AT91_SHDW_CPTWK0_MAX) { + dev_warn(&pdev->dev, + "shdwc wakeup counter 0x%x > 0x%x reduce it to 0x%x\n", + tmp, AT91_SHDW_CPTWK0_MAX, AT91_SHDW_CPTWK0_MAX); + tmp = AT91_SHDW_CPTWK0_MAX; + } + mode |= AT91_SHDW_CPTWK0_(tmp); + } + + if (of_property_read_bool(np, "atmel,wakeup-rtc-timer")) + mode |= AT91_SHDW_RTCWKEN; + + if (of_property_read_bool(np, "atmel,wakeup-rtt-timer")) + mode |= AT91_SHDW_RTTWKEN; + + writel(wakeup_mode | mode, at91_shdwc_base + AT91_SHDW_MR); +} + +static int at91_poweroff_probe(struct platform_device *pdev) +{ + struct resource *res; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + at91_shdwc_base = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(at91_shdwc_base)) { + dev_err(&pdev->dev, "Could not map reset controller address\n"); + return PTR_ERR(at91_shdwc_base); + } + + at91_wakeup_status(); + + if (pdev->dev.of_node) + at91_poweroff_dt_set_wakeup_mode(pdev); + + pm_power_off = at91_poweroff; + + return 0; +} + +static struct of_device_id at91_poweroff_of_match[] = { + { .compatible = "atmel,at91sam9260-shdwc", }, + { .compatible = "atmel,at91sam9rl-shdwc", }, + { .compatible = "atmel,at91sam9x5-shdwc", }, + { /*sentinel*/ } +}; + +static struct platform_driver at91_poweroff_driver = { + .probe = at91_poweroff_probe, + .driver = { + .name = "at91-poweroff", + .of_match_table = at91_poweroff_of_match, + }, +}; +module_platform_driver(at91_poweroff_driver); -- cgit v1.2.1 From 9e8be232b934418932e40da276f6871dce83fa7c Mon Sep 17 00:00:00 2001 From: Maxime Ripard Date: Wed, 2 Jul 2014 12:04:43 +0200 Subject: ARM: at91: setup: Switch to pr_fmt Most of the printed messages are using the "AT91:" prefix in the setup.c file, but not all of them. Moreover, those who add it hardcode it directly in the message, while the pr_fmt macro makes it easier for us to support such a case. Signed-off-by: Maxime Ripard Acked-by: Alexandre Belloni Signed-off-by: Nicolas Ferre --- arch/arm/mach-at91/setup.c | 42 ++++++++++++++++++++++-------------------- 1 file changed, 22 insertions(+), 20 deletions(-) diff --git a/arch/arm/mach-at91/setup.c b/arch/arm/mach-at91/setup.c index 1174b5fe74d8..c565572157db 100644 --- a/arch/arm/mach-at91/setup.c +++ b/arch/arm/mach-at91/setup.c @@ -5,6 +5,8 @@ * Under GPLv2 */ +#define pr_fmt(fmt) "AT91: " fmt + #include #include #include @@ -37,7 +39,7 @@ void __init at91rm9200_set_type(int type) else at91_soc_initdata.subtype = AT91_SOC_RM9200_BGA; - pr_info("AT91: filled in soc subtype: %s\n", + pr_info("filled in soc subtype: %s\n", at91_get_soc_subtype(&at91_soc_initdata)); } @@ -67,7 +69,7 @@ void __init at91_ioremap_ramc(int id, u32 addr, u32 size) } at91_ramc_base[id] = ioremap(addr, size); if (!at91_ramc_base[id]) - panic("Impossible to ioremap ramc.%d 0x%x\n", id, addr); + panic(pr_fmt("Impossible to ioremap ramc.%d 0x%x\n"), id, addr); } static struct map_desc sram_desc[2] __initdata; @@ -84,7 +86,7 @@ void __init at91_init_sram(int bank, unsigned long base, unsigned int length) desc->length = length; desc->type = MT_MEMORY_RWX_NONCACHED; - pr_info("AT91: sram at 0x%lx of 0x%x mapped at 0x%lx\n", + pr_info("sram at 0x%lx of 0x%x mapped at 0x%lx\n", base, length, desc->virtual); iotable_init(desc, 1); @@ -303,16 +305,16 @@ void __init at91_map_io(void) soc_detect(AT91_BASE_DBGU1); if (!at91_soc_is_detected()) - panic("AT91: Impossible to detect the SOC type"); + panic(pr_fmt("Impossible to detect the SOC type")); - pr_info("AT91: Detected soc type: %s\n", + pr_info("Detected soc type: %s\n", at91_get_soc_type(&at91_soc_initdata)); if (at91_soc_initdata.subtype != AT91_SOC_SUBTYPE_NONE) - pr_info("AT91: Detected soc subtype: %s\n", + pr_info("Detected soc subtype: %s\n", at91_get_soc_subtype(&at91_soc_initdata)); if (!at91_soc_is_enabled()) - panic("AT91: Soc not enabled"); + panic(pr_fmt("Soc not enabled")); if (at91_boot_soc.map_io) at91_boot_soc.map_io(); @@ -329,7 +331,7 @@ void __init at91_ioremap_shdwc(u32 base_addr) { at91_shdwc_base = ioremap(base_addr, 16); if (!at91_shdwc_base) - panic("Impossible to ioremap at91_shdwc_base\n"); + panic(pr_fmt("Impossible to ioremap at91_shdwc_base\n")); pm_power_off = at91sam9_poweroff; } @@ -339,7 +341,7 @@ void __init at91_ioremap_rstc(u32 base_addr) { at91_rstc_base = ioremap(base_addr, 16); if (!at91_rstc_base) - panic("Impossible to ioremap at91_rstc_base\n"); + panic(pr_fmt("Impossible to ioremap at91_rstc_base\n")); } void __iomem *at91_matrix_base; @@ -349,7 +351,7 @@ void __init at91_ioremap_matrix(u32 base_addr) { at91_matrix_base = ioremap(base_addr, 512); if (!at91_matrix_base) - panic("Impossible to ioremap at91_matrix_base\n"); + panic(pr_fmt("Impossible to ioremap at91_matrix_base\n")); } #if defined(CONFIG_OF) && !defined(CONFIG_ARCH_AT91X40) @@ -366,15 +368,15 @@ static void at91_dt_rstc(void) np = of_find_matching_node(NULL, rstc_ids); if (!np) - panic("unable to find compatible rstc node in dtb\n"); + panic(pr_fmt("unable to find compatible rstc node in dtb\n")); at91_rstc_base = of_iomap(np, 0); if (!at91_rstc_base) - panic("unable to map rstc cpu registers\n"); + panic(pr_fmt("unable to map rstc cpu registers\n")); of_id = of_match_node(rstc_ids, np); if (!of_id) - panic("AT91: rtsc no restart function available\n"); + panic(pr_fmt("rtsc no restart function available\n")); arm_pm_restart = of_id->data; @@ -396,17 +398,17 @@ static void at91_dt_ramc(void) np = of_find_matching_node(NULL, ramc_ids); if (!np) - panic("unable to find compatible ram controller node in dtb\n"); + panic(pr_fmt("unable to find compatible ram controller node in dtb\n")); at91_ramc_base[0] = of_iomap(np, 0); if (!at91_ramc_base[0]) - panic("unable to map ramc[0] cpu registers\n"); + panic(pr_fmt("unable to map ramc[0] cpu registers\n")); /* the controller may have 2 banks */ at91_ramc_base[1] = of_iomap(np, 1); of_id = of_match_node(ramc_ids, np); if (!of_id) - pr_warn("AT91: ramc no standby function available\n"); + pr_warn("ramc no standby function available\n"); else at91_pm_set_standby(of_id->data); @@ -452,23 +454,23 @@ static void at91_dt_shdwc(void) np = of_find_matching_node(NULL, shdwc_ids); if (!np) { - pr_debug("AT91: unable to find compatible shutdown (shdwc) controller node in dtb\n"); + pr_debug("unable to find compatible shutdown (shdwc) controller node in dtb\n"); return; } at91_shdwc_base = of_iomap(np, 0); if (!at91_shdwc_base) - panic("AT91: unable to map shdwc cpu registers\n"); + panic(pr_fmt("unable to map shdwc cpu registers\n")); wakeup_mode = at91_dtget_shdwc_wakeup_mode(np); if (wakeup_mode < 0) { - pr_warn("AT91: shdwc unknown wakeup mode\n"); + pr_warn("shdwc unknown wakeup mode\n"); goto end; } if (!of_property_read_u32(np, "atmel,wakeup-counter", ®)) { if (reg > AT91_SHDW_CPTWK0_MAX) { - pr_warn("AT91: shdwc wakeup counter 0x%x > 0x%x reduce it to 0x%x\n", + pr_warn("shdwc wakeup counter 0x%x > 0x%x reduce it to 0x%x\n", reg, AT91_SHDW_CPTWK0_MAX, AT91_SHDW_CPTWK0_MAX); reg = AT91_SHDW_CPTWK0_MAX; } -- cgit v1.2.1 From 8d248f0d3a1ddb3c671b4029f8950fbad09be7ae Mon Sep 17 00:00:00 2001 From: Maxime Ripard Date: Thu, 3 Jul 2014 15:58:49 +0200 Subject: ARM: at91: Rework ramc mapping code Adapt the ramc mapping code to handle multiple ram controllers in the DT. Signed-off-by: Maxime Ripard Acked-by: Alexandre Belloni Signed-off-by: Nicolas Ferre --- .../devicetree/bindings/arm/atmel-at91.txt | 7 ------ arch/arm/mach-at91/setup.c | 26 ++++++++++++---------- 2 files changed, 14 insertions(+), 19 deletions(-) diff --git a/Documentation/devicetree/bindings/arm/atmel-at91.txt b/Documentation/devicetree/bindings/arm/atmel-at91.txt index 33bcf8b57798..6e3e3e5c611f 100644 --- a/Documentation/devicetree/bindings/arm/atmel-at91.txt +++ b/Documentation/devicetree/bindings/arm/atmel-at91.txt @@ -63,7 +63,6 @@ RAMC SDRAM/DDR Controller required properties: "atmel,at91sam9g45-ddramc", "atmel,sama5d3-ddramc", - reg: Should contain registers location and length - For at91sam9263 and at91sam9g45 you must specify 2 entries. Examples: @@ -72,12 +71,6 @@ Examples: reg = <0xffffe800 0x200>; }; - ramc0: ramc@ffffe400 { - compatible = "atmel,at91sam9g45-ddramc"; - reg = <0xffffe400 0x200 - 0xffffe600 0x200>; - }; - SHDWC Shutdown Controller required properties: diff --git a/arch/arm/mach-at91/setup.c b/arch/arm/mach-at91/setup.c index c565572157db..0c8daf7a4a77 100644 --- a/arch/arm/mach-at91/setup.c +++ b/arch/arm/mach-at91/setup.c @@ -395,24 +395,26 @@ static void at91_dt_ramc(void) { struct device_node *np; const struct of_device_id *of_id; + int idx = 0; - np = of_find_matching_node(NULL, ramc_ids); - if (!np) - panic(pr_fmt("unable to find compatible ram controller node in dtb\n")); + for_each_matching_node(np, ramc_ids) { + at91_ramc_base[idx] = of_iomap(np, 0); + if (!at91_ramc_base[idx]) + panic(pr_fmt("unable to map ramc[%d] cpu registers\n"), idx); - at91_ramc_base[0] = of_iomap(np, 0); - if (!at91_ramc_base[0]) - panic(pr_fmt("unable to map ramc[0] cpu registers\n")); - /* the controller may have 2 banks */ - at91_ramc_base[1] = of_iomap(np, 1); + idx++; + } + + if (!idx) + panic(pr_fmt("unable to find compatible ram controller node in dtb\n")); of_id = of_match_node(ramc_ids, np); - if (!of_id) + if (!of_id) { pr_warn("ramc no standby function available\n"); - else - at91_pm_set_standby(of_id->data); + return; + } - of_node_put(np); + at91_pm_set_standby(of_id->data); } static struct of_device_id shdwc_ids[] = { -- cgit v1.2.1 From 4b0a683cf16eac06385c96c2fcf21766818f757c Mon Sep 17 00:00:00 2001 From: Maxime Ripard Date: Wed, 2 Jul 2014 17:47:50 +0200 Subject: ARM: at91: Remove the old-style reset probing Now that the reset code is a driver of its own, just let the usual DT probing mecanism do its job, and remove the code entirely in this case. Signed-off-by: Maxime Ripard Acked-by: Alexandre Belloni Signed-off-by: Nicolas Ferre --- arch/arm/mach-at91/setup.c | 29 ----------------------------- 1 file changed, 29 deletions(-) diff --git a/arch/arm/mach-at91/setup.c b/arch/arm/mach-at91/setup.c index 0c8daf7a4a77..640c21e43a42 100644 --- a/arch/arm/mach-at91/setup.c +++ b/arch/arm/mach-at91/setup.c @@ -355,34 +355,6 @@ void __init at91_ioremap_matrix(u32 base_addr) } #if defined(CONFIG_OF) && !defined(CONFIG_ARCH_AT91X40) -static struct of_device_id rstc_ids[] = { - { .compatible = "atmel,at91sam9260-rstc", .data = at91sam9_alt_restart }, - { .compatible = "atmel,at91sam9g45-rstc", .data = at91sam9g45_restart }, - { /*sentinel*/ } -}; - -static void at91_dt_rstc(void) -{ - struct device_node *np; - const struct of_device_id *of_id; - - np = of_find_matching_node(NULL, rstc_ids); - if (!np) - panic(pr_fmt("unable to find compatible rstc node in dtb\n")); - - at91_rstc_base = of_iomap(np, 0); - if (!at91_rstc_base) - panic(pr_fmt("unable to map rstc cpu registers\n")); - - of_id = of_match_node(rstc_ids, np); - if (!of_id) - panic(pr_fmt("rtsc no restart function available\n")); - - arm_pm_restart = of_id->data; - - of_node_put(np); -} - static struct of_device_id ramc_ids[] = { { .compatible = "atmel,at91rm9200-sdramc", .data = at91rm9200_standby }, { .compatible = "atmel,at91sam9260-sdramc", .data = at91sam9_sdram_standby }, @@ -509,7 +481,6 @@ void __init at91rm9200_dt_initialize(void) void __init at91_dt_initialize(void) { - at91_dt_rstc(); at91_dt_ramc(); at91_dt_shdwc(); -- cgit v1.2.1 From 65f2ce96a94d2a61fca486a48d9da30845b16d20 Mon Sep 17 00:00:00 2001 From: Maxime Ripard Date: Wed, 2 Jul 2014 17:49:28 +0200 Subject: ARM: at91/soc: Introduce register_devices callback Some core devices should be registered by the SoC itself rather than by every board using this SoC. Introduce a register_devices callback that should be called during the init_machine in order to do that. Signed-off-by: Maxime Ripard Acked-by: Alexandre Belloni Signed-off-by: Nicolas Ferre --- arch/arm/mach-at91/generic.h | 2 ++ arch/arm/mach-at91/setup.c | 5 +++++ arch/arm/mach-at91/soc.h | 1 + 3 files changed, 8 insertions(+) diff --git a/arch/arm/mach-at91/generic.h b/arch/arm/mach-at91/generic.h index 631fa3b8c16d..4c2c96195a50 100644 --- a/arch/arm/mach-at91/generic.h +++ b/arch/arm/mach-at91/generic.h @@ -37,6 +37,8 @@ extern int __init at91_aic5_of_init(struct device_node *node, extern void __init at91_sysirq_mask_rtc(u32 rtc_base); extern void __init at91_sysirq_mask_rtt(u32 rtt_base); + /* Devices */ +extern void __init at91_register_devices(void); /* Timer */ extern void at91rm9200_ioremap_st(u32 addr); diff --git a/arch/arm/mach-at91/setup.c b/arch/arm/mach-at91/setup.c index 640c21e43a42..25196d5096be 100644 --- a/arch/arm/mach-at91/setup.c +++ b/arch/arm/mach-at91/setup.c @@ -510,3 +510,8 @@ void __init at91_initialize(unsigned long main_clock) pinctrl_provide_dummies(); } + +void __init at91_register_devices(void) +{ + at91_boot_soc.register_devices(); +} diff --git a/arch/arm/mach-at91/soc.h b/arch/arm/mach-at91/soc.h index a1e1482c6da8..ab983f2cc7dd 100644 --- a/arch/arm/mach-at91/soc.h +++ b/arch/arm/mach-at91/soc.h @@ -11,6 +11,7 @@ struct at91_init_soc { void (*map_io)(void); void (*ioremap_registers)(void); void (*register_clocks)(void); + void (*register_devices)(void); void (*init)(void); }; -- cgit v1.2.1 From d8dfa2d4e912c091c51c864af1e4b02b3bd6e9a2 Mon Sep 17 00:00:00 2001 From: Maxime Ripard Date: Thu, 3 Jul 2014 11:10:10 +0200 Subject: ARM: at91: Probe the reset driver Register the reset device in the right SoCs so that the reset driver is actually probed even in the old-style probing case. Signed-off-by: Maxime Ripard Acked-by: Alexandre Belloni Signed-off-by: Nicolas Ferre --- arch/arm/mach-at91/at91sam9260.c | 26 ++++++++++++++++++++++++++ arch/arm/mach-at91/at91sam9261.c | 26 ++++++++++++++++++++++++++ arch/arm/mach-at91/at91sam9263.c | 26 ++++++++++++++++++++++++++ arch/arm/mach-at91/at91sam9g45.c | 31 +++++++++++++++++++++++++++++++ arch/arm/mach-at91/at91sam9rl.c | 26 ++++++++++++++++++++++++++ 5 files changed, 135 insertions(+) diff --git a/arch/arm/mach-at91/at91sam9260.c b/arch/arm/mach-at91/at91sam9260.c index 3477ba94c4c5..fd6b45a85966 100644 --- a/arch/arm/mach-at91/at91sam9260.c +++ b/arch/arm/mach-at91/at91sam9260.c @@ -11,6 +11,7 @@ */ #include +#include #include #include @@ -362,6 +363,30 @@ static void __init at91sam9260_initialize(void) at91_gpio_init(at91sam9260_gpio, 3); } +static struct resource rstc_resources[] = { + [0] = { + .start = AT91SAM9260_BASE_RSTC, + .end = AT91SAM9260_BASE_RSTC + SZ_16 - 1, + .flags = IORESOURCE_MEM, + }, + [1] = { + .start = AT91SAM9260_BASE_SDRAMC, + .end = AT91SAM9260_BASE_SDRAMC + SZ_512 - 1, + .flags = IORESOURCE_MEM, + }, +}; + +static struct platform_device rstc_device = { + .name = "at91-sam9260-reset", + .resource = rstc_resources, + .num_resources = ARRAY_SIZE(rstc_resources), +}; + +static void __init at91sam9260_register_devices(void) +{ + platform_device_register(&rstc_device); +} + /* -------------------------------------------------------------------- * Interrupt initialization * -------------------------------------------------------------------- */ @@ -411,5 +436,6 @@ AT91_SOC_START(at91sam9260) | (1 << AT91SAM9260_ID_IRQ2), .ioremap_registers = at91sam9260_ioremap_registers, .register_clocks = at91sam9260_register_clocks, + .register_devices = at91sam9260_register_devices, .init = at91sam9260_initialize, AT91_SOC_END diff --git a/arch/arm/mach-at91/at91sam9261.c b/arch/arm/mach-at91/at91sam9261.c index fb164a5d04a9..09c031676229 100644 --- a/arch/arm/mach-at91/at91sam9261.c +++ b/arch/arm/mach-at91/at91sam9261.c @@ -11,6 +11,7 @@ */ #include +#include #include #include @@ -321,6 +322,30 @@ static void __init at91sam9261_initialize(void) at91_gpio_init(at91sam9261_gpio, 3); } +static struct resource rstc_resources[] = { + [0] = { + .start = AT91SAM9261_BASE_RSTC, + .end = AT91SAM9261_BASE_RSTC + SZ_16 - 1, + .flags = IORESOURCE_MEM, + }, + [1] = { + .start = AT91SAM9261_BASE_SDRAMC, + .end = AT91SAM9261_BASE_SDRAMC + SZ_512 - 1, + .flags = IORESOURCE_MEM, + }, +}; + +static struct platform_device rstc_device = { + .name = "at91-sam9260-reset", + .resource = rstc_resources, + .num_resources = ARRAY_SIZE(rstc_resources), +}; + +static void __init at91sam9261_register_devices(void) +{ + platform_device_register(&rstc_device); +} + /* -------------------------------------------------------------------- * Interrupt initialization * -------------------------------------------------------------------- */ @@ -370,5 +395,6 @@ AT91_SOC_START(at91sam9261) | (1 << AT91SAM9261_ID_IRQ2), .ioremap_registers = at91sam9261_ioremap_registers, .register_clocks = at91sam9261_register_clocks, + .register_devices = at91sam9261_register_devices, .init = at91sam9261_initialize, AT91_SOC_END diff --git a/arch/arm/mach-at91/at91sam9263.c b/arch/arm/mach-at91/at91sam9263.c index 810fa5f15a51..e34ce7af5d0a 100644 --- a/arch/arm/mach-at91/at91sam9263.c +++ b/arch/arm/mach-at91/at91sam9263.c @@ -11,6 +11,7 @@ */ #include +#include #include #include @@ -344,6 +345,30 @@ static void __init at91sam9263_initialize(void) at91_gpio_init(at91sam9263_gpio, 5); } +static struct resource rstc_resources[] = { + [0] = { + .start = AT91SAM9263_BASE_RSTC, + .end = AT91SAM9263_BASE_RSTC + SZ_16 - 1, + .flags = IORESOURCE_MEM, + }, + [1] = { + .start = AT91SAM9263_BASE_SDRAMC0, + .end = AT91SAM9263_BASE_SDRAMC0 + SZ_512 - 1, + .flags = IORESOURCE_MEM, + }, +}; + +static struct platform_device rstc_device = { + .name = "at91-sam9260-reset", + .resource = rstc_resources, + .num_resources = ARRAY_SIZE(rstc_resources), +}; + +static void __init at91sam9263_register_devices(void) +{ + platform_device_register(&rstc_device); +} + /* -------------------------------------------------------------------- * Interrupt initialization * -------------------------------------------------------------------- */ @@ -392,5 +417,6 @@ AT91_SOC_START(at91sam9263) .extern_irq = (1 << AT91SAM9263_ID_IRQ0) | (1 << AT91SAM9263_ID_IRQ1), .ioremap_registers = at91sam9263_ioremap_registers, .register_clocks = at91sam9263_register_clocks, + .register_devices = at91sam9263_register_devices, .init = at91sam9263_initialize, AT91_SOC_END diff --git a/arch/arm/mach-at91/at91sam9g45.c b/arch/arm/mach-at91/at91sam9g45.c index 9d45496e4932..cc79f201abbc 100644 --- a/arch/arm/mach-at91/at91sam9g45.c +++ b/arch/arm/mach-at91/at91sam9g45.c @@ -13,6 +13,7 @@ #include #include #include +#include #include #include @@ -393,6 +394,35 @@ static void __init at91sam9g45_initialize(void) at91_gpio_init(at91sam9g45_gpio, 5); } +static struct resource rstc_resources[] = { + [0] = { + .start = AT91SAM9G45_BASE_RSTC, + .end = AT91SAM9G45_BASE_RSTC + SZ_16 - 1, + .flags = IORESOURCE_MEM, + }, + [1] = { + .start = AT91SAM9G45_BASE_DDRSDRC1, + .end = AT91SAM9G45_BASE_DDRSDRC1 + SZ_512 - 1, + .flags = IORESOURCE_MEM, + }, + [2] = { + .start = AT91SAM9G45_BASE_DDRSDRC0, + .end = AT91SAM9G45_BASE_DDRSDRC0 + SZ_512 - 1, + .flags = IORESOURCE_MEM, + }, +}; + +static struct platform_device rstc_device = { + .name = "at91-sam9g45-reset", + .resource = rstc_resources, + .num_resources = ARRAY_SIZE(rstc_resources), +}; + +static void __init at91sam9g45_register_devices(void) +{ + platform_device_register(&rstc_device); +} + /* -------------------------------------------------------------------- * Interrupt initialization * -------------------------------------------------------------------- */ @@ -441,5 +471,6 @@ AT91_SOC_START(at91sam9g45) .extern_irq = (1 << AT91SAM9G45_ID_IRQ0), .ioremap_registers = at91sam9g45_ioremap_registers, .register_clocks = at91sam9g45_register_clocks, + .register_devices = at91sam9g45_register_devices, .init = at91sam9g45_initialize, AT91_SOC_END diff --git a/arch/arm/mach-at91/at91sam9rl.c b/arch/arm/mach-at91/at91sam9rl.c index 878d5015daab..b03053e9b440 100644 --- a/arch/arm/mach-at91/at91sam9rl.c +++ b/arch/arm/mach-at91/at91sam9rl.c @@ -10,6 +10,7 @@ */ #include +#include #include #include @@ -332,6 +333,30 @@ static void __init at91sam9rl_initialize(void) at91_gpio_init(at91sam9rl_gpio, 4); } +static struct resource rstc_resources[] = { + [0] = { + .start = AT91SAM9RL_BASE_RSTC, + .end = AT91SAM9RL_BASE_RSTC + SZ_16 - 1, + .flags = IORESOURCE_MEM, + }, + [1] = { + .start = AT91SAM9RL_BASE_SDRAMC, + .end = AT91SAM9RL_BASE_SDRAMC + SZ_512 - 1, + .flags = IORESOURCE_MEM, + }, +}; + +static struct platform_device rstc_device = { + .name = "at91-sam9260-reset", + .resource = rstc_resources, + .num_resources = ARRAY_SIZE(rstc_resources), +}; + +static void __init at91sam9rl_register_devices(void) +{ + platform_device_register(&rstc_device); +} + /* -------------------------------------------------------------------- * Interrupt initialization * -------------------------------------------------------------------- */ @@ -382,5 +407,6 @@ AT91_SOC_START(at91sam9rl) #if defined(CONFIG_OLD_CLK_AT91) .register_clocks = at91sam9rl_register_clocks, #endif + .register_devices = at91sam9rl_register_devices, .init = at91sam9rl_initialize, AT91_SOC_END -- cgit v1.2.1 From e76265c627ca0aff6cc129486ac656b6b6dd06af Mon Sep 17 00:00:00 2001 From: Maxime Ripard Date: Thu, 3 Jul 2014 11:55:33 +0200 Subject: ARM: at91: Call at91_register_devices in the board files Make every board call the register_devices callback so that the devices declared by the SoC are registered. Signed-off-by: Maxime Ripard Acked-by: Alexandre Belloni Signed-off-by: Nicolas Ferre --- arch/arm/mach-at91/board-afeb-9260v1.c | 2 ++ arch/arm/mach-at91/board-cam60.c | 2 ++ arch/arm/mach-at91/board-cpu9krea.c | 2 ++ arch/arm/mach-at91/board-flexibity.c | 2 ++ arch/arm/mach-at91/board-sam9-l9260.c | 2 ++ arch/arm/mach-at91/board-sam9260ek.c | 2 ++ arch/arm/mach-at91/board-sam9261ek.c | 2 ++ arch/arm/mach-at91/board-sam9263ek.c | 2 ++ arch/arm/mach-at91/board-sam9m10g45ek.c | 2 ++ arch/arm/mach-at91/board-sam9rlek.c | 2 ++ arch/arm/mach-at91/board-snapper9260.c | 2 ++ 11 files changed, 22 insertions(+) diff --git a/arch/arm/mach-at91/board-afeb-9260v1.c b/arch/arm/mach-at91/board-afeb-9260v1.c index 597c649170aa..fc9621ccb606 100644 --- a/arch/arm/mach-at91/board-afeb-9260v1.c +++ b/arch/arm/mach-at91/board-afeb-9260v1.c @@ -167,6 +167,8 @@ static struct at91_cf_data afeb9260_cf_data = { static void __init afeb9260_board_init(void) { + at91_register_devices(); + /* Serial */ /* DBGU on ttyS0. (Rx & Tx only) */ at91_register_uart(0, 0, 0); diff --git a/arch/arm/mach-at91/board-cam60.c b/arch/arm/mach-at91/board-cam60.c index a30502c8d379..283655bd86c1 100644 --- a/arch/arm/mach-at91/board-cam60.c +++ b/arch/arm/mach-at91/board-cam60.c @@ -170,6 +170,8 @@ static void __init cam60_add_device_nand(void) static void __init cam60_board_init(void) { + at91_register_devices(); + /* Serial */ /* DBGU on ttyS0. (Rx & Tx only) */ at91_register_uart(0, 0, 0); diff --git a/arch/arm/mach-at91/board-cpu9krea.c b/arch/arm/mach-at91/board-cpu9krea.c index 2037f78c84e7..29a89032bb9a 100644 --- a/arch/arm/mach-at91/board-cpu9krea.c +++ b/arch/arm/mach-at91/board-cpu9krea.c @@ -322,6 +322,8 @@ static struct mci_platform_data __initdata cpu9krea_mci0_data = { static void __init cpu9krea_board_init(void) { + at91_register_devices(); + /* NOR */ cpu9krea_add_device_nor(); /* Serial */ diff --git a/arch/arm/mach-at91/board-flexibity.c b/arch/arm/mach-at91/board-flexibity.c index 68f1ab6bd08f..79bd411b0cee 100644 --- a/arch/arm/mach-at91/board-flexibity.c +++ b/arch/arm/mach-at91/board-flexibity.c @@ -138,6 +138,8 @@ static struct gpio_led flexibity_leds[] = { static void __init flexibity_board_init(void) { + at91_register_devices(); + /* Serial */ /* DBGU on ttyS0. (Rx & Tx only) */ at91_register_uart(0, 0, 0); diff --git a/arch/arm/mach-at91/board-sam9-l9260.c b/arch/arm/mach-at91/board-sam9-l9260.c index d24dda67e2d3..70309404f366 100644 --- a/arch/arm/mach-at91/board-sam9-l9260.c +++ b/arch/arm/mach-at91/board-sam9-l9260.c @@ -187,6 +187,8 @@ static struct gpio_led ek_leds[] = { static void __init ek_board_init(void) { + at91_register_devices(); + /* Serial */ /* DBGU on ttyS0. (Rx & Tx only) */ at91_register_uart(0, 0, 0); diff --git a/arch/arm/mach-at91/board-sam9260ek.c b/arch/arm/mach-at91/board-sam9260ek.c index 65dea12d685e..da9c5f1f2c49 100644 --- a/arch/arm/mach-at91/board-sam9260ek.c +++ b/arch/arm/mach-at91/board-sam9260ek.c @@ -307,6 +307,8 @@ static void __init ek_add_device_buttons(void) {} static void __init ek_board_init(void) { + at91_register_devices(); + /* Serial */ /* DBGU on ttyS0. (Rx & Tx only) */ at91_register_uart(0, 0, 0); diff --git a/arch/arm/mach-at91/board-sam9261ek.c b/arch/arm/mach-at91/board-sam9261ek.c index 4637432de08f..d1be10b09ab3 100644 --- a/arch/arm/mach-at91/board-sam9261ek.c +++ b/arch/arm/mach-at91/board-sam9261ek.c @@ -561,6 +561,8 @@ static struct gpio_led ek_leds[] = { static void __init ek_board_init(void) { + at91_register_devices(); + /* Serial */ /* DBGU on ttyS0. (Rx & Tx only) */ at91_register_uart(0, 0, 0); diff --git a/arch/arm/mach-at91/board-sam9263ek.c b/arch/arm/mach-at91/board-sam9263ek.c index fc446097f410..7ccaf3be6e80 100644 --- a/arch/arm/mach-at91/board-sam9263ek.c +++ b/arch/arm/mach-at91/board-sam9263ek.c @@ -439,6 +439,8 @@ static struct platform_device *devices[] __initdata = { static void __init ek_board_init(void) { + at91_register_devices(); + /* Serial */ /* DBGU on ttyS0. (Rx & Tx only) */ at91_register_uart(0, 0, 0); diff --git a/arch/arm/mach-at91/board-sam9m10g45ek.c b/arch/arm/mach-at91/board-sam9m10g45ek.c index b227732b0c83..7b8bc961a5d5 100644 --- a/arch/arm/mach-at91/board-sam9m10g45ek.c +++ b/arch/arm/mach-at91/board-sam9m10g45ek.c @@ -471,6 +471,8 @@ static struct platform_device *devices[] __initdata = { static void __init ek_board_init(void) { + at91_register_devices(); + /* Serial */ /* DGBU on ttyS0. (Rx & Tx only) */ at91_register_uart(0, 0, 0); diff --git a/arch/arm/mach-at91/board-sam9rlek.c b/arch/arm/mach-at91/board-sam9rlek.c index b64648b4a1fc..262e3f957d9f 100644 --- a/arch/arm/mach-at91/board-sam9rlek.c +++ b/arch/arm/mach-at91/board-sam9rlek.c @@ -292,6 +292,8 @@ static void __init ek_add_device_buttons(void) {} static void __init ek_board_init(void) { + at91_register_devices(); + /* Serial */ /* DBGU on ttyS0. (Rx & Tx only) */ at91_register_uart(0, 0, 0); diff --git a/arch/arm/mach-at91/board-snapper9260.c b/arch/arm/mach-at91/board-snapper9260.c index 1b870e6def0c..2a817b85569f 100644 --- a/arch/arm/mach-at91/board-snapper9260.c +++ b/arch/arm/mach-at91/board-snapper9260.c @@ -154,6 +154,8 @@ static void __init snapper9260_add_device_nand(void) static void __init snapper9260_board_init(void) { + at91_register_devices(); + at91_add_device_i2c(snapper9260_i2c_devices, ARRAY_SIZE(snapper9260_i2c_devices)); -- cgit v1.2.1 From 99d63fa1c8fe8853540116a6deaedc1ba1fc0468 Mon Sep 17 00:00:00 2001 From: Maxime Ripard Date: Thu, 3 Jul 2014 12:00:52 +0200 Subject: ARM: at91: Remove reset code from the machine code Now that the transition is over and that we probe our reset driver in every case, we can remove the legacy code from the machine directory. Signed-off-by: Maxime Ripard Acked-by: Alexandre Belloni Signed-off-by: Nicolas Ferre --- arch/arm/mach-at91/Kconfig | 8 ------ arch/arm/mach-at91/Makefile | 2 -- arch/arm/mach-at91/at91sam9260.c | 1 - arch/arm/mach-at91/at91sam9261.c | 1 - arch/arm/mach-at91/at91sam9263.c | 1 - arch/arm/mach-at91/at91sam9_alt_reset.S | 40 ----------------------------- arch/arm/mach-at91/at91sam9g45.c | 1 - arch/arm/mach-at91/at91sam9g45_reset.S | 45 --------------------------------- arch/arm/mach-at91/at91sam9rl.c | 1 - arch/arm/mach-at91/generic.h | 2 -- 10 files changed, 102 deletions(-) delete mode 100644 arch/arm/mach-at91/at91sam9_alt_reset.S delete mode 100644 arch/arm/mach-at91/at91sam9g45_reset.S diff --git a/arch/arm/mach-at91/Kconfig b/arch/arm/mach-at91/Kconfig index 6aa7ab47205e..6eb3c658761d 100644 --- a/arch/arm/mach-at91/Kconfig +++ b/arch/arm/mach-at91/Kconfig @@ -33,14 +33,6 @@ config OLD_IRQ_AT91 select MULTI_IRQ_HANDLER select SPARSE_IRQ -config AT91_SAM9_ALT_RESET - bool - default !ARCH_AT91X40 - -config AT91_SAM9G45_RESET - bool - default !ARCH_AT91X40 - config AT91_SAM9_TIME bool diff --git a/arch/arm/mach-at91/Makefile b/arch/arm/mach-at91/Makefile index d972fd67de83..306c82b3d45c 100644 --- a/arch/arm/mach-at91/Makefile +++ b/arch/arm/mach-at91/Makefile @@ -9,8 +9,6 @@ obj- := obj-$(CONFIG_OLD_IRQ_AT91) += irq.o obj-$(CONFIG_OLD_CLK_AT91) += clock.o -obj-$(CONFIG_AT91_SAM9_ALT_RESET) += at91sam9_alt_reset.o -obj-$(CONFIG_AT91_SAM9G45_RESET) += at91sam9g45_reset.o obj-$(CONFIG_AT91_SAM9_TIME) += at91sam926x_time.o obj-$(CONFIG_SOC_AT91SAM9) += sam9_smc.o diff --git a/arch/arm/mach-at91/at91sam9260.c b/arch/arm/mach-at91/at91sam9260.c index fd6b45a85966..00e4e5bb452f 100644 --- a/arch/arm/mach-at91/at91sam9260.c +++ b/arch/arm/mach-at91/at91sam9260.c @@ -355,7 +355,6 @@ static void __init at91sam9260_ioremap_registers(void) static void __init at91sam9260_initialize(void) { arm_pm_idle = at91sam9_idle; - arm_pm_restart = at91sam9_alt_restart; at91_sysirq_mask_rtt(AT91SAM9260_BASE_RTT); diff --git a/arch/arm/mach-at91/at91sam9261.c b/arch/arm/mach-at91/at91sam9261.c index 09c031676229..43db1bdf885d 100644 --- a/arch/arm/mach-at91/at91sam9261.c +++ b/arch/arm/mach-at91/at91sam9261.c @@ -314,7 +314,6 @@ static void __init at91sam9261_ioremap_registers(void) static void __init at91sam9261_initialize(void) { arm_pm_idle = at91sam9_idle; - arm_pm_restart = at91sam9_alt_restart; at91_sysirq_mask_rtt(AT91SAM9261_BASE_RTT); diff --git a/arch/arm/mach-at91/at91sam9263.c b/arch/arm/mach-at91/at91sam9263.c index e34ce7af5d0a..358e762f0c07 100644 --- a/arch/arm/mach-at91/at91sam9263.c +++ b/arch/arm/mach-at91/at91sam9263.c @@ -336,7 +336,6 @@ static void __init at91sam9263_ioremap_registers(void) static void __init at91sam9263_initialize(void) { arm_pm_idle = at91sam9_idle; - arm_pm_restart = at91sam9_alt_restart; at91_sysirq_mask_rtt(AT91SAM9263_BASE_RTT0); at91_sysirq_mask_rtt(AT91SAM9263_BASE_RTT1); diff --git a/arch/arm/mach-at91/at91sam9_alt_reset.S b/arch/arm/mach-at91/at91sam9_alt_reset.S deleted file mode 100644 index f039538d3bdb..000000000000 --- a/arch/arm/mach-at91/at91sam9_alt_reset.S +++ /dev/null @@ -1,40 +0,0 @@ -/* - * reset AT91SAM9G20 as per errata - * - * (C) BitBox Ltd 2010 - * - * unless the SDRAM is cleanly shutdown before we hit the - * reset register it can be left driving the data bus and - * killing the chance of a subsequent boot from NAND - * - * 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. - */ - -#include -#include -#include -#include "at91_rstc.h" - - .arm - - .globl at91sam9_alt_restart - -at91sam9_alt_restart: ldr r0, =at91_ramc_base @ preload constants - ldr r0, [r0] - ldr r4, =at91_rstc_base - ldr r1, [r4] - - mov r2, #1 - mov r3, #AT91_SDRAMC_LPCB_POWER_DOWN - ldr r4, =AT91_RSTC_KEY | AT91_RSTC_PERRST | AT91_RSTC_PROCRST - - .balign 32 @ align to cache line - - str r2, [r0, #AT91_SDRAMC_TR] @ disable SDRAM access - str r3, [r0, #AT91_SDRAMC_LPR] @ power down SDRAM - str r4, [r1, #AT91_RSTC_CR] @ reset processor - - b . diff --git a/arch/arm/mach-at91/at91sam9g45.c b/arch/arm/mach-at91/at91sam9g45.c index cc79f201abbc..9b55b7dc12c2 100644 --- a/arch/arm/mach-at91/at91sam9g45.c +++ b/arch/arm/mach-at91/at91sam9g45.c @@ -385,7 +385,6 @@ static void __init at91sam9g45_ioremap_registers(void) static void __init at91sam9g45_initialize(void) { arm_pm_idle = at91sam9_idle; - arm_pm_restart = at91sam9g45_restart; at91_sysirq_mask_rtc(AT91SAM9G45_BASE_RTC); at91_sysirq_mask_rtt(AT91SAM9G45_BASE_RTT); diff --git a/arch/arm/mach-at91/at91sam9g45_reset.S b/arch/arm/mach-at91/at91sam9g45_reset.S deleted file mode 100644 index c40c1e2ef80f..000000000000 --- a/arch/arm/mach-at91/at91sam9g45_reset.S +++ /dev/null @@ -1,45 +0,0 @@ -/* - * reset AT91SAM9G45 as per errata - * - * Copyright (C) 2011 Jean-Christophe PLAGNIOL-VILLARD - * - * unless the SDRAM is cleanly shutdown before we hit the - * reset register it can be left driving the data bus and - * killing the chance of a subsequent boot from NAND - * - * GPLv2 Only - */ - -#include -#include -#include -#include "at91_rstc.h" - .arm - -/* - * at91_ramc_base is an array void* - * init at NULL if only one DDR controler is present in or DT - */ - .globl at91sam9g45_restart - -at91sam9g45_restart: - ldr r5, =at91_ramc_base @ preload constants - ldr r0, [r5] - ldr r5, [r5, #4] @ ddr1 - cmp r5, #0 - ldr r4, =at91_rstc_base - ldr r1, [r4] - - mov r2, #1 - mov r3, #AT91_DDRSDRC_LPCB_POWER_DOWN - ldr r4, =AT91_RSTC_KEY | AT91_RSTC_PERRST | AT91_RSTC_PROCRST - - .balign 32 @ align to cache line - - strne r2, [r5, #AT91_DDRSDRC_RTR] @ disable DDR1 access - strne r3, [r5, #AT91_DDRSDRC_LPR] @ power down DDR1 - str r2, [r0, #AT91_DDRSDRC_RTR] @ disable DDR0 access - str r3, [r0, #AT91_DDRSDRC_LPR] @ power down DDR0 - str r4, [r1, #AT91_RSTC_CR] @ reset processor - - b . diff --git a/arch/arm/mach-at91/at91sam9rl.c b/arch/arm/mach-at91/at91sam9rl.c index b03053e9b440..d43d28ee8eaf 100644 --- a/arch/arm/mach-at91/at91sam9rl.c +++ b/arch/arm/mach-at91/at91sam9rl.c @@ -324,7 +324,6 @@ static void __init at91sam9rl_ioremap_registers(void) static void __init at91sam9rl_initialize(void) { arm_pm_idle = at91sam9_idle; - arm_pm_restart = at91sam9_alt_restart; at91_sysirq_mask_rtc(AT91SAM9RL_BASE_RTC); at91_sysirq_mask_rtt(AT91SAM9RL_BASE_RTT); diff --git a/arch/arm/mach-at91/generic.h b/arch/arm/mach-at91/generic.h index 4c2c96195a50..5d5e55237c41 100644 --- a/arch/arm/mach-at91/generic.h +++ b/arch/arm/mach-at91/generic.h @@ -66,8 +66,6 @@ extern void at91sam9_idle(void); /* reset */ extern void at91_ioremap_rstc(u32 base_addr); -extern void at91sam9_alt_restart(enum reboot_mode, const char *); -extern void at91sam9g45_restart(enum reboot_mode, const char *); /* shutdown */ extern void at91_ioremap_shdwc(u32 base_addr); -- cgit v1.2.1 From 5c0593db802c8a9da15216615e466fd4e729df6f Mon Sep 17 00:00:00 2001 From: Maxime Ripard Date: Mon, 7 Jul 2014 16:25:26 +0200 Subject: ARM: at91: Remove poweroff DT probing Now that the poweroff code is a driver of its own, remove the DT probing in mach-at91 and let the usual DT code do its job. Signed-off-by: Maxime Ripard Acked-by: Alexandre Belloni Signed-off-by: Nicolas Ferre --- arch/arm/mach-at91/setup.c | 77 ---------------------------------------------- 1 file changed, 77 deletions(-) diff --git a/arch/arm/mach-at91/setup.c b/arch/arm/mach-at91/setup.c index 25196d5096be..6fe03f31d3d2 100644 --- a/arch/arm/mach-at91/setup.c +++ b/arch/arm/mach-at91/setup.c @@ -389,82 +389,6 @@ static void at91_dt_ramc(void) at91_pm_set_standby(of_id->data); } -static struct of_device_id shdwc_ids[] = { - { .compatible = "atmel,at91sam9260-shdwc", }, - { .compatible = "atmel,at91sam9rl-shdwc", }, - { .compatible = "atmel,at91sam9x5-shdwc", }, - { /*sentinel*/ } -}; - -static const char *shdwc_wakeup_modes[] = { - [AT91_SHDW_WKMODE0_NONE] = "none", - [AT91_SHDW_WKMODE0_HIGH] = "high", - [AT91_SHDW_WKMODE0_LOW] = "low", - [AT91_SHDW_WKMODE0_ANYLEVEL] = "any", -}; - -const int at91_dtget_shdwc_wakeup_mode(struct device_node *np) -{ - const char *pm; - int err, i; - - err = of_property_read_string(np, "atmel,wakeup-mode", &pm); - if (err < 0) - return AT91_SHDW_WKMODE0_ANYLEVEL; - - for (i = 0; i < ARRAY_SIZE(shdwc_wakeup_modes); i++) - if (!strcasecmp(pm, shdwc_wakeup_modes[i])) - return i; - - return -ENODEV; -} - -static void at91_dt_shdwc(void) -{ - struct device_node *np; - int wakeup_mode; - u32 reg; - u32 mode = 0; - - np = of_find_matching_node(NULL, shdwc_ids); - if (!np) { - pr_debug("unable to find compatible shutdown (shdwc) controller node in dtb\n"); - return; - } - - at91_shdwc_base = of_iomap(np, 0); - if (!at91_shdwc_base) - panic(pr_fmt("unable to map shdwc cpu registers\n")); - - wakeup_mode = at91_dtget_shdwc_wakeup_mode(np); - if (wakeup_mode < 0) { - pr_warn("shdwc unknown wakeup mode\n"); - goto end; - } - - if (!of_property_read_u32(np, "atmel,wakeup-counter", ®)) { - if (reg > AT91_SHDW_CPTWK0_MAX) { - pr_warn("shdwc wakeup counter 0x%x > 0x%x reduce it to 0x%x\n", - reg, AT91_SHDW_CPTWK0_MAX, AT91_SHDW_CPTWK0_MAX); - reg = AT91_SHDW_CPTWK0_MAX; - } - mode |= AT91_SHDW_CPTWK0_(reg); - } - - if (of_property_read_bool(np, "atmel,wakeup-rtc-timer")) - mode |= AT91_SHDW_RTCWKEN; - - if (of_property_read_bool(np, "atmel,wakeup-rtt-timer")) - mode |= AT91_SHDW_RTTWKEN; - - at91_shdwc_write(AT91_SHDW_MR, wakeup_mode | mode); - -end: - pm_power_off = at91sam9_poweroff; - - of_node_put(np); -} - void __init at91rm9200_dt_initialize(void) { at91_dt_ramc(); @@ -482,7 +406,6 @@ void __init at91rm9200_dt_initialize(void) void __init at91_dt_initialize(void) { at91_dt_ramc(); - at91_dt_shdwc(); /* Init clock subsystem */ at91_dt_clock_init(); -- cgit v1.2.1 From 75ddf376d1aeac04688efc5c806f8a4baa995474 Mon Sep 17 00:00:00 2001 From: Maxime Ripard Date: Thu, 3 Jul 2014 14:39:43 +0200 Subject: ARM: at91: Register the poweroff driver Register the poweroff driver in the old-style SoC definition so that the driver is loaded and provide a shutdown hook. Signed-off-by: Maxime Ripard Acked-by: Alexandre Belloni Signed-off-by: Nicolas Ferre --- arch/arm/mach-at91/at91sam9260.c | 15 +++++++++++++++ arch/arm/mach-at91/at91sam9261.c | 15 +++++++++++++++ arch/arm/mach-at91/at91sam9263.c | 15 +++++++++++++++ arch/arm/mach-at91/at91sam9g45.c | 15 +++++++++++++++ arch/arm/mach-at91/at91sam9rl.c | 15 +++++++++++++++ 5 files changed, 75 insertions(+) diff --git a/arch/arm/mach-at91/at91sam9260.c b/arch/arm/mach-at91/at91sam9260.c index 00e4e5bb452f..601f2c708767 100644 --- a/arch/arm/mach-at91/at91sam9260.c +++ b/arch/arm/mach-at91/at91sam9260.c @@ -381,9 +381,24 @@ static struct platform_device rstc_device = { .num_resources = ARRAY_SIZE(rstc_resources), }; +static struct resource shdwc_resources[] = { + [0] = { + .start = AT91SAM9260_BASE_SHDWC, + .end = AT91SAM9260_BASE_SHDWC + SZ_16 - 1, + .flags = IORESOURCE_MEM, + }, +}; + +static struct platform_device shdwc_device = { + .name = "at91-poweroff", + .resource = shdwc_resources, + .num_resources = ARRAY_SIZE(shdwc_resources), +}; + static void __init at91sam9260_register_devices(void) { platform_device_register(&rstc_device); + platform_device_register(&shdwc_device); } /* -------------------------------------------------------------------- diff --git a/arch/arm/mach-at91/at91sam9261.c b/arch/arm/mach-at91/at91sam9261.c index 43db1bdf885d..5e8ac0d271b8 100644 --- a/arch/arm/mach-at91/at91sam9261.c +++ b/arch/arm/mach-at91/at91sam9261.c @@ -340,9 +340,24 @@ static struct platform_device rstc_device = { .num_resources = ARRAY_SIZE(rstc_resources), }; +static struct resource shdwc_resources[] = { + [0] = { + .start = AT91SAM9261_BASE_SHDWC, + .end = AT91SAM9261_BASE_SHDWC + SZ_16 - 1, + .flags = IORESOURCE_MEM, + }, +}; + +static struct platform_device shdwc_device = { + .name = "at91-poweroff", + .resource = shdwc_resources, + .num_resources = ARRAY_SIZE(shdwc_resources), +}; + static void __init at91sam9261_register_devices(void) { platform_device_register(&rstc_device); + platform_device_register(&shdwc_device); } /* -------------------------------------------------------------------- diff --git a/arch/arm/mach-at91/at91sam9263.c b/arch/arm/mach-at91/at91sam9263.c index 358e762f0c07..bcc8b04d3cfb 100644 --- a/arch/arm/mach-at91/at91sam9263.c +++ b/arch/arm/mach-at91/at91sam9263.c @@ -363,9 +363,24 @@ static struct platform_device rstc_device = { .num_resources = ARRAY_SIZE(rstc_resources), }; +static struct resource shdwc_resources[] = { + [0] = { + .start = AT91SAM9263_BASE_SHDWC, + .end = AT91SAM9263_BASE_SHDWC + SZ_16 - 1, + .flags = IORESOURCE_MEM, + }, +}; + +static struct platform_device shdwc_device = { + .name = "at91-poweroff", + .resource = shdwc_resources, + .num_resources = ARRAY_SIZE(shdwc_resources), +}; + static void __init at91sam9263_register_devices(void) { platform_device_register(&rstc_device); + platform_device_register(&shdwc_device); } /* -------------------------------------------------------------------- diff --git a/arch/arm/mach-at91/at91sam9g45.c b/arch/arm/mach-at91/at91sam9g45.c index 9b55b7dc12c2..665d378877a3 100644 --- a/arch/arm/mach-at91/at91sam9g45.c +++ b/arch/arm/mach-at91/at91sam9g45.c @@ -417,9 +417,24 @@ static struct platform_device rstc_device = { .num_resources = ARRAY_SIZE(rstc_resources), }; +static struct resource shdwc_resources[] = { + [0] = { + .start = AT91SAM9G45_BASE_SHDWC, + .end = AT91SAM9G45_BASE_SHDWC + SZ_16 - 1, + .flags = IORESOURCE_MEM, + }, +}; + +static struct platform_device shdwc_device = { + .name = "at91-poweroff", + .resource = shdwc_resources, + .num_resources = ARRAY_SIZE(shdwc_resources), +}; + static void __init at91sam9g45_register_devices(void) { platform_device_register(&rstc_device); + platform_device_register(&shdwc_device); } /* -------------------------------------------------------------------- diff --git a/arch/arm/mach-at91/at91sam9rl.c b/arch/arm/mach-at91/at91sam9rl.c index d43d28ee8eaf..5a18a76ac9e3 100644 --- a/arch/arm/mach-at91/at91sam9rl.c +++ b/arch/arm/mach-at91/at91sam9rl.c @@ -351,9 +351,24 @@ static struct platform_device rstc_device = { .num_resources = ARRAY_SIZE(rstc_resources), }; +static struct resource shdwc_resources[] = { + [0] = { + .start = AT91SAM9RL_BASE_SHDWC, + .end = AT91SAM9RL_BASE_SHDWC + SZ_16 - 1, + .flags = IORESOURCE_MEM, + }, +}; + +static struct platform_device shdwc_device = { + .name = "at91-poweroff", + .resource = shdwc_resources, + .num_resources = ARRAY_SIZE(shdwc_resources), +}; + static void __init at91sam9rl_register_devices(void) { platform_device_register(&rstc_device); + platform_device_register(&shdwc_device); } /* -------------------------------------------------------------------- -- cgit v1.2.1 From 4ae9ccf2fab87aaf9e0e906ba85675be020f6948 Mon Sep 17 00:00:00 2001 From: Maxime Ripard Date: Thu, 3 Jul 2014 15:41:56 +0200 Subject: ARM: at91: Remove poweroff code Now that all the SoC have been converted to use the new poweroff driver, we can remove the poweroff code defined in mach-at91. Signed-off-by: Maxime Ripard Acked-by: Alexandre Belloni Signed-off-by: Nicolas Ferre --- arch/arm/mach-at91/setup.c | 6 ------ 1 file changed, 6 deletions(-) diff --git a/arch/arm/mach-at91/setup.c b/arch/arm/mach-at91/setup.c index 6fe03f31d3d2..a46df6759c92 100644 --- a/arch/arm/mach-at91/setup.c +++ b/arch/arm/mach-at91/setup.c @@ -322,17 +322,11 @@ void __init at91_map_io(void) void __iomem *at91_shdwc_base = NULL; -static void at91sam9_poweroff(void) -{ - at91_shdwc_write(AT91_SHDW_CR, AT91_SHDW_KEY | AT91_SHDW_SHDW); -} - void __init at91_ioremap_shdwc(u32 base_addr) { at91_shdwc_base = ioremap(base_addr, 16); if (!at91_shdwc_base) panic(pr_fmt("Impossible to ioremap at91_shdwc_base\n")); - pm_power_off = at91sam9_poweroff; } void __iomem *at91_rstc_base; -- cgit v1.2.1 From 572e85e3aa58f11671759e9cfa6b9b5d79b131fd Mon Sep 17 00:00:00 2001 From: Maxime Ripard Date: Thu, 3 Jul 2014 14:46:05 +0200 Subject: ARM: at91/pm: Remove show_reset_status function Both the reset and poweroff drivers are now implementing what the show_reset_status function used to do. Hence, we can remove this rather hackish function that prevents us from doing further cleanup. Signed-off-by: Maxime Ripard Acked-by: Alexandre Belloni Signed-off-by: Nicolas Ferre --- arch/arm/mach-at91/pm.c | 72 ------------------------------------------------- 1 file changed, 72 deletions(-) diff --git a/arch/arm/mach-at91/pm.c b/arch/arm/mach-at91/pm.c index 5920373809c5..4073ab7f38f3 100644 --- a/arch/arm/mach-at91/pm.c +++ b/arch/arm/mach-at91/pm.c @@ -34,79 +34,8 @@ #include "pm.h" #include "gpio.h" -/* - * Show the reason for the previous system reset. - */ - -#include "at91_rstc.h" -#include "at91_shdwc.h" - static void (*at91_pm_standby)(void); -static void __init show_reset_status(void) -{ - static char reset[] __initdata = "reset"; - - static char general[] __initdata = "general"; - static char wakeup[] __initdata = "wakeup"; - static char watchdog[] __initdata = "watchdog"; - static char software[] __initdata = "software"; - static char user[] __initdata = "user"; - static char unknown[] __initdata = "unknown"; - - static char signal[] __initdata = "signal"; - static char rtc[] __initdata = "rtc"; - static char rtt[] __initdata = "rtt"; - static char restore[] __initdata = "power-restored"; - - char *reason, *r2 = reset; - u32 reset_type, wake_type; - - if (!at91_shdwc_base || !at91_rstc_base) - return; - - reset_type = at91_rstc_read(AT91_RSTC_SR) & AT91_RSTC_RSTTYP; - wake_type = at91_shdwc_read(AT91_SHDW_SR); - - switch (reset_type) { - case AT91_RSTC_RSTTYP_GENERAL: - reason = general; - break; - case AT91_RSTC_RSTTYP_WAKEUP: - /* board-specific code enabled the wakeup sources */ - reason = wakeup; - - /* "wakeup signal" */ - if (wake_type & AT91_SHDW_WAKEUP0) - r2 = signal; - else { - r2 = reason; - if (wake_type & AT91_SHDW_RTTWK) /* rtt wakeup */ - reason = rtt; - else if (wake_type & AT91_SHDW_RTCWK) /* rtc wakeup */ - reason = rtc; - else if (wake_type == 0) /* power-restored wakeup */ - reason = restore; - else /* unknown wakeup */ - reason = unknown; - } - break; - case AT91_RSTC_RSTTYP_WATCHDOG: - reason = watchdog; - break; - case AT91_RSTC_RSTTYP_SOFTWARE: - reason = software; - break; - case AT91_RSTC_RSTTYP_USER: - reason = user; - break; - default: - reason = unknown; - break; - } - pr_info("AT91: Starting after %s %s\n", reason, r2); -} - static int at91_pm_valid_state(suspend_state_t state) { switch (state) { @@ -346,7 +275,6 @@ static int __init at91_pm_init(void) suspend_set_ops(&at91_pm_ops); - show_reset_status(); return 0; } arch_initcall(at91_pm_init); -- cgit v1.2.1 From 351a4ffea259a11deebe0eb169c46ccbfc3abd36 Mon Sep 17 00:00:00 2001 From: Maxime Ripard Date: Thu, 3 Jul 2014 14:46:31 +0200 Subject: ARM: at91: Remove rstc and shdwnc global base addresses Now that there's no user left for the global variables holding the reset and shutdown controllers base address, we can remove these variables and their associated mapping function. Signed-off-by: Maxime Ripard Acked-by: Alexandre Belloni Signed-off-by: Nicolas Ferre --- arch/arm/mach-at91/at91sam9260.c | 2 -- arch/arm/mach-at91/at91sam9261.c | 2 -- arch/arm/mach-at91/at91sam9263.c | 2 -- arch/arm/mach-at91/at91sam9g45.c | 2 -- arch/arm/mach-at91/at91sam9rl.c | 2 -- arch/arm/mach-at91/generic.h | 6 ------ arch/arm/mach-at91/setup.c | 18 ------------------ 7 files changed, 34 deletions(-) diff --git a/arch/arm/mach-at91/at91sam9260.c b/arch/arm/mach-at91/at91sam9260.c index 601f2c708767..dbb069f4a0a6 100644 --- a/arch/arm/mach-at91/at91sam9260.c +++ b/arch/arm/mach-at91/at91sam9260.c @@ -343,8 +343,6 @@ static void __init at91sam9260_map_io(void) static void __init at91sam9260_ioremap_registers(void) { - at91_ioremap_shdwc(AT91SAM9260_BASE_SHDWC); - at91_ioremap_rstc(AT91SAM9260_BASE_RSTC); at91_ioremap_ramc(0, AT91SAM9260_BASE_SDRAMC, 512); at91sam926x_ioremap_pit(AT91SAM9260_BASE_PIT); at91sam9_ioremap_smc(0, AT91SAM9260_BASE_SMC); diff --git a/arch/arm/mach-at91/at91sam9261.c b/arch/arm/mach-at91/at91sam9261.c index 5e8ac0d271b8..80cae8728316 100644 --- a/arch/arm/mach-at91/at91sam9261.c +++ b/arch/arm/mach-at91/at91sam9261.c @@ -302,8 +302,6 @@ static void __init at91sam9261_map_io(void) static void __init at91sam9261_ioremap_registers(void) { - at91_ioremap_shdwc(AT91SAM9261_BASE_SHDWC); - at91_ioremap_rstc(AT91SAM9261_BASE_RSTC); at91_ioremap_ramc(0, AT91SAM9261_BASE_SDRAMC, 512); at91sam926x_ioremap_pit(AT91SAM9261_BASE_PIT); at91sam9_ioremap_smc(0, AT91SAM9261_BASE_SMC); diff --git a/arch/arm/mach-at91/at91sam9263.c b/arch/arm/mach-at91/at91sam9263.c index bcc8b04d3cfb..37bb02ce8b62 100644 --- a/arch/arm/mach-at91/at91sam9263.c +++ b/arch/arm/mach-at91/at91sam9263.c @@ -322,8 +322,6 @@ static void __init at91sam9263_map_io(void) static void __init at91sam9263_ioremap_registers(void) { - at91_ioremap_shdwc(AT91SAM9263_BASE_SHDWC); - at91_ioremap_rstc(AT91SAM9263_BASE_RSTC); at91_ioremap_ramc(0, AT91SAM9263_BASE_SDRAMC0, 512); at91_ioremap_ramc(1, AT91SAM9263_BASE_SDRAMC1, 512); at91sam926x_ioremap_pit(AT91SAM9263_BASE_PIT); diff --git a/arch/arm/mach-at91/at91sam9g45.c b/arch/arm/mach-at91/at91sam9g45.c index 665d378877a3..7a329703f31a 100644 --- a/arch/arm/mach-at91/at91sam9g45.c +++ b/arch/arm/mach-at91/at91sam9g45.c @@ -372,8 +372,6 @@ static void __init at91sam9g45_map_io(void) static void __init at91sam9g45_ioremap_registers(void) { - at91_ioremap_shdwc(AT91SAM9G45_BASE_SHDWC); - at91_ioremap_rstc(AT91SAM9G45_BASE_RSTC); at91_ioremap_ramc(0, AT91SAM9G45_BASE_DDRSDRC1, 512); at91_ioremap_ramc(1, AT91SAM9G45_BASE_DDRSDRC0, 512); at91sam926x_ioremap_pit(AT91SAM9G45_BASE_PIT); diff --git a/arch/arm/mach-at91/at91sam9rl.c b/arch/arm/mach-at91/at91sam9rl.c index 5a18a76ac9e3..1644aa250eba 100644 --- a/arch/arm/mach-at91/at91sam9rl.c +++ b/arch/arm/mach-at91/at91sam9rl.c @@ -312,8 +312,6 @@ static void __init at91sam9rl_map_io(void) static void __init at91sam9rl_ioremap_registers(void) { - at91_ioremap_shdwc(AT91SAM9RL_BASE_SHDWC); - at91_ioremap_rstc(AT91SAM9RL_BASE_RSTC); at91_ioremap_ramc(0, AT91SAM9RL_BASE_SDRAMC, 512); at91sam926x_ioremap_pit(AT91SAM9RL_BASE_PIT); at91sam9_ioremap_smc(0, AT91SAM9RL_BASE_SMC); diff --git a/arch/arm/mach-at91/generic.h b/arch/arm/mach-at91/generic.h index 5d5e55237c41..f42b0490ad98 100644 --- a/arch/arm/mach-at91/generic.h +++ b/arch/arm/mach-at91/generic.h @@ -64,12 +64,6 @@ extern void at91_irq_resume(void); /* idle */ extern void at91sam9_idle(void); -/* reset */ -extern void at91_ioremap_rstc(u32 base_addr); - -/* shutdown */ -extern void at91_ioremap_shdwc(u32 base_addr); - /* Matrix */ extern void at91_ioremap_matrix(u32 base_addr); diff --git a/arch/arm/mach-at91/setup.c b/arch/arm/mach-at91/setup.c index a46df6759c92..bf9b03df11f6 100644 --- a/arch/arm/mach-at91/setup.c +++ b/arch/arm/mach-at91/setup.c @@ -320,24 +320,6 @@ void __init at91_map_io(void) at91_boot_soc.map_io(); } -void __iomem *at91_shdwc_base = NULL; - -void __init at91_ioremap_shdwc(u32 base_addr) -{ - at91_shdwc_base = ioremap(base_addr, 16); - if (!at91_shdwc_base) - panic(pr_fmt("Impossible to ioremap at91_shdwc_base\n")); -} - -void __iomem *at91_rstc_base; - -void __init at91_ioremap_rstc(u32 base_addr) -{ - at91_rstc_base = ioremap(base_addr, 16); - if (!at91_rstc_base) - panic(pr_fmt("Impossible to ioremap at91_rstc_base\n")); -} - void __iomem *at91_matrix_base; EXPORT_SYMBOL_GPL(at91_matrix_base); -- cgit v1.2.1 From f55df0d654e10ad49e0e7edaeef02fa00e084d94 Mon Sep 17 00:00:00 2001 From: Maxime Ripard Date: Thu, 3 Jul 2014 14:54:25 +0200 Subject: ARM: at91: Remove rstc and shdwc headers These headers used to provide an "API" to access the rstc and shdwc registers. Now that no-one uses this API anymore, we can safely remove those. Signed-off-by: Maxime Ripard Acked-by: Alexandre Belloni Signed-off-by: Nicolas Ferre --- arch/arm/mach-at91/at91_rstc.h | 53 --------------------------------- arch/arm/mach-at91/at91_shdwc.h | 50 ------------------------------- arch/arm/mach-at91/at91sam9260.c | 1 - arch/arm/mach-at91/at91sam9261.c | 1 - arch/arm/mach-at91/at91sam9263.c | 1 - arch/arm/mach-at91/at91sam9rl.c | 1 - arch/arm/mach-at91/board-sam9260ek.c | 1 - arch/arm/mach-at91/board-sam9261ek.c | 1 - arch/arm/mach-at91/board-sam9263ek.c | 1 - arch/arm/mach-at91/board-sam9m10g45ek.c | 1 - arch/arm/mach-at91/board-sam9rlek.c | 1 - arch/arm/mach-at91/setup.c | 1 - 12 files changed, 113 deletions(-) delete mode 100644 arch/arm/mach-at91/at91_rstc.h delete mode 100644 arch/arm/mach-at91/at91_shdwc.h diff --git a/arch/arm/mach-at91/at91_rstc.h b/arch/arm/mach-at91/at91_rstc.h deleted file mode 100644 index a600e6992920..000000000000 --- a/arch/arm/mach-at91/at91_rstc.h +++ /dev/null @@ -1,53 +0,0 @@ -/* - * arch/arm/mach-at91/include/mach/at91_rstc.h - * - * Copyright (C) 2007 Andrew Victor - * Copyright (C) 2007 Atmel Corporation. - * - * Reset Controller (RSTC) - System peripherals regsters. - * Based on AT91SAM9261 datasheet revision D. - * - * 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 AT91_RSTC_H -#define AT91_RSTC_H - -#ifndef __ASSEMBLY__ -extern void __iomem *at91_rstc_base; - -#define at91_rstc_read(field) \ - __raw_readl(at91_rstc_base + field) - -#define at91_rstc_write(field, value) \ - __raw_writel(value, at91_rstc_base + field) -#else -.extern at91_rstc_base -#endif - -#define AT91_RSTC_CR 0x00 /* Reset Controller Control Register */ -#define AT91_RSTC_PROCRST (1 << 0) /* Processor Reset */ -#define AT91_RSTC_PERRST (1 << 2) /* Peripheral Reset */ -#define AT91_RSTC_EXTRST (1 << 3) /* External Reset */ -#define AT91_RSTC_KEY (0xa5 << 24) /* KEY Password */ - -#define AT91_RSTC_SR 0x04 /* Reset Controller Status Register */ -#define AT91_RSTC_URSTS (1 << 0) /* User Reset Status */ -#define AT91_RSTC_RSTTYP (7 << 8) /* Reset Type */ -#define AT91_RSTC_RSTTYP_GENERAL (0 << 8) -#define AT91_RSTC_RSTTYP_WAKEUP (1 << 8) -#define AT91_RSTC_RSTTYP_WATCHDOG (2 << 8) -#define AT91_RSTC_RSTTYP_SOFTWARE (3 << 8) -#define AT91_RSTC_RSTTYP_USER (4 << 8) -#define AT91_RSTC_NRSTL (1 << 16) /* NRST Pin Level */ -#define AT91_RSTC_SRCMP (1 << 17) /* Software Reset Command in Progress */ - -#define AT91_RSTC_MR 0x08 /* Reset Controller Mode Register */ -#define AT91_RSTC_URSTEN (1 << 0) /* User Reset Enable */ -#define AT91_RSTC_URSTIEN (1 << 4) /* User Reset Interrupt Enable */ -#define AT91_RSTC_ERSTL (0xf << 8) /* External Reset Length */ - -#endif diff --git a/arch/arm/mach-at91/at91_shdwc.h b/arch/arm/mach-at91/at91_shdwc.h deleted file mode 100644 index 9e29f31ec9a6..000000000000 --- a/arch/arm/mach-at91/at91_shdwc.h +++ /dev/null @@ -1,50 +0,0 @@ -/* - * arch/arm/mach-at91/include/mach/at91_shdwc.h - * - * Copyright (C) 2007 Andrew Victor - * Copyright (C) 2007 Atmel Corporation. - * - * Shutdown Controller (SHDWC) - System peripherals regsters. - * Based on AT91SAM9261 datasheet revision D. - * - * 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 AT91_SHDWC_H -#define AT91_SHDWC_H - -#ifndef __ASSEMBLY__ -extern void __iomem *at91_shdwc_base; - -#define at91_shdwc_read(field) \ - __raw_readl(at91_shdwc_base + field) - -#define at91_shdwc_write(field, value) \ - __raw_writel(value, at91_shdwc_base + field) -#endif - -#define AT91_SHDW_CR 0x00 /* Shut Down Control Register */ -#define AT91_SHDW_SHDW (1 << 0) /* Shut Down command */ -#define AT91_SHDW_KEY (0xa5 << 24) /* KEY Password */ - -#define AT91_SHDW_MR 0x04 /* Shut Down Mode Register */ -#define AT91_SHDW_WKMODE0 (3 << 0) /* Wake-up 0 Mode Selection */ -#define AT91_SHDW_WKMODE0_NONE 0 -#define AT91_SHDW_WKMODE0_HIGH 1 -#define AT91_SHDW_WKMODE0_LOW 2 -#define AT91_SHDW_WKMODE0_ANYLEVEL 3 -#define AT91_SHDW_CPTWK0_MAX 0xf /* Maximum Counter On Wake Up 0 */ -#define AT91_SHDW_CPTWK0 (AT91_SHDW_CPTWK0_MAX << 4) /* Counter On Wake Up 0 */ -#define AT91_SHDW_CPTWK0_(x) ((x) << 4) -#define AT91_SHDW_RTTWKEN (1 << 16) /* Real Time Timer Wake-up Enable */ -#define AT91_SHDW_RTCWKEN (1 << 17) /* Real Time Clock Wake-up Enable */ - -#define AT91_SHDW_SR 0x08 /* Shut Down Status Register */ -#define AT91_SHDW_WAKEUP0 (1 << 0) /* Wake-up 0 Status */ -#define AT91_SHDW_RTTWK (1 << 16) /* Real-time Timer Wake-up */ -#define AT91_SHDW_RTCWK (1 << 17) /* Real-time Clock Wake-up [SAM9RL] */ - -#endif diff --git a/arch/arm/mach-at91/at91sam9260.c b/arch/arm/mach-at91/at91sam9260.c index dbb069f4a0a6..b6948d677ca9 100644 --- a/arch/arm/mach-at91/at91sam9260.c +++ b/arch/arm/mach-at91/at91sam9260.c @@ -25,7 +25,6 @@ #include #include "at91_aic.h" -#include "at91_rstc.h" #include "soc.h" #include "generic.h" #include "sam9_smc.h" diff --git a/arch/arm/mach-at91/at91sam9261.c b/arch/arm/mach-at91/at91sam9261.c index 80cae8728316..c07339e370ee 100644 --- a/arch/arm/mach-at91/at91sam9261.c +++ b/arch/arm/mach-at91/at91sam9261.c @@ -24,7 +24,6 @@ #include #include "at91_aic.h" -#include "at91_rstc.h" #include "soc.h" #include "generic.h" #include "sam9_smc.h" diff --git a/arch/arm/mach-at91/at91sam9263.c b/arch/arm/mach-at91/at91sam9263.c index 37bb02ce8b62..33ab06ec5365 100644 --- a/arch/arm/mach-at91/at91sam9263.c +++ b/arch/arm/mach-at91/at91sam9263.c @@ -23,7 +23,6 @@ #include #include "at91_aic.h" -#include "at91_rstc.h" #include "soc.h" #include "generic.h" #include "sam9_smc.h" diff --git a/arch/arm/mach-at91/at91sam9rl.c b/arch/arm/mach-at91/at91sam9rl.c index 1644aa250eba..264999f33594 100644 --- a/arch/arm/mach-at91/at91sam9rl.c +++ b/arch/arm/mach-at91/at91sam9rl.c @@ -24,7 +24,6 @@ #include #include "at91_aic.h" -#include "at91_rstc.h" #include "soc.h" #include "generic.h" #include "sam9_smc.h" diff --git a/arch/arm/mach-at91/board-sam9260ek.c b/arch/arm/mach-at91/board-sam9260ek.c index da9c5f1f2c49..18f49c93f3f3 100644 --- a/arch/arm/mach-at91/board-sam9260ek.c +++ b/arch/arm/mach-at91/board-sam9260ek.c @@ -45,7 +45,6 @@ #include #include "at91_aic.h" -#include "at91_shdwc.h" #include "board.h" #include "sam9_smc.h" #include "generic.h" diff --git a/arch/arm/mach-at91/board-sam9261ek.c b/arch/arm/mach-at91/board-sam9261ek.c index d1be10b09ab3..5a23e7211203 100644 --- a/arch/arm/mach-at91/board-sam9261ek.c +++ b/arch/arm/mach-at91/board-sam9261ek.c @@ -49,7 +49,6 @@ #include #include "at91_aic.h" -#include "at91_shdwc.h" #include "board.h" #include "sam9_smc.h" #include "generic.h" diff --git a/arch/arm/mach-at91/board-sam9263ek.c b/arch/arm/mach-at91/board-sam9263ek.c index 7ccaf3be6e80..e8685652a73e 100644 --- a/arch/arm/mach-at91/board-sam9263ek.c +++ b/arch/arm/mach-at91/board-sam9263ek.c @@ -50,7 +50,6 @@ #include #include "at91_aic.h" -#include "at91_shdwc.h" #include "board.h" #include "sam9_smc.h" #include "generic.h" diff --git a/arch/arm/mach-at91/board-sam9m10g45ek.c b/arch/arm/mach-at91/board-sam9m10g45ek.c index 7b8bc961a5d5..0fc4bea272ea 100644 --- a/arch/arm/mach-at91/board-sam9m10g45ek.c +++ b/arch/arm/mach-at91/board-sam9m10g45ek.c @@ -48,7 +48,6 @@ #include #include "at91_aic.h" -#include "at91_shdwc.h" #include "board.h" #include "sam9_smc.h" #include "generic.h" diff --git a/arch/arm/mach-at91/board-sam9rlek.c b/arch/arm/mach-at91/board-sam9rlek.c index 262e3f957d9f..5cb82464be1a 100644 --- a/arch/arm/mach-at91/board-sam9rlek.c +++ b/arch/arm/mach-at91/board-sam9rlek.c @@ -35,7 +35,6 @@ #include "at91_aic.h" -#include "at91_shdwc.h" #include "board.h" #include "sam9_smc.h" #include "generic.h" diff --git a/arch/arm/mach-at91/setup.c b/arch/arm/mach-at91/setup.c index bf9b03df11f6..28ae63b8eb16 100644 --- a/arch/arm/mach-at91/setup.c +++ b/arch/arm/mach-at91/setup.c @@ -22,7 +22,6 @@ #include #include -#include "at91_shdwc.h" #include "soc.h" #include "generic.h" #include "pm.h" -- cgit v1.2.1 From c8260db557389c1fb46eeafb54414b03d9f19e49 Mon Sep 17 00:00:00 2001 From: Alexandre Belloni Date: Wed, 27 Aug 2014 18:06:51 +0200 Subject: ARM: at91: fix ramc standby function registration After the for_each_matching_node loop, we end up with a null value for np. Then, of_match_node() is not matching anything and we can't register the standby function and "ramc no standby function available" is printed. Fix that by selecting the first available standby function. For now, at91_pm_set_standby doesn't support multiple different standby functions and no existing SoCs have different RAM controllers. Signed-off-by: Alexandre Belloni Signed-off-by: Nicolas Ferre --- arch/arm/mach-at91/setup.c | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/arch/arm/mach-at91/setup.c b/arch/arm/mach-at91/setup.c index 28ae63b8eb16..d2cade21ebd7 100644 --- a/arch/arm/mach-at91/setup.c +++ b/arch/arm/mach-at91/setup.c @@ -343,25 +343,28 @@ static void at91_dt_ramc(void) struct device_node *np; const struct of_device_id *of_id; int idx = 0; + const void *standby = NULL; - for_each_matching_node(np, ramc_ids) { + for_each_matching_node_and_match(np, ramc_ids, &of_id) { at91_ramc_base[idx] = of_iomap(np, 0); if (!at91_ramc_base[idx]) panic(pr_fmt("unable to map ramc[%d] cpu registers\n"), idx); + if (!standby) + standby = of_id->data; + idx++; } if (!idx) panic(pr_fmt("unable to find compatible ram controller node in dtb\n")); - of_id = of_match_node(ramc_ids, np); - if (!of_id) { + if (!standby) { pr_warn("ramc no standby function available\n"); return; } - at91_pm_set_standby(of_id->data); + at91_pm_set_standby(standby); } void __init at91rm9200_dt_initialize(void) -- cgit v1.2.1 From 94d450edfdc6dab0bb92dcbb06f5ea3244558bf4 Mon Sep 17 00:00:00 2001 From: Nicolas Ferre Date: Mon, 1 Sep 2014 10:26:04 +0200 Subject: ARM: at91/power/reset: fix Kconfig "depends on" directive MACH_AT91 doesn't exist so we can't "depend" on it. Fix the typo by using the proper ARCH_AT91 config option. Signed-off-by: Nicolas Ferre --- drivers/power/reset/Kconfig | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/power/reset/Kconfig b/drivers/power/reset/Kconfig index c1c046d0464e..81b445122eea 100644 --- a/drivers/power/reset/Kconfig +++ b/drivers/power/reset/Kconfig @@ -16,7 +16,7 @@ config POWER_RESET_AS3722 config POWER_RESET_AT91_POWEROFF bool "Atmel AT91 poweroff driver" - depends on MACH_AT91 + depends on ARCH_AT91 default SOC_AT91SAM9 || SOC_SAMA5 help This driver supports poweroff for Atmel AT91SAM9 and SAMA5 @@ -24,7 +24,7 @@ config POWER_RESET_AT91_POWEROFF config POWER_RESET_AT91_RESET bool "Atmel AT91 reset driver" - depends on MACH_AT91 + depends on ARCH_AT91 default SOC_AT91SAM9 || SOC_SAMA5 help This driver supports restart for Atmel AT91SAM9 and SAMA5 -- cgit v1.2.1 From 405a72c5e78b5c560c8b2711d4000fa5eb063e1b Mon Sep 17 00:00:00 2001 From: Nicolas Ferre Date: Mon, 1 Sep 2014 16:11:19 +0200 Subject: power: reset: at91-poweroff: fix wakeup status register index The wakeup status is read from Shutdown Status Register and not the SHDW_CR which is the one at address 0. Signed-off-by: Nicolas Ferre --- drivers/power/reset/at91-poweroff.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/power/reset/at91-poweroff.c b/drivers/power/reset/at91-poweroff.c index 40bf42d146f1..c61000333bb9 100644 --- a/drivers/power/reset/at91-poweroff.c +++ b/drivers/power/reset/at91-poweroff.c @@ -51,7 +51,7 @@ static void __iomem *at91_shdwc_base; static void __init at91_wakeup_status(void) { - u32 reg = readl(at91_shdwc_base); + u32 reg = readl(at91_shdwc_base + AT91_SHDW_SR); char *reason = "unknown"; /* Simple power-on, just bail out */ -- cgit v1.2.1 From 52c3ffb018f4af038039f783d932ba5c99e4a87b Mon Sep 17 00:00:00 2001 From: Maxime Ripard Date: Tue, 1 Jul 2014 11:33:14 +0200 Subject: ARM: at91: PIT: Follow the general coding rules Replace all masks and bits definitions by matching calls to BIT and GENMASK. While we're at it, also fix a few style issues. Signed-off-by: Maxime Ripard Acked-by: Boris BREZILLON Acked-by: Alexandre Belloni Acked-by: Daniel Lezcano Signed-off-by: Nicolas Ferre --- arch/arm/mach-at91/at91sam926x_time.c | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/arch/arm/mach-at91/at91sam926x_time.c b/arch/arm/mach-at91/at91sam926x_time.c index 0a9e2fc8f796..fd3170b65fbb 100644 --- a/arch/arm/mach-at91/at91sam926x_time.c +++ b/arch/arm/mach-at91/at91sam926x_time.c @@ -9,11 +9,12 @@ * 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 #include @@ -22,17 +23,17 @@ #include #define AT91_PIT_MR 0x00 /* Mode Register */ -#define AT91_PIT_PITIEN (1 << 25) /* Timer Interrupt Enable */ -#define AT91_PIT_PITEN (1 << 24) /* Timer Enabled */ -#define AT91_PIT_PIV (0xfffff) /* Periodic Interval Value */ +#define AT91_PIT_PITIEN BIT(25) /* Timer Interrupt Enable */ +#define AT91_PIT_PITEN BIT(24) /* Timer Enabled */ +#define AT91_PIT_PIV GENMASK(19, 0) /* Periodic Interval Value */ #define AT91_PIT_SR 0x04 /* Status Register */ -#define AT91_PIT_PITS (1 << 0) /* Timer Status */ +#define AT91_PIT_PITS BIT(0) /* Timer Status */ #define AT91_PIT_PIVR 0x08 /* Periodic Interval Value Register */ #define AT91_PIT_PIIR 0x0c /* Periodic Interval Image Register */ -#define AT91_PIT_PICNT (0xfff << 20) /* Interval Counter */ -#define AT91_PIT_CPIV (0xfffff) /* Inverval Value */ +#define AT91_PIT_PICNT GENMASK(31, 20) /* Interval Counter */ +#define AT91_PIT_CPIV GENMASK(19, 0) /* Inverval Value */ #define PIT_CPIV(x) ((x) & AT91_PIT_CPIV) #define PIT_PICNT(x) (((x) & AT91_PIT_PICNT) >> 20) -- cgit v1.2.1 From 1bd59dbe53d9913420ae751d0447e769fd22cbf5 Mon Sep 17 00:00:00 2001 From: Maxime Ripard Date: Tue, 1 Jul 2014 11:33:15 +0200 Subject: ARM: at91: generic.h: Add include safe guards The generic.h header file doesn't have any safe guards against multiple inclusion. It only worked so far because all the symbols defined in it were extern, but this is a rather fragile assumption. Signed-off-by: Maxime Ripard Acked-by: Boris BREZILLON Acked-by: Alexandre Belloni Acked-by: Daniel Lezcano Signed-off-by: Nicolas Ferre --- arch/arm/mach-at91/generic.h | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/arch/arm/mach-at91/generic.h b/arch/arm/mach-at91/generic.h index f42b0490ad98..31cba6d9aa61 100644 --- a/arch/arm/mach-at91/generic.h +++ b/arch/arm/mach-at91/generic.h @@ -8,6 +8,9 @@ * published by the Free Software Foundation. */ +#ifndef _AT91_GENERIC_H +#define _AT91_GENERIC_H + #include #include #include @@ -84,3 +87,5 @@ extern int __init at91_gpio_of_irq_setup(struct device_node *node, struct device_node *parent); extern u32 at91_get_extern_irq(void); + +#endif /* _AT91_GENERIC_H */ -- cgit v1.2.1 From 2d7fdbe23c990a843177e3bff9a093d2f5fa4e02 Mon Sep 17 00:00:00 2001 From: Maxime Ripard Date: Tue, 1 Jul 2014 11:33:16 +0200 Subject: ARM: at91: PIT: Use DIV_ROUND_CLOSEST to compute the cycles Until now, the pit_cycle computation was dividing the rate by HZ, rounding to the closest integer, but without using the appropriate macro. Signed-off-by: Maxime Ripard Acked-by: Boris BREZILLON Acked-by: Alexandre Belloni Acked-by: Daniel Lezcano Signed-off-by: Nicolas Ferre --- arch/arm/mach-at91/at91sam926x_time.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/arm/mach-at91/at91sam926x_time.c b/arch/arm/mach-at91/at91sam926x_time.c index fd3170b65fbb..2e6f1a37ab90 100644 --- a/arch/arm/mach-at91/at91sam926x_time.c +++ b/arch/arm/mach-at91/at91sam926x_time.c @@ -252,7 +252,7 @@ void __init at91sam926x_pit_init(void) if (IS_ERR(mck)) panic("AT91: PIT: Unable to get mck clk\n"); pit_rate = clk_get_rate(mck) / 16; - pit_cycle = (pit_rate + HZ/2) / HZ; + pit_cycle = DIV_ROUND_CLOSEST(pit_rate, HZ); WARN_ON(((pit_cycle - 1) & ~AT91_PIT_PIV) != 0); /* Initialize and enable the timer */ -- cgit v1.2.1 From a7d84d73b9ca32871da8fc05e95ca47c04fbb110 Mon Sep 17 00:00:00 2001 From: Maxime Ripard Date: Tue, 1 Jul 2014 11:33:17 +0200 Subject: ARM: at91: PIT: Use of_have_populated_dt instead of CONFIG_OF Until now, the machines, even when CONFIG_OF was enabled, were calling at91sam926x_ioremap_pit to try to map the PIT address using the defined physical address. Obviously, with DT, it's not appropriate anymore, and some code was added to the function to deal with this case. Unfortunately, this code was conditionned on CONFIG_OF, which can be enabled, even though no DT was actually used, which would result in such a case, to this code being executed, without any reason. Moreover, the logic that was here before to bail out of the function just check in the DT to see if the PIT node is there, which is the case in all our DTSI. All this can be made much more straightforward just by using of_have_populated_dt to bail out. Signed-off-by: Maxime Ripard Acked-by: Boris BREZILLON Acked-by: Alexandre Belloni Acked-by: Daniel Lezcano Signed-off-by: Nicolas Ferre --- arch/arm/mach-at91/at91sam926x_time.c | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/arch/arm/mach-at91/at91sam926x_time.c b/arch/arm/mach-at91/at91sam926x_time.c index 2e6f1a37ab90..b87a12f05540 100644 --- a/arch/arm/mach-at91/at91sam926x_time.c +++ b/arch/arm/mach-at91/at91sam926x_time.c @@ -279,15 +279,9 @@ void __init at91sam926x_pit_init(void) void __init at91sam926x_ioremap_pit(u32 addr) { -#if defined(CONFIG_OF) - struct device_node *np = - of_find_matching_node(NULL, pit_timer_ids); - - if (np) { - of_node_put(np); + if (of_have_populated_dt()) return; - } -#endif + pit_base_addr = ioremap(addr, 16); if (!pit_base_addr) -- cgit v1.2.1 From f807a89cfe3e7379ec501810d67a5888edbb94f1 Mon Sep 17 00:00:00 2001 From: Maxime Ripard Date: Tue, 1 Jul 2014 11:33:18 +0200 Subject: ARM: at91: PIT: Rework probe functions The PIT timer driver until now had a single probe function, disregarding wether it was probed through DT or in the old-style way. This code later on was calling some DT function to retrieve the proper values for its base address, interrupts and clocks. While this was working, it was preventing the usage of CLOCKSOURCE_OF_DECLARE, and the two different probe path were not as clearly separated as they could be. Rework the probe path to take this into account, and switch to CLOCKSOURCE_OF_DECLARE. Signed-off-by: Maxime Ripard Acked-by: Boris BREZILLON Acked-by: Alexandre Belloni Acked-by: Daniel Lezcano Signed-off-by: Nicolas Ferre --- arch/arm/mach-at91/Kconfig | 1 + arch/arm/mach-at91/at91sam926x_time.c | 108 +++++++++++++++------------------- arch/arm/mach-at91/board-dt-sam9.c | 3 +- arch/arm/mach-at91/board-dt-sama5.c | 3 +- 4 files changed, 51 insertions(+), 64 deletions(-) diff --git a/arch/arm/mach-at91/Kconfig b/arch/arm/mach-at91/Kconfig index 6eb3c658761d..c3f9ebc5f9f5 100644 --- a/arch/arm/mach-at91/Kconfig +++ b/arch/arm/mach-at91/Kconfig @@ -34,6 +34,7 @@ config OLD_IRQ_AT91 select SPARSE_IRQ config AT91_SAM9_TIME + select CLKSRC_OF if OF bool config HAVE_AT91_SMD diff --git a/arch/arm/mach-at91/at91sam926x_time.c b/arch/arm/mach-at91/at91sam926x_time.c index b87a12f05540..af466eb68624 100644 --- a/arch/arm/mach-at91/at91sam926x_time.c +++ b/arch/arm/mach-at91/at91sam926x_time.c @@ -19,7 +19,6 @@ #include #include -#include #include #define AT91_PIT_MR 0x00 /* Mode Register */ @@ -176,81 +175,21 @@ static struct irqaction at91sam926x_pit_irq = { .name = "at91_tick", .flags = IRQF_SHARED | IRQF_TIMER | IRQF_IRQPOLL, .handler = at91sam926x_pit_interrupt, - .irq = NR_IRQS_LEGACY + AT91_ID_SYS, }; -#ifdef CONFIG_OF -static struct of_device_id pit_timer_ids[] = { - { .compatible = "atmel,at91sam9260-pit" }, - { /* sentinel */ } -}; - -static int __init of_at91sam926x_pit_init(void) -{ - struct device_node *np; - int ret; - - np = of_find_matching_node(NULL, pit_timer_ids); - if (!np) - goto err; - - pit_base_addr = of_iomap(np, 0); - if (!pit_base_addr) - goto node_err; - - mck = of_clk_get(np, 0); - - /* Get the interrupts property */ - ret = irq_of_parse_and_map(np, 0); - if (!ret) { - pr_crit("AT91: PIT: Unable to get IRQ from DT\n"); - if (!IS_ERR(mck)) - clk_put(mck); - goto ioremap_err; - } - at91sam926x_pit_irq.irq = ret; - - of_node_put(np); - - return 0; - -ioremap_err: - iounmap(pit_base_addr); -node_err: - of_node_put(np); -err: - return -EINVAL; -} -#else -static int __init of_at91sam926x_pit_init(void) -{ - return -EINVAL; -} -#endif - /* * Set up both clocksource and clockevent support. */ -void __init at91sam926x_pit_init(void) +static void __init at91sam926x_pit_common_init(void) { unsigned long pit_rate; unsigned bits; int ret; - mck = ERR_PTR(-ENOENT); - - /* For device tree enabled device: initialize here */ - of_at91sam926x_pit_init(); - /* * Use our actual MCK to figure out how many MCK/16 ticks per * 1/HZ period (instead of a compile-time constant LATCH). */ - if (IS_ERR(mck)) - mck = clk_get(NULL, "mck"); - - if (IS_ERR(mck)) - panic("AT91: PIT: Unable to get mck clk\n"); pit_rate = clk_get_rate(mck) / 16; pit_cycle = DIV_ROUND_CLOSEST(pit_rate, HZ); WARN_ON(((pit_cycle - 1) & ~AT91_PIT_PIV) != 0); @@ -277,6 +216,51 @@ void __init at91sam926x_pit_init(void) clockevents_register_device(&pit_clkevt); } +static void __init at91sam926x_pit_dt_init(struct device_node *node) +{ + unsigned int irq; + + pit_base_addr = of_iomap(node, 0); + if (!pit_base_addr) + return; + + mck = of_clk_get(node, 0); + if (IS_ERR(mck)) + /* Fallback on clkdev for !CCF-based boards */ + mck = clk_get(NULL, "mck"); + + if (IS_ERR(mck)) + panic("AT91: PIT: Unable to get mck clk\n"); + + /* Get the interrupts property */ + irq = irq_of_parse_and_map(node, 0); + if (!irq) { + pr_crit("AT91: PIT: Unable to get IRQ from DT\n"); + goto clk_err; + } + + at91sam926x_pit_irq.irq = irq; + + at91sam926x_pit_common_init(); + +clk_err: + clk_put(mck); + iounmap(pit_base_addr); +} +CLOCKSOURCE_OF_DECLARE(at91sam926x_pit, "atmel,at91sam9260-pit", + at91sam926x_pit_dt_init); + +void __init at91sam926x_pit_init(void) +{ + mck = clk_get(NULL, "mck"); + if (IS_ERR(mck)) + panic("AT91: PIT: Unable to get mck clk\n"); + + at91sam926x_pit_irq.irq = NR_IRQS_LEGACY + AT91_ID_SYS; + + at91sam926x_pit_common_init(); +} + void __init at91sam926x_ioremap_pit(u32 addr) { if (of_have_populated_dt()) diff --git a/arch/arm/mach-at91/board-dt-sam9.c b/arch/arm/mach-at91/board-dt-sam9.c index dfa8d48146fe..78962fd23859 100644 --- a/arch/arm/mach-at91/board-dt-sam9.c +++ b/arch/arm/mach-at91/board-dt-sam9.c @@ -14,6 +14,7 @@ #include #include #include +#include #include #include @@ -31,7 +32,7 @@ static void __init sam9_dt_timer_init(void) #if defined(CONFIG_COMMON_CLK) of_clk_init(NULL); #endif - at91sam926x_pit_init(); + clocksource_of_init(); } static const char *at91_dt_board_compat[] __initdata = { diff --git a/arch/arm/mach-at91/board-dt-sama5.c b/arch/arm/mach-at91/board-dt-sama5.c index d6fe04bcaabd..4f01e34fae0a 100644 --- a/arch/arm/mach-at91/board-dt-sama5.c +++ b/arch/arm/mach-at91/board-dt-sama5.c @@ -17,6 +17,7 @@ #include #include #include +#include #include #include @@ -32,7 +33,7 @@ static void __init sama5_dt_timer_init(void) #if defined(CONFIG_COMMON_CLK) of_clk_init(NULL); #endif - at91sam926x_pit_init(); + clocksource_of_init(); } static int ksz9021rn_phy_fixup(struct phy_device *phy) -- cgit v1.2.1 From bc1aeaeb75f169f870573f6d4e517f925269de4a Mon Sep 17 00:00:00 2001 From: Maxime Ripard Date: Tue, 2 Sep 2014 18:02:26 +0200 Subject: ARM: at91: dt: Remove init_time definitions The current AT91 DT boards have a completely generic init_time definition. Remove them from the machine declaration. Signed-off-by: Maxime Ripard Acked-by: Boris BREZILLON Acked-by: Alexandre Belloni Acked-by: Daniel Lezcano Signed-off-by: Nicolas Ferre Conflicts: arch/arm/mach-at91/board-dt-sam9.c arch/arm/mach-at91/board-dt-sama5.c --- arch/arm/mach-at91/board-dt-sam9.c | 11 ----------- arch/arm/mach-at91/board-dt-sama5.c | 10 ---------- 2 files changed, 21 deletions(-) diff --git a/arch/arm/mach-at91/board-dt-sam9.c b/arch/arm/mach-at91/board-dt-sam9.c index 78962fd23859..d3048ccdc41f 100644 --- a/arch/arm/mach-at91/board-dt-sam9.c +++ b/arch/arm/mach-at91/board-dt-sam9.c @@ -14,7 +14,6 @@ #include #include #include -#include #include #include @@ -26,15 +25,6 @@ #include "board.h" #include "generic.h" - -static void __init sam9_dt_timer_init(void) -{ -#if defined(CONFIG_COMMON_CLK) - of_clk_init(NULL); -#endif - clocksource_of_init(); -} - static const char *at91_dt_board_compat[] __initdata = { "atmel,at91sam9", NULL @@ -42,7 +32,6 @@ static const char *at91_dt_board_compat[] __initdata = { DT_MACHINE_START(at91sam_dt, "Atmel AT91SAM (Device Tree)") /* Maintainer: Atmel */ - .init_time = sam9_dt_timer_init, .map_io = at91_map_io, .init_early = at91_dt_initialize, .dt_compat = at91_dt_board_compat, diff --git a/arch/arm/mach-at91/board-dt-sama5.c b/arch/arm/mach-at91/board-dt-sama5.c index 4f01e34fae0a..be6aa2e2c343 100644 --- a/arch/arm/mach-at91/board-dt-sama5.c +++ b/arch/arm/mach-at91/board-dt-sama5.c @@ -17,7 +17,6 @@ #include #include #include -#include #include #include @@ -28,14 +27,6 @@ #include "at91_aic.h" #include "generic.h" -static void __init sama5_dt_timer_init(void) -{ -#if defined(CONFIG_COMMON_CLK) - of_clk_init(NULL); -#endif - clocksource_of_init(); -} - static int ksz9021rn_phy_fixup(struct phy_device *phy) { int value; @@ -70,7 +61,6 @@ static const char *sama5_dt_board_compat[] __initdata = { DT_MACHINE_START(sama5_dt, "Atmel SAMA5 (Device Tree)") /* Maintainer: Atmel */ - .init_time = sama5_dt_timer_init, .map_io = at91_map_io, .init_early = at91_dt_initialize, .init_machine = sama5_dt_device_init, -- cgit v1.2.1 From a981b29f027ace1b27fb4a96cef6cbfee49dbdd7 Mon Sep 17 00:00:00 2001 From: Maxime Ripard Date: Tue, 1 Jul 2014 11:33:20 +0200 Subject: ARM: at91: PIT: Use consistent exit path in probe The current probe code is, whenever it fails to retrieve a resource, either panicing, silently returning, or returning and displaying an error. Make the code consistent by always panicing whenever it fails to grab any resource. Signed-off-by: Maxime Ripard Acked-by: Boris BREZILLON Acked-by: Alexandre Belloni Acked-by: Daniel Lezcano Signed-off-by: Nicolas Ferre --- arch/arm/mach-at91/at91sam926x_time.c | 14 ++++---------- 1 file changed, 4 insertions(+), 10 deletions(-) diff --git a/arch/arm/mach-at91/at91sam926x_time.c b/arch/arm/mach-at91/at91sam926x_time.c index af466eb68624..641e860e83e7 100644 --- a/arch/arm/mach-at91/at91sam926x_time.c +++ b/arch/arm/mach-at91/at91sam926x_time.c @@ -208,7 +208,7 @@ static void __init at91sam926x_pit_common_init(void) /* Set up irq handler */ ret = setup_irq(at91sam926x_pit_irq.irq, &at91sam926x_pit_irq); if (ret) - pr_crit("AT91: PIT: Unable to setup IRQ\n"); + panic("AT91: PIT: Unable to setup IRQ\n"); /* Set up and register clockevents */ pit_clkevt.mult = div_sc(pit_rate, NSEC_PER_SEC, pit_clkevt.shift); @@ -222,7 +222,7 @@ static void __init at91sam926x_pit_dt_init(struct device_node *node) pit_base_addr = of_iomap(node, 0); if (!pit_base_addr) - return; + panic("AT91: PIT: Could not map PIT address\n"); mck = of_clk_get(node, 0); if (IS_ERR(mck)) @@ -234,18 +234,12 @@ static void __init at91sam926x_pit_dt_init(struct device_node *node) /* Get the interrupts property */ irq = irq_of_parse_and_map(node, 0); - if (!irq) { - pr_crit("AT91: PIT: Unable to get IRQ from DT\n"); - goto clk_err; - } + if (!irq) + panic("AT91: PIT: Unable to get IRQ from DT\n"); at91sam926x_pit_irq.irq = irq; at91sam926x_pit_common_init(); - -clk_err: - clk_put(mck); - iounmap(pit_base_addr); } CLOCKSOURCE_OF_DECLARE(at91sam926x_pit, "atmel,at91sam9260-pit", at91sam926x_pit_dt_init); -- cgit v1.2.1 From cffbfe63ec37cdaf8c9ede027759614faadd23cf Mon Sep 17 00:00:00 2001 From: Maxime Ripard Date: Tue, 1 Jul 2014 11:33:21 +0200 Subject: ARM: at91: PIT: Use pr_fmt All the panic messages hardcode the same prefix. Define the pr_fmt macro to unify its definition. Signed-off-by: Maxime Ripard Acked-by: Boris BREZILLON Acked-by: Alexandre Belloni Acked-by: Daniel Lezcano Signed-off-by: Nicolas Ferre --- arch/arm/mach-at91/at91sam926x_time.c | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/arch/arm/mach-at91/at91sam926x_time.c b/arch/arm/mach-at91/at91sam926x_time.c index 641e860e83e7..0abdcb36c555 100644 --- a/arch/arm/mach-at91/at91sam926x_time.c +++ b/arch/arm/mach-at91/at91sam926x_time.c @@ -10,6 +10,8 @@ * published by the Free Software Foundation. */ +#define pr_fmt(fmt) "AT91: PIT: " fmt + #include #include #include @@ -208,7 +210,7 @@ static void __init at91sam926x_pit_common_init(void) /* Set up irq handler */ ret = setup_irq(at91sam926x_pit_irq.irq, &at91sam926x_pit_irq); if (ret) - panic("AT91: PIT: Unable to setup IRQ\n"); + panic(pr_fmt("Unable to setup IRQ\n")); /* Set up and register clockevents */ pit_clkevt.mult = div_sc(pit_rate, NSEC_PER_SEC, pit_clkevt.shift); @@ -222,7 +224,7 @@ static void __init at91sam926x_pit_dt_init(struct device_node *node) pit_base_addr = of_iomap(node, 0); if (!pit_base_addr) - panic("AT91: PIT: Could not map PIT address\n"); + panic(pr_fmt("Could not map PIT address\n")); mck = of_clk_get(node, 0); if (IS_ERR(mck)) @@ -230,12 +232,12 @@ static void __init at91sam926x_pit_dt_init(struct device_node *node) mck = clk_get(NULL, "mck"); if (IS_ERR(mck)) - panic("AT91: PIT: Unable to get mck clk\n"); + panic(pr_fmt("Unable to get mck clk\n")); /* Get the interrupts property */ irq = irq_of_parse_and_map(node, 0); if (!irq) - panic("AT91: PIT: Unable to get IRQ from DT\n"); + panic(pr_fmt("Unable to get IRQ from DT\n")); at91sam926x_pit_irq.irq = irq; @@ -248,7 +250,7 @@ void __init at91sam926x_pit_init(void) { mck = clk_get(NULL, "mck"); if (IS_ERR(mck)) - panic("AT91: PIT: Unable to get mck clk\n"); + panic(pr_fmt("Unable to get mck clk\n")); at91sam926x_pit_irq.irq = NR_IRQS_LEGACY + AT91_ID_SYS; @@ -263,5 +265,5 @@ void __init at91sam926x_ioremap_pit(u32 addr) pit_base_addr = ioremap(addr, 16); if (!pit_base_addr) - panic("Impossible to ioremap PIT\n"); + panic(pr_fmt("Impossible to ioremap PIT\n")); } -- cgit v1.2.1 From 7f282e0137bfe712655e9f8da5ade105f6918741 Mon Sep 17 00:00:00 2001 From: Maxime Ripard Date: Tue, 1 Jul 2014 11:33:22 +0200 Subject: ARM: at91: PIT: use request_irq instead of setup_irq We can use the generic request_irq now to register a timer interrupt handler, instead of the more complex setup_irq. Signed-off-by: Maxime Ripard Acked-by: Boris BREZILLON Acked-by: Alexandre Belloni Acked-by: Daniel Lezcano Signed-off-by: Nicolas Ferre --- arch/arm/mach-at91/at91sam926x_time.c | 20 ++++++-------------- 1 file changed, 6 insertions(+), 14 deletions(-) diff --git a/arch/arm/mach-at91/at91sam926x_time.c b/arch/arm/mach-at91/at91sam926x_time.c index 0abdcb36c555..e476474cb05e 100644 --- a/arch/arm/mach-at91/at91sam926x_time.c +++ b/arch/arm/mach-at91/at91sam926x_time.c @@ -173,16 +173,10 @@ static irqreturn_t at91sam926x_pit_interrupt(int irq, void *dev_id) return IRQ_NONE; } -static struct irqaction at91sam926x_pit_irq = { - .name = "at91_tick", - .flags = IRQF_SHARED | IRQF_TIMER | IRQF_IRQPOLL, - .handler = at91sam926x_pit_interrupt, -}; - /* * Set up both clocksource and clockevent support. */ -static void __init at91sam926x_pit_common_init(void) +static void __init at91sam926x_pit_common_init(unsigned int pit_irq) { unsigned long pit_rate; unsigned bits; @@ -208,7 +202,9 @@ static void __init at91sam926x_pit_common_init(void) clocksource_register_hz(&pit_clk, pit_rate); /* Set up irq handler */ - ret = setup_irq(at91sam926x_pit_irq.irq, &at91sam926x_pit_irq); + ret = request_irq(pit_irq, at91sam926x_pit_interrupt, + IRQF_SHARED | IRQF_TIMER | IRQF_IRQPOLL, + "at91_tick", pit_base_addr); if (ret) panic(pr_fmt("Unable to setup IRQ\n")); @@ -239,9 +235,7 @@ static void __init at91sam926x_pit_dt_init(struct device_node *node) if (!irq) panic(pr_fmt("Unable to get IRQ from DT\n")); - at91sam926x_pit_irq.irq = irq; - - at91sam926x_pit_common_init(); + at91sam926x_pit_common_init(irq); } CLOCKSOURCE_OF_DECLARE(at91sam926x_pit, "atmel,at91sam9260-pit", at91sam926x_pit_dt_init); @@ -252,9 +246,7 @@ void __init at91sam926x_pit_init(void) if (IS_ERR(mck)) panic(pr_fmt("Unable to get mck clk\n")); - at91sam926x_pit_irq.irq = NR_IRQS_LEGACY + AT91_ID_SYS; - - at91sam926x_pit_common_init(); + at91sam926x_pit_common_init(NR_IRQS_LEGACY + AT91_ID_SYS); } void __init at91sam926x_ioremap_pit(u32 addr) -- cgit v1.2.1 From 64568d1dbd673aca3de8d2a17b9db507c5b85df7 Mon Sep 17 00:00:00 2001 From: Maxime Ripard Date: Tue, 1 Jul 2014 11:33:23 +0200 Subject: ARM: at91: PIT: (Almost) remove the global variables The timer driver is using some global variables to define some variables it has to use in most of its functions, like the base address. Use some container_of calls to have a single dynamic (and local) variable to hold this content. The only exception is in the !DT case, where the call chain to at91sam926x_ioremap_pit and then at91sam926x_pit_init as init_time makes it hard for the moment to pass the physical address of the timer. Signed-off-by: Maxime Ripard Acked-by: Boris BREZILLON Acked-by: Alexandre Belloni Acked-by: Daniel Lezcano Signed-off-by: Nicolas Ferre --- arch/arm/mach-at91/at91sam926x_time.c | 179 ++++++++++++++++++++-------------- 1 file changed, 108 insertions(+), 71 deletions(-) diff --git a/arch/arm/mach-at91/at91sam926x_time.c b/arch/arm/mach-at91/at91sam926x_time.c index e476474cb05e..9abb289dce72 100644 --- a/arch/arm/mach-at91/at91sam926x_time.c +++ b/arch/arm/mach-at91/at91sam926x_time.c @@ -20,6 +20,7 @@ #include #include #include +#include #include @@ -39,19 +40,35 @@ #define PIT_CPIV(x) ((x) & AT91_PIT_CPIV) #define PIT_PICNT(x) (((x) & AT91_PIT_PICNT) >> 20) -static u32 pit_cycle; /* write-once */ -static u32 pit_cnt; /* access only w/system irq blocked */ -static void __iomem *pit_base_addr __read_mostly; -static struct clk *mck; +struct pit_data { + struct clock_event_device clkevt; + struct clocksource clksrc; -static inline unsigned int pit_read(unsigned int reg_offset) + void __iomem *base; + u32 cycle; + u32 cnt; + unsigned int irq; + struct clk *mck; +}; + +static inline struct pit_data *clksrc_to_pit_data(struct clocksource *clksrc) { - return __raw_readl(pit_base_addr + reg_offset); + return container_of(clksrc, struct pit_data, clksrc); } -static inline void pit_write(unsigned int reg_offset, unsigned long value) +static inline struct pit_data *clkevt_to_pit_data(struct clock_event_device *clkevt) { - __raw_writel(value, pit_base_addr + reg_offset); + return container_of(clkevt, struct pit_data, clkevt); +} + +static inline unsigned int pit_read(void __iomem *base, unsigned int reg_offset) +{ + return __raw_readl(base + reg_offset); +} + +static inline void pit_write(void __iomem *base, unsigned int reg_offset, unsigned long value) +{ + __raw_writel(value, base + reg_offset); } /* @@ -60,40 +77,35 @@ static inline void pit_write(unsigned int reg_offset, unsigned long value) */ static cycle_t read_pit_clk(struct clocksource *cs) { + struct pit_data *data = clksrc_to_pit_data(cs); unsigned long flags; u32 elapsed; u32 t; raw_local_irq_save(flags); - elapsed = pit_cnt; - t = pit_read(AT91_PIT_PIIR); + elapsed = data->cnt; + t = pit_read(data->base, AT91_PIT_PIIR); raw_local_irq_restore(flags); - elapsed += PIT_PICNT(t) * pit_cycle; + elapsed += PIT_PICNT(t) * data->cycle; elapsed += PIT_CPIV(t); return elapsed; } -static struct clocksource pit_clk = { - .name = "pit", - .rating = 175, - .read = read_pit_clk, - .flags = CLOCK_SOURCE_IS_CONTINUOUS, -}; - - /* * Clockevent device: interrupts every 1/HZ (== pit_cycles * MCK/16) */ static void pit_clkevt_mode(enum clock_event_mode mode, struct clock_event_device *dev) { + struct pit_data *data = clkevt_to_pit_data(dev); + switch (mode) { case CLOCK_EVT_MODE_PERIODIC: /* update clocksource counter */ - pit_cnt += pit_cycle * PIT_PICNT(pit_read(AT91_PIT_PIVR)); - pit_write(AT91_PIT_MR, (pit_cycle - 1) | AT91_PIT_PITEN - | AT91_PIT_PITIEN); + data->cnt += data->cycle * PIT_PICNT(pit_read(data->base, AT91_PIT_PIVR)); + pit_write(data->base, AT91_PIT_MR, + (data->cycle - 1) | AT91_PIT_PITEN | AT91_PIT_PITIEN); break; case CLOCK_EVT_MODE_ONESHOT: BUG(); @@ -101,7 +113,8 @@ pit_clkevt_mode(enum clock_event_mode mode, struct clock_event_device *dev) case CLOCK_EVT_MODE_SHUTDOWN: case CLOCK_EVT_MODE_UNUSED: /* disable irq, leaving the clocksource active */ - pit_write(AT91_PIT_MR, (pit_cycle - 1) | AT91_PIT_PITEN); + pit_write(data->base, AT91_PIT_MR, + (data->cycle - 1) | AT91_PIT_PITEN); break; case CLOCK_EVT_MODE_RESUME: break; @@ -110,44 +123,40 @@ pit_clkevt_mode(enum clock_event_mode mode, struct clock_event_device *dev) static void at91sam926x_pit_suspend(struct clock_event_device *cedev) { + struct pit_data *data = clkevt_to_pit_data(cedev); + /* Disable timer */ - pit_write(AT91_PIT_MR, 0); + pit_write(data->base, AT91_PIT_MR, 0); } -static void at91sam926x_pit_reset(void) +static void at91sam926x_pit_reset(struct pit_data *data) { /* Disable timer and irqs */ - pit_write(AT91_PIT_MR, 0); + pit_write(data->base, AT91_PIT_MR, 0); /* Clear any pending interrupts, wait for PIT to stop counting */ - while (PIT_CPIV(pit_read(AT91_PIT_PIVR)) != 0) + while (PIT_CPIV(pit_read(data->base, AT91_PIT_PIVR)) != 0) cpu_relax(); /* Start PIT but don't enable IRQ */ - pit_write(AT91_PIT_MR, (pit_cycle - 1) | AT91_PIT_PITEN); + pit_write(data->base, AT91_PIT_MR, + (data->cycle - 1) | AT91_PIT_PITEN); } static void at91sam926x_pit_resume(struct clock_event_device *cedev) { - at91sam926x_pit_reset(); -} - -static struct clock_event_device pit_clkevt = { - .name = "pit", - .features = CLOCK_EVT_FEAT_PERIODIC, - .shift = 32, - .rating = 100, - .set_mode = pit_clkevt_mode, - .suspend = at91sam926x_pit_suspend, - .resume = at91sam926x_pit_resume, -}; + struct pit_data *data = clkevt_to_pit_data(cedev); + at91sam926x_pit_reset(data); +} /* * IRQ handler for the timer. */ static irqreturn_t at91sam926x_pit_interrupt(int irq, void *dev_id) { + struct pit_data *data = dev_id; + /* * irqs should be disabled here, but as the irq is shared they are only * guaranteed to be off if the timer irq is registered first. @@ -155,15 +164,15 @@ static irqreturn_t at91sam926x_pit_interrupt(int irq, void *dev_id) WARN_ON_ONCE(!irqs_disabled()); /* The PIT interrupt may be disabled, and is shared */ - if ((pit_clkevt.mode == CLOCK_EVT_MODE_PERIODIC) - && (pit_read(AT91_PIT_SR) & AT91_PIT_PITS)) { + if ((data->clkevt.mode == CLOCK_EVT_MODE_PERIODIC) && + (pit_read(data->base, AT91_PIT_SR) & AT91_PIT_PITS)) { unsigned nr_ticks; /* Get number of ticks performed before irq, and ack it */ - nr_ticks = PIT_PICNT(pit_read(AT91_PIT_PIVR)); + nr_ticks = PIT_PICNT(pit_read(data->base, AT91_PIT_PIVR)); do { - pit_cnt += pit_cycle; - pit_clkevt.event_handler(&pit_clkevt); + data->cnt += data->cycle; + data->clkevt.event_handler(&data->clkevt); nr_ticks--; } while (nr_ticks); @@ -176,7 +185,7 @@ static irqreturn_t at91sam926x_pit_interrupt(int irq, void *dev_id) /* * Set up both clocksource and clockevent support. */ -static void __init at91sam926x_pit_common_init(unsigned int pit_irq) +static void __init at91sam926x_pit_common_init(struct pit_data *data) { unsigned long pit_rate; unsigned bits; @@ -186,67 +195,95 @@ static void __init at91sam926x_pit_common_init(unsigned int pit_irq) * Use our actual MCK to figure out how many MCK/16 ticks per * 1/HZ period (instead of a compile-time constant LATCH). */ - pit_rate = clk_get_rate(mck) / 16; - pit_cycle = DIV_ROUND_CLOSEST(pit_rate, HZ); - WARN_ON(((pit_cycle - 1) & ~AT91_PIT_PIV) != 0); + pit_rate = clk_get_rate(data->mck) / 16; + data->cycle = DIV_ROUND_CLOSEST(pit_rate, HZ); + WARN_ON(((data->cycle - 1) & ~AT91_PIT_PIV) != 0); /* Initialize and enable the timer */ - at91sam926x_pit_reset(); + at91sam926x_pit_reset(data); /* * Register clocksource. The high order bits of PIV are unused, * so this isn't a 32-bit counter unless we get clockevent irqs. */ - bits = 12 /* PICNT */ + ilog2(pit_cycle) /* PIV */; - pit_clk.mask = CLOCKSOURCE_MASK(bits); - clocksource_register_hz(&pit_clk, pit_rate); + bits = 12 /* PICNT */ + ilog2(data->cycle) /* PIV */; + data->clksrc.mask = CLOCKSOURCE_MASK(bits); + data->clksrc.name = "pit"; + data->clksrc.rating = 175; + data->clksrc.read = read_pit_clk, + data->clksrc.flags = CLOCK_SOURCE_IS_CONTINUOUS, + clocksource_register_hz(&data->clksrc, pit_rate); /* Set up irq handler */ - ret = request_irq(pit_irq, at91sam926x_pit_interrupt, + ret = request_irq(data->irq, at91sam926x_pit_interrupt, IRQF_SHARED | IRQF_TIMER | IRQF_IRQPOLL, - "at91_tick", pit_base_addr); + "at91_tick", data); if (ret) panic(pr_fmt("Unable to setup IRQ\n")); /* Set up and register clockevents */ - pit_clkevt.mult = div_sc(pit_rate, NSEC_PER_SEC, pit_clkevt.shift); - pit_clkevt.cpumask = cpumask_of(0); - clockevents_register_device(&pit_clkevt); + data->clkevt.name = "pit"; + data->clkevt.features = CLOCK_EVT_FEAT_PERIODIC; + data->clkevt.shift = 32; + data->clkevt.mult = div_sc(pit_rate, NSEC_PER_SEC, data->clkevt.shift); + data->clkevt.rating = 100; + data->clkevt.cpumask = cpumask_of(0); + + data->clkevt.set_mode = pit_clkevt_mode; + data->clkevt.resume = at91sam926x_pit_resume; + data->clkevt.suspend = at91sam926x_pit_suspend; + clockevents_register_device(&data->clkevt); } static void __init at91sam926x_pit_dt_init(struct device_node *node) { - unsigned int irq; + struct pit_data *data; - pit_base_addr = of_iomap(node, 0); - if (!pit_base_addr) + data = kzalloc(sizeof(*data), GFP_KERNEL); + if (!data) + panic(pr_fmt("Unable to allocate memory\n")); + + data->base = of_iomap(node, 0); + if (!data->base) panic(pr_fmt("Could not map PIT address\n")); - mck = of_clk_get(node, 0); - if (IS_ERR(mck)) + data->mck = of_clk_get(node, 0); + if (IS_ERR(data->mck)) /* Fallback on clkdev for !CCF-based boards */ - mck = clk_get(NULL, "mck"); + data->mck = clk_get(NULL, "mck"); - if (IS_ERR(mck)) + if (IS_ERR(data->mck)) panic(pr_fmt("Unable to get mck clk\n")); /* Get the interrupts property */ - irq = irq_of_parse_and_map(node, 0); - if (!irq) + data->irq = irq_of_parse_and_map(node, 0); + if (!data->irq) panic(pr_fmt("Unable to get IRQ from DT\n")); - at91sam926x_pit_common_init(irq); + at91sam926x_pit_common_init(data); } CLOCKSOURCE_OF_DECLARE(at91sam926x_pit, "atmel,at91sam9260-pit", at91sam926x_pit_dt_init); +static void __iomem *pit_base_addr; + void __init at91sam926x_pit_init(void) { - mck = clk_get(NULL, "mck"); - if (IS_ERR(mck)) + struct pit_data *data; + + data = kzalloc(sizeof(*data), GFP_KERNEL); + if (!data) + panic(pr_fmt("Unable to allocate memory\n")); + + data->base = pit_base_addr; + + data->mck = clk_get(NULL, "mck"); + if (IS_ERR(data->mck)) panic(pr_fmt("Unable to get mck clk\n")); - at91sam926x_pit_common_init(NR_IRQS_LEGACY + AT91_ID_SYS); + data->irq = NR_IRQS_LEGACY + AT91_ID_SYS; + + at91sam926x_pit_common_init(data); } void __init at91sam926x_ioremap_pit(u32 addr) -- cgit v1.2.1 From d2992e51e799099e319aeaaa8648917954f59a3b Mon Sep 17 00:00:00 2001 From: Maxime Ripard Date: Tue, 2 Sep 2014 18:05:04 +0200 Subject: ARM: at91: soc: Add init_time callback Introduce an init_time callback to the at91_init_soc structure to be able to tweak the init_time machine callback on a per-soc basis, instead of having to rely on a global one. Signed-off-by: Maxime Ripard Acked-by: Boris BREZILLON Acked-by: Alexandre Belloni Acked-by: Daniel Lezcano Signed-off-by: Nicolas Ferre Conflicts: arch/arm/mach-at91/setup.c --- arch/arm/mach-at91/generic.h | 1 + arch/arm/mach-at91/setup.c | 5 +++++ arch/arm/mach-at91/soc.h | 1 + 3 files changed, 7 insertions(+) diff --git a/arch/arm/mach-at91/generic.h b/arch/arm/mach-at91/generic.h index 31cba6d9aa61..fb99dabab8f7 100644 --- a/arch/arm/mach-at91/generic.h +++ b/arch/arm/mach-at91/generic.h @@ -44,6 +44,7 @@ extern void __init at91_sysirq_mask_rtt(u32 rtt_base); extern void __init at91_register_devices(void); /* Timer */ +extern void __init at91_init_time(void); extern void at91rm9200_ioremap_st(u32 addr); extern void at91rm9200_timer_init(void); extern void at91sam926x_ioremap_pit(u32 addr); diff --git a/arch/arm/mach-at91/setup.c b/arch/arm/mach-at91/setup.c index d2cade21ebd7..51c22a2fcea4 100644 --- a/arch/arm/mach-at91/setup.c +++ b/arch/arm/mach-at91/setup.c @@ -416,3 +416,8 @@ void __init at91_register_devices(void) { at91_boot_soc.register_devices(); } + +void __init at91_init_time(void) +{ + at91_boot_soc.init_time(); +} diff --git a/arch/arm/mach-at91/soc.h b/arch/arm/mach-at91/soc.h index ab983f2cc7dd..2886b83dd0df 100644 --- a/arch/arm/mach-at91/soc.h +++ b/arch/arm/mach-at91/soc.h @@ -13,6 +13,7 @@ struct at91_init_soc { void (*register_clocks)(void); void (*register_devices)(void); void (*init)(void); + void (*init_time)(void); }; extern struct at91_init_soc at91_boot_soc; -- cgit v1.2.1 From 0f391f189eae2c479122dc934d8ffdcbe4257cfe Mon Sep 17 00:00:00 2001 From: Maxime Ripard Date: Tue, 1 Jul 2014 11:33:25 +0200 Subject: ARM: at91: Convert the boards to the init_time callback Now that we have the init_time callback in the at91_init_soc structure, convert all the boards and SoC to this. Signed-off-by: Maxime Ripard Acked-by: Boris BREZILLON Acked-by: Alexandre Belloni Acked-by: Daniel Lezcano Signed-off-by: Nicolas Ferre --- arch/arm/mach-at91/at91sam9260.c | 6 ++++++ arch/arm/mach-at91/at91sam9261.c | 6 ++++++ arch/arm/mach-at91/at91sam9263.c | 6 ++++++ arch/arm/mach-at91/at91sam9g45.c | 6 ++++++ arch/arm/mach-at91/at91sam9rl.c | 6 ++++++ arch/arm/mach-at91/board-afeb-9260v1.c | 2 +- arch/arm/mach-at91/board-cam60.c | 2 +- arch/arm/mach-at91/board-cpu9krea.c | 2 +- arch/arm/mach-at91/board-flexibity.c | 2 +- arch/arm/mach-at91/board-foxg20.c | 2 +- arch/arm/mach-at91/board-gsia18s.c | 2 +- arch/arm/mach-at91/board-pcontrol-g20.c | 2 +- arch/arm/mach-at91/board-sam9-l9260.c | 2 +- arch/arm/mach-at91/board-sam9260ek.c | 2 +- arch/arm/mach-at91/board-sam9261ek.c | 4 ++-- arch/arm/mach-at91/board-sam9263ek.c | 2 +- arch/arm/mach-at91/board-sam9g20ek.c | 4 ++-- arch/arm/mach-at91/board-sam9m10g45ek.c | 2 +- arch/arm/mach-at91/board-sam9rlek.c | 2 +- arch/arm/mach-at91/board-snapper9260.c | 2 +- arch/arm/mach-at91/board-stamp9g20.c | 4 ++-- 21 files changed, 49 insertions(+), 19 deletions(-) diff --git a/arch/arm/mach-at91/at91sam9260.c b/arch/arm/mach-at91/at91sam9260.c index b6948d677ca9..b61a255d4141 100644 --- a/arch/arm/mach-at91/at91sam9260.c +++ b/arch/arm/mach-at91/at91sam9260.c @@ -440,6 +440,11 @@ static unsigned int at91sam9260_default_irq_priority[NR_AIC_IRQS] __initdata = { 0, /* Advanced Interrupt Controller */ }; +static void __init at91sam9260_init_time(void) +{ + at91sam926x_pit_init(); +} + AT91_SOC_START(at91sam9260) .map_io = at91sam9260_map_io, .default_irq_priority = at91sam9260_default_irq_priority, @@ -449,4 +454,5 @@ AT91_SOC_START(at91sam9260) .register_clocks = at91sam9260_register_clocks, .register_devices = at91sam9260_register_devices, .init = at91sam9260_initialize, + .init_time = at91sam9260_init_time, AT91_SOC_END diff --git a/arch/arm/mach-at91/at91sam9261.c b/arch/arm/mach-at91/at91sam9261.c index c07339e370ee..badf20c2cd67 100644 --- a/arch/arm/mach-at91/at91sam9261.c +++ b/arch/arm/mach-at91/at91sam9261.c @@ -399,6 +399,11 @@ static unsigned int at91sam9261_default_irq_priority[NR_AIC_IRQS] __initdata = { 0, /* Advanced Interrupt Controller */ }; +static void __init at91sam9261_init_time(void) +{ + at91sam926x_pit_init(); +} + AT91_SOC_START(at91sam9261) .map_io = at91sam9261_map_io, .default_irq_priority = at91sam9261_default_irq_priority, @@ -408,4 +413,5 @@ AT91_SOC_START(at91sam9261) .register_clocks = at91sam9261_register_clocks, .register_devices = at91sam9261_register_devices, .init = at91sam9261_initialize, + .init_time = at91sam9261_init_time, AT91_SOC_END diff --git a/arch/arm/mach-at91/at91sam9263.c b/arch/arm/mach-at91/at91sam9263.c index 33ab06ec5365..ebb413e7f653 100644 --- a/arch/arm/mach-at91/at91sam9263.c +++ b/arch/arm/mach-at91/at91sam9263.c @@ -422,6 +422,11 @@ static unsigned int at91sam9263_default_irq_priority[NR_AIC_IRQS] __initdata = { 0, /* Advanced Interrupt Controller (IRQ1) */ }; +static void __init at91sam9263_init_time(void) +{ + at91sam926x_pit_init(); +} + AT91_SOC_START(at91sam9263) .map_io = at91sam9263_map_io, .default_irq_priority = at91sam9263_default_irq_priority, @@ -430,4 +435,5 @@ AT91_SOC_START(at91sam9263) .register_clocks = at91sam9263_register_clocks, .register_devices = at91sam9263_register_devices, .init = at91sam9263_initialize, + .init_time = at91sam9263_init_time, AT91_SOC_END diff --git a/arch/arm/mach-at91/at91sam9g45.c b/arch/arm/mach-at91/at91sam9g45.c index 7a329703f31a..f725814c556d 100644 --- a/arch/arm/mach-at91/at91sam9g45.c +++ b/arch/arm/mach-at91/at91sam9g45.c @@ -477,6 +477,11 @@ static unsigned int at91sam9g45_default_irq_priority[NR_AIC_IRQS] __initdata = { 0, /* Advanced Interrupt Controller (IRQ0) */ }; +static void __init at91sam9g45_init_time(void) +{ + at91sam926x_pit_init(); +} + AT91_SOC_START(at91sam9g45) .map_io = at91sam9g45_map_io, .default_irq_priority = at91sam9g45_default_irq_priority, @@ -485,4 +490,5 @@ AT91_SOC_START(at91sam9g45) .register_clocks = at91sam9g45_register_clocks, .register_devices = at91sam9g45_register_devices, .init = at91sam9g45_initialize, + .init_time = at91sam9g45_init_time, AT91_SOC_END diff --git a/arch/arm/mach-at91/at91sam9rl.c b/arch/arm/mach-at91/at91sam9rl.c index 264999f33594..361b4a6e7651 100644 --- a/arch/arm/mach-at91/at91sam9rl.c +++ b/arch/arm/mach-at91/at91sam9rl.c @@ -410,6 +410,11 @@ static unsigned int at91sam9rl_default_irq_priority[NR_AIC_IRQS] __initdata = { 0, /* Advanced Interrupt Controller */ }; +static void __init at91sam9rl_init_time(void) +{ + at91sam926x_pit_init(); +} + AT91_SOC_START(at91sam9rl) .map_io = at91sam9rl_map_io, .default_irq_priority = at91sam9rl_default_irq_priority, @@ -420,4 +425,5 @@ AT91_SOC_START(at91sam9rl) #endif .register_devices = at91sam9rl_register_devices, .init = at91sam9rl_initialize, + .init_time = at91sam9rl_init_time, AT91_SOC_END diff --git a/arch/arm/mach-at91/board-afeb-9260v1.c b/arch/arm/mach-at91/board-afeb-9260v1.c index fc9621ccb606..e76e35ce81e7 100644 --- a/arch/arm/mach-at91/board-afeb-9260v1.c +++ b/arch/arm/mach-at91/board-afeb-9260v1.c @@ -213,7 +213,7 @@ static void __init afeb9260_board_init(void) MACHINE_START(AFEB9260, "Custom afeb9260 board") /* Maintainer: Sergey Lapin */ - .init_time = at91sam926x_pit_init, + .init_time = at91_init_time, .map_io = at91_map_io, .handle_irq = at91_aic_handle_irq, .init_early = afeb9260_init_early, diff --git a/arch/arm/mach-at91/board-cam60.c b/arch/arm/mach-at91/board-cam60.c index 283655bd86c1..ae827dd2d0d2 100644 --- a/arch/arm/mach-at91/board-cam60.c +++ b/arch/arm/mach-at91/board-cam60.c @@ -190,7 +190,7 @@ static void __init cam60_board_init(void) MACHINE_START(CAM60, "KwikByte CAM60") /* Maintainer: KwikByte */ - .init_time = at91sam926x_pit_init, + .init_time = at91_init_time, .map_io = at91_map_io, .handle_irq = at91_aic_handle_irq, .init_early = cam60_init_early, diff --git a/arch/arm/mach-at91/board-cpu9krea.c b/arch/arm/mach-at91/board-cpu9krea.c index 29a89032bb9a..731c8318f4f5 100644 --- a/arch/arm/mach-at91/board-cpu9krea.c +++ b/arch/arm/mach-at91/board-cpu9krea.c @@ -377,7 +377,7 @@ MACHINE_START(CPUAT9260, "Eukrea CPU9260") MACHINE_START(CPUAT9G20, "Eukrea CPU9G20") #endif /* Maintainer: Eric Benard - EUKREA Electromatique */ - .init_time = at91sam926x_pit_init, + .init_time = at91_init_time, .map_io = at91_map_io, .handle_irq = at91_aic_handle_irq, .init_early = cpu9krea_init_early, diff --git a/arch/arm/mach-at91/board-flexibity.c b/arch/arm/mach-at91/board-flexibity.c index 79bd411b0cee..a6aa4a2432f2 100644 --- a/arch/arm/mach-at91/board-flexibity.c +++ b/arch/arm/mach-at91/board-flexibity.c @@ -162,7 +162,7 @@ static void __init flexibity_board_init(void) MACHINE_START(FLEXIBITY, "Flexibity Connect") /* Maintainer: Maxim Osipov */ - .init_time = at91sam926x_pit_init, + .init_time = at91_init_time, .map_io = at91_map_io, .handle_irq = at91_aic_handle_irq, .init_early = flexibity_init_early, diff --git a/arch/arm/mach-at91/board-foxg20.c b/arch/arm/mach-at91/board-foxg20.c index 8b22c60bb238..ec290b6ed9d8 100644 --- a/arch/arm/mach-at91/board-foxg20.c +++ b/arch/arm/mach-at91/board-foxg20.c @@ -263,7 +263,7 @@ static void __init foxg20_board_init(void) MACHINE_START(ACMENETUSFOXG20, "Acme Systems srl FOX Board G20") /* Maintainer: Sergio Tanzilli */ - .init_time = at91sam926x_pit_init, + .init_time = at91_init_time, .map_io = at91_map_io, .handle_irq = at91_aic_handle_irq, .init_early = foxg20_init_early, diff --git a/arch/arm/mach-at91/board-gsia18s.c b/arch/arm/mach-at91/board-gsia18s.c index b729dd1271bf..bf5cc55c7db6 100644 --- a/arch/arm/mach-at91/board-gsia18s.c +++ b/arch/arm/mach-at91/board-gsia18s.c @@ -576,7 +576,7 @@ static void __init gsia18s_board_init(void) } MACHINE_START(GSIA18S, "GS_IA18_S") - .init_time = at91sam926x_pit_init, + .init_time = at91_init_time, .map_io = at91_map_io, .handle_irq = at91_aic_handle_irq, .init_early = gsia18s_init_early, diff --git a/arch/arm/mach-at91/board-pcontrol-g20.c b/arch/arm/mach-at91/board-pcontrol-g20.c index b48d95ec5152..9c26b94ce448 100644 --- a/arch/arm/mach-at91/board-pcontrol-g20.c +++ b/arch/arm/mach-at91/board-pcontrol-g20.c @@ -219,7 +219,7 @@ static void __init pcontrol_g20_board_init(void) MACHINE_START(PCONTROL_G20, "PControl G20") /* Maintainer: pgsellmann@portner-elektronik.at */ - .init_time = at91sam926x_pit_init, + .init_time = at91_init_time, .map_io = at91_map_io, .handle_irq = at91_aic_handle_irq, .init_early = pcontrol_g20_init_early, diff --git a/arch/arm/mach-at91/board-sam9-l9260.c b/arch/arm/mach-at91/board-sam9-l9260.c index 70309404f366..c2166e3a236c 100644 --- a/arch/arm/mach-at91/board-sam9-l9260.c +++ b/arch/arm/mach-at91/board-sam9-l9260.c @@ -221,7 +221,7 @@ static void __init ek_board_init(void) MACHINE_START(SAM9_L9260, "Olimex SAM9-L9260") /* Maintainer: Olimex */ - .init_time = at91sam926x_pit_init, + .init_time = at91_init_time, .map_io = at91_map_io, .handle_irq = at91_aic_handle_irq, .init_early = ek_init_early, diff --git a/arch/arm/mach-at91/board-sam9260ek.c b/arch/arm/mach-at91/board-sam9260ek.c index 18f49c93f3f3..bf8a946b4cd0 100644 --- a/arch/arm/mach-at91/board-sam9260ek.c +++ b/arch/arm/mach-at91/board-sam9260ek.c @@ -345,7 +345,7 @@ static void __init ek_board_init(void) MACHINE_START(AT91SAM9260EK, "Atmel AT91SAM9260-EK") /* Maintainer: Atmel */ - .init_time = at91sam926x_pit_init, + .init_time = at91_init_time, .map_io = at91_map_io, .handle_irq = at91_aic_handle_irq, .init_early = ek_init_early, diff --git a/arch/arm/mach-at91/board-sam9261ek.c b/arch/arm/mach-at91/board-sam9261ek.c index 5a23e7211203..e85ada820bfb 100644 --- a/arch/arm/mach-at91/board-sam9261ek.c +++ b/arch/arm/mach-at91/board-sam9261ek.c @@ -604,7 +604,7 @@ static void __init ek_board_init(void) MACHINE_START(AT91SAM9261EK, "Atmel AT91SAM9261-EK") /* Maintainer: Atmel */ - .init_time = at91sam926x_pit_init, + .init_time = at91_init_time, .map_io = at91_map_io, .handle_irq = at91_aic_handle_irq, .init_early = ek_init_early, @@ -614,7 +614,7 @@ MACHINE_END MACHINE_START(AT91SAM9G10EK, "Atmel AT91SAM9G10-EK") /* Maintainer: Atmel */ - .init_time = at91sam926x_pit_init, + .init_time = at91_init_time, .map_io = at91_map_io, .handle_irq = at91_aic_handle_irq, .init_early = ek_init_early, diff --git a/arch/arm/mach-at91/board-sam9263ek.c b/arch/arm/mach-at91/board-sam9263ek.c index e8685652a73e..d76680f2a209 100644 --- a/arch/arm/mach-at91/board-sam9263ek.c +++ b/arch/arm/mach-at91/board-sam9263ek.c @@ -484,7 +484,7 @@ static void __init ek_board_init(void) MACHINE_START(AT91SAM9263EK, "Atmel AT91SAM9263-EK") /* Maintainer: Atmel */ - .init_time = at91sam926x_pit_init, + .init_time = at91_init_time, .map_io = at91_map_io, .handle_irq = at91_aic_handle_irq, .init_early = ek_init_early, diff --git a/arch/arm/mach-at91/board-sam9g20ek.c b/arch/arm/mach-at91/board-sam9g20ek.c index e1be6e25b380..49f075213451 100644 --- a/arch/arm/mach-at91/board-sam9g20ek.c +++ b/arch/arm/mach-at91/board-sam9g20ek.c @@ -410,7 +410,7 @@ static void __init ek_board_init(void) MACHINE_START(AT91SAM9G20EK, "Atmel AT91SAM9G20-EK") /* Maintainer: Atmel */ - .init_time = at91sam926x_pit_init, + .init_time = at91_init_time, .map_io = at91_map_io, .handle_irq = at91_aic_handle_irq, .init_early = ek_init_early, @@ -420,7 +420,7 @@ MACHINE_END MACHINE_START(AT91SAM9G20EK_2MMC, "Atmel AT91SAM9G20-EK 2 MMC Slot Mod") /* Maintainer: Atmel */ - .init_time = at91sam926x_pit_init, + .init_time = at91_init_time, .map_io = at91_map_io, .handle_irq = at91_aic_handle_irq, .init_early = ek_init_early, diff --git a/arch/arm/mach-at91/board-sam9m10g45ek.c b/arch/arm/mach-at91/board-sam9m10g45ek.c index 0fc4bea272ea..a517c7f7af92 100644 --- a/arch/arm/mach-at91/board-sam9m10g45ek.c +++ b/arch/arm/mach-at91/board-sam9m10g45ek.c @@ -518,7 +518,7 @@ static void __init ek_board_init(void) MACHINE_START(AT91SAM9M10G45EK, "Atmel AT91SAM9M10G45-EK") /* Maintainer: Atmel */ - .init_time = at91sam926x_pit_init, + .init_time = at91_init_time, .map_io = at91_map_io, .handle_irq = at91_aic_handle_irq, .init_early = ek_init_early, diff --git a/arch/arm/mach-at91/board-sam9rlek.c b/arch/arm/mach-at91/board-sam9rlek.c index 5cb82464be1a..8bca329b0293 100644 --- a/arch/arm/mach-at91/board-sam9rlek.c +++ b/arch/arm/mach-at91/board-sam9rlek.c @@ -324,7 +324,7 @@ static void __init ek_board_init(void) MACHINE_START(AT91SAM9RLEK, "Atmel AT91SAM9RL-EK") /* Maintainer: Atmel */ - .init_time = at91sam926x_pit_init, + .init_time = at91_init_time, .map_io = at91_map_io, .handle_irq = at91_aic_handle_irq, .init_early = ek_init_early, diff --git a/arch/arm/mach-at91/board-snapper9260.c b/arch/arm/mach-at91/board-snapper9260.c index 2a817b85569f..b4aff840a1a0 100644 --- a/arch/arm/mach-at91/board-snapper9260.c +++ b/arch/arm/mach-at91/board-snapper9260.c @@ -180,7 +180,7 @@ static void __init snapper9260_board_init(void) } MACHINE_START(SNAPPER_9260, "Bluewater Systems Snapper 9260/9G20 module") - .init_time = at91sam926x_pit_init, + .init_time = at91_init_time, .map_io = at91_map_io, .handle_irq = at91_aic_handle_irq, .init_early = snapper9260_init_early, diff --git a/arch/arm/mach-at91/board-stamp9g20.c b/arch/arm/mach-at91/board-stamp9g20.c index 3b575036ff96..e825641a1dee 100644 --- a/arch/arm/mach-at91/board-stamp9g20.c +++ b/arch/arm/mach-at91/board-stamp9g20.c @@ -275,7 +275,7 @@ static void __init stamp9g20evb_board_init(void) MACHINE_START(PORTUXG20, "taskit PortuxG20") /* Maintainer: taskit GmbH */ - .init_time = at91sam926x_pit_init, + .init_time = at91_init_time, .map_io = at91_map_io, .handle_irq = at91_aic_handle_irq, .init_early = stamp9g20_init_early, @@ -285,7 +285,7 @@ MACHINE_END MACHINE_START(STAMP9G20, "taskit Stamp9G20") /* Maintainer: taskit GmbH */ - .init_time = at91sam926x_pit_init, + .init_time = at91_init_time, .map_io = at91_map_io, .handle_irq = at91_aic_handle_irq, .init_early = stamp9g20_init_early, -- cgit v1.2.1 From 71bcada88b0f3c7f11fa5b8a4b30ae66dbfabbf3 Mon Sep 17 00:00:00 2001 From: Thor Thayer Date: Wed, 3 Sep 2014 10:27:54 -0500 Subject: edac: altera: Add Altera SDRAM EDAC support This patch adds support for the CycloneV and ArriaV SDRAM controllers. Correction and reporting of SBEs, Panic on DBEs. There was a discussion thread on whether this driver should be an mfd driver or just make use of syscon, which is already a mfd. Ultimately, the decision to use a simple syscon interface was reached.[1] [1] https://lkml.org/lkml/2014/7/30/514 [dinguyen] Fixed Kconfig to have EDAC_ALTERA_MC as a tristate to prevent a build failure for allmodconfig. Signed-off-by: Thor Thayer Acked-by: Borislav Petkov [dinguyen] cleaned up commit message Signed-off-by: Dinh Nguyen --- MAINTAINERS | 5 + drivers/edac/Kconfig | 9 + drivers/edac/Makefile | 2 + drivers/edac/altera_edac.c | 410 +++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 426 insertions(+) create mode 100644 drivers/edac/altera_edac.c diff --git a/MAINTAINERS b/MAINTAINERS index 1ff06dee651d..a5dd2d61777a 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -1376,6 +1376,11 @@ M: Dinh Nguyen S: Maintained F: drivers/clk/socfpga/ +ARM/SOCFPGA EDAC SUPPORT +M: Thor Thayer +S: Maintained +F: drivers/edac/altera_edac. + ARM/STI ARCHITECTURE M: Srinivas Kandagatla M: Maxime Coquelin diff --git a/drivers/edac/Kconfig b/drivers/edac/Kconfig index fd89ca982748..7072c2892d63 100644 --- a/drivers/edac/Kconfig +++ b/drivers/edac/Kconfig @@ -376,4 +376,13 @@ config EDAC_OCTEON_PCI Support for error detection and correction on the Cavium Octeon family of SOCs. +config EDAC_ALTERA_MC + tristate "Altera SDRAM Memory Controller EDAC" + depends on EDAC_MM_EDAC && ARCH_SOCFPGA + help + Support for error detection and correction on the + Altera SDRAM memory controller. Note that the + preloader must initialize the SDRAM before loading + the kernel. + endif # EDAC diff --git a/drivers/edac/Makefile b/drivers/edac/Makefile index c479a24d8f77..359aa499b200 100644 --- a/drivers/edac/Makefile +++ b/drivers/edac/Makefile @@ -65,3 +65,5 @@ obj-$(CONFIG_EDAC_OCTEON_PC) += octeon_edac-pc.o obj-$(CONFIG_EDAC_OCTEON_L2C) += octeon_edac-l2c.o obj-$(CONFIG_EDAC_OCTEON_LMC) += octeon_edac-lmc.o obj-$(CONFIG_EDAC_OCTEON_PCI) += octeon_edac-pci.o + +obj-$(CONFIG_EDAC_ALTERA_MC) += altera_edac.o diff --git a/drivers/edac/altera_edac.c b/drivers/edac/altera_edac.c new file mode 100644 index 000000000000..3c4929fda9d5 --- /dev/null +++ b/drivers/edac/altera_edac.c @@ -0,0 +1,410 @@ +/* + * Copyright Altera Corporation (C) 2014. All rights reserved. + * Copyright 2011-2012 Calxeda, Inc. + * + * 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 . + * + * Adapted from the highbank_mc_edac driver. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "edac_core.h" +#include "edac_module.h" + +#define EDAC_MOD_STR "altera_edac" +#define EDAC_VERSION "1" + +/* SDRAM Controller CtrlCfg Register */ +#define CTLCFG_OFST 0x00 + +/* SDRAM Controller CtrlCfg Register Bit Masks */ +#define CTLCFG_ECC_EN 0x400 +#define CTLCFG_ECC_CORR_EN 0x800 +#define CTLCFG_GEN_SB_ERR 0x2000 +#define CTLCFG_GEN_DB_ERR 0x4000 + +#define CTLCFG_ECC_AUTO_EN (CTLCFG_ECC_EN | \ + CTLCFG_ECC_CORR_EN) + +/* SDRAM Controller Address Width Register */ +#define DRAMADDRW_OFST 0x2C + +/* SDRAM Controller Address Widths Field Register */ +#define DRAMADDRW_COLBIT_MASK 0x001F +#define DRAMADDRW_COLBIT_SHIFT 0 +#define DRAMADDRW_ROWBIT_MASK 0x03E0 +#define DRAMADDRW_ROWBIT_SHIFT 5 +#define DRAMADDRW_BANKBIT_MASK 0x1C00 +#define DRAMADDRW_BANKBIT_SHIFT 10 +#define DRAMADDRW_CSBIT_MASK 0xE000 +#define DRAMADDRW_CSBIT_SHIFT 13 + +/* SDRAM Controller Interface Data Width Register */ +#define DRAMIFWIDTH_OFST 0x30 + +/* SDRAM Controller Interface Data Width Defines */ +#define DRAMIFWIDTH_16B_ECC 24 +#define DRAMIFWIDTH_32B_ECC 40 + +/* SDRAM Controller DRAM Status Register */ +#define DRAMSTS_OFST 0x38 + +/* SDRAM Controller DRAM Status Register Bit Masks */ +#define DRAMSTS_SBEERR 0x04 +#define DRAMSTS_DBEERR 0x08 +#define DRAMSTS_CORR_DROP 0x10 + +/* SDRAM Controller DRAM IRQ Register */ +#define DRAMINTR_OFST 0x3C + +/* SDRAM Controller DRAM IRQ Register Bit Masks */ +#define DRAMINTR_INTREN 0x01 +#define DRAMINTR_SBEMASK 0x02 +#define DRAMINTR_DBEMASK 0x04 +#define DRAMINTR_CORRDROPMASK 0x08 +#define DRAMINTR_INTRCLR 0x10 + +/* SDRAM Controller Single Bit Error Count Register */ +#define SBECOUNT_OFST 0x40 + +/* SDRAM Controller Single Bit Error Count Register Bit Masks */ +#define SBECOUNT_MASK 0x0F + +/* SDRAM Controller Double Bit Error Count Register */ +#define DBECOUNT_OFST 0x44 + +/* SDRAM Controller Double Bit Error Count Register Bit Masks */ +#define DBECOUNT_MASK 0x0F + +/* SDRAM Controller ECC Error Address Register */ +#define ERRADDR_OFST 0x48 + +/* SDRAM Controller ECC Error Address Register Bit Masks */ +#define ERRADDR_MASK 0xFFFFFFFF + +/* Altera SDRAM Memory Controller data */ +struct altr_sdram_mc_data { + struct regmap *mc_vbase; +}; + +static irqreturn_t altr_sdram_mc_err_handler(int irq, void *dev_id) +{ + struct mem_ctl_info *mci = dev_id; + struct altr_sdram_mc_data *drvdata = mci->pvt_info; + u32 status, err_count, err_addr; + + /* Error Address is shared by both SBE & DBE */ + regmap_read(drvdata->mc_vbase, ERRADDR_OFST, &err_addr); + + regmap_read(drvdata->mc_vbase, DRAMSTS_OFST, &status); + + if (status & DRAMSTS_DBEERR) { + regmap_read(drvdata->mc_vbase, DBECOUNT_OFST, &err_count); + panic("\nEDAC: [%d Uncorrectable errors @ 0x%08X]\n", + err_count, err_addr); + } + if (status & DRAMSTS_SBEERR) { + regmap_read(drvdata->mc_vbase, SBECOUNT_OFST, &err_count); + edac_mc_handle_error(HW_EVENT_ERR_CORRECTED, mci, err_count, + err_addr >> PAGE_SHIFT, + err_addr & ~PAGE_MASK, 0, + 0, 0, -1, mci->ctl_name, ""); + } + + regmap_write(drvdata->mc_vbase, DRAMINTR_OFST, + (DRAMINTR_INTRCLR | DRAMINTR_INTREN)); + + return IRQ_HANDLED; +} + +#ifdef CONFIG_EDAC_DEBUG +static ssize_t altr_sdr_mc_err_inject_write(struct file *file, + const char __user *data, + size_t count, loff_t *ppos) +{ + struct mem_ctl_info *mci = file->private_data; + struct altr_sdram_mc_data *drvdata = mci->pvt_info; + u32 *ptemp; + dma_addr_t dma_handle; + u32 reg, read_reg; + + ptemp = dma_alloc_coherent(mci->pdev, 16, &dma_handle, GFP_KERNEL); + if (!ptemp) { + dma_free_coherent(mci->pdev, 16, ptemp, dma_handle); + edac_printk(KERN_ERR, EDAC_MC, + "Inject: Buffer Allocation error\n"); + return -ENOMEM; + } + + regmap_read(drvdata->mc_vbase, CTLCFG_OFST, &read_reg); + read_reg &= ~(CTLCFG_GEN_SB_ERR | CTLCFG_GEN_DB_ERR); + + /* Error are injected by writing a word while the SBE or DBE + * bit in the CTLCFG register is set. Reading the word will + * trigger the SBE or DBE error and the corresponding IRQ. + */ + if (count == 3) { + edac_printk(KERN_ALERT, EDAC_MC, + "Inject Double bit error\n"); + regmap_write(drvdata->mc_vbase, CTLCFG_OFST, + (read_reg | CTLCFG_GEN_DB_ERR)); + } else { + edac_printk(KERN_ALERT, EDAC_MC, + "Inject Single bit error\n"); + regmap_write(drvdata->mc_vbase, CTLCFG_OFST, + (read_reg | CTLCFG_GEN_SB_ERR)); + } + + ptemp[0] = 0x5A5A5A5A; + ptemp[1] = 0xA5A5A5A5; + + /* Clear the error injection bits */ + regmap_write(drvdata->mc_vbase, CTLCFG_OFST, read_reg); + /* Ensure it has been written out */ + wmb(); + + /* + * To trigger the error, we need to read the data back + * (the data was written with errors above). + * The ACCESS_ONCE macros and printk are used to prevent the + * the compiler optimizing these reads out. + */ + reg = ACCESS_ONCE(ptemp[0]); + read_reg = ACCESS_ONCE(ptemp[1]); + /* Force Read */ + rmb(); + + edac_printk(KERN_ALERT, EDAC_MC, "Read Data [0x%X, 0x%X]\n", + reg, read_reg); + + dma_free_coherent(mci->pdev, 16, ptemp, dma_handle); + + return count; +} + +static const struct file_operations altr_sdr_mc_debug_inject_fops = { + .open = simple_open, + .write = altr_sdr_mc_err_inject_write, + .llseek = generic_file_llseek, +}; + +static void altr_sdr_mc_create_debugfs_nodes(struct mem_ctl_info *mci) +{ + if (mci->debugfs) + debugfs_create_file("inject_ctrl", S_IWUSR, mci->debugfs, mci, + &altr_sdr_mc_debug_inject_fops); +} +#else +static void altr_sdr_mc_create_debugfs_nodes(struct mem_ctl_info *mci) +{} +#endif + +/* Get total memory size in bytes */ +static u32 altr_sdram_get_total_mem_size(struct regmap *mc_vbase) +{ + u32 size, read_reg, row, bank, col, cs, width; + + if (regmap_read(mc_vbase, DRAMADDRW_OFST, &read_reg) < 0) + return 0; + + if (regmap_read(mc_vbase, DRAMIFWIDTH_OFST, &width) < 0) + return 0; + + col = (read_reg & DRAMADDRW_COLBIT_MASK) >> + DRAMADDRW_COLBIT_SHIFT; + row = (read_reg & DRAMADDRW_ROWBIT_MASK) >> + DRAMADDRW_ROWBIT_SHIFT; + bank = (read_reg & DRAMADDRW_BANKBIT_MASK) >> + DRAMADDRW_BANKBIT_SHIFT; + cs = (read_reg & DRAMADDRW_CSBIT_MASK) >> + DRAMADDRW_CSBIT_SHIFT; + + /* Correct for ECC as its not addressible */ + if (width == DRAMIFWIDTH_32B_ECC) + width = 32; + if (width == DRAMIFWIDTH_16B_ECC) + width = 16; + + /* calculate the SDRAM size base on this info */ + size = 1 << (row + bank + col); + size = size * cs * (width / 8); + return size; +} + +static int altr_sdram_probe(struct platform_device *pdev) +{ + struct edac_mc_layer layers[2]; + struct mem_ctl_info *mci; + struct altr_sdram_mc_data *drvdata; + struct regmap *mc_vbase; + struct dimm_info *dimm; + u32 read_reg, mem_size; + int irq; + int res = 0; + + /* Validate the SDRAM controller has ECC enabled */ + /* Grab the register range from the sdr controller in device tree */ + mc_vbase = syscon_regmap_lookup_by_phandle(pdev->dev.of_node, + "altr,sdr-syscon"); + if (IS_ERR(mc_vbase)) { + edac_printk(KERN_ERR, EDAC_MC, + "regmap for altr,sdr-syscon lookup failed.\n"); + return -ENODEV; + } + + if (regmap_read(mc_vbase, CTLCFG_OFST, &read_reg) || + ((read_reg & CTLCFG_ECC_AUTO_EN) != CTLCFG_ECC_AUTO_EN)) { + edac_printk(KERN_ERR, EDAC_MC, + "No ECC/ECC disabled [0x%08X]\n", read_reg); + return -ENODEV; + } + + /* Grab memory size from device tree. */ + mem_size = altr_sdram_get_total_mem_size(mc_vbase); + if (!mem_size) { + edac_printk(KERN_ERR, EDAC_MC, + "Unable to calculate memory size\n"); + return -ENODEV; + } + + /* Ensure the SDRAM Interrupt is disabled and cleared */ + if (regmap_write(mc_vbase, DRAMINTR_OFST, DRAMINTR_INTRCLR)) { + edac_printk(KERN_ERR, EDAC_MC, + "Error clearing SDRAM ECC IRQ\n"); + return -ENODEV; + } + + irq = platform_get_irq(pdev, 0); + if (irq < 0) { + edac_printk(KERN_ERR, EDAC_MC, + "No irq %d in DT\n", irq); + return -ENODEV; + } + + layers[0].type = EDAC_MC_LAYER_CHIP_SELECT; + layers[0].size = 1; + layers[0].is_virt_csrow = true; + layers[1].type = EDAC_MC_LAYER_CHANNEL; + layers[1].size = 1; + layers[1].is_virt_csrow = false; + mci = edac_mc_alloc(0, ARRAY_SIZE(layers), layers, + sizeof(struct altr_sdram_mc_data)); + if (!mci) + return -ENOMEM; + + mci->pdev = &pdev->dev; + drvdata = mci->pvt_info; + drvdata->mc_vbase = mc_vbase; + platform_set_drvdata(pdev, mci); + + if (!devres_open_group(&pdev->dev, NULL, GFP_KERNEL)) { + res = -ENOMEM; + goto free; + } + + mci->mtype_cap = MEM_FLAG_DDR3; + mci->edac_ctl_cap = EDAC_FLAG_NONE | EDAC_FLAG_SECDED; + mci->edac_cap = EDAC_FLAG_SECDED; + mci->mod_name = EDAC_MOD_STR; + mci->mod_ver = EDAC_VERSION; + mci->ctl_name = dev_name(&pdev->dev); + mci->scrub_mode = SCRUB_SW_SRC; + mci->dev_name = dev_name(&pdev->dev); + + dimm = *mci->dimms; + dimm->nr_pages = ((mem_size - 1) >> PAGE_SHIFT) + 1; + dimm->grain = 8; + dimm->dtype = DEV_X8; + dimm->mtype = MEM_DDR3; + dimm->edac_mode = EDAC_SECDED; + + res = edac_mc_add_mc(mci); + if (res < 0) + goto err; + + res = devm_request_irq(&pdev->dev, irq, altr_sdram_mc_err_handler, + 0, dev_name(&pdev->dev), mci); + if (res < 0) { + edac_mc_printk(mci, KERN_ERR, + "Unable to request irq %d\n", irq); + res = -ENODEV; + goto err2; + } + + if (regmap_write(drvdata->mc_vbase, DRAMINTR_OFST, + (DRAMINTR_INTRCLR | DRAMINTR_INTREN))) { + edac_mc_printk(mci, KERN_ERR, + "Error enabling SDRAM ECC IRQ\n"); + res = -ENODEV; + goto err2; + } + + altr_sdr_mc_create_debugfs_nodes(mci); + + devres_close_group(&pdev->dev, NULL); + + return 0; + +err2: + edac_mc_del_mc(&pdev->dev); +err: + devres_release_group(&pdev->dev, NULL); +free: + edac_mc_free(mci); + edac_printk(KERN_ERR, EDAC_MC, + "EDAC Probe Failed; Error %d\n", res); + + return res; +} + +static int altr_sdram_remove(struct platform_device *pdev) +{ + struct mem_ctl_info *mci = platform_get_drvdata(pdev); + + edac_mc_del_mc(&pdev->dev); + edac_mc_free(mci); + platform_set_drvdata(pdev, NULL); + + return 0; +} + +static const struct of_device_id altr_sdram_ctrl_of_match[] = { + { .compatible = "altr,sdram-edac", }, + {}, +}; +MODULE_DEVICE_TABLE(of, altr_sdram_ctrl_of_match); + +static struct platform_driver altr_sdram_edac_driver = { + .probe = altr_sdram_probe, + .remove = altr_sdram_remove, + .driver = { + .name = "altr_sdram_edac", + .of_match_table = altr_sdram_ctrl_of_match, + }, +}; + +module_platform_driver(altr_sdram_edac_driver); + +MODULE_LICENSE("GPL v2"); +MODULE_AUTHOR("Thor Thayer"); +MODULE_DESCRIPTION("EDAC Driver for Altera SDRAM Controller"); -- cgit v1.2.1 From d800386343df6fd07264665244b4d6a7ed21abec Mon Sep 17 00:00:00 2001 From: Suman Anna Date: Wed, 10 Sep 2014 14:20:58 -0500 Subject: Documentation: dt: add omap mailbox bindings Add the device tree bindings document for OMAP2+ mailbox. Cc: Rob Herring Cc: Pawel Moll Cc: Mark Rutland Cc: Ian Campbell Cc: Kumar Gala Signed-off-by: Suman Anna Signed-off-by: Tony Lindgren --- .../devicetree/bindings/mailbox/omap-mailbox.txt | 108 +++++++++++++++++++++ 1 file changed, 108 insertions(+) create mode 100644 Documentation/devicetree/bindings/mailbox/omap-mailbox.txt diff --git a/Documentation/devicetree/bindings/mailbox/omap-mailbox.txt b/Documentation/devicetree/bindings/mailbox/omap-mailbox.txt new file mode 100644 index 000000000000..48edc4b92afb --- /dev/null +++ b/Documentation/devicetree/bindings/mailbox/omap-mailbox.txt @@ -0,0 +1,108 @@ +OMAP2+ Mailbox Driver +===================== + +The OMAP mailbox hardware facilitates communication between different processors +using a queued mailbox interrupt mechanism. The IP block is external to the +various processor subsystems and is connected on an interconnect bus. The +communication is achieved through a set of registers for message storage and +interrupt configuration registers. + +Each mailbox IP block has a certain number of h/w fifo queues and output +interrupt lines. An output interrupt line is routed to an interrupt controller +within a processor subsystem, and there can be more than one line going to a +specific processor's interrupt controller. The interrupt line connections are +fixed for an instance and are dictated by the IP integration into the SoC +(excluding the SoCs that have a Interrupt Crossbar IP). Each interrupt line is +programmable through a set of interrupt configuration registers, and have a rx +and tx interrupt source per h/w fifo. Communication between different processors +is achieved through the appropriate programming of the rx and tx interrupt +sources on the appropriate interrupt lines. + +The number of h/w fifo queues and interrupt lines dictate the usable registers. +All the current OMAP SoCs except for the newest DRA7xx SoC has a single IP +instance. DRA7xx has multiple instances with different number of h/w fifo queues +and interrupt lines between different instances. The interrupt lines can also be +routed to different processor sub-systems on DRA7xx as they are routed through +the Crossbar, a kind of interrupt router/multiplexer. + +Mailbox Device Node: +==================== +A Mailbox device node is used to represent a Mailbox IP instance within a SoC. +The sub-mailboxes are represented as child nodes of this parent node. + +Required properties: +-------------------- +- compatible: Should be one of the following, + "ti,omap2-mailbox" for OMAP2420, OMAP2430 SoCs + "ti,omap3-mailbox" for OMAP3430, OMAP3630 SoCs + "ti,omap4-mailbox" for OMAP44xx, OMAP54xx, AM33xx, + AM43xx and DRA7xx SoCs +- reg: Contains the mailbox register address range (base + address and length) +- interrupts: Contains the interrupt information for the mailbox + device. The format is dependent on which interrupt + controller the OMAP device uses +- ti,hwmods: Name of the hwmod associated with the mailbox +- ti,mbox-num-users: Number of targets (processor devices) that the mailbox + device can interrupt +- ti,mbox-num-fifos: Number of h/w fifo queues within the mailbox IP block + +Child Nodes: +============ +A child node is used for representing the actual sub-mailbox device that is +used for the communication between the host processor and a remote processor. +Each child node should have a unique node name across all the different +mailbox device nodes. + +Required properties: +-------------------- +- ti,mbox-tx: sub-mailbox descriptor property defining a Tx fifo +- ti,mbox-rx: sub-mailbox descriptor property defining a Rx fifo + +Sub-mailbox Descriptor Data +--------------------------- +Each of the above ti,mbox-tx and ti,mbox-rx properties should have 3 cells of +data that represent the following: + Cell #1 (fifo_id) - mailbox fifo id used either for transmitting + (ti,mbox-tx) or for receiving (ti,mbox-rx) + Cell #2 (irq_id) - irq identifier index number to use from the parent's + interrupts data. Should be 0 for most of the cases, a + positive index value is seen only on mailboxes that have + multiple interrupt lines connected to the MPU processor. + Cell #3 (usr_id) - mailbox user id for identifying the interrupt line + associated with generating a tx/rx fifo interrupt. + +Example: +-------- + +/* OMAP4 */ +mailbox: mailbox@4a0f4000 { + compatible = "ti,omap4-mailbox"; + reg = <0x4a0f4000 0x200>; + interrupts = ; + ti,hwmods = "mailbox"; + ti,mbox-num-users = <3>; + ti,mbox-num-fifos = <8>; + mbox_ipu: mbox_ipu { + ti,mbox-tx = <0 0 0>; + ti,mbox-rx = <1 0 0>; + }; + mbox_dsp: mbox_dsp { + ti,mbox-tx = <3 0 0>; + ti,mbox-rx = <2 0 0>; + }; +}; + +/* AM33xx */ +mailbox: mailbox@480C8000 { + compatible = "ti,omap4-mailbox"; + reg = <0x480C8000 0x200>; + interrupts = <77>; + ti,hwmods = "mailbox"; + ti,mbox-num-users = <4>; + ti,mbox-num-fifos = <8>; + mbox_wkupm3: wkup_m3 { + ti,mbox-tx = <0 0 0>; + ti,mbox-rx = <0 0 3>; + }; +}; -- cgit v1.2.1 From 75288cc66dc478b32e43970dd6913396526504ae Mon Sep 17 00:00:00 2001 From: Suman Anna Date: Wed, 10 Sep 2014 14:20:59 -0500 Subject: mailbox/omap: add support for parsing dt devices Logic has been added to the OMAP2+ mailbox code to parse the mailbox dt nodes and construct the different sub-mailboxes associated with the instance. The DT representation of the sub-mailbox devices is different from legacy platform data representation to allow flexibility of interrupt configuration between Tx and Rx fifos (to also possibly allow simplex devices in the future). The DT representation gathers similar information that was being passed previously through the platform data, except for the interrupt type information, which is gathered through driver compatible match data. The non-DT support has to be maintained for now to not break OMAP3 legacy boot, and the legacy-style code will be cleaned up once OMAP3 is also converted to DT-boot only. Cc: Jassi Brar Cc: Rob Herring Signed-off-by: Suman Anna Acked-by: Pavel Machek Signed-off-by: Tony Lindgren --- drivers/mailbox/omap-mailbox.c | 156 ++++++++++++++++++++++++++++++++++------- 1 file changed, 132 insertions(+), 24 deletions(-) diff --git a/drivers/mailbox/omap-mailbox.c b/drivers/mailbox/omap-mailbox.c index a27e00e63a8a..bcc7ee129276 100644 --- a/drivers/mailbox/omap-mailbox.c +++ b/drivers/mailbox/omap-mailbox.c @@ -31,6 +31,7 @@ #include #include #include +#include #include #include #include @@ -94,6 +95,18 @@ struct omap_mbox_device { struct list_head elem; }; +struct omap_mbox_fifo_info { + int tx_id; + int tx_usr; + int tx_irq; + + int rx_id; + int rx_usr; + int rx_irq; + + const char *name; +}; + struct omap_mbox { const char *name; int irq; @@ -587,24 +600,118 @@ static int omap_mbox_unregister(struct omap_mbox_device *mdev) return 0; } +static const struct of_device_id omap_mailbox_of_match[] = { + { + .compatible = "ti,omap2-mailbox", + .data = (void *)MBOX_INTR_CFG_TYPE1, + }, + { + .compatible = "ti,omap3-mailbox", + .data = (void *)MBOX_INTR_CFG_TYPE1, + }, + { + .compatible = "ti,omap4-mailbox", + .data = (void *)MBOX_INTR_CFG_TYPE2, + }, + { + /* end */ + }, +}; +MODULE_DEVICE_TABLE(of, omap_mailbox_of_match); + static int omap_mbox_probe(struct platform_device *pdev) { struct resource *mem; int ret; struct omap_mbox **list, *mbox, *mboxblk; struct omap_mbox_pdata *pdata = pdev->dev.platform_data; - struct omap_mbox_dev_info *info; + struct omap_mbox_dev_info *info = NULL; + struct omap_mbox_fifo_info *finfo, *finfoblk; struct omap_mbox_device *mdev; struct omap_mbox_fifo *fifo; - u32 intr_type; + struct device_node *node = pdev->dev.of_node; + struct device_node *child; + const struct of_device_id *match; + u32 intr_type, info_count; + u32 num_users, num_fifos; + u32 tmp[3]; u32 l; int i; - if (!pdata || !pdata->info_cnt || !pdata->info) { + if (!node && (!pdata || !pdata->info_cnt || !pdata->info)) { pr_err("%s: platform not supported\n", __func__); return -ENODEV; } + if (node) { + match = of_match_device(omap_mailbox_of_match, &pdev->dev); + if (!match) + return -ENODEV; + intr_type = (u32)match->data; + + if (of_property_read_u32(node, "ti,mbox-num-users", + &num_users)) + return -ENODEV; + + if (of_property_read_u32(node, "ti,mbox-num-fifos", + &num_fifos)) + return -ENODEV; + + info_count = of_get_available_child_count(node); + if (!info_count) { + dev_err(&pdev->dev, "no available mbox devices found\n"); + return -ENODEV; + } + } else { /* non-DT device creation */ + info_count = pdata->info_cnt; + info = pdata->info; + intr_type = pdata->intr_type; + num_users = pdata->num_users; + num_fifos = pdata->num_fifos; + } + + finfoblk = devm_kzalloc(&pdev->dev, info_count * sizeof(*finfoblk), + GFP_KERNEL); + if (!finfoblk) + return -ENOMEM; + + finfo = finfoblk; + child = NULL; + for (i = 0; i < info_count; i++, finfo++) { + if (node) { + child = of_get_next_available_child(node, child); + ret = of_property_read_u32_array(child, "ti,mbox-tx", + tmp, ARRAY_SIZE(tmp)); + if (ret) + return ret; + finfo->tx_id = tmp[0]; + finfo->tx_irq = tmp[1]; + finfo->tx_usr = tmp[2]; + + ret = of_property_read_u32_array(child, "ti,mbox-rx", + tmp, ARRAY_SIZE(tmp)); + if (ret) + return ret; + finfo->rx_id = tmp[0]; + finfo->rx_irq = tmp[1]; + finfo->rx_usr = tmp[2]; + + finfo->name = child->name; + } else { + finfo->tx_id = info->tx_id; + finfo->rx_id = info->rx_id; + finfo->tx_usr = info->usr_id; + finfo->tx_irq = info->irq_id; + finfo->rx_usr = info->usr_id; + finfo->rx_irq = info->irq_id; + finfo->name = info->name; + info++; + } + if (finfo->tx_id >= num_fifos || finfo->rx_id >= num_fifos || + finfo->tx_usr >= num_users || finfo->rx_usr >= num_users) + return -EINVAL; + } + mdev = devm_kzalloc(&pdev->dev, sizeof(*mdev), GFP_KERNEL); if (!mdev) return -ENOMEM; @@ -615,41 +722,40 @@ static int omap_mbox_probe(struct platform_device *pdev) return PTR_ERR(mdev->mbox_base); /* allocate one extra for marking end of list */ - list = devm_kzalloc(&pdev->dev, (pdata->info_cnt + 1) * sizeof(*list), + list = devm_kzalloc(&pdev->dev, (info_count + 1) * sizeof(*list), GFP_KERNEL); if (!list) return -ENOMEM; - mboxblk = devm_kzalloc(&pdev->dev, pdata->info_cnt * sizeof(*mbox), + mboxblk = devm_kzalloc(&pdev->dev, info_count * sizeof(*mbox), GFP_KERNEL); if (!mboxblk) return -ENOMEM; - info = pdata->info; - intr_type = pdata->intr_type; mbox = mboxblk; - for (i = 0; i < pdata->info_cnt; i++, info++) { + finfo = finfoblk; + for (i = 0; i < info_count; i++, finfo++) { fifo = &mbox->tx_fifo; - fifo->msg = MAILBOX_MESSAGE(info->tx_id); - fifo->fifo_stat = MAILBOX_FIFOSTATUS(info->tx_id); - fifo->intr_bit = MAILBOX_IRQ_NOTFULL(info->tx_id); - fifo->irqenable = MAILBOX_IRQENABLE(intr_type, info->usr_id); - fifo->irqstatus = MAILBOX_IRQSTATUS(intr_type, info->usr_id); - fifo->irqdisable = MAILBOX_IRQDISABLE(intr_type, info->usr_id); + fifo->msg = MAILBOX_MESSAGE(finfo->tx_id); + fifo->fifo_stat = MAILBOX_FIFOSTATUS(finfo->tx_id); + fifo->intr_bit = MAILBOX_IRQ_NOTFULL(finfo->tx_id); + fifo->irqenable = MAILBOX_IRQENABLE(intr_type, finfo->tx_usr); + fifo->irqstatus = MAILBOX_IRQSTATUS(intr_type, finfo->tx_usr); + fifo->irqdisable = MAILBOX_IRQDISABLE(intr_type, finfo->tx_usr); fifo = &mbox->rx_fifo; - fifo->msg = MAILBOX_MESSAGE(info->rx_id); - fifo->msg_stat = MAILBOX_MSGSTATUS(info->rx_id); - fifo->intr_bit = MAILBOX_IRQ_NEWMSG(info->rx_id); - fifo->irqenable = MAILBOX_IRQENABLE(intr_type, info->usr_id); - fifo->irqstatus = MAILBOX_IRQSTATUS(intr_type, info->usr_id); - fifo->irqdisable = MAILBOX_IRQDISABLE(intr_type, info->usr_id); + fifo->msg = MAILBOX_MESSAGE(finfo->rx_id); + fifo->msg_stat = MAILBOX_MSGSTATUS(finfo->rx_id); + fifo->intr_bit = MAILBOX_IRQ_NEWMSG(finfo->rx_id); + fifo->irqenable = MAILBOX_IRQENABLE(intr_type, finfo->rx_usr); + fifo->irqstatus = MAILBOX_IRQSTATUS(intr_type, finfo->rx_usr); + fifo->irqdisable = MAILBOX_IRQDISABLE(intr_type, finfo->rx_usr); mbox->intr_type = intr_type; mbox->parent = mdev; - mbox->name = info->name; - mbox->irq = platform_get_irq(pdev, info->irq_id); + mbox->name = finfo->name; + mbox->irq = platform_get_irq(pdev, finfo->tx_irq); if (mbox->irq < 0) return mbox->irq; list[i] = mbox++; @@ -657,8 +763,8 @@ static int omap_mbox_probe(struct platform_device *pdev) mutex_init(&mdev->cfg_lock); mdev->dev = &pdev->dev; - mdev->num_users = pdata->num_users; - mdev->num_fifos = pdata->num_fifos; + mdev->num_users = num_users; + mdev->num_fifos = num_fifos; mdev->mboxes = list; ret = omap_mbox_register(mdev); if (ret) @@ -684,6 +790,7 @@ static int omap_mbox_probe(struct platform_device *pdev) if (ret < 0) goto unregister; + devm_kfree(&pdev->dev, finfoblk); return 0; unregister: @@ -708,6 +815,7 @@ static struct platform_driver omap_mbox_driver = { .driver = { .name = "omap-mailbox", .owner = THIS_MODULE, + .of_match_table = of_match_ptr(omap_mailbox_of_match), }, }; -- cgit v1.2.1 From 176da6c766ce3d5d8c88a7afdaefcc5fd6d8afac Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Mon, 8 Sep 2014 17:54:31 -0700 Subject: arm: omap: irq: make omap_irq_base global This is in preparation for removing the pointless irq_banks array. Signed-off-by: Felipe Balbi Signed-off-by: Tony Lindgren --- arch/arm/mach-omap2/irq.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/arm/mach-omap2/irq.c b/arch/arm/mach-omap2/irq.c index 604a976abf14..ae082c603445 100644 --- a/arch/arm/mach-omap2/irq.c +++ b/arch/arm/mach-omap2/irq.c @@ -68,6 +68,7 @@ static struct omap_irq_bank { }; static struct irq_domain *domain; +static void __iomem *omap_irq_base; /* Structure to save interrupt controller context */ struct omap3_intc_regs { @@ -161,7 +162,6 @@ omap_alloc_gc(void __iomem *base, unsigned int irq_start, unsigned int num) static void __init omap_init_irq(u32 base, int nr_irqs, struct device_node *node) { - void __iomem *omap_irq_base; unsigned long nr_of_irqs = 0; unsigned int nr_banks = 0; int i, j, irq_base; -- cgit v1.2.1 From 33c7c7b7f2eb76520cd8ddcb7fe458383783e0f8 Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Mon, 8 Sep 2014 17:54:32 -0700 Subject: arm: omap: irq: define INTC_ILR0 register this is currently used as a hardcoded 0x100 offset. Signed-off-by: Felipe Balbi Signed-off-by: Tony Lindgren --- arch/arm/mach-omap2/irq.c | 1 + 1 file changed, 1 insertion(+) diff --git a/arch/arm/mach-omap2/irq.c b/arch/arm/mach-omap2/irq.c index ae082c603445..bae03290cad4 100644 --- a/arch/arm/mach-omap2/irq.c +++ b/arch/arm/mach-omap2/irq.c @@ -41,6 +41,7 @@ #define INTC_MIR_CLEAR0 0x0088 #define INTC_MIR_SET0 0x008c #define INTC_PENDING_IRQ0 0x0098 +#define INTC_ILR0 0x0100 /* Number of IRQ state bits in each MIR register */ #define IRQ_BITS_PER_REG 32 -- cgit v1.2.1 From 71be00c90aba446779ea35a9740973a9be594257 Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Mon, 8 Sep 2014 17:54:32 -0700 Subject: arm: omap: irq: start to remove irq_banks array We have a single bank in that array, this patch is in preparation to remove that array. It just shifts everything to a new set of functions for register IO while also removing old ones. Signed-off-by: Felipe Balbi Signed-off-by: Tony Lindgren --- arch/arm/mach-omap2/irq.c | 64 ++++++++++++++++++++--------------------------- 1 file changed, 27 insertions(+), 37 deletions(-) diff --git a/arch/arm/mach-omap2/irq.c b/arch/arm/mach-omap2/irq.c index bae03290cad4..652c15bff176 100644 --- a/arch/arm/mach-omap2/irq.c +++ b/arch/arm/mach-omap2/irq.c @@ -82,21 +82,20 @@ struct omap3_intc_regs { }; /* INTC bank register get/set */ - -static void intc_bank_write_reg(u32 val, struct omap_irq_bank *bank, u16 reg) +static void intc_writel(u32 reg, u32 val) { - writel_relaxed(val, bank->base_reg + reg); + writel_relaxed(val, omap_irq_base + reg); } -static u32 intc_bank_read_reg(struct omap_irq_bank *bank, u16 reg) +static u32 intc_readl(u32 reg) { - return readl_relaxed(bank->base_reg + reg); + return readl_relaxed(omap_irq_base + reg); } /* XXX: FIQ and additional INTC support (only MPU at the moment) */ static void omap_ack_irq(struct irq_data *d) { - intc_bank_write_reg(0x1, &irq_banks[0], INTC_CONTROL); + intc_writel(INTC_CONTROL, 0x1); } static void omap_mask_ack_irq(struct irq_data *d) @@ -109,19 +108,19 @@ static void __init omap_irq_bank_init_one(struct omap_irq_bank *bank) { unsigned long tmp; - tmp = intc_bank_read_reg(bank, INTC_REVISION) & 0xff; + tmp = intc_readl(INTC_REVISION) & 0xff; pr_info("IRQ: Found an INTC at 0x%p (revision %ld.%ld) with %d interrupts\n", bank->base_reg, tmp >> 4, tmp & 0xf, bank->nr_irqs); - tmp = intc_bank_read_reg(bank, INTC_SYSCONFIG); + tmp = intc_readl(INTC_SYSCONFIG); tmp |= 1 << 1; /* soft reset */ - intc_bank_write_reg(tmp, bank, INTC_SYSCONFIG); + intc_writel(INTC_SYSCONFIG, tmp); - while (!(intc_bank_read_reg(bank, INTC_SYSSTATUS) & 0x1)) + while (!(intc_readl(INTC_SYSSTATUS) & 0x1)) /* Wait for reset to complete */; /* Enable autoidle */ - intc_bank_write_reg(1 << 0, bank, INTC_SYSCONFIG); + intc_writel(INTC_SYSCONFIG, 1 << 0); } int omap_irq_pending(void) @@ -133,7 +132,7 @@ int omap_irq_pending(void) int irq; for (irq = 0; irq < bank->nr_irqs; irq += 32) - if (intc_bank_read_reg(bank, INTC_PENDING_IRQ0 + + if (intc_readl(INTC_PENDING_IRQ0 + ((irq >> 5) << 5))) return 1; } @@ -307,22 +306,20 @@ void omap_intc_save_context(void) { int ind = 0, i = 0; for (ind = 0; ind < ARRAY_SIZE(irq_banks); ind++) { - struct omap_irq_bank *bank = irq_banks + ind; intc_context[ind].sysconfig = - intc_bank_read_reg(bank, INTC_SYSCONFIG); + intc_readl(INTC_SYSCONFIG); intc_context[ind].protection = - intc_bank_read_reg(bank, INTC_PROTECTION); + intc_readl(INTC_PROTECTION); intc_context[ind].idle = - intc_bank_read_reg(bank, INTC_IDLE); + intc_readl(INTC_IDLE); intc_context[ind].threshold = - intc_bank_read_reg(bank, INTC_THRESHOLD); + intc_readl(INTC_THRESHOLD); for (i = 0; i < INTCPS_NR_IRQS; i++) intc_context[ind].ilr[i] = - intc_bank_read_reg(bank, (0x100 + 0x4*i)); + intc_readl((INTC_ILR0 + 0x4 * i)); for (i = 0; i < INTCPS_NR_MIR_REGS; i++) intc_context[ind].mir[i] = - intc_bank_read_reg(&irq_banks[0], INTC_MIR0 + - (0x20 * i)); + intc_readl(INTC_MIR0 + (0x20 * i)); } } @@ -331,23 +328,16 @@ void omap_intc_restore_context(void) int ind = 0, i = 0; for (ind = 0; ind < ARRAY_SIZE(irq_banks); ind++) { - struct omap_irq_bank *bank = irq_banks + ind; - intc_bank_write_reg(intc_context[ind].sysconfig, - bank, INTC_SYSCONFIG); - intc_bank_write_reg(intc_context[ind].sysconfig, - bank, INTC_SYSCONFIG); - intc_bank_write_reg(intc_context[ind].protection, - bank, INTC_PROTECTION); - intc_bank_write_reg(intc_context[ind].idle, - bank, INTC_IDLE); - intc_bank_write_reg(intc_context[ind].threshold, - bank, INTC_THRESHOLD); + intc_writel(INTC_SYSCONFIG, intc_context[ind].sysconfig); + intc_writel(INTC_PROTECTION, intc_context[ind].protection); + intc_writel(INTC_IDLE, intc_context[ind].idle); + intc_writel(INTC_THRESHOLD, intc_context[ind].threshold); for (i = 0; i < INTCPS_NR_IRQS; i++) - intc_bank_write_reg(intc_context[ind].ilr[i], - bank, (0x100 + 0x4*i)); + intc_writel(INTC_ILR0 + 0x4 * i, + intc_context[ind].ilr[i]); for (i = 0; i < INTCPS_NR_MIR_REGS; i++) - intc_bank_write_reg(intc_context[ind].mir[i], - &irq_banks[0], INTC_MIR0 + (0x20 * i)); + intc_writel(INTC_MIR0 + 0x20 * i, + intc_context[ind].mir[i]); } /* MIRs are saved and restore with other PRCM registers */ } @@ -364,13 +354,13 @@ void omap3_intc_prepare_idle(void) * Disable autoidle as it can stall interrupt controller, * cf. errata ID i540 for 3430 (all revisions up to 3.1.x) */ - intc_bank_write_reg(0, &irq_banks[0], INTC_SYSCONFIG); + intc_writel(INTC_SYSCONFIG, 0); } void omap3_intc_resume_idle(void) { /* Re-enable autoidle */ - intc_bank_write_reg(1, &irq_banks[0], INTC_SYSCONFIG); + intc_writel(INTC_SYSCONFIG, 1); } asmlinkage void __exception_irq_entry omap3_intc_handle_irq(struct pt_regs *regs) -- cgit v1.2.1 From 421b090c83280f9f47d7c2532a6a0ba35446d6b1 Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Mon, 8 Sep 2014 17:54:34 -0700 Subject: arm: omap: irq: add a global omap_nr_irqs variable this will cache number of irqs. Also in preparation for removal of irq_banks array. Signed-off-by: Felipe Balbi Signed-off-by: Tony Lindgren --- arch/arm/mach-omap2/irq.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/arch/arm/mach-omap2/irq.c b/arch/arm/mach-omap2/irq.c index 652c15bff176..82b8d383ab9c 100644 --- a/arch/arm/mach-omap2/irq.c +++ b/arch/arm/mach-omap2/irq.c @@ -70,6 +70,7 @@ static struct omap_irq_bank { static struct irq_domain *domain; static void __iomem *omap_irq_base; +static int omap_nr_irqs = 96; /* Structure to save interrupt controller context */ struct omap3_intc_regs { @@ -170,6 +171,8 @@ static void __init omap_init_irq(u32 base, int nr_irqs, if (WARN_ON(!omap_irq_base)) return; + omap_nr_irqs = nr_irqs; + irq_base = irq_alloc_descs(-1, 0, nr_irqs, 0); if (irq_base < 0) { pr_warn("Couldn't allocate IRQ numbers\n"); -- cgit v1.2.1 From a88ab43083c6f3cb518f63cc5f53d8304092efc0 Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Mon, 8 Sep 2014 17:54:35 -0700 Subject: arm: omap: irq: remove rest of irq_banks usage now we can finally remove the pointless irq_banks array. Signed-off-by: Felipe Balbi Signed-off-by: Tony Lindgren --- arch/arm/mach-omap2/irq.c | 128 +++++++++++++++++----------------------------- 1 file changed, 47 insertions(+), 81 deletions(-) diff --git a/arch/arm/mach-omap2/irq.c b/arch/arm/mach-omap2/irq.c index 82b8d383ab9c..5ea7def91ab0 100644 --- a/arch/arm/mach-omap2/irq.c +++ b/arch/arm/mach-omap2/irq.c @@ -49,8 +49,8 @@ #define OMAP3_IRQ_BASE OMAP2_L4_IO_ADDRESS(OMAP34XX_IC_BASE) #define INTCPS_SIR_IRQ_OFFSET 0x0040 /* omap2/3 active interrupt offset */ #define ACTIVEIRQ_MASK 0x7f /* omap2/3 active interrupt bits */ +#define INTCPS_NR_ILR_REGS 128 #define INTCPS_NR_MIR_REGS 3 -#define INTCPS_NR_IRQS 96 /* * OMAP2 has a number of different interrupt controllers, each interrupt @@ -58,15 +58,6 @@ * fairly consistent for each bank, but not all registers are implemented * for each bank.. when in doubt, consult the TRM. */ -static struct omap_irq_bank { - void __iomem *base_reg; - unsigned int nr_irqs; -} __attribute__ ((aligned(4))) irq_banks[] = { - { - /* MPU INTC */ - .nr_irqs = 96, - }, -}; static struct irq_domain *domain; static void __iomem *omap_irq_base; @@ -78,7 +69,7 @@ struct omap3_intc_regs { u32 protection; u32 idle; u32 threshold; - u32 ilr[INTCPS_NR_IRQS]; + u32 ilr[INTCPS_NR_ILR_REGS]; u32 mir[INTCPS_NR_MIR_REGS]; }; @@ -105,13 +96,14 @@ static void omap_mask_ack_irq(struct irq_data *d) omap_ack_irq(d); } -static void __init omap_irq_bank_init_one(struct omap_irq_bank *bank) +static void __init omap_irq_soft_reset(void) { unsigned long tmp; tmp = intc_readl(INTC_REVISION) & 0xff; + pr_info("IRQ: Found an INTC at 0x%p (revision %ld.%ld) with %d interrupts\n", - bank->base_reg, tmp >> 4, tmp & 0xf, bank->nr_irqs); + omap_irq_base, tmp >> 4, tmp & 0xf, omap_nr_irqs); tmp = intc_readl(INTC_SYSCONFIG); tmp |= 1 << 1; /* soft reset */ @@ -126,17 +118,12 @@ static void __init omap_irq_bank_init_one(struct omap_irq_bank *bank) int omap_irq_pending(void) { - int i; + int irq; - for (i = 0; i < ARRAY_SIZE(irq_banks); i++) { - struct omap_irq_bank *bank = irq_banks + i; - int irq; - - for (irq = 0; irq < bank->nr_irqs; irq += 32) - if (intc_readl(INTC_PENDING_IRQ0 + - ((irq >> 5) << 5))) - return 1; - } + for (irq = 0; irq < omap_nr_irqs; irq += 32) + if (intc_readl(INTC_PENDING_IRQ0 + + ((irq >> 5) << 5))) + return 1; return 0; } @@ -163,9 +150,7 @@ omap_alloc_gc(void __iomem *base, unsigned int irq_start, unsigned int num) static void __init omap_init_irq(u32 base, int nr_irqs, struct device_node *node) { - unsigned long nr_of_irqs = 0; - unsigned int nr_banks = 0; - int i, j, irq_base; + int j, irq_base; omap_irq_base = ioremap(base, SZ_4K); if (WARN_ON(!omap_irq_base)) @@ -180,31 +165,12 @@ static void __init omap_init_irq(u32 base, int nr_irqs, } domain = irq_domain_add_legacy(node, nr_irqs, irq_base, 0, - &irq_domain_simple_ops, NULL); - - for (i = 0; i < ARRAY_SIZE(irq_banks); i++) { - struct omap_irq_bank *bank = irq_banks + i; - - bank->nr_irqs = nr_irqs; - - /* Static mapping, never released */ - bank->base_reg = ioremap(base, SZ_4K); - if (!bank->base_reg) { - pr_err("Could not ioremap irq bank%i\n", i); - continue; - } - - omap_irq_bank_init_one(bank); + &irq_domain_simple_ops, NULL); - for (j = 0; j < bank->nr_irqs; j += 32) - omap_alloc_gc(bank->base_reg + j, j + irq_base, 32); + omap_irq_soft_reset(); - nr_of_irqs += bank->nr_irqs; - nr_banks++; - } - - pr_info("Total of %ld interrupts on %d active controller%s\n", - nr_of_irqs, nr_banks, nr_banks > 1 ? "s" : ""); + for (j = 0; j < omap_nr_irqs; j += 32) + omap_alloc_gc(omap_irq_base + j, j + irq_base, 32); } void __init omap2_init_irq(void) @@ -303,45 +269,45 @@ void __init omap_intc_of_init(void) } #if defined(CONFIG_ARCH_OMAP3) || defined(CONFIG_SOC_AM33XX) -static struct omap3_intc_regs intc_context[ARRAY_SIZE(irq_banks)]; +static struct omap3_intc_regs intc_context; void omap_intc_save_context(void) { - int ind = 0, i = 0; - for (ind = 0; ind < ARRAY_SIZE(irq_banks); ind++) { - intc_context[ind].sysconfig = - intc_readl(INTC_SYSCONFIG); - intc_context[ind].protection = - intc_readl(INTC_PROTECTION); - intc_context[ind].idle = - intc_readl(INTC_IDLE); - intc_context[ind].threshold = - intc_readl(INTC_THRESHOLD); - for (i = 0; i < INTCPS_NR_IRQS; i++) - intc_context[ind].ilr[i] = - intc_readl((INTC_ILR0 + 0x4 * i)); - for (i = 0; i < INTCPS_NR_MIR_REGS; i++) - intc_context[ind].mir[i] = - intc_readl(INTC_MIR0 + (0x20 * i)); - } + int i; + + intc_context.sysconfig = + intc_readl(INTC_SYSCONFIG); + intc_context.protection = + intc_readl(INTC_PROTECTION); + intc_context.idle = + intc_readl(INTC_IDLE); + intc_context.threshold = + intc_readl(INTC_THRESHOLD); + + for (i = 0; i < omap_nr_irqs; i++) + intc_context.ilr[i] = + intc_readl((INTC_ILR0 + 0x4 * i)); + for (i = 0; i < INTCPS_NR_MIR_REGS; i++) + intc_context.mir[i] = + intc_readl(INTC_MIR0 + (0x20 * i)); } void omap_intc_restore_context(void) { - int ind = 0, i = 0; - - for (ind = 0; ind < ARRAY_SIZE(irq_banks); ind++) { - intc_writel(INTC_SYSCONFIG, intc_context[ind].sysconfig); - intc_writel(INTC_PROTECTION, intc_context[ind].protection); - intc_writel(INTC_IDLE, intc_context[ind].idle); - intc_writel(INTC_THRESHOLD, intc_context[ind].threshold); - for (i = 0; i < INTCPS_NR_IRQS; i++) - intc_writel(INTC_ILR0 + 0x4 * i, - intc_context[ind].ilr[i]); - for (i = 0; i < INTCPS_NR_MIR_REGS; i++) - intc_writel(INTC_MIR0 + 0x20 * i, - intc_context[ind].mir[i]); - } + int i; + + intc_writel(INTC_SYSCONFIG, intc_context.sysconfig); + intc_writel(INTC_PROTECTION, intc_context.protection); + intc_writel(INTC_IDLE, intc_context.idle); + intc_writel(INTC_THRESHOLD, intc_context.threshold); + + for (i = 0; i < omap_nr_irqs; i++) + intc_writel(INTC_ILR0 + 0x4 * i, + intc_context.ilr[i]); + + for (i = 0; i < INTCPS_NR_MIR_REGS; i++) + intc_writel(INTC_MIR0 + 0x20 * i, + intc_context.mir[i]); /* MIRs are saved and restore with other PRCM registers */ } -- cgit v1.2.1 From 33ca0be0836c834ceba12761b506ff73cf46a376 Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Mon, 8 Sep 2014 17:54:37 -0700 Subject: arm: omap: irq: remove unused macro no functional changes. Signed-off-by: Felipe Balbi Signed-off-by: Tony Lindgren --- arch/arm/mach-omap2/irq.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/arch/arm/mach-omap2/irq.c b/arch/arm/mach-omap2/irq.c index 5ea7def91ab0..39afbd5c1cfe 100644 --- a/arch/arm/mach-omap2/irq.c +++ b/arch/arm/mach-omap2/irq.c @@ -42,8 +42,6 @@ #define INTC_MIR_SET0 0x008c #define INTC_PENDING_IRQ0 0x0098 #define INTC_ILR0 0x0100 -/* Number of IRQ state bits in each MIR register */ -#define IRQ_BITS_PER_REG 32 #define OMAP2_IRQ_BASE OMAP2_L4_IO_ADDRESS(OMAP24XX_IC_BASE) #define OMAP3_IRQ_BASE OMAP2_L4_IO_ADDRESS(OMAP34XX_IC_BASE) -- cgit v1.2.1 From 1198365625bf22294263267df3360cb2a4c76f2d Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Mon, 8 Sep 2014 17:54:37 -0700 Subject: arm: omap: irq: switch over to intc_readl on omap_intc_handle_irq an almost blind conversion from readl_relaxed to our newly introduced intc_readl(). While at that, also remove some hardcoded register addresses. Signed-off-by: Felipe Balbi Signed-off-by: Tony Lindgren --- arch/arm/mach-omap2/irq.c | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/arch/arm/mach-omap2/irq.c b/arch/arm/mach-omap2/irq.c index 39afbd5c1cfe..e1fc4f7c3eb5 100644 --- a/arch/arm/mach-omap2/irq.c +++ b/arch/arm/mach-omap2/irq.c @@ -41,11 +41,13 @@ #define INTC_MIR_CLEAR0 0x0088 #define INTC_MIR_SET0 0x008c #define INTC_PENDING_IRQ0 0x0098 +#define INTC_PENDING_IRQ1 0x00b8 +#define INTC_PENDING_IRQ2 0x00d8 +#define INTC_PENDING_IRQ3 0x00f8 #define INTC_ILR0 0x0100 #define OMAP2_IRQ_BASE OMAP2_L4_IO_ADDRESS(OMAP24XX_IC_BASE) #define OMAP3_IRQ_BASE OMAP2_L4_IO_ADDRESS(OMAP34XX_IC_BASE) -#define INTCPS_SIR_IRQ_OFFSET 0x0040 /* omap2/3 active interrupt offset */ #define ACTIVEIRQ_MASK 0x7f /* omap2/3 active interrupt bits */ #define INTCPS_NR_ILR_REGS 128 #define INTCPS_NR_MIR_REGS 3 @@ -192,26 +194,26 @@ static inline void omap_intc_handle_irq(void __iomem *base_addr, struct pt_regs int handled_irq = 0; do { - irqnr = readl_relaxed(base_addr + 0x98); + irqnr = intc_readl(INTC_PENDING_IRQ0); if (irqnr) goto out; - irqnr = readl_relaxed(base_addr + 0xb8); + irqnr = intc_readl(INTC_PENDING_IRQ1); if (irqnr) goto out; - irqnr = readl_relaxed(base_addr + 0xd8); + irqnr = intc_readl(INTC_PENDING_IRQ2); #if IS_ENABLED(CONFIG_SOC_TI81XX) || IS_ENABLED(CONFIG_SOC_AM33XX) if (irqnr) goto out; - irqnr = readl_relaxed(base_addr + 0xf8); + irqnr = intc_readl(INTC_PENDING_IRQ3); #endif out: if (!irqnr) break; - irqnr = readl_relaxed(base_addr + INTCPS_SIR_IRQ_OFFSET); + irqnr = intc_readl(INTC_SIR); irqnr &= ACTIVEIRQ_MASK; if (irqnr) { -- cgit v1.2.1 From d1e66d69616db9325f397e004c54042622d48e83 Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Mon, 8 Sep 2014 17:54:37 -0700 Subject: arm: omap: irq: remove unnecessary base_addr argument omap_intc_handle_irq now had an unnecessary base_addr argument. Let's remove it and fix all callers. Signed-off-by: Felipe Balbi Signed-off-by: Tony Lindgren --- arch/arm/mach-omap2/irq.c | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/arch/arm/mach-omap2/irq.c b/arch/arm/mach-omap2/irq.c index e1fc4f7c3eb5..718b776db61e 100644 --- a/arch/arm/mach-omap2/irq.c +++ b/arch/arm/mach-omap2/irq.c @@ -46,8 +46,6 @@ #define INTC_PENDING_IRQ3 0x00f8 #define INTC_ILR0 0x0100 -#define OMAP2_IRQ_BASE OMAP2_L4_IO_ADDRESS(OMAP24XX_IC_BASE) -#define OMAP3_IRQ_BASE OMAP2_L4_IO_ADDRESS(OMAP34XX_IC_BASE) #define ACTIVEIRQ_MASK 0x7f /* omap2/3 active interrupt bits */ #define INTCPS_NR_ILR_REGS 128 #define INTCPS_NR_MIR_REGS 3 @@ -188,7 +186,7 @@ void __init ti81xx_init_irq(void) omap_init_irq(OMAP34XX_IC_BASE, 128, NULL); } -static inline void omap_intc_handle_irq(void __iomem *base_addr, struct pt_regs *regs) +static inline void omap_intc_handle_irq(struct pt_regs *regs) { u32 irqnr; int handled_irq = 0; @@ -232,8 +230,7 @@ out: asmlinkage void __exception_irq_entry omap2_intc_handle_irq(struct pt_regs *regs) { - void __iomem *base_addr = OMAP2_IRQ_BASE; - omap_intc_handle_irq(base_addr, regs); + omap_intc_handle_irq(regs); } int __init intc_of_init(struct device_node *node, @@ -334,7 +331,6 @@ void omap3_intc_resume_idle(void) asmlinkage void __exception_irq_entry omap3_intc_handle_irq(struct pt_regs *regs) { - void __iomem *base_addr = OMAP3_IRQ_BASE; - omap_intc_handle_irq(base_addr, regs); + omap_intc_handle_irq(regs); } #endif /* CONFIG_ARCH_OMAP3 */ -- cgit v1.2.1 From 272a8b04aba793347476ed768c5a2fe4fce046f9 Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Mon, 8 Sep 2014 17:54:38 -0700 Subject: arm: omap: irq: rename omap3_intc_regs just to make it clearer that it can be used on all omaps. Signed-off-by: Felipe Balbi Signed-off-by: Tony Lindgren --- arch/arm/mach-omap2/irq.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/arch/arm/mach-omap2/irq.c b/arch/arm/mach-omap2/irq.c index 718b776db61e..1b3000618e64 100644 --- a/arch/arm/mach-omap2/irq.c +++ b/arch/arm/mach-omap2/irq.c @@ -62,7 +62,7 @@ static void __iomem *omap_irq_base; static int omap_nr_irqs = 96; /* Structure to save interrupt controller context */ -struct omap3_intc_regs { +struct omap_intc_regs { u32 sysconfig; u32 protection; u32 idle; @@ -266,7 +266,7 @@ void __init omap_intc_of_init(void) } #if defined(CONFIG_ARCH_OMAP3) || defined(CONFIG_SOC_AM33XX) -static struct omap3_intc_regs intc_context; +static struct omap_intc_regs intc_context; void omap_intc_save_context(void) { -- cgit v1.2.1 From f8cc9eaf26dc026f134996a0cc6e1d1ce157ce9c Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Mon, 8 Sep 2014 17:54:40 -0700 Subject: arm: omap: irq: always define omap3 support remove ifdef around omap3 INTC support. This will make it easier to reuse code for PM. Signed-off-by: Felipe Balbi Signed-off-by: Tony Lindgren --- arch/arm/mach-omap2/irq.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/arch/arm/mach-omap2/irq.c b/arch/arm/mach-omap2/irq.c index 1b3000618e64..4e8705d3d8d8 100644 --- a/arch/arm/mach-omap2/irq.c +++ b/arch/arm/mach-omap2/irq.c @@ -265,7 +265,6 @@ void __init omap_intc_of_init(void) of_irq_init(irq_match); } -#if defined(CONFIG_ARCH_OMAP3) || defined(CONFIG_SOC_AM33XX) static struct omap_intc_regs intc_context; void omap_intc_save_context(void) @@ -333,4 +332,3 @@ asmlinkage void __exception_irq_entry omap3_intc_handle_irq(struct pt_regs *regs { omap_intc_handle_irq(regs); } -#endif /* CONFIG_ARCH_OMAP3 */ -- cgit v1.2.1 From 131b48c061726d4ac98f70a2beae35280a8de5cf Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Mon, 8 Sep 2014 17:54:42 -0700 Subject: arm: omap: irq: reorganize code a little bit no functional changes, just moving code around. Signed-off-by: Felipe Balbi Signed-off-by: Tony Lindgren --- arch/arm/mach-omap2/irq.c | 133 +++++++++++++++++++++++----------------------- 1 file changed, 66 insertions(+), 67 deletions(-) diff --git a/arch/arm/mach-omap2/irq.c b/arch/arm/mach-omap2/irq.c index 4e8705d3d8d8..480a9a7c8445 100644 --- a/arch/arm/mach-omap2/irq.c +++ b/arch/arm/mach-omap2/irq.c @@ -57,10 +57,6 @@ * for each bank.. when in doubt, consult the TRM. */ -static struct irq_domain *domain; -static void __iomem *omap_irq_base; -static int omap_nr_irqs = 96; - /* Structure to save interrupt controller context */ struct omap_intc_regs { u32 sysconfig; @@ -70,6 +66,11 @@ struct omap_intc_regs { u32 ilr[INTCPS_NR_ILR_REGS]; u32 mir[INTCPS_NR_MIR_REGS]; }; +static struct omap_intc_regs intc_context; + +static struct irq_domain *domain; +static void __iomem *omap_irq_base; +static int omap_nr_irqs = 96; /* INTC bank register get/set */ static void intc_writel(u32 reg, u32 val) @@ -82,6 +83,61 @@ static u32 intc_readl(u32 reg) return readl_relaxed(omap_irq_base + reg); } +void omap_intc_save_context(void) +{ + int i; + + intc_context.sysconfig = + intc_readl(INTC_SYSCONFIG); + intc_context.protection = + intc_readl(INTC_PROTECTION); + intc_context.idle = + intc_readl(INTC_IDLE); + intc_context.threshold = + intc_readl(INTC_THRESHOLD); + + for (i = 0; i < omap_nr_irqs; i++) + intc_context.ilr[i] = + intc_readl((INTC_ILR0 + 0x4 * i)); + for (i = 0; i < INTCPS_NR_MIR_REGS; i++) + intc_context.mir[i] = + intc_readl(INTC_MIR0 + (0x20 * i)); +} + +void omap_intc_restore_context(void) +{ + int i; + + intc_writel(INTC_SYSCONFIG, intc_context.sysconfig); + intc_writel(INTC_PROTECTION, intc_context.protection); + intc_writel(INTC_IDLE, intc_context.idle); + intc_writel(INTC_THRESHOLD, intc_context.threshold); + + for (i = 0; i < omap_nr_irqs; i++) + intc_writel(INTC_ILR0 + 0x4 * i, + intc_context.ilr[i]); + + for (i = 0; i < INTCPS_NR_MIR_REGS; i++) + intc_writel(INTC_MIR0 + 0x20 * i, + intc_context.mir[i]); + /* MIRs are saved and restore with other PRCM registers */ +} + +void omap3_intc_prepare_idle(void) +{ + /* + * Disable autoidle as it can stall interrupt controller, + * cf. errata ID i540 for 3430 (all revisions up to 3.1.x) + */ + intc_writel(INTC_SYSCONFIG, 0); +} + +void omap3_intc_resume_idle(void) +{ + /* Re-enable autoidle */ + intc_writel(INTC_SYSCONFIG, 1); +} + /* XXX: FIQ and additional INTC support (only MPU at the moment) */ static void omap_ack_irq(struct irq_data *d) { @@ -125,6 +181,12 @@ int omap_irq_pending(void) return 0; } +void omap3_intc_suspend(void) +{ + /* A pending interrupt would prevent OMAP from entering suspend */ + omap_ack_irq(NULL); +} + static __init void omap_alloc_gc(void __iomem *base, unsigned int irq_start, unsigned int num) { @@ -265,69 +327,6 @@ void __init omap_intc_of_init(void) of_irq_init(irq_match); } -static struct omap_intc_regs intc_context; - -void omap_intc_save_context(void) -{ - int i; - - intc_context.sysconfig = - intc_readl(INTC_SYSCONFIG); - intc_context.protection = - intc_readl(INTC_PROTECTION); - intc_context.idle = - intc_readl(INTC_IDLE); - intc_context.threshold = - intc_readl(INTC_THRESHOLD); - - for (i = 0; i < omap_nr_irqs; i++) - intc_context.ilr[i] = - intc_readl((INTC_ILR0 + 0x4 * i)); - for (i = 0; i < INTCPS_NR_MIR_REGS; i++) - intc_context.mir[i] = - intc_readl(INTC_MIR0 + (0x20 * i)); -} - -void omap_intc_restore_context(void) -{ - int i; - - intc_writel(INTC_SYSCONFIG, intc_context.sysconfig); - intc_writel(INTC_PROTECTION, intc_context.protection); - intc_writel(INTC_IDLE, intc_context.idle); - intc_writel(INTC_THRESHOLD, intc_context.threshold); - - for (i = 0; i < omap_nr_irqs; i++) - intc_writel(INTC_ILR0 + 0x4 * i, - intc_context.ilr[i]); - - for (i = 0; i < INTCPS_NR_MIR_REGS; i++) - intc_writel(INTC_MIR0 + 0x20 * i, - intc_context.mir[i]); - /* MIRs are saved and restore with other PRCM registers */ -} - -void omap3_intc_suspend(void) -{ - /* A pending interrupt would prevent OMAP from entering suspend */ - omap_ack_irq(NULL); -} - -void omap3_intc_prepare_idle(void) -{ - /* - * Disable autoidle as it can stall interrupt controller, - * cf. errata ID i540 for 3430 (all revisions up to 3.1.x) - */ - intc_writel(INTC_SYSCONFIG, 0); -} - -void omap3_intc_resume_idle(void) -{ - /* Re-enable autoidle */ - intc_writel(INTC_SYSCONFIG, 1); -} - asmlinkage void __exception_irq_entry omap3_intc_handle_irq(struct pt_regs *regs) { omap_intc_handle_irq(regs); -- cgit v1.2.1 From 00b6b031ab527afa2981bdffea7752279a290c26 Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Mon, 8 Sep 2014 17:54:43 -0700 Subject: arm: omap: irq: make intc_of_init static nobody uses that function outside of this file, so we don't need to expose it. Signed-off-by: Felipe Balbi Signed-off-by: Tony Lindgren --- arch/arm/mach-omap2/common.h | 10 ---------- arch/arm/mach-omap2/irq.c | 2 +- 2 files changed, 1 insertion(+), 11 deletions(-) diff --git a/arch/arm/mach-omap2/common.h b/arch/arm/mach-omap2/common.h index 98fe235f6670..34ab9ee9ee43 100644 --- a/arch/arm/mach-omap2/common.h +++ b/arch/arm/mach-omap2/common.h @@ -229,16 +229,6 @@ extern void __iomem *omap4_get_l2cache_base(void); #endif struct device_node; -#ifdef CONFIG_OF -int __init intc_of_init(struct device_node *node, - struct device_node *parent); -#else -int __init intc_of_init(struct device_node *node, - struct device_node *parent) -{ - return 0; -} -#endif #ifdef CONFIG_SMP extern void __iomem *omap4_get_scu_base(void); diff --git a/arch/arm/mach-omap2/irq.c b/arch/arm/mach-omap2/irq.c index 480a9a7c8445..fc886e2e0283 100644 --- a/arch/arm/mach-omap2/irq.c +++ b/arch/arm/mach-omap2/irq.c @@ -295,7 +295,7 @@ asmlinkage void __exception_irq_entry omap2_intc_handle_irq(struct pt_regs *regs omap_intc_handle_irq(regs); } -int __init intc_of_init(struct device_node *node, +static int __init intc_of_init(struct device_node *node, struct device_node *parent) { struct resource res; -- cgit v1.2.1 From b15c76b74896f1a2c60fff13fdf20d07468de323 Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Mon, 8 Sep 2014 17:54:43 -0700 Subject: arm: omap: irq: call set_handle_irq() from intc_of_init this will let us drop .handle_irq and .init_irq fields from our generic machine_descs. Signed-off-by: Felipe Balbi Signed-off-by: Tony Lindgren --- arch/arm/mach-omap2/irq.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/arch/arm/mach-omap2/irq.c b/arch/arm/mach-omap2/irq.c index fc886e2e0283..ee9f1e810700 100644 --- a/arch/arm/mach-omap2/irq.c +++ b/arch/arm/mach-omap2/irq.c @@ -314,6 +314,8 @@ static int __init intc_of_init(struct device_node *node, omap_init_irq(res.start, nr_irq, of_node_get(node)); + set_handle_irq(omap2_intc_handle_irq); + return 0; } -- cgit v1.2.1 From b65ecd46125929941076e7af77b5e685a332c33c Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Mon, 8 Sep 2014 17:54:43 -0700 Subject: arm: omap: irq: use IRQCHIP_DECLARE macro IRQCHIP_DECLARE macro is used to declare the same of_device_id structure for irqchips, it's just a helper. No functional changes. Note that we're temporarily including irqchip.h with its full path, until we move this driver to drivers/irqchip/. Signed-off-by: Felipe Balbi Signed-off-by: Tony Lindgren --- arch/arm/mach-omap2/irq.c | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/arch/arm/mach-omap2/irq.c b/arch/arm/mach-omap2/irq.c index ee9f1e810700..280124371c07 100644 --- a/arch/arm/mach-omap2/irq.c +++ b/arch/arm/mach-omap2/irq.c @@ -26,6 +26,7 @@ #include "soc.h" #include "iomap.h" #include "common.h" +#include "../../drivers/irqchip/irqchip.h" /* selected INTC register offsets */ @@ -319,14 +320,11 @@ static int __init intc_of_init(struct device_node *node, return 0; } -static const struct of_device_id irq_match[] __initconst = { - { .compatible = "ti,omap2-intc", .data = intc_of_init, }, - { } -}; +IRQCHIP_DECLARE(omap_intc, "ti,omap2-intc", intc_of_init); void __init omap_intc_of_init(void) { - of_irq_init(irq_match); + of_irq_init(&irqchip_of_match_omap_intc); } asmlinkage void __exception_irq_entry omap3_intc_handle_irq(struct pt_regs *regs) -- cgit v1.2.1 From e66c49b515aa4cbd621a4844f980ff487327aa7d Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Mon, 8 Sep 2014 17:54:45 -0700 Subject: arm: omap: irq: drop .handle_irq and .init_irq fields now we can safely drop those fields from our machine_desc. While at that, also drop the now unused omap_intc_of_init() definition. Signed-off-by: Felipe Balbi Signed-off-by: Tony Lindgren --- arch/arm/mach-omap2/board-generic.c | 14 -------------- arch/arm/mach-omap2/common.h | 1 - arch/arm/mach-omap2/irq.c | 5 ----- 3 files changed, 20 deletions(-) diff --git a/arch/arm/mach-omap2/board-generic.c b/arch/arm/mach-omap2/board-generic.c index 0b311d51425f..495305903401 100644 --- a/arch/arm/mach-omap2/board-generic.c +++ b/arch/arm/mach-omap2/board-generic.c @@ -52,8 +52,6 @@ DT_MACHINE_START(OMAP242X_DT, "Generic OMAP2420 (Flattened Device Tree)") .reserve = omap_reserve, .map_io = omap242x_map_io, .init_early = omap2420_init_early, - .init_irq = omap_intc_of_init, - .handle_irq = omap2_intc_handle_irq, .init_machine = omap_generic_init, .init_time = omap2_sync32k_timer_init, .dt_compat = omap242x_boards_compat, @@ -71,8 +69,6 @@ DT_MACHINE_START(OMAP243X_DT, "Generic OMAP2430 (Flattened Device Tree)") .reserve = omap_reserve, .map_io = omap243x_map_io, .init_early = omap2430_init_early, - .init_irq = omap_intc_of_init, - .handle_irq = omap2_intc_handle_irq, .init_machine = omap_generic_init, .init_time = omap2_sync32k_timer_init, .dt_compat = omap243x_boards_compat, @@ -91,8 +87,6 @@ DT_MACHINE_START(OMAP3_DT, "Generic OMAP3 (Flattened Device Tree)") .reserve = omap_reserve, .map_io = omap3_map_io, .init_early = omap3430_init_early, - .init_irq = omap_intc_of_init, - .handle_irq = omap3_intc_handle_irq, .init_machine = omap_generic_init, .init_late = omap3_init_late, .init_time = omap3_sync32k_timer_init, @@ -109,8 +103,6 @@ DT_MACHINE_START(OMAP36XX_DT, "Generic OMAP36xx (Flattened Device Tree)") .reserve = omap_reserve, .map_io = omap3_map_io, .init_early = omap3630_init_early, - .init_irq = omap_intc_of_init, - .handle_irq = omap3_intc_handle_irq, .init_machine = omap_generic_init, .init_late = omap3_init_late, .init_time = omap3_sync32k_timer_init, @@ -128,8 +120,6 @@ DT_MACHINE_START(OMAP3_GP_DT, "Generic OMAP3-GP (Flattened Device Tree)") .reserve = omap_reserve, .map_io = omap3_map_io, .init_early = omap3430_init_early, - .init_irq = omap_intc_of_init, - .handle_irq = omap3_intc_handle_irq, .init_machine = omap_generic_init, .init_late = omap3_init_late, .init_time = omap3_secure_sync32k_timer_init, @@ -146,8 +136,6 @@ DT_MACHINE_START(AM3517_DT, "Generic AM3517 (Flattened Device Tree)") .reserve = omap_reserve, .map_io = omap3_map_io, .init_early = am35xx_init_early, - .init_irq = omap_intc_of_init, - .handle_irq = omap3_intc_handle_irq, .init_machine = omap_generic_init, .init_late = omap3_init_late, .init_time = omap3_gptimer_timer_init, @@ -166,8 +154,6 @@ DT_MACHINE_START(AM33XX_DT, "Generic AM33XX (Flattened Device Tree)") .reserve = omap_reserve, .map_io = am33xx_map_io, .init_early = am33xx_init_early, - .init_irq = omap_intc_of_init, - .handle_irq = omap3_intc_handle_irq, .init_machine = omap_generic_init, .init_late = am33xx_init_late, .init_time = omap3_gptimer_timer_init, diff --git a/arch/arm/mach-omap2/common.h b/arch/arm/mach-omap2/common.h index 34ab9ee9ee43..ed906bcca133 100644 --- a/arch/arm/mach-omap2/common.h +++ b/arch/arm/mach-omap2/common.h @@ -221,7 +221,6 @@ void omap3_intc_prepare_idle(void); void omap3_intc_resume_idle(void); void omap2_intc_handle_irq(struct pt_regs *regs); void omap3_intc_handle_irq(struct pt_regs *regs); -void omap_intc_of_init(void); void omap_gic_of_init(void); #ifdef CONFIG_CACHE_L2X0 diff --git a/arch/arm/mach-omap2/irq.c b/arch/arm/mach-omap2/irq.c index 280124371c07..465d7257056a 100644 --- a/arch/arm/mach-omap2/irq.c +++ b/arch/arm/mach-omap2/irq.c @@ -322,11 +322,6 @@ static int __init intc_of_init(struct device_node *node, IRQCHIP_DECLARE(omap_intc, "ti,omap2-intc", intc_of_init); -void __init omap_intc_of_init(void) -{ - of_irq_init(&irqchip_of_match_omap_intc); -} - asmlinkage void __exception_irq_entry omap3_intc_handle_irq(struct pt_regs *regs) { omap_intc_handle_irq(regs); -- cgit v1.2.1 From a35db9a4cb800f343cd4bbfeeb6568807a16aad8 Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Mon, 8 Sep 2014 17:54:46 -0700 Subject: arm: omap: irq: add specific compatibles for omap3 and am33xx devices with this, we can use a compatible flag to figure out how many irq lines are wired up, no need for our TI-specific ti,intc-size binding. Signed-off-by: Felipe Balbi Signed-off-by: Tony Lindgren --- arch/arm/mach-omap2/irq.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/arch/arm/mach-omap2/irq.c b/arch/arm/mach-omap2/irq.c index 465d7257056a..97845df61030 100644 --- a/arch/arm/mach-omap2/irq.c +++ b/arch/arm/mach-omap2/irq.c @@ -320,7 +320,9 @@ static int __init intc_of_init(struct device_node *node, return 0; } -IRQCHIP_DECLARE(omap_intc, "ti,omap2-intc", intc_of_init); +IRQCHIP_DECLARE(omap2_intc, "ti,omap2-intc", intc_of_init); +IRQCHIP_DECLARE(omap3_intc, "ti,omap3-intc", intc_of_init); +IRQCHIP_DECLARE(am33xx_intc, "ti,am33xx-intc", intc_of_init); asmlinkage void __exception_irq_entry omap3_intc_handle_irq(struct pt_regs *regs) { -- cgit v1.2.1 From 470f30deaeb870dcc65d2357fbb675deb3e779de Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Mon, 8 Sep 2014 17:54:47 -0700 Subject: arm: omap: irq: use compatible flag to figure out number of IRQ lines so far, only am33xx has 128 lines, all other devices have only 96. Signed-off-by: Felipe Balbi Signed-off-by: Tony Lindgren --- arch/arm/mach-omap2/irq.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/arch/arm/mach-omap2/irq.c b/arch/arm/mach-omap2/irq.c index 97845df61030..7a4ead3684d9 100644 --- a/arch/arm/mach-omap2/irq.c +++ b/arch/arm/mach-omap2/irq.c @@ -310,6 +310,9 @@ static int __init intc_of_init(struct device_node *node, return -EINVAL; } + if (of_device_is_compatible(node, "ti,am33xx-intc")) + nr_irq = 128; + if (of_property_read_u32(node, "ti,intc-size", &nr_irq)) pr_warn("unable to get intc-size, default to %d\n", nr_irq); -- cgit v1.2.1 From cab82b76f30cecfc09eeeb8a7b1bb78de2e0dadc Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Mon, 8 Sep 2014 17:54:48 -0700 Subject: arm: boot: dts: am33xx/omap3: fix intc compatible flag that way, our intc driver can figure out how many IRQ lines INTC has. Signed-off-by: Felipe Balbi Signed-off-by: Tony Lindgren --- arch/arm/boot/dts/am33xx.dtsi | 2 +- arch/arm/boot/dts/omap3.dtsi | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/arch/arm/boot/dts/am33xx.dtsi b/arch/arm/boot/dts/am33xx.dtsi index 3a0a161342ba..2fbafb835072 100644 --- a/arch/arm/boot/dts/am33xx.dtsi +++ b/arch/arm/boot/dts/am33xx.dtsi @@ -133,7 +133,7 @@ }; intc: interrupt-controller@48200000 { - compatible = "ti,omap2-intc"; + compatible = "ti,am33xx-intc"; interrupt-controller; #interrupt-cells = <1>; ti,intc-size = <128>; diff --git a/arch/arm/boot/dts/omap3.dtsi b/arch/arm/boot/dts/omap3.dtsi index 3136ed1a04ba..baab669efc36 100644 --- a/arch/arm/boot/dts/omap3.dtsi +++ b/arch/arm/boot/dts/omap3.dtsi @@ -141,7 +141,7 @@ }; intc: interrupt-controller@48200000 { - compatible = "ti,omap2-intc"; + compatible = "ti,omap3-intc"; interrupt-controller; #interrupt-cells = <1>; ti,intc-size = <96>; -- cgit v1.2.1 From a05d92b0940d4b96c44ed42402acc242c16fa9b5 Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Mon, 8 Sep 2014 17:54:48 -0700 Subject: arm: omap: irq: drop ti,intc-size support we don't need that anymore since specific devices are passing correct compatible flags. Signed-off-by: Felipe Balbi Signed-off-by: Tony Lindgren --- arch/arm/mach-omap2/irq.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/arch/arm/mach-omap2/irq.c b/arch/arm/mach-omap2/irq.c index 7a4ead3684d9..e70c26edc7fb 100644 --- a/arch/arm/mach-omap2/irq.c +++ b/arch/arm/mach-omap2/irq.c @@ -313,9 +313,6 @@ static int __init intc_of_init(struct device_node *node, if (of_device_is_compatible(node, "ti,am33xx-intc")) nr_irq = 128; - if (of_property_read_u32(node, "ti,intc-size", &nr_irq)) - pr_warn("unable to get intc-size, default to %d\n", nr_irq); - omap_init_irq(res.start, nr_irq, of_node_get(node)); set_handle_irq(omap2_intc_handle_irq); -- cgit v1.2.1 From c2fb3b33f279a6f798b5bbb7fde7cfcdf9401039 Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Mon, 8 Sep 2014 17:54:49 -0700 Subject: arm: boot: dts: omap2/3/am33xx: drop ti,intc-size we are now infering number of IRQ lines based on correct compatible flag, which renders this binding completely useless. Signed-off-by: Felipe Balbi Signed-off-by: Tony Lindgren --- arch/arm/boot/dts/am33xx.dtsi | 1 - arch/arm/boot/dts/omap2.dtsi | 1 - arch/arm/boot/dts/omap3.dtsi | 1 - 3 files changed, 3 deletions(-) diff --git a/arch/arm/boot/dts/am33xx.dtsi b/arch/arm/boot/dts/am33xx.dtsi index 2fbafb835072..1336984745e2 100644 --- a/arch/arm/boot/dts/am33xx.dtsi +++ b/arch/arm/boot/dts/am33xx.dtsi @@ -136,7 +136,6 @@ compatible = "ti,am33xx-intc"; interrupt-controller; #interrupt-cells = <1>; - ti,intc-size = <128>; reg = <0x48200000 0x1000>; }; diff --git a/arch/arm/boot/dts/omap2.dtsi b/arch/arm/boot/dts/omap2.dtsi index 8f8c07da4ac1..59d1c297bb30 100644 --- a/arch/arm/boot/dts/omap2.dtsi +++ b/arch/arm/boot/dts/omap2.dtsi @@ -75,7 +75,6 @@ compatible = "ti,omap2-intc"; interrupt-controller; #interrupt-cells = <1>; - ti,intc-size = <96>; reg = <0x480FE000 0x1000>; }; diff --git a/arch/arm/boot/dts/omap3.dtsi b/arch/arm/boot/dts/omap3.dtsi index baab669efc36..4bea9a851648 100644 --- a/arch/arm/boot/dts/omap3.dtsi +++ b/arch/arm/boot/dts/omap3.dtsi @@ -144,7 +144,6 @@ compatible = "ti,omap3-intc"; interrupt-controller; #interrupt-cells = <1>; - ti,intc-size = <96>; reg = <0x48200000 0x1000>; }; -- cgit v1.2.1 From a4d3c5d91fd109dd852351193781352f0512d0c3 Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Mon, 8 Sep 2014 17:54:51 -0700 Subject: arm: omap: irq: move some more code around We want .init_irq to call set_irq_handle() for legacy platforms. Note that this code will also be dropped once omap2/3 devices are completely moved to DT. Signed-off-by: Felipe Balbi Signed-off-by: Tony Lindgren --- arch/arm/mach-omap2/irq.c | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/arch/arm/mach-omap2/irq.c b/arch/arm/mach-omap2/irq.c index e70c26edc7fb..587def4ef0d7 100644 --- a/arch/arm/mach-omap2/irq.c +++ b/arch/arm/mach-omap2/irq.c @@ -234,21 +234,6 @@ static void __init omap_init_irq(u32 base, int nr_irqs, omap_alloc_gc(omap_irq_base + j, j + irq_base, 32); } -void __init omap2_init_irq(void) -{ - omap_init_irq(OMAP24XX_IC_BASE, 96, NULL); -} - -void __init omap3_init_irq(void) -{ - omap_init_irq(OMAP34XX_IC_BASE, 96, NULL); -} - -void __init ti81xx_init_irq(void) -{ - omap_init_irq(OMAP34XX_IC_BASE, 128, NULL); -} - static inline void omap_intc_handle_irq(struct pt_regs *regs) { u32 irqnr; @@ -296,6 +281,21 @@ asmlinkage void __exception_irq_entry omap2_intc_handle_irq(struct pt_regs *regs omap_intc_handle_irq(regs); } +void __init omap2_init_irq(void) +{ + omap_init_irq(OMAP24XX_IC_BASE, 96, NULL); +} + +void __init omap3_init_irq(void) +{ + omap_init_irq(OMAP34XX_IC_BASE, 96, NULL); +} + +void __init ti81xx_init_irq(void) +{ + omap_init_irq(OMAP34XX_IC_BASE, 128, NULL); +} + static int __init intc_of_init(struct device_node *node, struct device_node *parent) { -- cgit v1.2.1 From be0a768596a204af6f124bffc2588457c18375fd Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Mon, 8 Sep 2014 17:54:52 -0700 Subject: arm: omap: irq: call set_handle_irq() from .init_irq the idea is that board-files won't need to set .handle_irq on their machine_descs, which lets us drop a little more pointless code. Signed-off-by: Felipe Balbi Signed-off-by: Tony Lindgren --- arch/arm/mach-omap2/irq.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/arch/arm/mach-omap2/irq.c b/arch/arm/mach-omap2/irq.c index 587def4ef0d7..994009ce6001 100644 --- a/arch/arm/mach-omap2/irq.c +++ b/arch/arm/mach-omap2/irq.c @@ -284,16 +284,19 @@ asmlinkage void __exception_irq_entry omap2_intc_handle_irq(struct pt_regs *regs void __init omap2_init_irq(void) { omap_init_irq(OMAP24XX_IC_BASE, 96, NULL); + set_handle_irq(omap2_intc_handle_irq); } void __init omap3_init_irq(void) { omap_init_irq(OMAP34XX_IC_BASE, 96, NULL); + set_handle_irq(omap2_intc_handle_irq); } void __init ti81xx_init_irq(void) { omap_init_irq(OMAP34XX_IC_BASE, 128, NULL); + set_handle_irq(omap2_intc_handle_irq); } static int __init intc_of_init(struct device_node *node, -- cgit v1.2.1 From 05f1e7387c21b7075bed6ae7e7412435a7002fe4 Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Mon, 8 Sep 2014 17:54:52 -0700 Subject: arm: omap: irq: drop omap3_intc_handle_irq() now that we're calling set_handle_irq() from init_irq(), we can safely drop all callers to omap3_intc_handle_irq() and its definition. Signed-off-by: Felipe Balbi Signed-off-by: Tony Lindgren --- arch/arm/mach-omap2/board-3430sdp.c | 1 - arch/arm/mach-omap2/board-am3517crane.c | 1 - arch/arm/mach-omap2/board-am3517evm.c | 1 - arch/arm/mach-omap2/board-cm-t35.c | 2 -- arch/arm/mach-omap2/board-cm-t3517.c | 1 - arch/arm/mach-omap2/board-devkit8000.c | 1 - arch/arm/mach-omap2/board-ldp.c | 1 - arch/arm/mach-omap2/board-omap3beagle.c | 1 - arch/arm/mach-omap2/board-omap3logic.c | 2 -- arch/arm/mach-omap2/board-omap3pandora.c | 1 - arch/arm/mach-omap2/board-omap3stalker.c | 1 - arch/arm/mach-omap2/board-omap3touchbook.c | 1 - arch/arm/mach-omap2/board-overo.c | 1 - arch/arm/mach-omap2/board-rx51.c | 1 - arch/arm/mach-omap2/common.h | 1 - arch/arm/mach-omap2/irq.c | 5 ----- 16 files changed, 22 deletions(-) diff --git a/arch/arm/mach-omap2/board-3430sdp.c b/arch/arm/mach-omap2/board-3430sdp.c index d95d0ef1354a..d21a3048d06b 100644 --- a/arch/arm/mach-omap2/board-3430sdp.c +++ b/arch/arm/mach-omap2/board-3430sdp.c @@ -625,7 +625,6 @@ MACHINE_START(OMAP_3430SDP, "OMAP3430 3430SDP board") .map_io = omap3_map_io, .init_early = omap3430_init_early, .init_irq = omap3_init_irq, - .handle_irq = omap3_intc_handle_irq, .init_machine = omap_3430sdp_init, .init_late = omap3430_init_late, .init_time = omap3_sync32k_timer_init, diff --git a/arch/arm/mach-omap2/board-am3517crane.c b/arch/arm/mach-omap2/board-am3517crane.c index 0d499a1878f6..212c3160de18 100644 --- a/arch/arm/mach-omap2/board-am3517crane.c +++ b/arch/arm/mach-omap2/board-am3517crane.c @@ -142,7 +142,6 @@ MACHINE_START(CRANEBOARD, "AM3517/05 CRANEBOARD") .map_io = omap3_map_io, .init_early = am35xx_init_early, .init_irq = omap3_init_irq, - .handle_irq = omap3_intc_handle_irq, .init_machine = am3517_crane_init, .init_late = am35xx_init_late, .init_time = omap3_sync32k_timer_init, diff --git a/arch/arm/mach-omap2/board-am3517evm.c b/arch/arm/mach-omap2/board-am3517evm.c index 4f9383cecf76..1c091b3fa312 100644 --- a/arch/arm/mach-omap2/board-am3517evm.c +++ b/arch/arm/mach-omap2/board-am3517evm.c @@ -366,7 +366,6 @@ MACHINE_START(OMAP3517EVM, "OMAP3517/AM3517 EVM") .map_io = omap3_map_io, .init_early = am35xx_init_early, .init_irq = omap3_init_irq, - .handle_irq = omap3_intc_handle_irq, .init_machine = am3517_evm_init, .init_late = am35xx_init_late, .init_time = omap3_sync32k_timer_init, diff --git a/arch/arm/mach-omap2/board-cm-t35.c b/arch/arm/mach-omap2/board-cm-t35.c index 018353d88b96..c6df8eec4553 100644 --- a/arch/arm/mach-omap2/board-cm-t35.c +++ b/arch/arm/mach-omap2/board-cm-t35.c @@ -766,7 +766,6 @@ MACHINE_START(CM_T35, "Compulab CM-T35") .map_io = omap3_map_io, .init_early = omap35xx_init_early, .init_irq = omap3_init_irq, - .handle_irq = omap3_intc_handle_irq, .init_machine = cm_t35_init, .init_late = omap35xx_init_late, .init_time = omap3_sync32k_timer_init, @@ -779,7 +778,6 @@ MACHINE_START(CM_T3730, "Compulab CM-T3730") .map_io = omap3_map_io, .init_early = omap3630_init_early, .init_irq = omap3_init_irq, - .handle_irq = omap3_intc_handle_irq, .init_machine = cm_t3730_init, .init_late = omap3630_init_late, .init_time = omap3_sync32k_timer_init, diff --git a/arch/arm/mach-omap2/board-cm-t3517.c b/arch/arm/mach-omap2/board-cm-t3517.c index 4eb5e6f2f7f5..8a2c1677964c 100644 --- a/arch/arm/mach-omap2/board-cm-t3517.c +++ b/arch/arm/mach-omap2/board-cm-t3517.c @@ -329,7 +329,6 @@ MACHINE_START(CM_T3517, "Compulab CM-T3517") .map_io = omap3_map_io, .init_early = am35xx_init_early, .init_irq = omap3_init_irq, - .handle_irq = omap3_intc_handle_irq, .init_machine = cm_t3517_init, .init_late = am35xx_init_late, .init_time = omap3_gptimer_timer_init, diff --git a/arch/arm/mach-omap2/board-devkit8000.c b/arch/arm/mach-omap2/board-devkit8000.c index cdc4fb9960a9..d8e4f346936a 100644 --- a/arch/arm/mach-omap2/board-devkit8000.c +++ b/arch/arm/mach-omap2/board-devkit8000.c @@ -647,7 +647,6 @@ MACHINE_START(DEVKIT8000, "OMAP3 Devkit8000") .map_io = omap3_map_io, .init_early = omap35xx_init_early, .init_irq = omap3_init_irq, - .handle_irq = omap3_intc_handle_irq, .init_machine = devkit8000_init, .init_late = omap35xx_init_late, .init_time = omap3_secure_sync32k_timer_init, diff --git a/arch/arm/mach-omap2/board-ldp.c b/arch/arm/mach-omap2/board-ldp.c index 44a59c3abfb0..c2975af4cd5d 100644 --- a/arch/arm/mach-omap2/board-ldp.c +++ b/arch/arm/mach-omap2/board-ldp.c @@ -422,7 +422,6 @@ MACHINE_START(OMAP_LDP, "OMAP LDP board") .map_io = omap3_map_io, .init_early = omap3430_init_early, .init_irq = omap3_init_irq, - .handle_irq = omap3_intc_handle_irq, .init_machine = omap_ldp_init, .init_late = omap3430_init_late, .init_time = omap3_sync32k_timer_init, diff --git a/arch/arm/mach-omap2/board-omap3beagle.c b/arch/arm/mach-omap2/board-omap3beagle.c index e2e52031f056..81de1c68b360 100644 --- a/arch/arm/mach-omap2/board-omap3beagle.c +++ b/arch/arm/mach-omap2/board-omap3beagle.c @@ -588,7 +588,6 @@ MACHINE_START(OMAP3_BEAGLE, "OMAP3 Beagle Board") .map_io = omap3_map_io, .init_early = omap3_init_early, .init_irq = omap3_init_irq, - .handle_irq = omap3_intc_handle_irq, .init_machine = omap3_beagle_init, .init_late = omap3_init_late, .init_time = omap3_secure_sync32k_timer_init, diff --git a/arch/arm/mach-omap2/board-omap3logic.c b/arch/arm/mach-omap2/board-omap3logic.c index bab51e64c4b5..6049f60a8813 100644 --- a/arch/arm/mach-omap2/board-omap3logic.c +++ b/arch/arm/mach-omap2/board-omap3logic.c @@ -230,7 +230,6 @@ MACHINE_START(OMAP3_TORPEDO, "Logic OMAP3 Torpedo board") .map_io = omap3_map_io, .init_early = omap35xx_init_early, .init_irq = omap3_init_irq, - .handle_irq = omap3_intc_handle_irq, .init_machine = omap3logic_init, .init_late = omap35xx_init_late, .init_time = omap3_sync32k_timer_init, @@ -243,7 +242,6 @@ MACHINE_START(OMAP3530_LV_SOM, "OMAP Logic 3530 LV SOM board") .map_io = omap3_map_io, .init_early = omap35xx_init_early, .init_irq = omap3_init_irq, - .handle_irq = omap3_intc_handle_irq, .init_machine = omap3logic_init, .init_late = omap35xx_init_late, .init_time = omap3_sync32k_timer_init, diff --git a/arch/arm/mach-omap2/board-omap3pandora.c b/arch/arm/mach-omap2/board-omap3pandora.c index cf18340eb3bb..f32201656cf3 100644 --- a/arch/arm/mach-omap2/board-omap3pandora.c +++ b/arch/arm/mach-omap2/board-omap3pandora.c @@ -624,7 +624,6 @@ MACHINE_START(OMAP3_PANDORA, "Pandora Handheld Console") .map_io = omap3_map_io, .init_early = omap35xx_init_early, .init_irq = omap3_init_irq, - .handle_irq = omap3_intc_handle_irq, .init_machine = omap3pandora_init, .init_late = omap35xx_init_late, .init_time = omap3_sync32k_timer_init, diff --git a/arch/arm/mach-omap2/board-omap3stalker.c b/arch/arm/mach-omap2/board-omap3stalker.c index a2e035e0792a..6311f4b1ee44 100644 --- a/arch/arm/mach-omap2/board-omap3stalker.c +++ b/arch/arm/mach-omap2/board-omap3stalker.c @@ -426,7 +426,6 @@ MACHINE_START(SBC3530, "OMAP3 STALKER") .map_io = omap3_map_io, .init_early = omap35xx_init_early, .init_irq = omap3_init_irq, - .handle_irq = omap3_intc_handle_irq, .init_machine = omap3_stalker_init, .init_late = omap35xx_init_late, .init_time = omap3_secure_sync32k_timer_init, diff --git a/arch/arm/mach-omap2/board-omap3touchbook.c b/arch/arm/mach-omap2/board-omap3touchbook.c index 70b904c010c6..a01993e5500f 100644 --- a/arch/arm/mach-omap2/board-omap3touchbook.c +++ b/arch/arm/mach-omap2/board-omap3touchbook.c @@ -388,7 +388,6 @@ MACHINE_START(TOUCHBOOK, "OMAP3 touchbook Board") .map_io = omap3_map_io, .init_early = omap3430_init_early, .init_irq = omap3_init_irq, - .handle_irq = omap3_intc_handle_irq, .init_machine = omap3_touchbook_init, .init_late = omap3430_init_late, .init_time = omap3_secure_sync32k_timer_init, diff --git a/arch/arm/mach-omap2/board-overo.c b/arch/arm/mach-omap2/board-overo.c index f6d384111911..2dae6ccd39bb 100644 --- a/arch/arm/mach-omap2/board-overo.c +++ b/arch/arm/mach-omap2/board-overo.c @@ -564,7 +564,6 @@ MACHINE_START(OVERO, "Gumstix Overo") .map_io = omap3_map_io, .init_early = omap35xx_init_early, .init_irq = omap3_init_irq, - .handle_irq = omap3_intc_handle_irq, .init_machine = overo_init, .init_late = omap35xx_init_late, .init_time = omap3_sync32k_timer_init, diff --git a/arch/arm/mach-omap2/board-rx51.c b/arch/arm/mach-omap2/board-rx51.c index db168c9627a1..2d1e5a6beb85 100644 --- a/arch/arm/mach-omap2/board-rx51.c +++ b/arch/arm/mach-omap2/board-rx51.c @@ -134,7 +134,6 @@ MACHINE_START(NOKIA_RX51, "Nokia RX-51 board") .map_io = omap3_map_io, .init_early = omap3430_init_early, .init_irq = omap3_init_irq, - .handle_irq = omap3_intc_handle_irq, .init_machine = rx51_init, .init_late = omap3430_init_late, .init_time = omap3_sync32k_timer_init, diff --git a/arch/arm/mach-omap2/common.h b/arch/arm/mach-omap2/common.h index ed906bcca133..7d689d230e8c 100644 --- a/arch/arm/mach-omap2/common.h +++ b/arch/arm/mach-omap2/common.h @@ -220,7 +220,6 @@ void omap3_intc_suspend(void); void omap3_intc_prepare_idle(void); void omap3_intc_resume_idle(void); void omap2_intc_handle_irq(struct pt_regs *regs); -void omap3_intc_handle_irq(struct pt_regs *regs); void omap_gic_of_init(void); #ifdef CONFIG_CACHE_L2X0 diff --git a/arch/arm/mach-omap2/irq.c b/arch/arm/mach-omap2/irq.c index 994009ce6001..4dfeac6f2f65 100644 --- a/arch/arm/mach-omap2/irq.c +++ b/arch/arm/mach-omap2/irq.c @@ -326,8 +326,3 @@ static int __init intc_of_init(struct device_node *node, IRQCHIP_DECLARE(omap2_intc, "ti,omap2-intc", intc_of_init); IRQCHIP_DECLARE(omap3_intc, "ti,omap3-intc", intc_of_init); IRQCHIP_DECLARE(am33xx_intc, "ti,am33xx-intc", intc_of_init); - -asmlinkage void __exception_irq_entry omap3_intc_handle_irq(struct pt_regs *regs) -{ - omap_intc_handle_irq(regs); -} -- cgit v1.2.1 From 2aced8924638104d1e09ebb86f87f6ca265d325b Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Mon, 8 Sep 2014 17:54:52 -0700 Subject: arm: omap: irq: drop omap2_intc_handle_irq() that was just a no-op wrapper around omap_intc_handle_irq anyway. Signed-off-by: Felipe Balbi Signed-off-by: Tony Lindgren --- arch/arm/mach-omap2/common.h | 1 - arch/arm/mach-omap2/irq.c | 16 ++++++---------- 2 files changed, 6 insertions(+), 11 deletions(-) diff --git a/arch/arm/mach-omap2/common.h b/arch/arm/mach-omap2/common.h index 7d689d230e8c..180009343adb 100644 --- a/arch/arm/mach-omap2/common.h +++ b/arch/arm/mach-omap2/common.h @@ -219,7 +219,6 @@ void omap_intc_restore_context(void); void omap3_intc_suspend(void); void omap3_intc_prepare_idle(void); void omap3_intc_resume_idle(void); -void omap2_intc_handle_irq(struct pt_regs *regs); void omap_gic_of_init(void); #ifdef CONFIG_CACHE_L2X0 diff --git a/arch/arm/mach-omap2/irq.c b/arch/arm/mach-omap2/irq.c index 4dfeac6f2f65..14716a88d22d 100644 --- a/arch/arm/mach-omap2/irq.c +++ b/arch/arm/mach-omap2/irq.c @@ -234,7 +234,8 @@ static void __init omap_init_irq(u32 base, int nr_irqs, omap_alloc_gc(omap_irq_base + j, j + irq_base, 32); } -static inline void omap_intc_handle_irq(struct pt_regs *regs) +static asmlinkage void __exception_irq_entry +omap_intc_handle_irq(struct pt_regs *regs) { u32 irqnr; int handled_irq = 0; @@ -276,27 +277,22 @@ out: omap_ack_irq(NULL); } -asmlinkage void __exception_irq_entry omap2_intc_handle_irq(struct pt_regs *regs) -{ - omap_intc_handle_irq(regs); -} - void __init omap2_init_irq(void) { omap_init_irq(OMAP24XX_IC_BASE, 96, NULL); - set_handle_irq(omap2_intc_handle_irq); + set_handle_irq(omap_intc_handle_irq); } void __init omap3_init_irq(void) { omap_init_irq(OMAP34XX_IC_BASE, 96, NULL); - set_handle_irq(omap2_intc_handle_irq); + set_handle_irq(omap_intc_handle_irq); } void __init ti81xx_init_irq(void) { omap_init_irq(OMAP34XX_IC_BASE, 128, NULL); - set_handle_irq(omap2_intc_handle_irq); + set_handle_irq(omap_intc_handle_irq); } static int __init intc_of_init(struct device_node *node, @@ -318,7 +314,7 @@ static int __init intc_of_init(struct device_node *node, omap_init_irq(res.start, nr_irq, of_node_get(node)); - set_handle_irq(omap2_intc_handle_irq); + set_handle_irq(omap_intc_handle_irq); return 0; } -- cgit v1.2.1 From 3384f86fe5c1074fddabeeeed72e413eb28f0fcf Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Mon, 8 Sep 2014 17:54:54 -0700 Subject: arm: omap: irq: remove unnecessary header There's no need for that header to be included. Signed-off-by: Felipe Balbi Signed-off-by: Tony Lindgren --- arch/arm/mach-omap2/irq.c | 1 - 1 file changed, 1 deletion(-) diff --git a/arch/arm/mach-omap2/irq.c b/arch/arm/mach-omap2/irq.c index 14716a88d22d..a405b963c779 100644 --- a/arch/arm/mach-omap2/irq.c +++ b/arch/arm/mach-omap2/irq.c @@ -24,7 +24,6 @@ #include #include "soc.h" -#include "iomap.h" #include "common.h" #include "../../drivers/irqchip/irqchip.h" -- cgit v1.2.1 From a74f0a176e3e048df78816ec383b219f8ac6867e Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Mon, 8 Sep 2014 17:54:55 -0700 Subject: arm: omap: irq: remove nr_irqs argument we can set our global omap_nr_irqs early on and drop the extra argument to omap_init_irq(). Signed-off-by: Felipe Balbi Signed-off-by: Tony Lindgren --- arch/arm/mach-omap2/irq.c | 25 +++++++++++++------------ 1 file changed, 13 insertions(+), 12 deletions(-) diff --git a/arch/arm/mach-omap2/irq.c b/arch/arm/mach-omap2/irq.c index a405b963c779..44d9cf4e74de 100644 --- a/arch/arm/mach-omap2/irq.c +++ b/arch/arm/mach-omap2/irq.c @@ -207,8 +207,7 @@ omap_alloc_gc(void __iomem *base, unsigned int irq_start, unsigned int num) IRQ_NOREQUEST | IRQ_NOPROBE, 0); } -static void __init omap_init_irq(u32 base, int nr_irqs, - struct device_node *node) +static void __init omap_init_irq(u32 base, struct device_node *node) { int j, irq_base; @@ -216,15 +215,13 @@ static void __init omap_init_irq(u32 base, int nr_irqs, if (WARN_ON(!omap_irq_base)) return; - omap_nr_irqs = nr_irqs; - - irq_base = irq_alloc_descs(-1, 0, nr_irqs, 0); + irq_base = irq_alloc_descs(-1, 0, omap_nr_irqs, 0); if (irq_base < 0) { pr_warn("Couldn't allocate IRQ numbers\n"); irq_base = 0; } - domain = irq_domain_add_legacy(node, nr_irqs, irq_base, 0, + domain = irq_domain_add_legacy(node, omap_nr_irqs, irq_base, 0, &irq_domain_simple_ops, NULL); omap_irq_soft_reset(); @@ -278,19 +275,22 @@ out: void __init omap2_init_irq(void) { - omap_init_irq(OMAP24XX_IC_BASE, 96, NULL); + omap_nr_irqs = 96; + omap_init_irq(OMAP24XX_IC_BASE, NULL); set_handle_irq(omap_intc_handle_irq); } void __init omap3_init_irq(void) { - omap_init_irq(OMAP34XX_IC_BASE, 96, NULL); + omap_nr_irqs = 96; + omap_init_irq(OMAP34XX_IC_BASE, NULL); set_handle_irq(omap_intc_handle_irq); } void __init ti81xx_init_irq(void) { - omap_init_irq(OMAP34XX_IC_BASE, 128, NULL); + omap_nr_irqs = 96; + omap_init_irq(OMAP34XX_IC_BASE, NULL); set_handle_irq(omap_intc_handle_irq); } @@ -298,7 +298,8 @@ static int __init intc_of_init(struct device_node *node, struct device_node *parent) { struct resource res; - u32 nr_irq = 96; + + omap_nr_irqs = 96; if (WARN_ON(!node)) return -ENODEV; @@ -309,9 +310,9 @@ static int __init intc_of_init(struct device_node *node, } if (of_device_is_compatible(node, "ti,am33xx-intc")) - nr_irq = 128; + omap_nr_irqs = 128; - omap_init_irq(res.start, nr_irq, of_node_get(node)); + omap_init_irq(res.start, of_node_get(node)); set_handle_irq(omap_intc_handle_irq); -- cgit v1.2.1 From 52b1e1291334d79bb3d2fcaec15f7fc42eedbd83 Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Mon, 8 Sep 2014 17:54:57 -0700 Subject: arm: omap: irq: introduce omap_nr_pending that variable will tell us how many INTC_PENDING_IRQn registers we have. It'll be used on a following patch to cleanup omap_intc_handle_irq() a bit. Signed-off-by: Felipe Balbi Signed-off-by: Tony Lindgren --- arch/arm/mach-omap2/irq.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/arch/arm/mach-omap2/irq.c b/arch/arm/mach-omap2/irq.c index 44d9cf4e74de..583a1c7de855 100644 --- a/arch/arm/mach-omap2/irq.c +++ b/arch/arm/mach-omap2/irq.c @@ -70,6 +70,7 @@ static struct omap_intc_regs intc_context; static struct irq_domain *domain; static void __iomem *omap_irq_base; +static int omap_nr_pending = 3; static int omap_nr_irqs = 96; /* INTC bank register get/set */ @@ -276,6 +277,7 @@ out: void __init omap2_init_irq(void) { omap_nr_irqs = 96; + omap_nr_pending = 3; omap_init_irq(OMAP24XX_IC_BASE, NULL); set_handle_irq(omap_intc_handle_irq); } @@ -283,6 +285,7 @@ void __init omap2_init_irq(void) void __init omap3_init_irq(void) { omap_nr_irqs = 96; + omap_nr_pending = 3; omap_init_irq(OMAP34XX_IC_BASE, NULL); set_handle_irq(omap_intc_handle_irq); } @@ -290,6 +293,7 @@ void __init omap3_init_irq(void) void __init ti81xx_init_irq(void) { omap_nr_irqs = 96; + omap_nr_pending = 4; omap_init_irq(OMAP34XX_IC_BASE, NULL); set_handle_irq(omap_intc_handle_irq); } @@ -299,6 +303,7 @@ static int __init intc_of_init(struct device_node *node, { struct resource res; + omap_nr_pending = 3; omap_nr_irqs = 96; if (WARN_ON(!node)) @@ -309,8 +314,10 @@ static int __init intc_of_init(struct device_node *node, return -EINVAL; } - if (of_device_is_compatible(node, "ti,am33xx-intc")) + if (of_device_is_compatible(node, "ti,am33xx-intc")) { omap_nr_irqs = 128; + omap_nr_pending = 4; + } omap_init_irq(res.start, of_node_get(node)); -- cgit v1.2.1 From d6a7c5c84f5ddab54717914ad293ed9d99d644ff Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Mon, 8 Sep 2014 17:54:57 -0700 Subject: arm: omap: irq: get rid of ifdef hack we don't need the ifdef if we have omap_nr_pending telling us how many pending registers we have on current platform. This solves a possible problem where we could try to handle bogus interrupts on OMAP2 and OMAP3 if using single zImage kernel, because we would end up reading the following pending FIQ register. Signed-off-by: Felipe Balbi Signed-off-by: Tony Lindgren --- arch/arm/mach-omap2/irq.c | 22 +++++++--------------- 1 file changed, 7 insertions(+), 15 deletions(-) diff --git a/arch/arm/mach-omap2/irq.c b/arch/arm/mach-omap2/irq.c index 583a1c7de855..43785ee27840 100644 --- a/arch/arm/mach-omap2/irq.c +++ b/arch/arm/mach-omap2/irq.c @@ -234,24 +234,16 @@ static void __init omap_init_irq(u32 base, struct device_node *node) static asmlinkage void __exception_irq_entry omap_intc_handle_irq(struct pt_regs *regs) { - u32 irqnr; + u32 irqnr = 0; int handled_irq = 0; + int i; do { - irqnr = intc_readl(INTC_PENDING_IRQ0); - if (irqnr) - goto out; - - irqnr = intc_readl(INTC_PENDING_IRQ1); - if (irqnr) - goto out; - - irqnr = intc_readl(INTC_PENDING_IRQ2); -#if IS_ENABLED(CONFIG_SOC_TI81XX) || IS_ENABLED(CONFIG_SOC_AM33XX) - if (irqnr) - goto out; - irqnr = intc_readl(INTC_PENDING_IRQ3); -#endif + for (i = 0; i < omap_nr_pending; i++) { + irqnr = intc_readl(INTC_PENDING_IRQ0 + (0x20 * i)); + if (irqnr) + goto out; + } out: if (!irqnr) -- cgit v1.2.1 From 55601c9f24670ba926ebdd4d712ac3b177232330 Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Mon, 8 Sep 2014 17:54:58 -0700 Subject: arm: omap: intc: switch over to linear irq domain now that we don't need to support legacy board-files, we can completely switch over to a linear irq domain and make use of irq_alloc_domain_generic_chips() to allocate all generic irq chips for us. Signed-off-by: Felipe Balbi Signed-off-by: Tony Lindgren --- arch/arm/mach-omap2/irq.c | 88 ++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 79 insertions(+), 9 deletions(-) diff --git a/arch/arm/mach-omap2/irq.c b/arch/arm/mach-omap2/irq.c index 43785ee27840..b2993e45e84c 100644 --- a/arch/arm/mach-omap2/irq.c +++ b/arch/arm/mach-omap2/irq.c @@ -188,14 +188,51 @@ void omap3_intc_suspend(void) omap_ack_irq(NULL); } -static __init void -omap_alloc_gc(void __iomem *base, unsigned int irq_start, unsigned int num) +static int __init omap_alloc_gc_of(struct irq_domain *d, void __iomem *base) +{ + int ret; + int i; + + ret = irq_alloc_domain_generic_chips(d, 32, 1, "INTC", + handle_level_irq, IRQ_NOREQUEST | IRQ_NOPROBE, + IRQ_LEVEL, 0); + if (ret) { + pr_warn("Failed to allocate irq chips\n"); + return ret; + } + + for (i = 0; i < omap_nr_pending; i++) { + struct irq_chip_generic *gc; + struct irq_chip_type *ct; + + gc = irq_get_domain_generic_chip(d, 32 * i); + gc->reg_base = base; + ct = gc->chip_types; + + ct->type = IRQ_TYPE_LEVEL_MASK; + ct->handler = handle_level_irq; + + ct->chip.irq_ack = omap_mask_ack_irq; + ct->chip.irq_mask = irq_gc_mask_disable_reg; + ct->chip.irq_unmask = irq_gc_unmask_enable_reg; + + ct->chip.flags |= IRQCHIP_SKIP_SET_WAKE; + + ct->regs.enable = INTC_MIR_CLEAR0 + 32 * i; + ct->regs.disable = INTC_MIR_SET0 + 32 * i; + } + + return 0; +} + +static void __init omap_alloc_gc_legacy(void __iomem *base, + unsigned int irq_start, unsigned int num) { struct irq_chip_generic *gc; struct irq_chip_type *ct; gc = irq_alloc_generic_chip("INTC", 1, irq_start, base, - handle_level_irq); + handle_level_irq); ct = gc->chip_types; ct->chip.irq_ack = omap_mask_ack_irq; ct->chip.irq_mask = irq_gc_mask_disable_reg; @@ -205,16 +242,36 @@ omap_alloc_gc(void __iomem *base, unsigned int irq_start, unsigned int num) ct->regs.enable = INTC_MIR_CLEAR0; ct->regs.disable = INTC_MIR_SET0; irq_setup_generic_chip(gc, IRQ_MSK(num), IRQ_GC_INIT_MASK_CACHE, - IRQ_NOREQUEST | IRQ_NOPROBE, 0); + IRQ_NOREQUEST | IRQ_NOPROBE, 0); } -static void __init omap_init_irq(u32 base, struct device_node *node) +static int __init omap_init_irq_of(struct device_node *node) +{ + int ret; + + omap_irq_base = of_iomap(node, 0); + if (WARN_ON(!omap_irq_base)) + return -ENOMEM; + + domain = irq_domain_add_linear(node, omap_nr_irqs, + &irq_generic_chip_ops, NULL); + + omap_irq_soft_reset(); + + ret = omap_alloc_gc_of(domain, omap_irq_base); + if (ret < 0) + irq_domain_remove(domain); + + return ret; +} + +static int __init omap_init_irq_legacy(u32 base) { int j, irq_base; omap_irq_base = ioremap(base, SZ_4K); if (WARN_ON(!omap_irq_base)) - return; + return -ENOMEM; irq_base = irq_alloc_descs(-1, 0, omap_nr_irqs, 0); if (irq_base < 0) { @@ -222,13 +279,23 @@ static void __init omap_init_irq(u32 base, struct device_node *node) irq_base = 0; } - domain = irq_domain_add_legacy(node, omap_nr_irqs, irq_base, 0, + domain = irq_domain_add_legacy(NULL, omap_nr_irqs, irq_base, 0, &irq_domain_simple_ops, NULL); omap_irq_soft_reset(); for (j = 0; j < omap_nr_irqs; j += 32) - omap_alloc_gc(omap_irq_base + j, j + irq_base, 32); + omap_alloc_gc_legacy(omap_irq_base + j, j + irq_base, 32); + + return 0; +} + +static int __init omap_init_irq(u32 base, struct device_node *node) +{ + if (node) + return omap_init_irq_of(node); + else + return omap_init_irq_legacy(base); } static asmlinkage void __exception_irq_entry @@ -294,6 +361,7 @@ static int __init intc_of_init(struct device_node *node, struct device_node *parent) { struct resource res; + int ret; omap_nr_pending = 3; omap_nr_irqs = 96; @@ -311,7 +379,9 @@ static int __init intc_of_init(struct device_node *node, omap_nr_pending = 4; } - omap_init_irq(res.start, of_node_get(node)); + ret = omap_init_irq(-1, of_node_get(node)); + if (ret < 0) + return ret; set_handle_irq(omap_intc_handle_irq); -- cgit v1.2.1 From 7d80335e29701e9c2cc55ec0ea79afe6351aa20b Mon Sep 17 00:00:00 2001 From: Maxime Ripard Date: Mon, 15 Sep 2014 16:02:24 +0200 Subject: ARM: at91: Give the PIT irq as an argument of at91sam926x_pit_init This allows to remove the dependency of the timer driver on mach/hardware.h and having an hardcoded interrupt number in the driver itself. Signed-off-by: Maxime Ripard Signed-off-by: Nicolas Ferre --- arch/arm/mach-at91/at91sam9260.c | 2 +- arch/arm/mach-at91/at91sam9261.c | 2 +- arch/arm/mach-at91/at91sam9263.c | 2 +- arch/arm/mach-at91/at91sam926x_time.c | 6 ++---- arch/arm/mach-at91/at91sam9g45.c | 2 +- arch/arm/mach-at91/at91sam9rl.c | 2 +- arch/arm/mach-at91/generic.h | 2 +- 7 files changed, 8 insertions(+), 10 deletions(-) diff --git a/arch/arm/mach-at91/at91sam9260.c b/arch/arm/mach-at91/at91sam9260.c index b61a255d4141..aab1f969a7c3 100644 --- a/arch/arm/mach-at91/at91sam9260.c +++ b/arch/arm/mach-at91/at91sam9260.c @@ -442,7 +442,7 @@ static unsigned int at91sam9260_default_irq_priority[NR_AIC_IRQS] __initdata = { static void __init at91sam9260_init_time(void) { - at91sam926x_pit_init(); + at91sam926x_pit_init(NR_IRQS_LEGACY + AT91_ID_SYS); } AT91_SOC_START(at91sam9260) diff --git a/arch/arm/mach-at91/at91sam9261.c b/arch/arm/mach-at91/at91sam9261.c index badf20c2cd67..a8bd35963332 100644 --- a/arch/arm/mach-at91/at91sam9261.c +++ b/arch/arm/mach-at91/at91sam9261.c @@ -401,7 +401,7 @@ static unsigned int at91sam9261_default_irq_priority[NR_AIC_IRQS] __initdata = { static void __init at91sam9261_init_time(void) { - at91sam926x_pit_init(); + at91sam926x_pit_init(NR_IRQS_LEGACY + AT91_ID_SYS); } AT91_SOC_START(at91sam9261) diff --git a/arch/arm/mach-at91/at91sam9263.c b/arch/arm/mach-at91/at91sam9263.c index ebb413e7f653..fbff228cc63e 100644 --- a/arch/arm/mach-at91/at91sam9263.c +++ b/arch/arm/mach-at91/at91sam9263.c @@ -424,7 +424,7 @@ static unsigned int at91sam9263_default_irq_priority[NR_AIC_IRQS] __initdata = { static void __init at91sam9263_init_time(void) { - at91sam926x_pit_init(); + at91sam926x_pit_init(NR_IRQS_LEGACY + AT91_ID_SYS); } AT91_SOC_START(at91sam9263) diff --git a/arch/arm/mach-at91/at91sam926x_time.c b/arch/arm/mach-at91/at91sam926x_time.c index 9abb289dce72..d5289098b3df 100644 --- a/arch/arm/mach-at91/at91sam926x_time.c +++ b/arch/arm/mach-at91/at91sam926x_time.c @@ -22,8 +22,6 @@ #include #include -#include - #define AT91_PIT_MR 0x00 /* Mode Register */ #define AT91_PIT_PITIEN BIT(25) /* Timer Interrupt Enable */ #define AT91_PIT_PITEN BIT(24) /* Timer Enabled */ @@ -267,7 +265,7 @@ CLOCKSOURCE_OF_DECLARE(at91sam926x_pit, "atmel,at91sam9260-pit", static void __iomem *pit_base_addr; -void __init at91sam926x_pit_init(void) +void __init at91sam926x_pit_init(int irq) { struct pit_data *data; @@ -281,7 +279,7 @@ void __init at91sam926x_pit_init(void) if (IS_ERR(data->mck)) panic(pr_fmt("Unable to get mck clk\n")); - data->irq = NR_IRQS_LEGACY + AT91_ID_SYS; + data->irq = irq; at91sam926x_pit_common_init(data); } diff --git a/arch/arm/mach-at91/at91sam9g45.c b/arch/arm/mach-at91/at91sam9g45.c index f725814c556d..405427ec05f8 100644 --- a/arch/arm/mach-at91/at91sam9g45.c +++ b/arch/arm/mach-at91/at91sam9g45.c @@ -479,7 +479,7 @@ static unsigned int at91sam9g45_default_irq_priority[NR_AIC_IRQS] __initdata = { static void __init at91sam9g45_init_time(void) { - at91sam926x_pit_init(); + at91sam926x_pit_init(NR_IRQS_LEGACY + AT91_ID_SYS); } AT91_SOC_START(at91sam9g45) diff --git a/arch/arm/mach-at91/at91sam9rl.c b/arch/arm/mach-at91/at91sam9rl.c index 361b4a6e7651..f553e4ea034b 100644 --- a/arch/arm/mach-at91/at91sam9rl.c +++ b/arch/arm/mach-at91/at91sam9rl.c @@ -412,7 +412,7 @@ static unsigned int at91sam9rl_default_irq_priority[NR_AIC_IRQS] __initdata = { static void __init at91sam9rl_init_time(void) { - at91sam926x_pit_init(); + at91sam926x_pit_init(NR_IRQS_LEGACY + AT91_ID_SYS); } AT91_SOC_START(at91sam9rl) diff --git a/arch/arm/mach-at91/generic.h b/arch/arm/mach-at91/generic.h index fb99dabab8f7..189d7c7e7f60 100644 --- a/arch/arm/mach-at91/generic.h +++ b/arch/arm/mach-at91/generic.h @@ -48,7 +48,7 @@ extern void __init at91_init_time(void); extern void at91rm9200_ioremap_st(u32 addr); extern void at91rm9200_timer_init(void); extern void at91sam926x_ioremap_pit(u32 addr); -extern void at91sam926x_pit_init(void); +extern void at91sam926x_pit_init(int irq); extern void at91x40_timer_init(void); /* Clocks */ -- cgit v1.2.1 From b052ff30cd450c91a32e8e928979bca021462996 Mon Sep 17 00:00:00 2001 From: Maxime Ripard Date: Tue, 2 Sep 2014 18:12:35 +0200 Subject: ARM: at91: PIT: Move the driver to drivers/clocksource Now that we don't depend on anyting in the mach-at91 directory, we can just move the driver to where it belongs. Signed-off-by: Maxime Ripard Acked-by: Boris BREZILLON Acked-by: Alexandre Belloni Acked-by: Daniel Lezcano Signed-off-by: Nicolas Ferre Conflicts: arch/arm/mach-at91/Kconfig arch/arm/mach-at91/Makefile --- arch/arm/mach-at91/Kconfig | 6 - arch/arm/mach-at91/Makefile | 1 - arch/arm/mach-at91/at91sam926x_time.c | 296 ---------------------------------- drivers/clocksource/Kconfig | 4 + drivers/clocksource/Makefile | 1 + drivers/clocksource/timer-atmel-pit.c | 296 ++++++++++++++++++++++++++++++++++ 6 files changed, 301 insertions(+), 303 deletions(-) delete mode 100644 arch/arm/mach-at91/at91sam926x_time.c create mode 100644 drivers/clocksource/timer-atmel-pit.c diff --git a/arch/arm/mach-at91/Kconfig b/arch/arm/mach-at91/Kconfig index c3f9ebc5f9f5..474c855fa83c 100644 --- a/arch/arm/mach-at91/Kconfig +++ b/arch/arm/mach-at91/Kconfig @@ -33,16 +33,11 @@ config OLD_IRQ_AT91 select MULTI_IRQ_HANDLER select SPARSE_IRQ -config AT91_SAM9_TIME - select CLKSRC_OF if OF - bool - config HAVE_AT91_SMD bool config SOC_AT91SAM9 bool - select AT91_SAM9_TIME select ATMEL_AIC_IRQ if !OLD_IRQ_AT91 select CPU_ARM926T select GENERIC_CLOCKEVENTS @@ -51,7 +46,6 @@ config SOC_AT91SAM9 config SOC_SAMA5 bool - select AT91_SAM9_TIME select ATMEL_AIC5_IRQ select CPU_V7 select GENERIC_CLOCKEVENTS diff --git a/arch/arm/mach-at91/Makefile b/arch/arm/mach-at91/Makefile index 306c82b3d45c..61d04f9314e7 100644 --- a/arch/arm/mach-at91/Makefile +++ b/arch/arm/mach-at91/Makefile @@ -9,7 +9,6 @@ obj- := obj-$(CONFIG_OLD_IRQ_AT91) += irq.o obj-$(CONFIG_OLD_CLK_AT91) += clock.o -obj-$(CONFIG_AT91_SAM9_TIME) += at91sam926x_time.o obj-$(CONFIG_SOC_AT91SAM9) += sam9_smc.o # CPU-specific support diff --git a/arch/arm/mach-at91/at91sam926x_time.c b/arch/arm/mach-at91/at91sam926x_time.c deleted file mode 100644 index d5289098b3df..000000000000 --- a/arch/arm/mach-at91/at91sam926x_time.c +++ /dev/null @@ -1,296 +0,0 @@ -/* - * at91sam926x_time.c - Periodic Interval Timer (PIT) for at91sam926x - * - * Copyright (C) 2005-2006 M. Amine SAYA, ATMEL Rousset, France - * Revision 2005 M. Nicolas Diremdjian, ATMEL Rousset, France - * Converted to ClockSource/ClockEvents by David Brownell. - * - * 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. - */ - -#define pr_fmt(fmt) "AT91: PIT: " fmt - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#define AT91_PIT_MR 0x00 /* Mode Register */ -#define AT91_PIT_PITIEN BIT(25) /* Timer Interrupt Enable */ -#define AT91_PIT_PITEN BIT(24) /* Timer Enabled */ -#define AT91_PIT_PIV GENMASK(19, 0) /* Periodic Interval Value */ - -#define AT91_PIT_SR 0x04 /* Status Register */ -#define AT91_PIT_PITS BIT(0) /* Timer Status */ - -#define AT91_PIT_PIVR 0x08 /* Periodic Interval Value Register */ -#define AT91_PIT_PIIR 0x0c /* Periodic Interval Image Register */ -#define AT91_PIT_PICNT GENMASK(31, 20) /* Interval Counter */ -#define AT91_PIT_CPIV GENMASK(19, 0) /* Inverval Value */ - -#define PIT_CPIV(x) ((x) & AT91_PIT_CPIV) -#define PIT_PICNT(x) (((x) & AT91_PIT_PICNT) >> 20) - -struct pit_data { - struct clock_event_device clkevt; - struct clocksource clksrc; - - void __iomem *base; - u32 cycle; - u32 cnt; - unsigned int irq; - struct clk *mck; -}; - -static inline struct pit_data *clksrc_to_pit_data(struct clocksource *clksrc) -{ - return container_of(clksrc, struct pit_data, clksrc); -} - -static inline struct pit_data *clkevt_to_pit_data(struct clock_event_device *clkevt) -{ - return container_of(clkevt, struct pit_data, clkevt); -} - -static inline unsigned int pit_read(void __iomem *base, unsigned int reg_offset) -{ - return __raw_readl(base + reg_offset); -} - -static inline void pit_write(void __iomem *base, unsigned int reg_offset, unsigned long value) -{ - __raw_writel(value, base + reg_offset); -} - -/* - * Clocksource: just a monotonic counter of MCK/16 cycles. - * We don't care whether or not PIT irqs are enabled. - */ -static cycle_t read_pit_clk(struct clocksource *cs) -{ - struct pit_data *data = clksrc_to_pit_data(cs); - unsigned long flags; - u32 elapsed; - u32 t; - - raw_local_irq_save(flags); - elapsed = data->cnt; - t = pit_read(data->base, AT91_PIT_PIIR); - raw_local_irq_restore(flags); - - elapsed += PIT_PICNT(t) * data->cycle; - elapsed += PIT_CPIV(t); - return elapsed; -} - -/* - * Clockevent device: interrupts every 1/HZ (== pit_cycles * MCK/16) - */ -static void -pit_clkevt_mode(enum clock_event_mode mode, struct clock_event_device *dev) -{ - struct pit_data *data = clkevt_to_pit_data(dev); - - switch (mode) { - case CLOCK_EVT_MODE_PERIODIC: - /* update clocksource counter */ - data->cnt += data->cycle * PIT_PICNT(pit_read(data->base, AT91_PIT_PIVR)); - pit_write(data->base, AT91_PIT_MR, - (data->cycle - 1) | AT91_PIT_PITEN | AT91_PIT_PITIEN); - break; - case CLOCK_EVT_MODE_ONESHOT: - BUG(); - /* FALLTHROUGH */ - case CLOCK_EVT_MODE_SHUTDOWN: - case CLOCK_EVT_MODE_UNUSED: - /* disable irq, leaving the clocksource active */ - pit_write(data->base, AT91_PIT_MR, - (data->cycle - 1) | AT91_PIT_PITEN); - break; - case CLOCK_EVT_MODE_RESUME: - break; - } -} - -static void at91sam926x_pit_suspend(struct clock_event_device *cedev) -{ - struct pit_data *data = clkevt_to_pit_data(cedev); - - /* Disable timer */ - pit_write(data->base, AT91_PIT_MR, 0); -} - -static void at91sam926x_pit_reset(struct pit_data *data) -{ - /* Disable timer and irqs */ - pit_write(data->base, AT91_PIT_MR, 0); - - /* Clear any pending interrupts, wait for PIT to stop counting */ - while (PIT_CPIV(pit_read(data->base, AT91_PIT_PIVR)) != 0) - cpu_relax(); - - /* Start PIT but don't enable IRQ */ - pit_write(data->base, AT91_PIT_MR, - (data->cycle - 1) | AT91_PIT_PITEN); -} - -static void at91sam926x_pit_resume(struct clock_event_device *cedev) -{ - struct pit_data *data = clkevt_to_pit_data(cedev); - - at91sam926x_pit_reset(data); -} - -/* - * IRQ handler for the timer. - */ -static irqreturn_t at91sam926x_pit_interrupt(int irq, void *dev_id) -{ - struct pit_data *data = dev_id; - - /* - * irqs should be disabled here, but as the irq is shared they are only - * guaranteed to be off if the timer irq is registered first. - */ - WARN_ON_ONCE(!irqs_disabled()); - - /* The PIT interrupt may be disabled, and is shared */ - if ((data->clkevt.mode == CLOCK_EVT_MODE_PERIODIC) && - (pit_read(data->base, AT91_PIT_SR) & AT91_PIT_PITS)) { - unsigned nr_ticks; - - /* Get number of ticks performed before irq, and ack it */ - nr_ticks = PIT_PICNT(pit_read(data->base, AT91_PIT_PIVR)); - do { - data->cnt += data->cycle; - data->clkevt.event_handler(&data->clkevt); - nr_ticks--; - } while (nr_ticks); - - return IRQ_HANDLED; - } - - return IRQ_NONE; -} - -/* - * Set up both clocksource and clockevent support. - */ -static void __init at91sam926x_pit_common_init(struct pit_data *data) -{ - unsigned long pit_rate; - unsigned bits; - int ret; - - /* - * Use our actual MCK to figure out how many MCK/16 ticks per - * 1/HZ period (instead of a compile-time constant LATCH). - */ - pit_rate = clk_get_rate(data->mck) / 16; - data->cycle = DIV_ROUND_CLOSEST(pit_rate, HZ); - WARN_ON(((data->cycle - 1) & ~AT91_PIT_PIV) != 0); - - /* Initialize and enable the timer */ - at91sam926x_pit_reset(data); - - /* - * Register clocksource. The high order bits of PIV are unused, - * so this isn't a 32-bit counter unless we get clockevent irqs. - */ - bits = 12 /* PICNT */ + ilog2(data->cycle) /* PIV */; - data->clksrc.mask = CLOCKSOURCE_MASK(bits); - data->clksrc.name = "pit"; - data->clksrc.rating = 175; - data->clksrc.read = read_pit_clk, - data->clksrc.flags = CLOCK_SOURCE_IS_CONTINUOUS, - clocksource_register_hz(&data->clksrc, pit_rate); - - /* Set up irq handler */ - ret = request_irq(data->irq, at91sam926x_pit_interrupt, - IRQF_SHARED | IRQF_TIMER | IRQF_IRQPOLL, - "at91_tick", data); - if (ret) - panic(pr_fmt("Unable to setup IRQ\n")); - - /* Set up and register clockevents */ - data->clkevt.name = "pit"; - data->clkevt.features = CLOCK_EVT_FEAT_PERIODIC; - data->clkevt.shift = 32; - data->clkevt.mult = div_sc(pit_rate, NSEC_PER_SEC, data->clkevt.shift); - data->clkevt.rating = 100; - data->clkevt.cpumask = cpumask_of(0); - - data->clkevt.set_mode = pit_clkevt_mode; - data->clkevt.resume = at91sam926x_pit_resume; - data->clkevt.suspend = at91sam926x_pit_suspend; - clockevents_register_device(&data->clkevt); -} - -static void __init at91sam926x_pit_dt_init(struct device_node *node) -{ - struct pit_data *data; - - data = kzalloc(sizeof(*data), GFP_KERNEL); - if (!data) - panic(pr_fmt("Unable to allocate memory\n")); - - data->base = of_iomap(node, 0); - if (!data->base) - panic(pr_fmt("Could not map PIT address\n")); - - data->mck = of_clk_get(node, 0); - if (IS_ERR(data->mck)) - /* Fallback on clkdev for !CCF-based boards */ - data->mck = clk_get(NULL, "mck"); - - if (IS_ERR(data->mck)) - panic(pr_fmt("Unable to get mck clk\n")); - - /* Get the interrupts property */ - data->irq = irq_of_parse_and_map(node, 0); - if (!data->irq) - panic(pr_fmt("Unable to get IRQ from DT\n")); - - at91sam926x_pit_common_init(data); -} -CLOCKSOURCE_OF_DECLARE(at91sam926x_pit, "atmel,at91sam9260-pit", - at91sam926x_pit_dt_init); - -static void __iomem *pit_base_addr; - -void __init at91sam926x_pit_init(int irq) -{ - struct pit_data *data; - - data = kzalloc(sizeof(*data), GFP_KERNEL); - if (!data) - panic(pr_fmt("Unable to allocate memory\n")); - - data->base = pit_base_addr; - - data->mck = clk_get(NULL, "mck"); - if (IS_ERR(data->mck)) - panic(pr_fmt("Unable to get mck clk\n")); - - data->irq = irq; - - at91sam926x_pit_common_init(data); -} - -void __init at91sam926x_ioremap_pit(u32 addr) -{ - if (of_have_populated_dt()) - return; - - pit_base_addr = ioremap(addr, 16); - - if (!pit_base_addr) - panic(pr_fmt("Impossible to ioremap PIT\n")); -} diff --git a/drivers/clocksource/Kconfig b/drivers/clocksource/Kconfig index cfd6519df661..82a2ebe41e27 100644 --- a/drivers/clocksource/Kconfig +++ b/drivers/clocksource/Kconfig @@ -120,6 +120,10 @@ config CLKSRC_ARM_GLOBAL_TIMER_SCHED_CLOCK help Use ARM global timer clock source as sched_clock +config ATMEL_PIT + select CLKSRC_OF if OF + def_bool SOC_AT91SAM9 || SOC_SAMA5 + config CLKSRC_METAG_GENERIC def_bool y if METAG help diff --git a/drivers/clocksource/Makefile b/drivers/clocksource/Makefile index 7fd9fd1dff42..e566f6c7ded4 100644 --- a/drivers/clocksource/Makefile +++ b/drivers/clocksource/Makefile @@ -1,4 +1,5 @@ obj-$(CONFIG_CLKSRC_OF) += clksrc-of.o +obj-$(CONFIG_ATMEL_PIT) += timer-atmel-pit.o obj-$(CONFIG_ATMEL_TCB_CLKSRC) += tcb_clksrc.o obj-$(CONFIG_X86_PM_TIMER) += acpi_pm.o obj-$(CONFIG_SCx200HR_TIMER) += scx200_hrt.o diff --git a/drivers/clocksource/timer-atmel-pit.c b/drivers/clocksource/timer-atmel-pit.c new file mode 100644 index 000000000000..d5289098b3df --- /dev/null +++ b/drivers/clocksource/timer-atmel-pit.c @@ -0,0 +1,296 @@ +/* + * at91sam926x_time.c - Periodic Interval Timer (PIT) for at91sam926x + * + * Copyright (C) 2005-2006 M. Amine SAYA, ATMEL Rousset, France + * Revision 2005 M. Nicolas Diremdjian, ATMEL Rousset, France + * Converted to ClockSource/ClockEvents by David Brownell. + * + * 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. + */ + +#define pr_fmt(fmt) "AT91: PIT: " fmt + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define AT91_PIT_MR 0x00 /* Mode Register */ +#define AT91_PIT_PITIEN BIT(25) /* Timer Interrupt Enable */ +#define AT91_PIT_PITEN BIT(24) /* Timer Enabled */ +#define AT91_PIT_PIV GENMASK(19, 0) /* Periodic Interval Value */ + +#define AT91_PIT_SR 0x04 /* Status Register */ +#define AT91_PIT_PITS BIT(0) /* Timer Status */ + +#define AT91_PIT_PIVR 0x08 /* Periodic Interval Value Register */ +#define AT91_PIT_PIIR 0x0c /* Periodic Interval Image Register */ +#define AT91_PIT_PICNT GENMASK(31, 20) /* Interval Counter */ +#define AT91_PIT_CPIV GENMASK(19, 0) /* Inverval Value */ + +#define PIT_CPIV(x) ((x) & AT91_PIT_CPIV) +#define PIT_PICNT(x) (((x) & AT91_PIT_PICNT) >> 20) + +struct pit_data { + struct clock_event_device clkevt; + struct clocksource clksrc; + + void __iomem *base; + u32 cycle; + u32 cnt; + unsigned int irq; + struct clk *mck; +}; + +static inline struct pit_data *clksrc_to_pit_data(struct clocksource *clksrc) +{ + return container_of(clksrc, struct pit_data, clksrc); +} + +static inline struct pit_data *clkevt_to_pit_data(struct clock_event_device *clkevt) +{ + return container_of(clkevt, struct pit_data, clkevt); +} + +static inline unsigned int pit_read(void __iomem *base, unsigned int reg_offset) +{ + return __raw_readl(base + reg_offset); +} + +static inline void pit_write(void __iomem *base, unsigned int reg_offset, unsigned long value) +{ + __raw_writel(value, base + reg_offset); +} + +/* + * Clocksource: just a monotonic counter of MCK/16 cycles. + * We don't care whether or not PIT irqs are enabled. + */ +static cycle_t read_pit_clk(struct clocksource *cs) +{ + struct pit_data *data = clksrc_to_pit_data(cs); + unsigned long flags; + u32 elapsed; + u32 t; + + raw_local_irq_save(flags); + elapsed = data->cnt; + t = pit_read(data->base, AT91_PIT_PIIR); + raw_local_irq_restore(flags); + + elapsed += PIT_PICNT(t) * data->cycle; + elapsed += PIT_CPIV(t); + return elapsed; +} + +/* + * Clockevent device: interrupts every 1/HZ (== pit_cycles * MCK/16) + */ +static void +pit_clkevt_mode(enum clock_event_mode mode, struct clock_event_device *dev) +{ + struct pit_data *data = clkevt_to_pit_data(dev); + + switch (mode) { + case CLOCK_EVT_MODE_PERIODIC: + /* update clocksource counter */ + data->cnt += data->cycle * PIT_PICNT(pit_read(data->base, AT91_PIT_PIVR)); + pit_write(data->base, AT91_PIT_MR, + (data->cycle - 1) | AT91_PIT_PITEN | AT91_PIT_PITIEN); + break; + case CLOCK_EVT_MODE_ONESHOT: + BUG(); + /* FALLTHROUGH */ + case CLOCK_EVT_MODE_SHUTDOWN: + case CLOCK_EVT_MODE_UNUSED: + /* disable irq, leaving the clocksource active */ + pit_write(data->base, AT91_PIT_MR, + (data->cycle - 1) | AT91_PIT_PITEN); + break; + case CLOCK_EVT_MODE_RESUME: + break; + } +} + +static void at91sam926x_pit_suspend(struct clock_event_device *cedev) +{ + struct pit_data *data = clkevt_to_pit_data(cedev); + + /* Disable timer */ + pit_write(data->base, AT91_PIT_MR, 0); +} + +static void at91sam926x_pit_reset(struct pit_data *data) +{ + /* Disable timer and irqs */ + pit_write(data->base, AT91_PIT_MR, 0); + + /* Clear any pending interrupts, wait for PIT to stop counting */ + while (PIT_CPIV(pit_read(data->base, AT91_PIT_PIVR)) != 0) + cpu_relax(); + + /* Start PIT but don't enable IRQ */ + pit_write(data->base, AT91_PIT_MR, + (data->cycle - 1) | AT91_PIT_PITEN); +} + +static void at91sam926x_pit_resume(struct clock_event_device *cedev) +{ + struct pit_data *data = clkevt_to_pit_data(cedev); + + at91sam926x_pit_reset(data); +} + +/* + * IRQ handler for the timer. + */ +static irqreturn_t at91sam926x_pit_interrupt(int irq, void *dev_id) +{ + struct pit_data *data = dev_id; + + /* + * irqs should be disabled here, but as the irq is shared they are only + * guaranteed to be off if the timer irq is registered first. + */ + WARN_ON_ONCE(!irqs_disabled()); + + /* The PIT interrupt may be disabled, and is shared */ + if ((data->clkevt.mode == CLOCK_EVT_MODE_PERIODIC) && + (pit_read(data->base, AT91_PIT_SR) & AT91_PIT_PITS)) { + unsigned nr_ticks; + + /* Get number of ticks performed before irq, and ack it */ + nr_ticks = PIT_PICNT(pit_read(data->base, AT91_PIT_PIVR)); + do { + data->cnt += data->cycle; + data->clkevt.event_handler(&data->clkevt); + nr_ticks--; + } while (nr_ticks); + + return IRQ_HANDLED; + } + + return IRQ_NONE; +} + +/* + * Set up both clocksource and clockevent support. + */ +static void __init at91sam926x_pit_common_init(struct pit_data *data) +{ + unsigned long pit_rate; + unsigned bits; + int ret; + + /* + * Use our actual MCK to figure out how many MCK/16 ticks per + * 1/HZ period (instead of a compile-time constant LATCH). + */ + pit_rate = clk_get_rate(data->mck) / 16; + data->cycle = DIV_ROUND_CLOSEST(pit_rate, HZ); + WARN_ON(((data->cycle - 1) & ~AT91_PIT_PIV) != 0); + + /* Initialize and enable the timer */ + at91sam926x_pit_reset(data); + + /* + * Register clocksource. The high order bits of PIV are unused, + * so this isn't a 32-bit counter unless we get clockevent irqs. + */ + bits = 12 /* PICNT */ + ilog2(data->cycle) /* PIV */; + data->clksrc.mask = CLOCKSOURCE_MASK(bits); + data->clksrc.name = "pit"; + data->clksrc.rating = 175; + data->clksrc.read = read_pit_clk, + data->clksrc.flags = CLOCK_SOURCE_IS_CONTINUOUS, + clocksource_register_hz(&data->clksrc, pit_rate); + + /* Set up irq handler */ + ret = request_irq(data->irq, at91sam926x_pit_interrupt, + IRQF_SHARED | IRQF_TIMER | IRQF_IRQPOLL, + "at91_tick", data); + if (ret) + panic(pr_fmt("Unable to setup IRQ\n")); + + /* Set up and register clockevents */ + data->clkevt.name = "pit"; + data->clkevt.features = CLOCK_EVT_FEAT_PERIODIC; + data->clkevt.shift = 32; + data->clkevt.mult = div_sc(pit_rate, NSEC_PER_SEC, data->clkevt.shift); + data->clkevt.rating = 100; + data->clkevt.cpumask = cpumask_of(0); + + data->clkevt.set_mode = pit_clkevt_mode; + data->clkevt.resume = at91sam926x_pit_resume; + data->clkevt.suspend = at91sam926x_pit_suspend; + clockevents_register_device(&data->clkevt); +} + +static void __init at91sam926x_pit_dt_init(struct device_node *node) +{ + struct pit_data *data; + + data = kzalloc(sizeof(*data), GFP_KERNEL); + if (!data) + panic(pr_fmt("Unable to allocate memory\n")); + + data->base = of_iomap(node, 0); + if (!data->base) + panic(pr_fmt("Could not map PIT address\n")); + + data->mck = of_clk_get(node, 0); + if (IS_ERR(data->mck)) + /* Fallback on clkdev for !CCF-based boards */ + data->mck = clk_get(NULL, "mck"); + + if (IS_ERR(data->mck)) + panic(pr_fmt("Unable to get mck clk\n")); + + /* Get the interrupts property */ + data->irq = irq_of_parse_and_map(node, 0); + if (!data->irq) + panic(pr_fmt("Unable to get IRQ from DT\n")); + + at91sam926x_pit_common_init(data); +} +CLOCKSOURCE_OF_DECLARE(at91sam926x_pit, "atmel,at91sam9260-pit", + at91sam926x_pit_dt_init); + +static void __iomem *pit_base_addr; + +void __init at91sam926x_pit_init(int irq) +{ + struct pit_data *data; + + data = kzalloc(sizeof(*data), GFP_KERNEL); + if (!data) + panic(pr_fmt("Unable to allocate memory\n")); + + data->base = pit_base_addr; + + data->mck = clk_get(NULL, "mck"); + if (IS_ERR(data->mck)) + panic(pr_fmt("Unable to get mck clk\n")); + + data->irq = irq; + + at91sam926x_pit_common_init(data); +} + +void __init at91sam926x_ioremap_pit(u32 addr) +{ + if (of_have_populated_dt()) + return; + + pit_base_addr = ioremap(addr, 16); + + if (!pit_base_addr) + panic(pr_fmt("Impossible to ioremap PIT\n")); +} -- cgit v1.2.1 From e92ce89c29fe104bc1246913f385093bbae7b564 Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Tue, 16 Sep 2014 15:31:40 -0500 Subject: arm: omap2: n8x0: move i2c devices to DT By moving i2c devices to DT we can clean up i2c_board_info and fix a problem with moving INTC to irq domain where IRQs can be renumbered on each boot. Cc: Aaro Koskinen Signed-off-by: Felipe Balbi Signed-off-by: Tony Lindgren --- arch/arm/boot/dts/omap2420-n810.dts | 7 +++++++ arch/arm/boot/dts/omap2420-n8x0-common.dtsi | 6 ++++++ arch/arm/mach-omap2/board-n8x0.c | 26 +++----------------------- arch/arm/mach-omap2/common-board-devices.h | 5 +++++ arch/arm/mach-omap2/pdata-quirks.c | 2 ++ 5 files changed, 23 insertions(+), 23 deletions(-) diff --git a/arch/arm/boot/dts/omap2420-n810.dts b/arch/arm/boot/dts/omap2420-n810.dts index 21baec154b78..b604d26bd48c 100644 --- a/arch/arm/boot/dts/omap2420-n810.dts +++ b/arch/arm/boot/dts/omap2420-n810.dts @@ -6,3 +6,10 @@ model = "Nokia N810"; compatible = "nokia,n810", "nokia,n8x0", "ti,omap2420", "ti,omap2"; }; + +&i2c2 { + aic3x@18 { + compatible = "tlv320aic3x"; + reg = <0x18>; + }; +}; diff --git a/arch/arm/boot/dts/omap2420-n8x0-common.dtsi b/arch/arm/boot/dts/omap2420-n8x0-common.dtsi index 89608b206519..24c50db2a478 100644 --- a/arch/arm/boot/dts/omap2420-n8x0-common.dtsi +++ b/arch/arm/boot/dts/omap2420-n8x0-common.dtsi @@ -27,6 +27,12 @@ &i2c1 { clock-frequency = <400000>; + + pmic@72 { + compatible = "menelaus"; + reg = <0x72>; + interrupts = <7 IRQ_TYPE_EDGE_RISING>; + }; }; &i2c2 { diff --git a/arch/arm/mach-omap2/board-n8x0.c b/arch/arm/mach-omap2/board-n8x0.c index aead77a4bc6d..97767a27ca9d 100644 --- a/arch/arm/mach-omap2/board-n8x0.c +++ b/arch/arm/mach-omap2/board-n8x0.c @@ -33,6 +33,7 @@ #include "mmc.h" #include "soc.h" #include "gpmc-onenand.h" +#include "common-board-devices.h" #define TUSB6010_ASYNC_CS 1 #define TUSB6010_SYNC_CS 4 @@ -568,29 +569,14 @@ static int n8x0_menelaus_late_init(struct device *dev) } #endif -static struct menelaus_platform_data n8x0_menelaus_platform_data __initdata = { +struct menelaus_platform_data n8x0_menelaus_platform_data __initdata = { .late_init = n8x0_menelaus_late_init, }; -static struct i2c_board_info __initdata n8x0_i2c_board_info_1[] __initdata = { - { - I2C_BOARD_INFO("menelaus", 0x72), - .irq = 7 + OMAP_INTC_START, - .platform_data = &n8x0_menelaus_platform_data, - }, -}; - -static struct aic3x_pdata n810_aic33_data __initdata = { +struct aic3x_pdata n810_aic33_data __initdata = { .gpio_reset = 118, }; -static struct i2c_board_info n810_i2c_board_info_2[] __initdata = { - { - I2C_BOARD_INFO("tlv320aic3x", 0x18), - .platform_data = &n810_aic33_data, - }, -}; - static int __init n8x0_late_initcall(void) { if (!board_caps) @@ -612,11 +598,5 @@ void * __init n8x0_legacy_init(void) board_check_revision(); spi_register_board_info(n800_spi_board_info, ARRAY_SIZE(n800_spi_board_info)); - i2c_register_board_info(0, n8x0_i2c_board_info_1, - ARRAY_SIZE(n8x0_i2c_board_info_1)); - if (board_is_n810()) - i2c_register_board_info(1, n810_i2c_board_info_2, - ARRAY_SIZE(n810_i2c_board_info_2)); - return &mmc1_data; } diff --git a/arch/arm/mach-omap2/common-board-devices.h b/arch/arm/mach-omap2/common-board-devices.h index f338177e6900..07c88ae083fb 100644 --- a/arch/arm/mach-omap2/common-board-devices.h +++ b/arch/arm/mach-omap2/common-board-devices.h @@ -1,6 +1,8 @@ #ifndef __OMAP_COMMON_BOARD_DEVICES__ #define __OMAP_COMMON_BOARD_DEVICES__ +#include +#include #include "twl-common.h" #define NAND_BLOCK_SIZE SZ_128K @@ -12,4 +14,7 @@ void omap_ads7846_init(int bus_num, int gpio_pendown, int gpio_debounce, struct ads7846_platform_data *board_pdata); void *n8x0_legacy_init(void); +extern struct menelaus_platform_data n8x0_menelaus_platform_data; +extern struct aic3x_pdata n810_aic33_data; + #endif /* __OMAP_COMMON_BOARD_DEVICES__ */ diff --git a/arch/arm/mach-omap2/pdata-quirks.c b/arch/arm/mach-omap2/pdata-quirks.c index 8695fd4ea476..06a0ccfa00a2 100644 --- a/arch/arm/mach-omap2/pdata-quirks.c +++ b/arch/arm/mach-omap2/pdata-quirks.c @@ -336,6 +336,8 @@ static struct pdata_init auxdata_quirks[] __initdata = { struct of_dev_auxdata omap_auxdata_lookup[] __initdata = { #ifdef CONFIG_MACH_NOKIA_N8X0 OF_DEV_AUXDATA("ti,omap2420-mmc", 0x4809c000, "mmci-omap.0", NULL), + OF_DEV_AUXDATA("menelaus", 0x72, "1-0072", &n8x0_menelaus_platform_data), + OF_DEV_AUXDATA("tlv320aic3x", 0x18, "2-0018", &n810_aic33_data), #endif #ifdef CONFIG_ARCH_OMAP3 OF_DEV_AUXDATA("ti,omap3-padconf", 0x48002030, "48002030.pinmux", &pcs_pdata), -- cgit v1.2.1 From eaacabc0d9b637c82788c66955b4ba0efebd5500 Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Mon, 15 Sep 2014 16:15:01 -0500 Subject: irqchip: add irq-omap-intc.h header OMAP INTC irqchip driver will be moved under drivers/irqchip/ soon but we still have a dependency with mach-omap2 when it comes to idle functions. In order to make it easy to share those function prototypes with OMAP PM code, we introduce this new header. To avoid modifying several board-files and some of the PM-related code, we just include the new header from common.h which was already included by all users of IRQ-related PM code. Signed-off-by: Felipe Balbi Signed-off-by: Tony Lindgren --- arch/arm/mach-omap2/common.h | 10 +--------- include/linux/irqchip/irq-omap-intc.h | 32 ++++++++++++++++++++++++++++++++ 2 files changed, 33 insertions(+), 9 deletions(-) create mode 100644 include/linux/irqchip/irq-omap-intc.h diff --git a/arch/arm/mach-omap2/common.h b/arch/arm/mach-omap2/common.h index 180009343adb..377eea849e7b 100644 --- a/arch/arm/mach-omap2/common.h +++ b/arch/arm/mach-omap2/common.h @@ -32,6 +32,7 @@ #include #include #include +#include #include @@ -210,15 +211,6 @@ extern struct device *omap2_get_iva_device(void); extern struct device *omap2_get_l3_device(void); extern struct device *omap4_get_dsp_device(void); -void omap2_init_irq(void); -void omap3_init_irq(void); -void ti81xx_init_irq(void); -extern int omap_irq_pending(void); -void omap_intc_save_context(void); -void omap_intc_restore_context(void); -void omap3_intc_suspend(void); -void omap3_intc_prepare_idle(void); -void omap3_intc_resume_idle(void); void omap_gic_of_init(void); #ifdef CONFIG_CACHE_L2X0 diff --git a/include/linux/irqchip/irq-omap-intc.h b/include/linux/irqchip/irq-omap-intc.h new file mode 100644 index 000000000000..e06b370cfc0d --- /dev/null +++ b/include/linux/irqchip/irq-omap-intc.h @@ -0,0 +1,32 @@ +/** + * irq-omap-intc.h - INTC Idle Functions + * + * Copyright (C) 2014 Texas Instruments Incorporated - http://www.ti.com + * + * Author: Felipe Balbi + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 of + * the 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. + */ + +#ifndef __INCLUDE_LINUX_IRQCHIP_IRQ_OMAP_INTC_H +#define __INCLUDE_LINUX_IRQCHIP_IRQ_OMAP_INTC_H + +void omap2_init_irq(void); +void omap3_init_irq(void); +void ti81xx_init_irq(void); + +int omap_irq_pending(void); +void omap_intc_save_context(void); +void omap_intc_restore_context(void); +void omap3_intc_suspend(void); +void omap3_intc_prepare_idle(void); +void omap3_intc_resume_idle(void); + +#endif /* __INCLUDE_LINUX_IRQCHIP_IRQ_OMAP_INTC_H */ -- cgit v1.2.1 From 8598066cddd186809c4edf5aae5f018c00079e8c Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Mon, 15 Sep 2014 16:15:02 -0500 Subject: arm: omap: irq: move irq.c to drivers/irqchip/ Just move the code over as it has no dependencies on arch/arm/ anymore. Signed-off-by: Felipe Balbi Signed-off-by: Tony Lindgren --- arch/arm/mach-omap2/Kconfig | 1 + arch/arm/mach-omap2/Makefile | 3 +- arch/arm/mach-omap2/irq.c | 393 --------------------------------------- drivers/irqchip/Kconfig | 5 + drivers/irqchip/Makefile | 1 + drivers/irqchip/irq-omap-intc.c | 394 ++++++++++++++++++++++++++++++++++++++++ 6 files changed, 402 insertions(+), 395 deletions(-) delete mode 100644 arch/arm/mach-omap2/irq.c create mode 100644 drivers/irqchip/irq-omap-intc.c diff --git a/arch/arm/mach-omap2/Kconfig b/arch/arm/mach-omap2/Kconfig index 11ccf0b4e5c2..691d62a8a74b 100644 --- a/arch/arm/mach-omap2/Kconfig +++ b/arch/arm/mach-omap2/Kconfig @@ -86,6 +86,7 @@ config ARCH_OMAP2PLUS select PINCTRL select SOC_BUS select TI_PRIV_EDMA + select OMAP_IRQCHIP help Systems based on OMAP2, OMAP3, OMAP4 or OMAP5 diff --git a/arch/arm/mach-omap2/Makefile b/arch/arm/mach-omap2/Makefile index 69bbcba8842f..0b6095c78af1 100644 --- a/arch/arm/mach-omap2/Makefile +++ b/arch/arm/mach-omap2/Makefile @@ -10,7 +10,6 @@ obj-y := id.o io.o control.o mux.o devices.o fb.o serial.o gpmc.o timer.o pm.o \ common.o gpio.o dma.o wd_timer.o display.o i2c.o hdq1w.o omap_hwmod.o \ omap_device.o sram.o drm.o -omap-2-3-common = irq.o hwmod-common = omap_hwmod.o omap_hwmod_reset.o \ omap_hwmod_common_data.o clock-common = clock.o clock_common_data.o \ @@ -20,7 +19,7 @@ secure-common = omap-smc.o omap-secure.o obj-$(CONFIG_ARCH_OMAP2) += $(omap-2-3-common) $(hwmod-common) obj-$(CONFIG_ARCH_OMAP3) += $(omap-2-3-common) $(hwmod-common) $(secure-common) obj-$(CONFIG_ARCH_OMAP4) += $(hwmod-common) $(secure-common) -obj-$(CONFIG_SOC_AM33XX) += irq.o $(hwmod-common) +obj-$(CONFIG_SOC_AM33XX) += $(hwmod-common) obj-$(CONFIG_SOC_OMAP5) += $(hwmod-common) $(secure-common) obj-$(CONFIG_SOC_AM43XX) += $(hwmod-common) $(secure-common) obj-$(CONFIG_SOC_DRA7XX) += $(hwmod-common) $(secure-common) diff --git a/arch/arm/mach-omap2/irq.c b/arch/arm/mach-omap2/irq.c deleted file mode 100644 index b2993e45e84c..000000000000 --- a/arch/arm/mach-omap2/irq.c +++ /dev/null @@ -1,393 +0,0 @@ -/* - * linux/arch/arm/mach-omap2/irq.c - * - * Interrupt handler for OMAP2 boards. - * - * Copyright (C) 2005 Nokia Corporation - * Author: Paul Mundt - * - * 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 -#include -#include -#include -#include -#include - -#include "soc.h" -#include "common.h" -#include "../../drivers/irqchip/irqchip.h" - -/* selected INTC register offsets */ - -#define INTC_REVISION 0x0000 -#define INTC_SYSCONFIG 0x0010 -#define INTC_SYSSTATUS 0x0014 -#define INTC_SIR 0x0040 -#define INTC_CONTROL 0x0048 -#define INTC_PROTECTION 0x004C -#define INTC_IDLE 0x0050 -#define INTC_THRESHOLD 0x0068 -#define INTC_MIR0 0x0084 -#define INTC_MIR_CLEAR0 0x0088 -#define INTC_MIR_SET0 0x008c -#define INTC_PENDING_IRQ0 0x0098 -#define INTC_PENDING_IRQ1 0x00b8 -#define INTC_PENDING_IRQ2 0x00d8 -#define INTC_PENDING_IRQ3 0x00f8 -#define INTC_ILR0 0x0100 - -#define ACTIVEIRQ_MASK 0x7f /* omap2/3 active interrupt bits */ -#define INTCPS_NR_ILR_REGS 128 -#define INTCPS_NR_MIR_REGS 3 - -/* - * OMAP2 has a number of different interrupt controllers, each interrupt - * controller is identified as its own "bank". Register definitions are - * fairly consistent for each bank, but not all registers are implemented - * for each bank.. when in doubt, consult the TRM. - */ - -/* Structure to save interrupt controller context */ -struct omap_intc_regs { - u32 sysconfig; - u32 protection; - u32 idle; - u32 threshold; - u32 ilr[INTCPS_NR_ILR_REGS]; - u32 mir[INTCPS_NR_MIR_REGS]; -}; -static struct omap_intc_regs intc_context; - -static struct irq_domain *domain; -static void __iomem *omap_irq_base; -static int omap_nr_pending = 3; -static int omap_nr_irqs = 96; - -/* INTC bank register get/set */ -static void intc_writel(u32 reg, u32 val) -{ - writel_relaxed(val, omap_irq_base + reg); -} - -static u32 intc_readl(u32 reg) -{ - return readl_relaxed(omap_irq_base + reg); -} - -void omap_intc_save_context(void) -{ - int i; - - intc_context.sysconfig = - intc_readl(INTC_SYSCONFIG); - intc_context.protection = - intc_readl(INTC_PROTECTION); - intc_context.idle = - intc_readl(INTC_IDLE); - intc_context.threshold = - intc_readl(INTC_THRESHOLD); - - for (i = 0; i < omap_nr_irqs; i++) - intc_context.ilr[i] = - intc_readl((INTC_ILR0 + 0x4 * i)); - for (i = 0; i < INTCPS_NR_MIR_REGS; i++) - intc_context.mir[i] = - intc_readl(INTC_MIR0 + (0x20 * i)); -} - -void omap_intc_restore_context(void) -{ - int i; - - intc_writel(INTC_SYSCONFIG, intc_context.sysconfig); - intc_writel(INTC_PROTECTION, intc_context.protection); - intc_writel(INTC_IDLE, intc_context.idle); - intc_writel(INTC_THRESHOLD, intc_context.threshold); - - for (i = 0; i < omap_nr_irqs; i++) - intc_writel(INTC_ILR0 + 0x4 * i, - intc_context.ilr[i]); - - for (i = 0; i < INTCPS_NR_MIR_REGS; i++) - intc_writel(INTC_MIR0 + 0x20 * i, - intc_context.mir[i]); - /* MIRs are saved and restore with other PRCM registers */ -} - -void omap3_intc_prepare_idle(void) -{ - /* - * Disable autoidle as it can stall interrupt controller, - * cf. errata ID i540 for 3430 (all revisions up to 3.1.x) - */ - intc_writel(INTC_SYSCONFIG, 0); -} - -void omap3_intc_resume_idle(void) -{ - /* Re-enable autoidle */ - intc_writel(INTC_SYSCONFIG, 1); -} - -/* XXX: FIQ and additional INTC support (only MPU at the moment) */ -static void omap_ack_irq(struct irq_data *d) -{ - intc_writel(INTC_CONTROL, 0x1); -} - -static void omap_mask_ack_irq(struct irq_data *d) -{ - irq_gc_mask_disable_reg(d); - omap_ack_irq(d); -} - -static void __init omap_irq_soft_reset(void) -{ - unsigned long tmp; - - tmp = intc_readl(INTC_REVISION) & 0xff; - - pr_info("IRQ: Found an INTC at 0x%p (revision %ld.%ld) with %d interrupts\n", - omap_irq_base, tmp >> 4, tmp & 0xf, omap_nr_irqs); - - tmp = intc_readl(INTC_SYSCONFIG); - tmp |= 1 << 1; /* soft reset */ - intc_writel(INTC_SYSCONFIG, tmp); - - while (!(intc_readl(INTC_SYSSTATUS) & 0x1)) - /* Wait for reset to complete */; - - /* Enable autoidle */ - intc_writel(INTC_SYSCONFIG, 1 << 0); -} - -int omap_irq_pending(void) -{ - int irq; - - for (irq = 0; irq < omap_nr_irqs; irq += 32) - if (intc_readl(INTC_PENDING_IRQ0 + - ((irq >> 5) << 5))) - return 1; - return 0; -} - -void omap3_intc_suspend(void) -{ - /* A pending interrupt would prevent OMAP from entering suspend */ - omap_ack_irq(NULL); -} - -static int __init omap_alloc_gc_of(struct irq_domain *d, void __iomem *base) -{ - int ret; - int i; - - ret = irq_alloc_domain_generic_chips(d, 32, 1, "INTC", - handle_level_irq, IRQ_NOREQUEST | IRQ_NOPROBE, - IRQ_LEVEL, 0); - if (ret) { - pr_warn("Failed to allocate irq chips\n"); - return ret; - } - - for (i = 0; i < omap_nr_pending; i++) { - struct irq_chip_generic *gc; - struct irq_chip_type *ct; - - gc = irq_get_domain_generic_chip(d, 32 * i); - gc->reg_base = base; - ct = gc->chip_types; - - ct->type = IRQ_TYPE_LEVEL_MASK; - ct->handler = handle_level_irq; - - ct->chip.irq_ack = omap_mask_ack_irq; - ct->chip.irq_mask = irq_gc_mask_disable_reg; - ct->chip.irq_unmask = irq_gc_unmask_enable_reg; - - ct->chip.flags |= IRQCHIP_SKIP_SET_WAKE; - - ct->regs.enable = INTC_MIR_CLEAR0 + 32 * i; - ct->regs.disable = INTC_MIR_SET0 + 32 * i; - } - - return 0; -} - -static void __init omap_alloc_gc_legacy(void __iomem *base, - unsigned int irq_start, unsigned int num) -{ - struct irq_chip_generic *gc; - struct irq_chip_type *ct; - - gc = irq_alloc_generic_chip("INTC", 1, irq_start, base, - handle_level_irq); - ct = gc->chip_types; - ct->chip.irq_ack = omap_mask_ack_irq; - ct->chip.irq_mask = irq_gc_mask_disable_reg; - ct->chip.irq_unmask = irq_gc_unmask_enable_reg; - ct->chip.flags |= IRQCHIP_SKIP_SET_WAKE; - - ct->regs.enable = INTC_MIR_CLEAR0; - ct->regs.disable = INTC_MIR_SET0; - irq_setup_generic_chip(gc, IRQ_MSK(num), IRQ_GC_INIT_MASK_CACHE, - IRQ_NOREQUEST | IRQ_NOPROBE, 0); -} - -static int __init omap_init_irq_of(struct device_node *node) -{ - int ret; - - omap_irq_base = of_iomap(node, 0); - if (WARN_ON(!omap_irq_base)) - return -ENOMEM; - - domain = irq_domain_add_linear(node, omap_nr_irqs, - &irq_generic_chip_ops, NULL); - - omap_irq_soft_reset(); - - ret = omap_alloc_gc_of(domain, omap_irq_base); - if (ret < 0) - irq_domain_remove(domain); - - return ret; -} - -static int __init omap_init_irq_legacy(u32 base) -{ - int j, irq_base; - - omap_irq_base = ioremap(base, SZ_4K); - if (WARN_ON(!omap_irq_base)) - return -ENOMEM; - - irq_base = irq_alloc_descs(-1, 0, omap_nr_irqs, 0); - if (irq_base < 0) { - pr_warn("Couldn't allocate IRQ numbers\n"); - irq_base = 0; - } - - domain = irq_domain_add_legacy(NULL, omap_nr_irqs, irq_base, 0, - &irq_domain_simple_ops, NULL); - - omap_irq_soft_reset(); - - for (j = 0; j < omap_nr_irqs; j += 32) - omap_alloc_gc_legacy(omap_irq_base + j, j + irq_base, 32); - - return 0; -} - -static int __init omap_init_irq(u32 base, struct device_node *node) -{ - if (node) - return omap_init_irq_of(node); - else - return omap_init_irq_legacy(base); -} - -static asmlinkage void __exception_irq_entry -omap_intc_handle_irq(struct pt_regs *regs) -{ - u32 irqnr = 0; - int handled_irq = 0; - int i; - - do { - for (i = 0; i < omap_nr_pending; i++) { - irqnr = intc_readl(INTC_PENDING_IRQ0 + (0x20 * i)); - if (irqnr) - goto out; - } - -out: - if (!irqnr) - break; - - irqnr = intc_readl(INTC_SIR); - irqnr &= ACTIVEIRQ_MASK; - - if (irqnr) { - irqnr = irq_find_mapping(domain, irqnr); - handle_IRQ(irqnr, regs); - handled_irq = 1; - } - } while (irqnr); - - /* If an irq is masked or deasserted while active, we will - * keep ending up here with no irq handled. So remove it from - * the INTC with an ack.*/ - if (!handled_irq) - omap_ack_irq(NULL); -} - -void __init omap2_init_irq(void) -{ - omap_nr_irqs = 96; - omap_nr_pending = 3; - omap_init_irq(OMAP24XX_IC_BASE, NULL); - set_handle_irq(omap_intc_handle_irq); -} - -void __init omap3_init_irq(void) -{ - omap_nr_irqs = 96; - omap_nr_pending = 3; - omap_init_irq(OMAP34XX_IC_BASE, NULL); - set_handle_irq(omap_intc_handle_irq); -} - -void __init ti81xx_init_irq(void) -{ - omap_nr_irqs = 96; - omap_nr_pending = 4; - omap_init_irq(OMAP34XX_IC_BASE, NULL); - set_handle_irq(omap_intc_handle_irq); -} - -static int __init intc_of_init(struct device_node *node, - struct device_node *parent) -{ - struct resource res; - int ret; - - omap_nr_pending = 3; - omap_nr_irqs = 96; - - if (WARN_ON(!node)) - return -ENODEV; - - if (of_address_to_resource(node, 0, &res)) { - WARN(1, "unable to get intc registers\n"); - return -EINVAL; - } - - if (of_device_is_compatible(node, "ti,am33xx-intc")) { - omap_nr_irqs = 128; - omap_nr_pending = 4; - } - - ret = omap_init_irq(-1, of_node_get(node)); - if (ret < 0) - return ret; - - set_handle_irq(omap_intc_handle_irq); - - return 0; -} - -IRQCHIP_DECLARE(omap2_intc, "ti,omap2-intc", intc_of_init); -IRQCHIP_DECLARE(omap3_intc, "ti,omap3-intc", intc_of_init); -IRQCHIP_DECLARE(am33xx_intc, "ti,am33xx-intc", intc_of_init); diff --git a/drivers/irqchip/Kconfig b/drivers/irqchip/Kconfig index b8632bf9a7f3..9d539decf864 100644 --- a/drivers/irqchip/Kconfig +++ b/drivers/irqchip/Kconfig @@ -75,6 +75,11 @@ config OR1K_PIC bool select IRQ_DOMAIN +config OMAP_IRQCHIP + bool + select GENERIC_IRQ_CHIP + select IRQ_DOMAIN + config ORION_IRQCHIP bool select IRQ_DOMAIN diff --git a/drivers/irqchip/Makefile b/drivers/irqchip/Makefile index 73052ba9ca62..d0a2613c73bc 100644 --- a/drivers/irqchip/Makefile +++ b/drivers/irqchip/Makefile @@ -13,6 +13,7 @@ obj-$(CONFIG_ARCH_MOXART) += irq-moxart.o obj-$(CONFIG_CLPS711X_IRQCHIP) += irq-clps711x.o obj-$(CONFIG_OR1K_PIC) += irq-or1k-pic.o obj-$(CONFIG_ORION_IRQCHIP) += irq-orion.o +obj-$(CONFIG_OMAP_IRQCHIP) += irq-omap-intc.o obj-$(CONFIG_ARCH_SUNXI) += irq-sun4i.o obj-$(CONFIG_ARCH_SUNXI) += irq-sunxi-nmi.o obj-$(CONFIG_ARCH_SPEAR3XX) += spear-shirq.o diff --git a/drivers/irqchip/irq-omap-intc.c b/drivers/irqchip/irq-omap-intc.c new file mode 100644 index 000000000000..1478f1a3c400 --- /dev/null +++ b/drivers/irqchip/irq-omap-intc.c @@ -0,0 +1,394 @@ +/* + * linux/arch/arm/mach-omap2/irq.c + * + * Interrupt handler for OMAP2 boards. + * + * Copyright (C) 2005 Nokia Corporation + * Author: Paul Mundt + * + * 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 +#include +#include +#include +#include + +#include "irqchip.h" + +/* Define these here for now until we drop all board-files */ +#define OMAP24XX_IC_BASE 0x480fe000 +#define OMAP34XX_IC_BASE 0x48200000 + +/* selected INTC register offsets */ + +#define INTC_REVISION 0x0000 +#define INTC_SYSCONFIG 0x0010 +#define INTC_SYSSTATUS 0x0014 +#define INTC_SIR 0x0040 +#define INTC_CONTROL 0x0048 +#define INTC_PROTECTION 0x004C +#define INTC_IDLE 0x0050 +#define INTC_THRESHOLD 0x0068 +#define INTC_MIR0 0x0084 +#define INTC_MIR_CLEAR0 0x0088 +#define INTC_MIR_SET0 0x008c +#define INTC_PENDING_IRQ0 0x0098 +#define INTC_PENDING_IRQ1 0x00b8 +#define INTC_PENDING_IRQ2 0x00d8 +#define INTC_PENDING_IRQ3 0x00f8 +#define INTC_ILR0 0x0100 + +#define ACTIVEIRQ_MASK 0x7f /* omap2/3 active interrupt bits */ +#define INTCPS_NR_ILR_REGS 128 +#define INTCPS_NR_MIR_REGS 3 + +/* + * OMAP2 has a number of different interrupt controllers, each interrupt + * controller is identified as its own "bank". Register definitions are + * fairly consistent for each bank, but not all registers are implemented + * for each bank.. when in doubt, consult the TRM. + */ + +/* Structure to save interrupt controller context */ +struct omap_intc_regs { + u32 sysconfig; + u32 protection; + u32 idle; + u32 threshold; + u32 ilr[INTCPS_NR_ILR_REGS]; + u32 mir[INTCPS_NR_MIR_REGS]; +}; +static struct omap_intc_regs intc_context; + +static struct irq_domain *domain; +static void __iomem *omap_irq_base; +static int omap_nr_pending = 3; +static int omap_nr_irqs = 96; + +/* INTC bank register get/set */ +static void intc_writel(u32 reg, u32 val) +{ + writel_relaxed(val, omap_irq_base + reg); +} + +static u32 intc_readl(u32 reg) +{ + return readl_relaxed(omap_irq_base + reg); +} + +void omap_intc_save_context(void) +{ + int i; + + intc_context.sysconfig = + intc_readl(INTC_SYSCONFIG); + intc_context.protection = + intc_readl(INTC_PROTECTION); + intc_context.idle = + intc_readl(INTC_IDLE); + intc_context.threshold = + intc_readl(INTC_THRESHOLD); + + for (i = 0; i < omap_nr_irqs; i++) + intc_context.ilr[i] = + intc_readl((INTC_ILR0 + 0x4 * i)); + for (i = 0; i < INTCPS_NR_MIR_REGS; i++) + intc_context.mir[i] = + intc_readl(INTC_MIR0 + (0x20 * i)); +} + +void omap_intc_restore_context(void) +{ + int i; + + intc_writel(INTC_SYSCONFIG, intc_context.sysconfig); + intc_writel(INTC_PROTECTION, intc_context.protection); + intc_writel(INTC_IDLE, intc_context.idle); + intc_writel(INTC_THRESHOLD, intc_context.threshold); + + for (i = 0; i < omap_nr_irqs; i++) + intc_writel(INTC_ILR0 + 0x4 * i, + intc_context.ilr[i]); + + for (i = 0; i < INTCPS_NR_MIR_REGS; i++) + intc_writel(INTC_MIR0 + 0x20 * i, + intc_context.mir[i]); + /* MIRs are saved and restore with other PRCM registers */ +} + +void omap3_intc_prepare_idle(void) +{ + /* + * Disable autoidle as it can stall interrupt controller, + * cf. errata ID i540 for 3430 (all revisions up to 3.1.x) + */ + intc_writel(INTC_SYSCONFIG, 0); +} + +void omap3_intc_resume_idle(void) +{ + /* Re-enable autoidle */ + intc_writel(INTC_SYSCONFIG, 1); +} + +/* XXX: FIQ and additional INTC support (only MPU at the moment) */ +static void omap_ack_irq(struct irq_data *d) +{ + intc_writel(INTC_CONTROL, 0x1); +} + +static void omap_mask_ack_irq(struct irq_data *d) +{ + irq_gc_mask_disable_reg(d); + omap_ack_irq(d); +} + +static void __init omap_irq_soft_reset(void) +{ + unsigned long tmp; + + tmp = intc_readl(INTC_REVISION) & 0xff; + + pr_info("IRQ: Found an INTC at 0x%p (revision %ld.%ld) with %d interrupts\n", + omap_irq_base, tmp >> 4, tmp & 0xf, omap_nr_irqs); + + tmp = intc_readl(INTC_SYSCONFIG); + tmp |= 1 << 1; /* soft reset */ + intc_writel(INTC_SYSCONFIG, tmp); + + while (!(intc_readl(INTC_SYSSTATUS) & 0x1)) + /* Wait for reset to complete */; + + /* Enable autoidle */ + intc_writel(INTC_SYSCONFIG, 1 << 0); +} + +int omap_irq_pending(void) +{ + int irq; + + for (irq = 0; irq < omap_nr_irqs; irq += 32) + if (intc_readl(INTC_PENDING_IRQ0 + + ((irq >> 5) << 5))) + return 1; + return 0; +} + +void omap3_intc_suspend(void) +{ + /* A pending interrupt would prevent OMAP from entering suspend */ + omap_ack_irq(NULL); +} + +static int __init omap_alloc_gc_of(struct irq_domain *d, void __iomem *base) +{ + int ret; + int i; + + ret = irq_alloc_domain_generic_chips(d, 32, 1, "INTC", + handle_level_irq, IRQ_NOREQUEST | IRQ_NOPROBE, + IRQ_LEVEL, 0); + if (ret) { + pr_warn("Failed to allocate irq chips\n"); + return ret; + } + + for (i = 0; i < omap_nr_pending; i++) { + struct irq_chip_generic *gc; + struct irq_chip_type *ct; + + gc = irq_get_domain_generic_chip(d, 32 * i); + gc->reg_base = base; + ct = gc->chip_types; + + ct->type = IRQ_TYPE_LEVEL_MASK; + ct->handler = handle_level_irq; + + ct->chip.irq_ack = omap_mask_ack_irq; + ct->chip.irq_mask = irq_gc_mask_disable_reg; + ct->chip.irq_unmask = irq_gc_unmask_enable_reg; + + ct->chip.flags |= IRQCHIP_SKIP_SET_WAKE; + + ct->regs.enable = INTC_MIR_CLEAR0 + 32 * i; + ct->regs.disable = INTC_MIR_SET0 + 32 * i; + } + + return 0; +} + +static void __init omap_alloc_gc_legacy(void __iomem *base, + unsigned int irq_start, unsigned int num) +{ + struct irq_chip_generic *gc; + struct irq_chip_type *ct; + + gc = irq_alloc_generic_chip("INTC", 1, irq_start, base, + handle_level_irq); + ct = gc->chip_types; + ct->chip.irq_ack = omap_mask_ack_irq; + ct->chip.irq_mask = irq_gc_mask_disable_reg; + ct->chip.irq_unmask = irq_gc_unmask_enable_reg; + ct->chip.flags |= IRQCHIP_SKIP_SET_WAKE; + + ct->regs.enable = INTC_MIR_CLEAR0; + ct->regs.disable = INTC_MIR_SET0; + irq_setup_generic_chip(gc, IRQ_MSK(num), IRQ_GC_INIT_MASK_CACHE, + IRQ_NOREQUEST | IRQ_NOPROBE, 0); +} + +static int __init omap_init_irq_of(struct device_node *node) +{ + int ret; + + omap_irq_base = of_iomap(node, 0); + if (WARN_ON(!omap_irq_base)) + return -ENOMEM; + + domain = irq_domain_add_linear(node, omap_nr_irqs, + &irq_generic_chip_ops, NULL); + + omap_irq_soft_reset(); + + ret = omap_alloc_gc_of(domain, omap_irq_base); + if (ret < 0) + irq_domain_remove(domain); + + return ret; +} + +static int __init omap_init_irq_legacy(u32 base) +{ + int j, irq_base; + + omap_irq_base = ioremap(base, SZ_4K); + if (WARN_ON(!omap_irq_base)) + return -ENOMEM; + + irq_base = irq_alloc_descs(-1, 0, omap_nr_irqs, 0); + if (irq_base < 0) { + pr_warn("Couldn't allocate IRQ numbers\n"); + irq_base = 0; + } + + domain = irq_domain_add_legacy(NULL, omap_nr_irqs, irq_base, 0, + &irq_domain_simple_ops, NULL); + + omap_irq_soft_reset(); + + for (j = 0; j < omap_nr_irqs; j += 32) + omap_alloc_gc_legacy(omap_irq_base + j, j + irq_base, 32); + + return 0; +} + +static int __init omap_init_irq(u32 base, struct device_node *node) +{ + if (node) + return omap_init_irq_of(node); + else + return omap_init_irq_legacy(base); +} + +static asmlinkage void __exception_irq_entry +omap_intc_handle_irq(struct pt_regs *regs) +{ + u32 irqnr = 0; + int handled_irq = 0; + int i; + + do { + for (i = 0; i < omap_nr_pending; i++) { + irqnr = intc_readl(INTC_PENDING_IRQ0 + (0x20 * i)); + if (irqnr) + goto out; + } + +out: + if (!irqnr) + break; + + irqnr = intc_readl(INTC_SIR); + irqnr &= ACTIVEIRQ_MASK; + + if (irqnr) { + irqnr = irq_find_mapping(domain, irqnr); + handle_IRQ(irqnr, regs); + handled_irq = 1; + } + } while (irqnr); + + /* If an irq is masked or deasserted while active, we will + * keep ending up here with no irq handled. So remove it from + * the INTC with an ack.*/ + if (!handled_irq) + omap_ack_irq(NULL); +} + +void __init omap2_init_irq(void) +{ + omap_nr_irqs = 96; + omap_nr_pending = 3; + omap_init_irq(OMAP24XX_IC_BASE, NULL); + set_handle_irq(omap_intc_handle_irq); +} + +void __init omap3_init_irq(void) +{ + omap_nr_irqs = 96; + omap_nr_pending = 3; + omap_init_irq(OMAP34XX_IC_BASE, NULL); + set_handle_irq(omap_intc_handle_irq); +} + +void __init ti81xx_init_irq(void) +{ + omap_nr_irqs = 96; + omap_nr_pending = 4; + omap_init_irq(OMAP34XX_IC_BASE, NULL); + set_handle_irq(omap_intc_handle_irq); +} + +static int __init intc_of_init(struct device_node *node, + struct device_node *parent) +{ + struct resource res; + int ret; + + omap_nr_pending = 3; + omap_nr_irqs = 96; + + if (WARN_ON(!node)) + return -ENODEV; + + if (of_address_to_resource(node, 0, &res)) { + WARN(1, "unable to get intc registers\n"); + return -EINVAL; + } + + if (of_device_is_compatible(node, "ti,am33xx-intc")) { + omap_nr_irqs = 128; + omap_nr_pending = 4; + } + + ret = omap_init_irq(-1, of_node_get(node)); + if (ret < 0) + return ret; + + set_handle_irq(omap_intc_handle_irq); + + return 0; +} + +IRQCHIP_DECLARE(omap2_intc, "ti,omap2-intc", intc_of_init); +IRQCHIP_DECLARE(omap3_intc, "ti,omap3-intc", intc_of_init); +IRQCHIP_DECLARE(am33xx_intc, "ti,am33xx-intc", intc_of_init); -- cgit v1.2.1 From 6bd0f16e322b4572fe1e0ff3b0e78c800c2633b4 Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Mon, 15 Sep 2014 16:15:03 -0500 Subject: irqchip: omap-intc: minor improvement to omap_irq_pending() We already hold the number of Pending registers in omap_nr_pending. Let's use that instead. Acked-by: Jason Cooper Signed-off-by: Felipe Balbi Signed-off-by: Tony Lindgren --- drivers/irqchip/irq-omap-intc.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/drivers/irqchip/irq-omap-intc.c b/drivers/irqchip/irq-omap-intc.c index 1478f1a3c400..2933760f95aa 100644 --- a/drivers/irqchip/irq-omap-intc.c +++ b/drivers/irqchip/irq-omap-intc.c @@ -174,11 +174,10 @@ static void __init omap_irq_soft_reset(void) int omap_irq_pending(void) { - int irq; + int i; - for (irq = 0; irq < omap_nr_irqs; irq += 32) - if (intc_readl(INTC_PENDING_IRQ0 + - ((irq >> 5) << 5))) + for (i = 0; i < omap_nr_pending; i++) + if (intc_readl(INTC_PENDING_IRQ0 + (0x20 * i))) return 1; return 0; } -- cgit v1.2.1 From 503b8d12e7462df0aea9b31ab65ac17b538540c9 Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Mon, 15 Sep 2014 16:15:04 -0500 Subject: irqchip: omap-intc: comment style cleanup no functional changes, just making sure comment follows Coding Style. Acked-by: Jason Cooper Signed-off-by: Felipe Balbi Signed-off-by: Tony Lindgren --- drivers/irqchip/irq-omap-intc.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/drivers/irqchip/irq-omap-intc.c b/drivers/irqchip/irq-omap-intc.c index 2933760f95aa..f4d562ee3f29 100644 --- a/drivers/irqchip/irq-omap-intc.c +++ b/drivers/irqchip/irq-omap-intc.c @@ -326,9 +326,11 @@ out: } } while (irqnr); - /* If an irq is masked or deasserted while active, we will + /* + * If an irq is masked or deasserted while active, we will * keep ending up here with no irq handled. So remove it from - * the INTC with an ack.*/ + * the INTC with an ack. + */ if (!handled_irq) omap_ack_irq(NULL); } -- cgit v1.2.1 From 8bb3b375c8837ea73740883a90b1d7719b2653cc Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Mon, 15 Sep 2014 16:15:05 -0500 Subject: irqchip: omap-intc: remove unnecesary of_address_to_resource() call of_iomap(), which is called from omap_init_irq_of(), already takes care of making sure we have a valid resource to deal with. Because of that, we can safely remove our explicit call to of_address_to_resource(). Acked-by: Jason Cooper Signed-off-by: Felipe Balbi Signed-off-by: Tony Lindgren --- drivers/irqchip/irq-omap-intc.c | 6 ------ 1 file changed, 6 deletions(-) diff --git a/drivers/irqchip/irq-omap-intc.c b/drivers/irqchip/irq-omap-intc.c index f4d562ee3f29..e97b53539386 100644 --- a/drivers/irqchip/irq-omap-intc.c +++ b/drivers/irqchip/irq-omap-intc.c @@ -362,7 +362,6 @@ void __init ti81xx_init_irq(void) static int __init intc_of_init(struct device_node *node, struct device_node *parent) { - struct resource res; int ret; omap_nr_pending = 3; @@ -371,11 +370,6 @@ static int __init intc_of_init(struct device_node *node, if (WARN_ON(!node)) return -ENODEV; - if (of_address_to_resource(node, 0, &res)) { - WARN(1, "unable to get intc registers\n"); - return -EINVAL; - } - if (of_device_is_compatible(node, "ti,am33xx-intc")) { omap_nr_irqs = 128; omap_nr_pending = 4; -- cgit v1.2.1 From 9836ee9f87dc669c8ce2b24ba986600a6977ca8b Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Mon, 15 Sep 2014 16:15:06 -0500 Subject: irqchip: omap-intc: enable IP protection When PROTECTION bit in enabled in PROTECTION register, INTC's registers are only accessible from privileged mode. Acked-by: Jason Cooper Signed-off-by: Felipe Balbi Signed-off-by: Tony Lindgren --- drivers/irqchip/irq-omap-intc.c | 22 ++++++++++++++++++++-- 1 file changed, 20 insertions(+), 2 deletions(-) diff --git a/drivers/irqchip/irq-omap-intc.c b/drivers/irqchip/irq-omap-intc.c index e97b53539386..7681b1329976 100644 --- a/drivers/irqchip/irq-omap-intc.c +++ b/drivers/irqchip/irq-omap-intc.c @@ -51,6 +51,8 @@ #define INTCPS_NR_ILR_REGS 128 #define INTCPS_NR_MIR_REGS 3 +#define INTC_PROTECTION_ENABLE (1 << 0) + /* * OMAP2 has a number of different interrupt controllers, each interrupt * controller is identified as its own "bank". Register definitions are @@ -290,12 +292,28 @@ static int __init omap_init_irq_legacy(u32 base) return 0; } +static void __init omap_irq_enable_protection(void) +{ + u32 reg; + + reg = intc_readl(INTC_PROTECTION); + reg |= INTC_PROTECTION_ENABLE; + intc_writel(INTC_PROTECTION, reg); +} + static int __init omap_init_irq(u32 base, struct device_node *node) { + int ret; + if (node) - return omap_init_irq_of(node); + ret = omap_init_irq_of(node); else - return omap_init_irq_legacy(base); + ret = omap_init_irq_legacy(base); + + if (ret == 0) + omap_irq_enable_protection(); + + return ret; } static asmlinkage void __exception_irq_entry -- cgit v1.2.1 From b30791498a140d9460dfb9cb8edcb6fd1065363c Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Mon, 15 Sep 2014 16:15:07 -0500 Subject: irqchip: omap-intc: enable TURBO idle mode When TURBO bit is set in the INTC_IDLE register, the input synchronizer clock will be autogated based on activity on the INTC. Because this idle mode increases the interrupt latency by 2 clock cycles, we're only enabling it during suspend. Acked-by: Jason Cooper Signed-off-by: Felipe Balbi Signed-off-by: Tony Lindgren --- drivers/irqchip/irq-omap-intc.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/drivers/irqchip/irq-omap-intc.c b/drivers/irqchip/irq-omap-intc.c index 7681b1329976..dfa2d9de5361 100644 --- a/drivers/irqchip/irq-omap-intc.c +++ b/drivers/irqchip/irq-omap-intc.c @@ -51,6 +51,9 @@ #define INTCPS_NR_ILR_REGS 128 #define INTCPS_NR_MIR_REGS 3 +#define INTC_IDLE_FUNCIDLE (1 << 0) +#define INTC_IDLE_TURBO (1 << 1) + #define INTC_PROTECTION_ENABLE (1 << 0) /* @@ -134,12 +137,14 @@ void omap3_intc_prepare_idle(void) * cf. errata ID i540 for 3430 (all revisions up to 3.1.x) */ intc_writel(INTC_SYSCONFIG, 0); + intc_writel(INTC_IDLE, INTC_IDLE_TURBO); } void omap3_intc_resume_idle(void) { /* Re-enable autoidle */ intc_writel(INTC_SYSCONFIG, 1); + intc_writel(INTC_IDLE, 0); } /* XXX: FIQ and additional INTC support (only MPU at the moment) */ -- cgit v1.2.1 From 74b6c8ef546dec98f1817147d31f17a1f3aab09e Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Mon, 15 Sep 2014 16:15:08 -0500 Subject: irqchip: omap-intc: correct maximum number or MIR registers maximum number of MIR register is 4, rather than 3. Fix that. Acked-by: Jason Cooper Signed-off-by: Felipe Balbi Signed-off-by: Tony Lindgren --- drivers/irqchip/irq-omap-intc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/irqchip/irq-omap-intc.c b/drivers/irqchip/irq-omap-intc.c index dfa2d9de5361..976d4c15fefd 100644 --- a/drivers/irqchip/irq-omap-intc.c +++ b/drivers/irqchip/irq-omap-intc.c @@ -49,7 +49,7 @@ #define ACTIVEIRQ_MASK 0x7f /* omap2/3 active interrupt bits */ #define INTCPS_NR_ILR_REGS 128 -#define INTCPS_NR_MIR_REGS 3 +#define INTCPS_NR_MIR_REGS 4 #define INTC_IDLE_FUNCIDLE (1 << 0) #define INTC_IDLE_TURBO (1 << 1) -- cgit v1.2.1 From 64d5947b9ee1284b38b8e212e7c94024452b2bb4 Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Mon, 15 Sep 2014 16:15:09 -0500 Subject: irqchip: omap-intc: remove unnecessary comments no fuctional changes. Acked-by: Jason Cooper Signed-off-by: Felipe Balbi Signed-off-by: Tony Lindgren --- drivers/irqchip/irq-omap-intc.c | 9 --------- 1 file changed, 9 deletions(-) diff --git a/drivers/irqchip/irq-omap-intc.c b/drivers/irqchip/irq-omap-intc.c index 976d4c15fefd..f3814e79192d 100644 --- a/drivers/irqchip/irq-omap-intc.c +++ b/drivers/irqchip/irq-omap-intc.c @@ -56,14 +56,6 @@ #define INTC_PROTECTION_ENABLE (1 << 0) -/* - * OMAP2 has a number of different interrupt controllers, each interrupt - * controller is identified as its own "bank". Register definitions are - * fairly consistent for each bank, but not all registers are implemented - * for each bank.. when in doubt, consult the TRM. - */ - -/* Structure to save interrupt controller context */ struct omap_intc_regs { u32 sysconfig; u32 protection; @@ -79,7 +71,6 @@ static void __iomem *omap_irq_base; static int omap_nr_pending = 3; static int omap_nr_irqs = 96; -/* INTC bank register get/set */ static void intc_writel(u32 reg, u32 val) { writel_relaxed(val, omap_irq_base + reg); -- cgit v1.2.1 From 9765d2d9430971ca89b09af076e7a8ff250df473 Mon Sep 17 00:00:00 2001 From: Chen-Yu Tsai Date: Tue, 26 Aug 2014 11:54:55 +0800 Subject: rtc: sun6i: Add sun6i RTC driver This patch introduces the driver for the RTC in the Allwinner A31 and A23 SoCs. Unlike the RTC found in A10/A20 SoCs, which was part of the timer, the RTC in A31/A23 are a separate hardware block, which also contain a few controls for the RTC block hardware (a regulator and RTC block GPIO pin latches), while also having separate interrupts for the alarms. The hardware is different enough to make a different driver for it. Signed-off-by: Chen-Yu Tsai Reviewed-by: Varka Bhadram Signed-off-by: Maxime Ripard --- .../devicetree/bindings/rtc/sun6i-rtc.txt | 17 + drivers/rtc/Kconfig | 7 + drivers/rtc/Makefile | 1 + drivers/rtc/rtc-sun6i.c | 447 +++++++++++++++++++++ 4 files changed, 472 insertions(+) create mode 100644 Documentation/devicetree/bindings/rtc/sun6i-rtc.txt create mode 100644 drivers/rtc/rtc-sun6i.c diff --git a/Documentation/devicetree/bindings/rtc/sun6i-rtc.txt b/Documentation/devicetree/bindings/rtc/sun6i-rtc.txt new file mode 100644 index 000000000000..f007e428a1ab --- /dev/null +++ b/Documentation/devicetree/bindings/rtc/sun6i-rtc.txt @@ -0,0 +1,17 @@ +* sun6i Real Time Clock + +RTC controller for the Allwinner A31 + +Required properties: +- compatible : Should be "allwinner,sun6i-a31-rtc" +- reg : physical base address of the controller and length of + memory mapped region. +- interrupts : IRQ lines for the RTC alarm 0 and alarm 1, in that order. + +Example: + +rtc: rtc@01f00000 { + compatible = "allwinner,sun6i-a31-rtc"; + reg = <0x01f00000 0x54>; + interrupts = <0 40 4>, <0 41 4>; +}; diff --git a/drivers/rtc/Kconfig b/drivers/rtc/Kconfig index a168e96142b9..f2300b4e9b17 100644 --- a/drivers/rtc/Kconfig +++ b/drivers/rtc/Kconfig @@ -1175,6 +1175,13 @@ config RTC_DRV_SUN4V If you say Y here you will get support for the Hypervisor based RTC on SUN4V systems. +config RTC_DRV_SUN6I + tristate "Allwinner A31 RTC" + depends on MACH_SUN6I || MACH_SUN8I + help + If you say Y here you will get support for the RTC found on + Allwinner A31. + config RTC_DRV_SUNXI tristate "Allwinner sun4i/sun7i RTC" depends on ARCH_SUNXI diff --git a/drivers/rtc/Makefile b/drivers/rtc/Makefile index 56f061c7c815..9055b7dd3dc5 100644 --- a/drivers/rtc/Makefile +++ b/drivers/rtc/Makefile @@ -128,6 +128,7 @@ obj-$(CONFIG_RTC_DRV_STARFIRE) += rtc-starfire.o obj-$(CONFIG_RTC_DRV_STK17TA8) += rtc-stk17ta8.o obj-$(CONFIG_RTC_DRV_STMP) += rtc-stmp3xxx.o obj-$(CONFIG_RTC_DRV_SUN4V) += rtc-sun4v.o +obj-$(CONFIG_RTC_DRV_SUN6I) += rtc-sun6i.o obj-$(CONFIG_RTC_DRV_SUNXI) += rtc-sunxi.o obj-$(CONFIG_RTC_DRV_TEGRA) += rtc-tegra.o obj-$(CONFIG_RTC_DRV_TEST) += rtc-test.o diff --git a/drivers/rtc/rtc-sun6i.c b/drivers/rtc/rtc-sun6i.c new file mode 100644 index 000000000000..c169a2cd4727 --- /dev/null +++ b/drivers/rtc/rtc-sun6i.c @@ -0,0 +1,447 @@ +/* + * An RTC driver for Allwinner A31/A23 + * + * Copyright (c) 2014, Chen-Yu Tsai + * + * based on rtc-sunxi.c + * + * An RTC driver for Allwinner A10/A20 + * + * Copyright (c) 2013, Carlo Caione + * + * 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. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* Control register */ +#define SUN6I_LOSC_CTRL 0x0000 +#define SUN6I_LOSC_CTRL_ALM_DHMS_ACC BIT(9) +#define SUN6I_LOSC_CTRL_RTC_HMS_ACC BIT(8) +#define SUN6I_LOSC_CTRL_RTC_YMD_ACC BIT(7) +#define SUN6I_LOSC_CTRL_ACC_MASK GENMASK(9, 7) + +/* RTC */ +#define SUN6I_RTC_YMD 0x0010 +#define SUN6I_RTC_HMS 0x0014 + +/* Alarm 0 (counter) */ +#define SUN6I_ALRM_COUNTER 0x0020 +#define SUN6I_ALRM_CUR_VAL 0x0024 +#define SUN6I_ALRM_EN 0x0028 +#define SUN6I_ALRM_EN_CNT_EN BIT(0) +#define SUN6I_ALRM_IRQ_EN 0x002c +#define SUN6I_ALRM_IRQ_EN_CNT_IRQ_EN BIT(0) +#define SUN6I_ALRM_IRQ_STA 0x0030 +#define SUN6I_ALRM_IRQ_STA_CNT_IRQ_PEND BIT(0) + +/* Alarm 1 (wall clock) */ +#define SUN6I_ALRM1_EN 0x0044 +#define SUN6I_ALRM1_IRQ_EN 0x0048 +#define SUN6I_ALRM1_IRQ_STA 0x004c +#define SUN6I_ALRM1_IRQ_STA_WEEK_IRQ_PEND BIT(0) + +/* Alarm config */ +#define SUN6I_ALARM_CONFIG 0x0050 +#define SUN6I_ALARM_CONFIG_WAKEUP BIT(0) + +/* + * Get date values + */ +#define SUN6I_DATE_GET_DAY_VALUE(x) ((x) & 0x0000001f) +#define SUN6I_DATE_GET_MON_VALUE(x) (((x) & 0x00000f00) >> 8) +#define SUN6I_DATE_GET_YEAR_VALUE(x) (((x) & 0x003f0000) >> 16) +#define SUN6I_LEAP_GET_VALUE(x) (((x) & 0x00400000) >> 22) + +/* + * Get time values + */ +#define SUN6I_TIME_GET_SEC_VALUE(x) ((x) & 0x0000003f) +#define SUN6I_TIME_GET_MIN_VALUE(x) (((x) & 0x00003f00) >> 8) +#define SUN6I_TIME_GET_HOUR_VALUE(x) (((x) & 0x001f0000) >> 16) + +/* + * Set date values + */ +#define SUN6I_DATE_SET_DAY_VALUE(x) ((x) & 0x0000001f) +#define SUN6I_DATE_SET_MON_VALUE(x) ((x) << 8 & 0x00000f00) +#define SUN6I_DATE_SET_YEAR_VALUE(x) ((x) << 16 & 0x003f0000) +#define SUN6I_LEAP_SET_VALUE(x) ((x) << 22 & 0x00400000) + +/* + * Set time values + */ +#define SUN6I_TIME_SET_SEC_VALUE(x) ((x) & 0x0000003f) +#define SUN6I_TIME_SET_MIN_VALUE(x) ((x) << 8 & 0x00003f00) +#define SUN6I_TIME_SET_HOUR_VALUE(x) ((x) << 16 & 0x001f0000) + +/* + * The year parameter passed to the driver is usually an offset relative to + * the year 1900. This macro is used to convert this offset to another one + * relative to the minimum year allowed by the hardware. + * + * The year range is 1970 - 2033. This range is selected to match Allwinner's + * driver, even though it is somewhat limited. + */ +#define SUN6I_YEAR_MIN 1970 +#define SUN6I_YEAR_MAX 2033 +#define SUN6I_YEAR_OFF (SUN6I_YEAR_MIN - 1900) + +struct sun6i_rtc_dev { + struct rtc_device *rtc; + struct device *dev; + void __iomem *base; + int irq; + unsigned long alarm; +}; + +static irqreturn_t sun6i_rtc_alarmirq(int irq, void *id) +{ + struct sun6i_rtc_dev *chip = (struct sun6i_rtc_dev *) id; + u32 val; + + val = readl(chip->base + SUN6I_ALRM_IRQ_STA); + + if (val & SUN6I_ALRM_IRQ_STA_CNT_IRQ_PEND) { + val |= SUN6I_ALRM_IRQ_STA_CNT_IRQ_PEND; + writel(val, chip->base + SUN6I_ALRM_IRQ_STA); + + rtc_update_irq(chip->rtc, 1, RTC_AF | RTC_IRQF); + + return IRQ_HANDLED; + } + + return IRQ_NONE; +} + +static void sun6i_rtc_setaie(int to, struct sun6i_rtc_dev *chip) +{ + u32 alrm_val = 0; + u32 alrm_irq_val = 0; + u32 alrm_wake_val = 0; + + if (to) { + alrm_val = SUN6I_ALRM_EN_CNT_EN; + alrm_irq_val = SUN6I_ALRM_IRQ_EN_CNT_IRQ_EN; + alrm_wake_val = SUN6I_ALARM_CONFIG_WAKEUP; + } else { + writel(SUN6I_ALRM_IRQ_STA_CNT_IRQ_PEND, + chip->base + SUN6I_ALRM_IRQ_STA); + } + + writel(alrm_val, chip->base + SUN6I_ALRM_EN); + writel(alrm_irq_val, chip->base + SUN6I_ALRM_IRQ_EN); + writel(alrm_wake_val, chip->base + SUN6I_ALARM_CONFIG); +} + +static int sun6i_rtc_gettime(struct device *dev, struct rtc_time *rtc_tm) +{ + struct sun6i_rtc_dev *chip = dev_get_drvdata(dev); + u32 date, time; + + /* + * read again in case it changes + */ + do { + date = readl(chip->base + SUN6I_RTC_YMD); + time = readl(chip->base + SUN6I_RTC_HMS); + } while ((date != readl(chip->base + SUN6I_RTC_YMD)) || + (time != readl(chip->base + SUN6I_RTC_HMS))); + + rtc_tm->tm_sec = SUN6I_TIME_GET_SEC_VALUE(time); + rtc_tm->tm_min = SUN6I_TIME_GET_MIN_VALUE(time); + rtc_tm->tm_hour = SUN6I_TIME_GET_HOUR_VALUE(time); + + rtc_tm->tm_mday = SUN6I_DATE_GET_DAY_VALUE(date); + rtc_tm->tm_mon = SUN6I_DATE_GET_MON_VALUE(date); + rtc_tm->tm_year = SUN6I_DATE_GET_YEAR_VALUE(date); + + rtc_tm->tm_mon -= 1; + + /* + * switch from (data_year->min)-relative offset to + * a (1900)-relative one + */ + rtc_tm->tm_year += SUN6I_YEAR_OFF; + + return rtc_valid_tm(rtc_tm); +} + +static int sun6i_rtc_getalarm(struct device *dev, struct rtc_wkalrm *wkalrm) +{ + struct sun6i_rtc_dev *chip = dev_get_drvdata(dev); + u32 alrm_st; + u32 alrm_en; + + alrm_en = readl(chip->base + SUN6I_ALRM_IRQ_EN); + alrm_st = readl(chip->base + SUN6I_ALRM_IRQ_STA); + wkalrm->enabled = !!(alrm_en & SUN6I_ALRM_EN_CNT_EN); + wkalrm->pending = !!(alrm_st & SUN6I_ALRM_EN_CNT_EN); + rtc_time_to_tm(chip->alarm, &wkalrm->time); + + return 0; +} + +static int sun6i_rtc_setalarm(struct device *dev, struct rtc_wkalrm *wkalrm) +{ + struct sun6i_rtc_dev *chip = dev_get_drvdata(dev); + struct rtc_time *alrm_tm = &wkalrm->time; + struct rtc_time tm_now; + unsigned long time_now = 0; + unsigned long time_set = 0; + unsigned long time_gap = 0; + int ret = 0; + + ret = sun6i_rtc_gettime(dev, &tm_now); + if (ret < 0) { + dev_err(dev, "Error in getting time\n"); + return -EINVAL; + } + + rtc_tm_to_time(alrm_tm, &time_set); + rtc_tm_to_time(&tm_now, &time_now); + if (time_set <= time_now) { + dev_err(dev, "Date to set in the past\n"); + return -EINVAL; + } + + time_gap = time_set - time_now; + + if (time_gap > U32_MAX) { + dev_err(dev, "Date too far in the future\n"); + return -EINVAL; + } + + sun6i_rtc_setaie(0, chip); + writel(0, chip->base + SUN6I_ALRM_COUNTER); + usleep_range(100, 300); + + writel(time_gap, chip->base + SUN6I_ALRM_COUNTER); + chip->alarm = time_set; + + sun6i_rtc_setaie(wkalrm->enabled, chip); + + return 0; +} + +static int sun6i_rtc_wait(struct sun6i_rtc_dev *chip, int offset, + unsigned int mask, unsigned int ms_timeout) +{ + const unsigned long timeout = jiffies + msecs_to_jiffies(ms_timeout); + u32 reg; + + do { + reg = readl(chip->base + offset); + reg &= mask; + + if (!reg) + return 0; + + } while (time_before(jiffies, timeout)); + + return -ETIMEDOUT; +} + +static int sun6i_rtc_settime(struct device *dev, struct rtc_time *rtc_tm) +{ + struct sun6i_rtc_dev *chip = dev_get_drvdata(dev); + u32 date = 0; + u32 time = 0; + int year; + + year = rtc_tm->tm_year + 1900; + if (year < SUN6I_YEAR_MIN || year > SUN6I_YEAR_MAX) { + dev_err(dev, "rtc only supports year in range %d - %d\n", + SUN6I_YEAR_MIN, SUN6I_YEAR_MAX); + return -EINVAL; + } + + rtc_tm->tm_year -= SUN6I_YEAR_OFF; + rtc_tm->tm_mon += 1; + + date = SUN6I_DATE_SET_DAY_VALUE(rtc_tm->tm_mday) | + SUN6I_DATE_SET_MON_VALUE(rtc_tm->tm_mon) | + SUN6I_DATE_SET_YEAR_VALUE(rtc_tm->tm_year); + + if (is_leap_year(year)) + date |= SUN6I_LEAP_SET_VALUE(1); + + time = SUN6I_TIME_SET_SEC_VALUE(rtc_tm->tm_sec) | + SUN6I_TIME_SET_MIN_VALUE(rtc_tm->tm_min) | + SUN6I_TIME_SET_HOUR_VALUE(rtc_tm->tm_hour); + + /* Check whether registers are writable */ + if (sun6i_rtc_wait(chip, SUN6I_LOSC_CTRL, + SUN6I_LOSC_CTRL_ACC_MASK, 50)) { + dev_err(dev, "rtc is still busy.\n"); + return -EBUSY; + } + + writel(time, chip->base + SUN6I_RTC_HMS); + + /* + * After writing the RTC HH-MM-SS register, the + * SUN6I_LOSC_CTRL_RTC_HMS_ACC bit is set and it will not + * be cleared until the real writing operation is finished + */ + + if (sun6i_rtc_wait(chip, SUN6I_LOSC_CTRL, + SUN6I_LOSC_CTRL_RTC_HMS_ACC, 50)) { + dev_err(dev, "Failed to set rtc time.\n"); + return -ETIMEDOUT; + } + + writel(date, chip->base + SUN6I_RTC_YMD); + + /* + * After writing the RTC YY-MM-DD register, the + * SUN6I_LOSC_CTRL_RTC_YMD_ACC bit is set and it will not + * be cleared until the real writing operation is finished + */ + + if (sun6i_rtc_wait(chip, SUN6I_LOSC_CTRL, + SUN6I_LOSC_CTRL_RTC_YMD_ACC, 50)) { + dev_err(dev, "Failed to set rtc time.\n"); + return -ETIMEDOUT; + } + + return 0; +} + +static int sun6i_rtc_alarm_irq_enable(struct device *dev, unsigned int enabled) +{ + struct sun6i_rtc_dev *chip = dev_get_drvdata(dev); + + if (!enabled) + sun6i_rtc_setaie(enabled, chip); + + return 0; +} + +static const struct rtc_class_ops sun6i_rtc_ops = { + .read_time = sun6i_rtc_gettime, + .set_time = sun6i_rtc_settime, + .read_alarm = sun6i_rtc_getalarm, + .set_alarm = sun6i_rtc_setalarm, + .alarm_irq_enable = sun6i_rtc_alarm_irq_enable +}; + +static int sun6i_rtc_probe(struct platform_device *pdev) +{ + struct sun6i_rtc_dev *chip; + struct resource *res; + int ret; + + chip = devm_kzalloc(&pdev->dev, sizeof(*chip), GFP_KERNEL); + if (!chip) + return -ENOMEM; + + platform_set_drvdata(pdev, chip); + chip->dev = &pdev->dev; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + chip->base = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(chip->base)) + return PTR_ERR(chip->base); + + chip->irq = platform_get_irq(pdev, 0); + if (chip->irq < 0) { + dev_err(&pdev->dev, "No IRQ resource\n"); + return chip->irq; + } + + ret = devm_request_irq(&pdev->dev, chip->irq, sun6i_rtc_alarmirq, + 0, dev_name(&pdev->dev), chip); + if (ret) { + dev_err(&pdev->dev, "Could not request IRQ\n"); + return ret; + } + + /* clear the alarm counter value */ + writel(0, chip->base + SUN6I_ALRM_COUNTER); + + /* disable counter alarm */ + writel(0, chip->base + SUN6I_ALRM_EN); + + /* disable counter alarm interrupt */ + writel(0, chip->base + SUN6I_ALRM_IRQ_EN); + + /* disable week alarm */ + writel(0, chip->base + SUN6I_ALRM1_EN); + + /* disable week alarm interrupt */ + writel(0, chip->base + SUN6I_ALRM1_IRQ_EN); + + /* clear counter alarm pending interrupts */ + writel(SUN6I_ALRM_IRQ_STA_CNT_IRQ_PEND, + chip->base + SUN6I_ALRM_IRQ_STA); + + /* clear week alarm pending interrupts */ + writel(SUN6I_ALRM1_IRQ_STA_WEEK_IRQ_PEND, + chip->base + SUN6I_ALRM1_IRQ_STA); + + /* disable alarm wakeup */ + writel(0, chip->base + SUN6I_ALARM_CONFIG); + + chip->rtc = rtc_device_register("rtc-sun6i", &pdev->dev, + &sun6i_rtc_ops, THIS_MODULE); + if (IS_ERR(chip->rtc)) { + dev_err(&pdev->dev, "unable to register device\n"); + return PTR_ERR(chip->rtc); + } + + dev_info(&pdev->dev, "RTC enabled\n"); + + return 0; +} + +static int sun6i_rtc_remove(struct platform_device *pdev) +{ + struct sun6i_rtc_dev *chip = platform_get_drvdata(pdev); + + rtc_device_unregister(chip->rtc); + + return 0; +} + +static const struct of_device_id sun6i_rtc_dt_ids[] = { + { .compatible = "allwinner,sun6i-a31-rtc" }, + { /* sentinel */ }, +}; +MODULE_DEVICE_TABLE(of, sun6i_rtc_dt_ids); + +static struct platform_driver sun6i_rtc_driver = { + .probe = sun6i_rtc_probe, + .remove = sun6i_rtc_remove, + .driver = { + .name = "sun6i-rtc", + .of_match_table = sun6i_rtc_dt_ids, + }, +}; + +module_platform_driver(sun6i_rtc_driver); + +MODULE_DESCRIPTION("sun6i RTC driver"); +MODULE_AUTHOR("Chen-Yu Tsai "); +MODULE_LICENSE("GPL"); -- cgit v1.2.1 From 64a1925c00706253aa887431fc45f96db48aa727 Mon Sep 17 00:00:00 2001 From: Chen-Yu Tsai Date: Tue, 26 Aug 2014 11:54:56 +0800 Subject: rtc: sunxi: Depend on platforms sun4i/sun7i that actually have the rtc Now that we have Kconfig options for individual sunxi platforms, let the rtc-sunxi driver depend on the platforms that actually have this hardware, sun4i and sun7i. Signed-off-by: Chen-Yu Tsai Signed-off-by: Maxime Ripard --- drivers/rtc/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/rtc/Kconfig b/drivers/rtc/Kconfig index f2300b4e9b17..6b34bc94a3f6 100644 --- a/drivers/rtc/Kconfig +++ b/drivers/rtc/Kconfig @@ -1184,7 +1184,7 @@ config RTC_DRV_SUN6I config RTC_DRV_SUNXI tristate "Allwinner sun4i/sun7i RTC" - depends on ARCH_SUNXI + depends on MACH_SUN4I || MACH_SUN7I help If you say Y here you will get support for the RTC found on Allwinner A10/A20. -- cgit v1.2.1 From a4dfb8c41043dd6c2b9defbe846c44389c4b6f02 Mon Sep 17 00:00:00 2001 From: Sandeep Nair Date: Fri, 28 Feb 2014 10:47:50 -0500 Subject: Documentation: dt: soc: add Keystone Navigator QMSS bindings The QMSS (Queue Manager Sub System) found on Keystone SOCs is one of the main hardware sub system which forms the backbone of the Keystone Multi-core Navigator. QMSS consist of queue managers, packed-data structure processors(PDSP), linking RAM, descriptor pools and infrastructure Packet DMA. The Queue Manager is a hardware module that is responsible for accelerating management of the packet queues. Packets are queued/de-queued by writing or reading descriptor address to a particular memory mapped location. The PDSPs perform QMSS related functions like accumulation, QoS, or event management. Linking RAM registers are used to link the descriptors which are stored in descriptor RAM. Descriptor RAM is configurable as internal or external memory. Cc: Greg Kroah-Hartman Cc: Kumar Gala Cc: Olof Johansson Cc: Arnd Bergmann Cc: Grant Likely Cc: Rob Herring Cc: Mark Rutland Signed-off-by: Sandeep Nair Signed-off-by: Santosh Shilimkar --- .../bindings/soc/ti/keystone-navigator-qmss.txt | 232 +++++++++++++++++++++ 1 file changed, 232 insertions(+) create mode 100644 Documentation/devicetree/bindings/soc/ti/keystone-navigator-qmss.txt diff --git a/Documentation/devicetree/bindings/soc/ti/keystone-navigator-qmss.txt b/Documentation/devicetree/bindings/soc/ti/keystone-navigator-qmss.txt new file mode 100644 index 000000000000..d8e8cdb733f9 --- /dev/null +++ b/Documentation/devicetree/bindings/soc/ti/keystone-navigator-qmss.txt @@ -0,0 +1,232 @@ +* Texas Instruments Keystone Navigator Queue Management SubSystem driver + +The QMSS (Queue Manager Sub System) found on Keystone SOCs is one of +the main hardware sub system which forms the backbone of the Keystone +multi-core Navigator. QMSS consist of queue managers, packed-data structure +processors(PDSP), linking RAM, descriptor pools and infrastructure +Packet DMA. +The Queue Manager is a hardware module that is responsible for accelerating +management of the packet queues. Packets are queued/de-queued by writing or +reading descriptor address to a particular memory mapped location. The PDSPs +perform QMSS related functions like accumulation, QoS, or event management. +Linking RAM registers are used to link the descriptors which are stored in +descriptor RAM. Descriptor RAM is configurable as internal or external memory. +The QMSS driver manages the PDSP setups, linking RAM regions, +queue pool management (allocation, push, pop and notify) and descriptor +pool management. + + +Required properties: +- compatible : Must be "ti,keystone-navigator-qmss"; +- clocks : phandle to the reference clock for this device. +- queue-range : total range of queue numbers for the device. +- linkram0 :
for internal link ram, where size is the total + link ram entries. +- linkram1 :
for external link ram, where size is the total + external link ram entries. If the address is specified as "0" + driver will allocate memory. +- qmgrs : child node describing the individual queue managers on the + SoC. On keystone 1 devices there should be only one node. + On keystone 2 devices there can be more than 1 node. + -- managed-queues : the actual queues managed by each queue manager + instance, specified as <"base queue #" "# of queues">. + -- reg : Address and size of the register set for the device. + Register regions should be specified in the following + order + - Queue Peek region. + - Queue status RAM. + - Queue configuration region. + - Descriptor memory setup region. + - Queue Management/Queue Proxy region for queue Push. + - Queue Management/Queue Proxy region for queue Pop. +- queue-pools : child node classifying the queue ranges into pools. + Queue ranges are grouped into 3 type of pools: + - qpend : pool of qpend(interruptible) queues + - general-purpose : pool of general queues, primarly used + as free descriptor queues or the + transmit DMA queues. + - accumulator : pool of queues on PDSP accumulator channel + Each range can have the following properties: + -- qrange : number of queues to use per queue range, specified as + <"base queue #" "# of queues">. + -- interrupts : Optional property to specify the interrupt mapping + for interruptible queues. The driver additionaly sets + the interrupt affinity hint based on the cpu mask. + -- qalloc-by-id : Optional property to specify that the queues in this + range can only be allocated by queue id. + -- accumulator : Accumulator channel specification. Any of the PDSPs in + QMSS can be loaded with the accumulator firmware. The + accumulator firmware’s job is to poll a select number of + queues looking for descriptors that have been pushed + into them. Descriptors are popped from the queue and + placed in a buffer provided by the host. When the list + becomes full or a programmed time period expires, the + accumulator triggers an interrupt to the host to read + the buffer for descriptor information. This firmware + comes in 16, 32, and 48 channel builds. Each of these + channels can be configured to monitor 32 contiguous + queues. Accumulator channel property is specified as: + + pdsp-id : QMSS PDSP running accumulator firmware + on which the channel has to be + configured + channel : Accumulator channel number + entries : Size of the accumulator descriptor list + pacing mode : Interrupt pacing mode + 0 : None, i.e interrupt on list full only + 1 : Time delay since last interrupt + 2 : Time delay since first new packet + 3 : Time delay since last new packet + latency : time to delay the interrupt, specified + in microseconds. + -- multi-queue : Optional property to specify that the channel has to + monitor upto 32 queues starting at the base queue #. +- descriptor-regions : child node describing the memory regions for keystone + navigator packet DMA descriptors. The memory for + descriptors will be allocated by the driver. + -- id : region number in QMSS. + -- region-spec : specifies the number of descriptors in the + region, specified as + <"# of descriptors" "descriptor size">. + -- link-index : start index, i.e. index of the first + descriptor in the region. + +Optional properties: +- dma-coherent : Present if DMA operations are coherent. +- pdsps : child node describing the PDSP configuration. + -- firmware : firmware to be loaded on the PDSP. + -- id : the qmss pdsp that will run the firmware. + -- reg : Address and size of the register set for the PDSP. + Register regions should be specified in the following + order + - PDSP internal RAM region. + - PDSP control/status region registers. + - QMSS interrupt distributor registers. + - PDSP command interface region. + +Example: + +qmss: qmss@2a40000 { + compatible = "ti,keystone-qmss"; + dma-coherent; + #address-cells = <1>; + #size-cells = <1>; + clocks = <&chipclk13>; + ranges; + queue-range = <0 0x4000>; + linkram0 = <0x100000 0x8000>; + linkram1 = <0x0 0x10000>; + + qmgrs { + #address-cells = <1>; + #size-cells = <1>; + ranges; + qmgr0 { + managed-queues = <0 0x2000>; + reg = <0x2a40000 0x20000>, + <0x2a06000 0x400>, + <0x2a02000 0x1000>, + <0x2a03000 0x1000>, + <0x23a80000 0x20000>, + <0x2a80000 0x20000>; + }; + + qmgr1 { + managed-queues = <0x2000 0x2000>; + reg = <0x2a60000 0x20000>, + <0x2a06400 0x400>, + <0x2a04000 0x1000>, + <0x2a05000 0x1000>, + <0x23aa0000 0x20000>, + <0x2aa0000 0x20000>; + }; + }; + queue-pools { + qpend { + qpend-0 { + qrange = <658 8>; + interrupts =<0 40 0xf04 0 41 0xf04 0 42 0xf04 + 0 43 0xf04 0 44 0xf04 0 45 0xf04 + 0 46 0xf04 0 47 0xf04>; + }; + qpend-1 { + qrange = <8704 16>; + interrupts = <0 48 0xf04 0 49 0xf04 0 50 0xf04 + 0 51 0xf04 0 52 0xf04 0 53 0xf04 + 0 54 0xf04 0 55 0xf04 0 56 0xf04 + 0 57 0xf04 0 58 0xf04 0 59 0xf04 + 0 60 0xf04 0 61 0xf04 0 62 0xf04 + 0 63 0xf04>; + qalloc-by-id; + }; + qpend-2 { + qrange = <8720 16>; + interrupts = <0 64 0xf04 0 65 0xf04 0 66 0xf04 + 0 59 0xf04 0 68 0xf04 0 69 0xf04 + 0 70 0xf04 0 71 0xf04 0 72 0xf04 + 0 73 0xf04 0 74 0xf04 0 75 0xf04 + 0 76 0xf04 0 77 0xf04 0 78 0xf04 + 0 79 0xf04>; + }; + }; + general-purpose { + gp-0 { + qrange = <4000 64>; + }; + netcp-tx { + qrange = <640 9>; + qalloc-by-id; + }; + }; + accumulator { + acc-0 { + qrange = <128 32>; + accumulator = <0 36 16 2 50>; + interrupts = <0 215 0xf01>; + multi-queue; + qalloc-by-id; + }; + acc-1 { + qrange = <160 32>; + accumulator = <0 37 16 2 50>; + interrupts = <0 216 0xf01>; + multi-queue; + }; + acc-2 { + qrange = <192 32>; + accumulator = <0 38 16 2 50>; + interrupts = <0 217 0xf01>; + multi-queue; + }; + acc-3 { + qrange = <224 32>; + accumulator = <0 39 16 2 50>; + interrupts = <0 218 0xf01>; + multi-queue; + }; + }; + }; + descriptor-regions { + #address-cells = <1>; + #size-cells = <1>; + ranges; + region-12 { + id = <12>; + region-spec = <8192 128>; /* num_desc desc_size */ + link-index = <0x4000>; + }; + }; + pdsps { + #address-cells = <1>; + #size-cells = <1>; + ranges; + pdsp0@0x2a10000 { + firmware = "keystone/qmss_pdsp_acc48_k2_le_1_0_0_8.fw"; + reg = <0x2a10000 0x1000>, + <0x2a0f000 0x100>, + <0x2a0c000 0x3c8>, + <0x2a20000 0x4000>; + id = <0>; + }; + }; +}; /* qmss */ -- cgit v1.2.1 From 41f93af900a20d1a0a358b522b5129c89677e9dc Mon Sep 17 00:00:00 2001 From: Sandeep Nair Date: Fri, 28 Feb 2014 10:47:50 -0500 Subject: soc: ti: add Keystone Navigator QMSS driver The QMSS (Queue Manager Sub System) found on Keystone SOCs is one of the main hardware sub system which forms the backbone of the Keystone Multi-core Navigator. QMSS consist of queue managers, packed-data structure processors(PDSP), linking RAM, descriptor pools and infrastructure Packet DMA. The Queue Manager is a hardware module that is responsible for accelerating management of the packet queues. Packets are queued/de-queued by writing or reading descriptor address to a particular memory mapped location. The PDSPs perform QMSS related functions like accumulation, QoS, or event management. Linking RAM registers are used to link the descriptors which are stored in descriptor RAM. Descriptor RAM is configurable as internal or external memory. The QMSS driver manages the PDSP setups, linking RAM regions, queue pool management (allocation, push, pop and notify) and descriptor pool management. The specifics on the device tree bindings for QMSS can be found in: Documentation/devicetree/bindings/soc/keystone-navigator-qmss.txt Cc: Greg Kroah-Hartman Cc: Kumar Gala Cc: Olof Johansson Cc: Arnd Bergmann Cc: Grant Likely Cc: Rob Herring Cc: Mark Rutland Signed-off-by: Sandeep Nair Signed-off-by: Santosh Shilimkar --- drivers/Kconfig | 2 + drivers/soc/Kconfig | 1 + drivers/soc/Makefile | 1 + drivers/soc/ti/Kconfig | 21 + drivers/soc/ti/Makefile | 4 + drivers/soc/ti/knav_qmss.h | 386 ++++++++ drivers/soc/ti/knav_qmss_acc.c | 591 +++++++++++++ drivers/soc/ti/knav_qmss_queue.c | 1816 ++++++++++++++++++++++++++++++++++++++ include/linux/soc/ti/knav_qmss.h | 90 ++ 9 files changed, 2912 insertions(+) create mode 100644 drivers/soc/ti/Kconfig create mode 100644 drivers/soc/ti/Makefile create mode 100644 drivers/soc/ti/knav_qmss.h create mode 100644 drivers/soc/ti/knav_qmss_acc.c create mode 100644 drivers/soc/ti/knav_qmss_queue.c create mode 100644 include/linux/soc/ti/knav_qmss.h diff --git a/drivers/Kconfig b/drivers/Kconfig index 622fa266b29e..1a693d3f9d51 100644 --- a/drivers/Kconfig +++ b/drivers/Kconfig @@ -148,6 +148,8 @@ source "drivers/remoteproc/Kconfig" source "drivers/rpmsg/Kconfig" +source "drivers/soc/Kconfig" + source "drivers/devfreq/Kconfig" source "drivers/extcon/Kconfig" diff --git a/drivers/soc/Kconfig b/drivers/soc/Kconfig index c8543855aa82..49e3f0cc71af 100644 --- a/drivers/soc/Kconfig +++ b/drivers/soc/Kconfig @@ -1,5 +1,6 @@ menu "SOC (System On Chip) specific Drivers" source "drivers/soc/qcom/Kconfig" +source "drivers/soc/ti/Kconfig" endmenu diff --git a/drivers/soc/Makefile b/drivers/soc/Makefile index 3b1b95d932d1..0d6e35dfea8c 100644 --- a/drivers/soc/Makefile +++ b/drivers/soc/Makefile @@ -4,3 +4,4 @@ obj-$(CONFIG_ARCH_QCOM) += qcom/ obj-$(CONFIG_ARCH_TEGRA) += tegra/ +obj-$(CONFIG_SOC_TI) += ti/ diff --git a/drivers/soc/ti/Kconfig b/drivers/soc/ti/Kconfig new file mode 100644 index 000000000000..f73896f762e8 --- /dev/null +++ b/drivers/soc/ti/Kconfig @@ -0,0 +1,21 @@ +# +# TI SOC drivers +# +menuconfig SOC_TI + bool "TI SOC drivers support" + +if SOC_TI + +config KEYSTONE_NAVIGATOR_QMSS + tristate "Keystone Queue Manager Sub System" + depends on ARCH_KEYSTONE + help + Say y here to support the Keystone multicore Navigator Queue + Manager support. The Queue Manager is a hardware module that + is responsible for accelerating management of the packet queues. + Packets are queued/de-queued by writing/reading descriptor address + to a particular memory mapped location in the Queue Manager module. + + If unsure, say N. + +endif # SOC_TI diff --git a/drivers/soc/ti/Makefile b/drivers/soc/ti/Makefile new file mode 100644 index 000000000000..bf85cacd5b85 --- /dev/null +++ b/drivers/soc/ti/Makefile @@ -0,0 +1,4 @@ +# +# TI Keystone SOC drivers +# +obj-$(CONFIG_KEYSTONE_NAVIGATOR_QMSS) += knav_qmss_queue.o knav_qmss_acc.o diff --git a/drivers/soc/ti/knav_qmss.h b/drivers/soc/ti/knav_qmss.h new file mode 100644 index 000000000000..bc9dcc8cc3ce --- /dev/null +++ b/drivers/soc/ti/knav_qmss.h @@ -0,0 +1,386 @@ +/* + * Keystone Navigator QMSS driver internal header + * + * Copyright (C) 2014 Texas Instruments Incorporated - http://www.ti.com + * Author: Sandeep Nair + * Cyril Chemparathy + * Santosh Shilimkar + * + * 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. + */ + +#ifndef __KNAV_QMSS_H__ +#define __KNAV_QMSS_H__ + +#define THRESH_GTE BIT(7) +#define THRESH_LT 0 + +#define PDSP_CTRL_PC_MASK 0xffff0000 +#define PDSP_CTRL_SOFT_RESET BIT(0) +#define PDSP_CTRL_ENABLE BIT(1) +#define PDSP_CTRL_RUNNING BIT(15) + +#define ACC_MAX_CHANNEL 48 +#define ACC_DEFAULT_PERIOD 25 /* usecs */ + +#define ACC_CHANNEL_INT_BASE 2 + +#define ACC_LIST_ENTRY_TYPE 1 +#define ACC_LIST_ENTRY_WORDS (1 << ACC_LIST_ENTRY_TYPE) +#define ACC_LIST_ENTRY_QUEUE_IDX 0 +#define ACC_LIST_ENTRY_DESC_IDX (ACC_LIST_ENTRY_WORDS - 1) + +#define ACC_CMD_DISABLE_CHANNEL 0x80 +#define ACC_CMD_ENABLE_CHANNEL 0x81 +#define ACC_CFG_MULTI_QUEUE BIT(21) + +#define ACC_INTD_OFFSET_EOI (0x0010) +#define ACC_INTD_OFFSET_COUNT(ch) (0x0300 + 4 * (ch)) +#define ACC_INTD_OFFSET_STATUS(ch) (0x0200 + 4 * ((ch) / 32)) + +#define RANGE_MAX_IRQS 64 + +#define ACC_DESCS_MAX SZ_1K +#define ACC_DESCS_MASK (ACC_DESCS_MAX - 1) +#define DESC_SIZE_MASK 0xful +#define DESC_PTR_MASK (~DESC_SIZE_MASK) + +#define KNAV_NAME_SIZE 32 + +enum knav_acc_result { + ACC_RET_IDLE, + ACC_RET_SUCCESS, + ACC_RET_INVALID_COMMAND, + ACC_RET_INVALID_CHANNEL, + ACC_RET_INACTIVE_CHANNEL, + ACC_RET_ACTIVE_CHANNEL, + ACC_RET_INVALID_QUEUE, + ACC_RET_INVALID_RET, +}; + +struct knav_reg_config { + u32 revision; + u32 __pad1; + u32 divert; + u32 link_ram_base0; + u32 link_ram_size0; + u32 link_ram_base1; + u32 __pad2[2]; + u32 starvation[0]; +}; + +struct knav_reg_region { + u32 base; + u32 start_index; + u32 size_count; + u32 __pad; +}; + +struct knav_reg_pdsp_regs { + u32 control; + u32 status; + u32 cycle_count; + u32 stall_count; +}; + +struct knav_reg_acc_command { + u32 command; + u32 queue_mask; + u32 list_phys; + u32 queue_num; + u32 timer_config; +}; + +struct knav_link_ram_block { + dma_addr_t phys; + void *virt; + size_t size; +}; + +struct knav_acc_info { + u32 pdsp_id; + u32 start_channel; + u32 list_entries; + u32 pacing_mode; + u32 timer_count; + int mem_size; + int list_size; + struct knav_pdsp_info *pdsp; +}; + +struct knav_acc_channel { + u32 channel; + u32 list_index; + u32 open_mask; + u32 *list_cpu[2]; + dma_addr_t list_dma[2]; + char name[KNAV_NAME_SIZE]; + atomic_t retrigger_count; +}; + +struct knav_pdsp_info { + const char *name; + struct knav_reg_pdsp_regs __iomem *regs; + union { + void __iomem *command; + struct knav_reg_acc_command __iomem *acc_command; + u32 __iomem *qos_command; + }; + void __iomem *intd; + u32 __iomem *iram; + const char *firmware; + u32 id; + struct list_head list; +}; + +struct knav_qmgr_info { + unsigned start_queue; + unsigned num_queues; + struct knav_reg_config __iomem *reg_config; + struct knav_reg_region __iomem *reg_region; + struct knav_reg_queue __iomem *reg_push, *reg_pop, *reg_peek; + void __iomem *reg_status; + struct list_head list; +}; + +#define KNAV_NUM_LINKRAM 2 + +/** + * struct knav_queue_stats: queue statistics + * pushes: number of push operations + * pops: number of pop operations + * push_errors: number of push errors + * pop_errors: number of pop errors + * notifies: notifier counts + */ +struct knav_queue_stats { + atomic_t pushes; + atomic_t pops; + atomic_t push_errors; + atomic_t pop_errors; + atomic_t notifies; +}; + +/** + * struct knav_reg_queue: queue registers + * @entry_count: valid entries in the queue + * @byte_count: total byte count in thhe queue + * @packet_size: packet size for the queue + * @ptr_size_thresh: packet pointer size threshold + */ +struct knav_reg_queue { + u32 entry_count; + u32 byte_count; + u32 packet_size; + u32 ptr_size_thresh; +}; + +/** + * struct knav_region: qmss region info + * @dma_start, dma_end: start and end dma address + * @virt_start, virt_end: start and end virtual address + * @desc_size: descriptor size + * @used_desc: consumed descriptors + * @id: region number + * @num_desc: total descriptors + * @link_index: index of the first descriptor + * @name: region name + * @list: instance in the device's region list + * @pools: list of descriptor pools in the region + */ +struct knav_region { + dma_addr_t dma_start, dma_end; + void *virt_start, *virt_end; + unsigned desc_size; + unsigned used_desc; + unsigned id; + unsigned num_desc; + unsigned link_index; + const char *name; + struct list_head list; + struct list_head pools; +}; + +/** + * struct knav_pool: qmss pools + * @dev: device pointer + * @region: qmss region info + * @queue: queue registers + * @kdev: qmss device pointer + * @region_offset: offset from the base + * @num_desc: total descriptors + * @desc_size: descriptor size + * @region_id: region number + * @name: pool name + * @list: list head + * @region_inst: instance in the region's pool list + */ +struct knav_pool { + struct device *dev; + struct knav_region *region; + struct knav_queue *queue; + struct knav_device *kdev; + int region_offset; + int num_desc; + int desc_size; + int region_id; + const char *name; + struct list_head list; + struct list_head region_inst; +}; + +/** + * struct knav_queue_inst: qmss queue instace properties + * @descs: descriptor pointer + * @desc_head, desc_tail, desc_count: descriptor counters + * @acc: accumulator channel pointer + * @kdev: qmss device pointer + * @range: range info + * @qmgr: queue manager info + * @id: queue instace id + * @irq_num: irq line number + * @notify_needed: notifier needed based on queue type + * @num_notifiers: total notifiers + * @handles: list head + * @name: queue instance name + * @irq_name: irq line name + */ +struct knav_queue_inst { + u32 *descs; + atomic_t desc_head, desc_tail, desc_count; + struct knav_acc_channel *acc; + struct knav_device *kdev; + struct knav_range_info *range; + struct knav_qmgr_info *qmgr; + u32 id; + int irq_num; + int notify_needed; + atomic_t num_notifiers; + struct list_head handles; + const char *name; + const char *irq_name; +}; + +/** + * struct knav_queue: qmss queue properties + * @reg_push, reg_pop, reg_peek: push, pop queue registers + * @inst: qmss queue instace properties + * @notifier_fn: notifier function + * @notifier_fn_arg: notifier function argument + * @notifier_enabled: notier enabled for a give queue + * @rcu: rcu head + * @flags: queue flags + * @list: list head + */ +struct knav_queue { + struct knav_reg_queue __iomem *reg_push, *reg_pop, *reg_peek; + struct knav_queue_inst *inst; + struct knav_queue_stats stats; + knav_queue_notify_fn notifier_fn; + void *notifier_fn_arg; + atomic_t notifier_enabled; + struct rcu_head rcu; + unsigned flags; + struct list_head list; +}; + +struct knav_device { + struct device *dev; + unsigned base_id; + unsigned num_queues; + unsigned num_queues_in_use; + unsigned inst_shift; + struct knav_link_ram_block link_rams[KNAV_NUM_LINKRAM]; + void *instances; + struct list_head regions; + struct list_head queue_ranges; + struct list_head pools; + struct list_head pdsps; + struct list_head qmgrs; +}; + +struct knav_range_ops { + int (*init_range)(struct knav_range_info *range); + int (*free_range)(struct knav_range_info *range); + int (*init_queue)(struct knav_range_info *range, + struct knav_queue_inst *inst); + int (*open_queue)(struct knav_range_info *range, + struct knav_queue_inst *inst, unsigned flags); + int (*close_queue)(struct knav_range_info *range, + struct knav_queue_inst *inst); + int (*set_notify)(struct knav_range_info *range, + struct knav_queue_inst *inst, bool enabled); +}; + +struct knav_irq_info { + int irq; + u32 cpu_map; +}; + +struct knav_range_info { + const char *name; + struct knav_device *kdev; + unsigned queue_base; + unsigned num_queues; + void *queue_base_inst; + unsigned flags; + struct list_head list; + struct knav_range_ops *ops; + struct knav_acc_info acc_info; + struct knav_acc_channel *acc; + unsigned num_irqs; + struct knav_irq_info irqs[RANGE_MAX_IRQS]; +}; + +#define RANGE_RESERVED BIT(0) +#define RANGE_HAS_IRQ BIT(1) +#define RANGE_HAS_ACCUMULATOR BIT(2) +#define RANGE_MULTI_QUEUE BIT(3) + +#define for_each_region(kdev, region) \ + list_for_each_entry(region, &kdev->regions, list) + +#define first_region(kdev) \ + list_first_entry(&kdev->regions, \ + struct knav_region, list) + +#define for_each_queue_range(kdev, range) \ + list_for_each_entry(range, &kdev->queue_ranges, list) + +#define first_queue_range(kdev) \ + list_first_entry(&kdev->queue_ranges, \ + struct knav_range_info, list) + +#define for_each_pool(kdev, pool) \ + list_for_each_entry(pool, &kdev->pools, list) + +#define for_each_pdsp(kdev, pdsp) \ + list_for_each_entry(pdsp, &kdev->pdsps, list) + +#define for_each_qmgr(kdev, qmgr) \ + list_for_each_entry(qmgr, &kdev->qmgrs, list) + +static inline struct knav_pdsp_info * +knav_find_pdsp(struct knav_device *kdev, unsigned pdsp_id) +{ + struct knav_pdsp_info *pdsp; + + for_each_pdsp(kdev, pdsp) + if (pdsp_id == pdsp->id) + return pdsp; + return NULL; +} + +extern int knav_init_acc_range(struct knav_device *kdev, + struct device_node *node, + struct knav_range_info *range); +extern void knav_queue_notify(struct knav_queue_inst *inst); + +#endif /* __KNAV_QMSS_H__ */ diff --git a/drivers/soc/ti/knav_qmss_acc.c b/drivers/soc/ti/knav_qmss_acc.c new file mode 100644 index 000000000000..6fbfde6e748f --- /dev/null +++ b/drivers/soc/ti/knav_qmss_acc.c @@ -0,0 +1,591 @@ +/* + * Keystone accumulator queue manager + * + * Copyright (C) 2014 Texas Instruments Incorporated - http://www.ti.com + * Author: Sandeep Nair + * Cyril Chemparathy + * Santosh Shilimkar + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include + +#include "knav_qmss.h" + +#define knav_range_offset_to_inst(kdev, range, q) \ + (range->queue_base_inst + (q << kdev->inst_shift)) + +static void __knav_acc_notify(struct knav_range_info *range, + struct knav_acc_channel *acc) +{ + struct knav_device *kdev = range->kdev; + struct knav_queue_inst *inst; + int range_base, queue; + + range_base = kdev->base_id + range->queue_base; + + if (range->flags & RANGE_MULTI_QUEUE) { + for (queue = 0; queue < range->num_queues; queue++) { + inst = knav_range_offset_to_inst(kdev, range, + queue); + if (inst->notify_needed) { + inst->notify_needed = 0; + dev_dbg(kdev->dev, "acc-irq: notifying %d\n", + range_base + queue); + knav_queue_notify(inst); + } + } + } else { + queue = acc->channel - range->acc_info.start_channel; + inst = knav_range_offset_to_inst(kdev, range, queue); + dev_dbg(kdev->dev, "acc-irq: notifying %d\n", + range_base + queue); + knav_queue_notify(inst); + } +} + +static int knav_acc_set_notify(struct knav_range_info *range, + struct knav_queue_inst *kq, + bool enabled) +{ + struct knav_pdsp_info *pdsp = range->acc_info.pdsp; + struct knav_device *kdev = range->kdev; + u32 mask, offset; + + /* + * when enabling, we need to re-trigger an interrupt if we + * have descriptors pending + */ + if (!enabled || atomic_read(&kq->desc_count) <= 0) + return 0; + + kq->notify_needed = 1; + atomic_inc(&kq->acc->retrigger_count); + mask = BIT(kq->acc->channel % 32); + offset = ACC_INTD_OFFSET_STATUS(kq->acc->channel); + dev_dbg(kdev->dev, "setup-notify: re-triggering irq for %s\n", + kq->acc->name); + writel_relaxed(mask, pdsp->intd + offset); + return 0; +} + +static irqreturn_t knav_acc_int_handler(int irq, void *_instdata) +{ + struct knav_acc_channel *acc; + struct knav_queue_inst *kq = NULL; + struct knav_range_info *range; + struct knav_pdsp_info *pdsp; + struct knav_acc_info *info; + struct knav_device *kdev; + + u32 *list, *list_cpu, val, idx, notifies; + int range_base, channel, queue = 0; + dma_addr_t list_dma; + + range = _instdata; + info = &range->acc_info; + kdev = range->kdev; + pdsp = range->acc_info.pdsp; + acc = range->acc; + + range_base = kdev->base_id + range->queue_base; + if ((range->flags & RANGE_MULTI_QUEUE) == 0) { + for (queue = 0; queue < range->num_irqs; queue++) + if (range->irqs[queue].irq == irq) + break; + kq = knav_range_offset_to_inst(kdev, range, queue); + acc += queue; + } + + channel = acc->channel; + list_dma = acc->list_dma[acc->list_index]; + list_cpu = acc->list_cpu[acc->list_index]; + dev_dbg(kdev->dev, "acc-irq: channel %d, list %d, virt %p, phys %x\n", + channel, acc->list_index, list_cpu, list_dma); + if (atomic_read(&acc->retrigger_count)) { + atomic_dec(&acc->retrigger_count); + __knav_acc_notify(range, acc); + writel_relaxed(1, pdsp->intd + ACC_INTD_OFFSET_COUNT(channel)); + /* ack the interrupt */ + writel_relaxed(ACC_CHANNEL_INT_BASE + channel, + pdsp->intd + ACC_INTD_OFFSET_EOI); + + return IRQ_HANDLED; + } + + notifies = readl_relaxed(pdsp->intd + ACC_INTD_OFFSET_COUNT(channel)); + WARN_ON(!notifies); + dma_sync_single_for_cpu(kdev->dev, list_dma, info->list_size, + DMA_FROM_DEVICE); + + for (list = list_cpu; list < list_cpu + (info->list_size / sizeof(u32)); + list += ACC_LIST_ENTRY_WORDS) { + if (ACC_LIST_ENTRY_WORDS == 1) { + dev_dbg(kdev->dev, + "acc-irq: list %d, entry @%p, %08x\n", + acc->list_index, list, list[0]); + } else if (ACC_LIST_ENTRY_WORDS == 2) { + dev_dbg(kdev->dev, + "acc-irq: list %d, entry @%p, %08x %08x\n", + acc->list_index, list, list[0], list[1]); + } else if (ACC_LIST_ENTRY_WORDS == 4) { + dev_dbg(kdev->dev, + "acc-irq: list %d, entry @%p, %08x %08x %08x %08x\n", + acc->list_index, list, list[0], list[1], + list[2], list[3]); + } + + val = list[ACC_LIST_ENTRY_DESC_IDX]; + if (!val) + break; + + if (range->flags & RANGE_MULTI_QUEUE) { + queue = list[ACC_LIST_ENTRY_QUEUE_IDX] >> 16; + if (queue < range_base || + queue >= range_base + range->num_queues) { + dev_err(kdev->dev, + "bad queue %d, expecting %d-%d\n", + queue, range_base, + range_base + range->num_queues); + break; + } + queue -= range_base; + kq = knav_range_offset_to_inst(kdev, range, + queue); + } + + if (atomic_inc_return(&kq->desc_count) >= ACC_DESCS_MAX) { + atomic_dec(&kq->desc_count); + dev_err(kdev->dev, + "acc-irq: queue %d full, entry dropped\n", + queue + range_base); + continue; + } + + idx = atomic_inc_return(&kq->desc_tail) & ACC_DESCS_MASK; + kq->descs[idx] = val; + kq->notify_needed = 1; + dev_dbg(kdev->dev, "acc-irq: enqueue %08x at %d, queue %d\n", + val, idx, queue + range_base); + } + + __knav_acc_notify(range, acc); + memset(list_cpu, 0, info->list_size); + dma_sync_single_for_device(kdev->dev, list_dma, info->list_size, + DMA_TO_DEVICE); + + /* flip to the other list */ + acc->list_index ^= 1; + + /* reset the interrupt counter */ + writel_relaxed(1, pdsp->intd + ACC_INTD_OFFSET_COUNT(channel)); + + /* ack the interrupt */ + writel_relaxed(ACC_CHANNEL_INT_BASE + channel, + pdsp->intd + ACC_INTD_OFFSET_EOI); + + return IRQ_HANDLED; +} + +int knav_range_setup_acc_irq(struct knav_range_info *range, + int queue, bool enabled) +{ + struct knav_device *kdev = range->kdev; + struct knav_acc_channel *acc; + unsigned long cpu_map; + int ret = 0, irq; + u32 old, new; + + if (range->flags & RANGE_MULTI_QUEUE) { + acc = range->acc; + irq = range->irqs[0].irq; + cpu_map = range->irqs[0].cpu_map; + } else { + acc = range->acc + queue; + irq = range->irqs[queue].irq; + cpu_map = range->irqs[queue].cpu_map; + } + + old = acc->open_mask; + if (enabled) + new = old | BIT(queue); + else + new = old & ~BIT(queue); + acc->open_mask = new; + + dev_dbg(kdev->dev, + "setup-acc-irq: open mask old %08x, new %08x, channel %s\n", + old, new, acc->name); + + if (likely(new == old)) + return 0; + + if (new && !old) { + dev_dbg(kdev->dev, + "setup-acc-irq: requesting %s for channel %s\n", + acc->name, acc->name); + ret = request_irq(irq, knav_acc_int_handler, 0, acc->name, + range); + if (!ret && cpu_map) { + ret = irq_set_affinity_hint(irq, to_cpumask(&cpu_map)); + if (ret) { + dev_warn(range->kdev->dev, + "Failed to set IRQ affinity\n"); + return ret; + } + } + } + + if (old && !new) { + dev_dbg(kdev->dev, "setup-acc-irq: freeing %s for channel %s\n", + acc->name, acc->name); + free_irq(irq, range); + } + + return ret; +} + +static const char *knav_acc_result_str(enum knav_acc_result result) +{ + static const char * const result_str[] = { + [ACC_RET_IDLE] = "idle", + [ACC_RET_SUCCESS] = "success", + [ACC_RET_INVALID_COMMAND] = "invalid command", + [ACC_RET_INVALID_CHANNEL] = "invalid channel", + [ACC_RET_INACTIVE_CHANNEL] = "inactive channel", + [ACC_RET_ACTIVE_CHANNEL] = "active channel", + [ACC_RET_INVALID_QUEUE] = "invalid queue", + [ACC_RET_INVALID_RET] = "invalid return code", + }; + + if (result >= ARRAY_SIZE(result_str)) + return result_str[ACC_RET_INVALID_RET]; + else + return result_str[result]; +} + +static enum knav_acc_result +knav_acc_write(struct knav_device *kdev, struct knav_pdsp_info *pdsp, + struct knav_reg_acc_command *cmd) +{ + u32 result; + + dev_dbg(kdev->dev, "acc command %08x %08x %08x %08x %08x\n", + cmd->command, cmd->queue_mask, cmd->list_phys, + cmd->queue_num, cmd->timer_config); + + writel_relaxed(cmd->timer_config, &pdsp->acc_command->timer_config); + writel_relaxed(cmd->queue_num, &pdsp->acc_command->queue_num); + writel_relaxed(cmd->list_phys, &pdsp->acc_command->list_phys); + writel_relaxed(cmd->queue_mask, &pdsp->acc_command->queue_mask); + writel_relaxed(cmd->command, &pdsp->acc_command->command); + + /* wait for the command to clear */ + do { + result = readl_relaxed(&pdsp->acc_command->command); + } while ((result >> 8) & 0xff); + + return (result >> 24) & 0xff; +} + +static void knav_acc_setup_cmd(struct knav_device *kdev, + struct knav_range_info *range, + struct knav_reg_acc_command *cmd, + int queue) +{ + struct knav_acc_info *info = &range->acc_info; + struct knav_acc_channel *acc; + int queue_base; + u32 queue_mask; + + if (range->flags & RANGE_MULTI_QUEUE) { + acc = range->acc; + queue_base = range->queue_base; + queue_mask = BIT(range->num_queues) - 1; + } else { + acc = range->acc + queue; + queue_base = range->queue_base + queue; + queue_mask = 0; + } + + memset(cmd, 0, sizeof(*cmd)); + cmd->command = acc->channel; + cmd->queue_mask = queue_mask; + cmd->list_phys = acc->list_dma[0]; + cmd->queue_num = info->list_entries << 16; + cmd->queue_num |= queue_base; + + cmd->timer_config = ACC_LIST_ENTRY_TYPE << 18; + if (range->flags & RANGE_MULTI_QUEUE) + cmd->timer_config |= ACC_CFG_MULTI_QUEUE; + cmd->timer_config |= info->pacing_mode << 16; + cmd->timer_config |= info->timer_count; +} + +static void knav_acc_stop(struct knav_device *kdev, + struct knav_range_info *range, + int queue) +{ + struct knav_reg_acc_command cmd; + struct knav_acc_channel *acc; + enum knav_acc_result result; + + acc = range->acc + queue; + + knav_acc_setup_cmd(kdev, range, &cmd, queue); + cmd.command |= ACC_CMD_DISABLE_CHANNEL << 8; + result = knav_acc_write(kdev, range->acc_info.pdsp, &cmd); + + dev_dbg(kdev->dev, "stopped acc channel %s, result %s\n", + acc->name, knav_acc_result_str(result)); +} + +static enum knav_acc_result knav_acc_start(struct knav_device *kdev, + struct knav_range_info *range, + int queue) +{ + struct knav_reg_acc_command cmd; + struct knav_acc_channel *acc; + enum knav_acc_result result; + + acc = range->acc + queue; + + knav_acc_setup_cmd(kdev, range, &cmd, queue); + cmd.command |= ACC_CMD_ENABLE_CHANNEL << 8; + result = knav_acc_write(kdev, range->acc_info.pdsp, &cmd); + + dev_dbg(kdev->dev, "started acc channel %s, result %s\n", + acc->name, knav_acc_result_str(result)); + + return result; +} + +static int knav_acc_init_range(struct knav_range_info *range) +{ + struct knav_device *kdev = range->kdev; + struct knav_acc_channel *acc; + enum knav_acc_result result; + int queue; + + for (queue = 0; queue < range->num_queues; queue++) { + acc = range->acc + queue; + + knav_acc_stop(kdev, range, queue); + acc->list_index = 0; + result = knav_acc_start(kdev, range, queue); + + if (result != ACC_RET_SUCCESS) + return -EIO; + + if (range->flags & RANGE_MULTI_QUEUE) + return 0; + } + return 0; +} + +static int knav_acc_init_queue(struct knav_range_info *range, + struct knav_queue_inst *kq) +{ + unsigned id = kq->id - range->queue_base; + + kq->descs = devm_kzalloc(range->kdev->dev, + ACC_DESCS_MAX * sizeof(u32), GFP_KERNEL); + if (!kq->descs) + return -ENOMEM; + + kq->acc = range->acc; + if ((range->flags & RANGE_MULTI_QUEUE) == 0) + kq->acc += id; + return 0; +} + +static int knav_acc_open_queue(struct knav_range_info *range, + struct knav_queue_inst *inst, unsigned flags) +{ + unsigned id = inst->id - range->queue_base; + + return knav_range_setup_acc_irq(range, id, true); +} + +static int knav_acc_close_queue(struct knav_range_info *range, + struct knav_queue_inst *inst) +{ + unsigned id = inst->id - range->queue_base; + + return knav_range_setup_acc_irq(range, id, false); +} + +static int knav_acc_free_range(struct knav_range_info *range) +{ + struct knav_device *kdev = range->kdev; + struct knav_acc_channel *acc; + struct knav_acc_info *info; + int channel, channels; + + info = &range->acc_info; + + if (range->flags & RANGE_MULTI_QUEUE) + channels = 1; + else + channels = range->num_queues; + + for (channel = 0; channel < channels; channel++) { + acc = range->acc + channel; + if (!acc->list_cpu[0]) + continue; + dma_unmap_single(kdev->dev, acc->list_dma[0], + info->mem_size, DMA_BIDIRECTIONAL); + free_pages_exact(acc->list_cpu[0], info->mem_size); + } + devm_kfree(range->kdev->dev, range->acc); + return 0; +} + +struct knav_range_ops knav_acc_range_ops = { + .set_notify = knav_acc_set_notify, + .init_queue = knav_acc_init_queue, + .open_queue = knav_acc_open_queue, + .close_queue = knav_acc_close_queue, + .init_range = knav_acc_init_range, + .free_range = knav_acc_free_range, +}; + +/** + * knav_init_acc_range: Initialise accumulator ranges + * + * @kdev: qmss device + * @node: device node + * @range: qmms range information + * + * Return 0 on success or error + */ +int knav_init_acc_range(struct knav_device *kdev, + struct device_node *node, + struct knav_range_info *range) +{ + struct knav_acc_channel *acc; + struct knav_pdsp_info *pdsp; + struct knav_acc_info *info; + int ret, channel, channels; + int list_size, mem_size; + dma_addr_t list_dma; + void *list_mem; + u32 config[5]; + + range->flags |= RANGE_HAS_ACCUMULATOR; + info = &range->acc_info; + + ret = of_property_read_u32_array(node, "accumulator", config, 5); + if (ret) + return ret; + + info->pdsp_id = config[0]; + info->start_channel = config[1]; + info->list_entries = config[2]; + info->pacing_mode = config[3]; + info->timer_count = config[4] / ACC_DEFAULT_PERIOD; + + if (info->start_channel > ACC_MAX_CHANNEL) { + dev_err(kdev->dev, "channel %d invalid for range %s\n", + info->start_channel, range->name); + return -EINVAL; + } + + if (info->pacing_mode > 3) { + dev_err(kdev->dev, "pacing mode %d invalid for range %s\n", + info->pacing_mode, range->name); + return -EINVAL; + } + + pdsp = knav_find_pdsp(kdev, info->pdsp_id); + if (!pdsp) { + dev_err(kdev->dev, "pdsp id %d not found for range %s\n", + info->pdsp_id, range->name); + return -EINVAL; + } + + info->pdsp = pdsp; + channels = range->num_queues; + if (of_get_property(node, "multi-queue", NULL)) { + range->flags |= RANGE_MULTI_QUEUE; + channels = 1; + if (range->queue_base & (32 - 1)) { + dev_err(kdev->dev, + "misaligned multi-queue accumulator range %s\n", + range->name); + return -EINVAL; + } + if (range->num_queues > 32) { + dev_err(kdev->dev, + "too many queues in accumulator range %s\n", + range->name); + return -EINVAL; + } + } + + /* figure out list size */ + list_size = info->list_entries; + list_size *= ACC_LIST_ENTRY_WORDS * sizeof(u32); + info->list_size = list_size; + mem_size = PAGE_ALIGN(list_size * 2); + info->mem_size = mem_size; + range->acc = devm_kzalloc(kdev->dev, channels * sizeof(*range->acc), + GFP_KERNEL); + if (!range->acc) + return -ENOMEM; + + for (channel = 0; channel < channels; channel++) { + acc = range->acc + channel; + acc->channel = info->start_channel + channel; + + /* allocate memory for the two lists */ + list_mem = alloc_pages_exact(mem_size, GFP_KERNEL | GFP_DMA); + if (!list_mem) + return -ENOMEM; + + list_dma = dma_map_single(kdev->dev, list_mem, mem_size, + DMA_BIDIRECTIONAL); + if (dma_mapping_error(kdev->dev, list_dma)) { + free_pages_exact(list_mem, mem_size); + return -ENOMEM; + } + + memset(list_mem, 0, mem_size); + dma_sync_single_for_device(kdev->dev, list_dma, mem_size, + DMA_TO_DEVICE); + scnprintf(acc->name, sizeof(acc->name), "hwqueue-acc-%d", + acc->channel); + acc->list_cpu[0] = list_mem; + acc->list_cpu[1] = list_mem + list_size; + acc->list_dma[0] = list_dma; + acc->list_dma[1] = list_dma + list_size; + dev_dbg(kdev->dev, "%s: channel %d, phys %08x, virt %8p\n", + acc->name, acc->channel, list_dma, list_mem); + } + + range->ops = &knav_acc_range_ops; + return 0; +} +EXPORT_SYMBOL_GPL(knav_init_acc_range); diff --git a/drivers/soc/ti/knav_qmss_queue.c b/drivers/soc/ti/knav_qmss_queue.c new file mode 100644 index 000000000000..0a2c8634c48b --- /dev/null +++ b/drivers/soc/ti/knav_qmss_queue.c @@ -0,0 +1,1816 @@ +/* + * Keystone Queue Manager subsystem driver + * + * Copyright (C) 2014 Texas Instruments Incorporated - http://www.ti.com + * Authors: Sandeep Nair + * Cyril Chemparathy + * Santosh Shilimkar + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "knav_qmss.h" + +static struct knav_device *kdev; +static DEFINE_MUTEX(knav_dev_lock); + +/* Queue manager register indices in DTS */ +#define KNAV_QUEUE_PEEK_REG_INDEX 0 +#define KNAV_QUEUE_STATUS_REG_INDEX 1 +#define KNAV_QUEUE_CONFIG_REG_INDEX 2 +#define KNAV_QUEUE_REGION_REG_INDEX 3 +#define KNAV_QUEUE_PUSH_REG_INDEX 4 +#define KNAV_QUEUE_POP_REG_INDEX 5 + +/* PDSP register indices in DTS */ +#define KNAV_QUEUE_PDSP_IRAM_REG_INDEX 0 +#define KNAV_QUEUE_PDSP_REGS_REG_INDEX 1 +#define KNAV_QUEUE_PDSP_INTD_REG_INDEX 2 +#define KNAV_QUEUE_PDSP_CMD_REG_INDEX 3 + +#define knav_queue_idx_to_inst(kdev, idx) \ + (kdev->instances + (idx << kdev->inst_shift)) + +#define for_each_handle_rcu(qh, inst) \ + list_for_each_entry_rcu(qh, &inst->handles, list) + +#define for_each_instance(idx, inst, kdev) \ + for (idx = 0, inst = kdev->instances; \ + idx < (kdev)->num_queues_in_use; \ + idx++, inst = knav_queue_idx_to_inst(kdev, idx)) + +/** + * knav_queue_notify: qmss queue notfier call + * + * @inst: qmss queue instance like accumulator + */ +void knav_queue_notify(struct knav_queue_inst *inst) +{ + struct knav_queue *qh; + + if (!inst) + return; + + rcu_read_lock(); + for_each_handle_rcu(qh, inst) { + if (atomic_read(&qh->notifier_enabled) <= 0) + continue; + if (WARN_ON(!qh->notifier_fn)) + continue; + atomic_inc(&qh->stats.notifies); + qh->notifier_fn(qh->notifier_fn_arg); + } + rcu_read_unlock(); +} +EXPORT_SYMBOL_GPL(knav_queue_notify); + +static irqreturn_t knav_queue_int_handler(int irq, void *_instdata) +{ + struct knav_queue_inst *inst = _instdata; + + knav_queue_notify(inst); + return IRQ_HANDLED; +} + +static int knav_queue_setup_irq(struct knav_range_info *range, + struct knav_queue_inst *inst) +{ + unsigned queue = inst->id - range->queue_base; + unsigned long cpu_map; + int ret = 0, irq; + + if (range->flags & RANGE_HAS_IRQ) { + irq = range->irqs[queue].irq; + cpu_map = range->irqs[queue].cpu_map; + ret = request_irq(irq, knav_queue_int_handler, 0, + inst->irq_name, inst); + if (ret) + return ret; + disable_irq(irq); + if (cpu_map) { + ret = irq_set_affinity_hint(irq, to_cpumask(&cpu_map)); + if (ret) { + dev_warn(range->kdev->dev, + "Failed to set IRQ affinity\n"); + return ret; + } + } + } + return ret; +} + +static void knav_queue_free_irq(struct knav_queue_inst *inst) +{ + struct knav_range_info *range = inst->range; + unsigned queue = inst->id - inst->range->queue_base; + int irq; + + if (range->flags & RANGE_HAS_IRQ) { + irq = range->irqs[queue].irq; + irq_set_affinity_hint(irq, NULL); + free_irq(irq, inst); + } +} + +static inline bool knav_queue_is_busy(struct knav_queue_inst *inst) +{ + return !list_empty(&inst->handles); +} + +static inline bool knav_queue_is_reserved(struct knav_queue_inst *inst) +{ + return inst->range->flags & RANGE_RESERVED; +} + +static inline bool knav_queue_is_shared(struct knav_queue_inst *inst) +{ + struct knav_queue *tmp; + + rcu_read_lock(); + for_each_handle_rcu(tmp, inst) { + if (tmp->flags & KNAV_QUEUE_SHARED) { + rcu_read_unlock(); + return true; + } + } + rcu_read_unlock(); + return false; +} + +static inline bool knav_queue_match_type(struct knav_queue_inst *inst, + unsigned type) +{ + if ((type == KNAV_QUEUE_QPEND) && + (inst->range->flags & RANGE_HAS_IRQ)) { + return true; + } else if ((type == KNAV_QUEUE_ACC) && + (inst->range->flags & RANGE_HAS_ACCUMULATOR)) { + return true; + } else if ((type == KNAV_QUEUE_GP) && + !(inst->range->flags & + (RANGE_HAS_ACCUMULATOR | RANGE_HAS_IRQ))) { + return true; + } + return false; +} + +static inline struct knav_queue_inst * +knav_queue_match_id_to_inst(struct knav_device *kdev, unsigned id) +{ + struct knav_queue_inst *inst; + int idx; + + for_each_instance(idx, inst, kdev) { + if (inst->id == id) + return inst; + } + return NULL; +} + +static inline struct knav_queue_inst *knav_queue_find_by_id(int id) +{ + if (kdev->base_id <= id && + kdev->base_id + kdev->num_queues > id) { + id -= kdev->base_id; + return knav_queue_match_id_to_inst(kdev, id); + } + return NULL; +} + +static struct knav_queue *__knav_queue_open(struct knav_queue_inst *inst, + const char *name, unsigned flags) +{ + struct knav_queue *qh; + unsigned id; + int ret = 0; + + qh = devm_kzalloc(inst->kdev->dev, sizeof(*qh), GFP_KERNEL); + if (!qh) + return ERR_PTR(-ENOMEM); + + qh->flags = flags; + qh->inst = inst; + id = inst->id - inst->qmgr->start_queue; + qh->reg_push = &inst->qmgr->reg_push[id]; + qh->reg_pop = &inst->qmgr->reg_pop[id]; + qh->reg_peek = &inst->qmgr->reg_peek[id]; + + /* first opener? */ + if (!knav_queue_is_busy(inst)) { + struct knav_range_info *range = inst->range; + + inst->name = kstrndup(name, KNAV_NAME_SIZE, GFP_KERNEL); + if (range->ops && range->ops->open_queue) + ret = range->ops->open_queue(range, inst, flags); + + if (ret) { + devm_kfree(inst->kdev->dev, qh); + return ERR_PTR(ret); + } + } + list_add_tail_rcu(&qh->list, &inst->handles); + return qh; +} + +static struct knav_queue * +knav_queue_open_by_id(const char *name, unsigned id, unsigned flags) +{ + struct knav_queue_inst *inst; + struct knav_queue *qh; + + mutex_lock(&knav_dev_lock); + + qh = ERR_PTR(-ENODEV); + inst = knav_queue_find_by_id(id); + if (!inst) + goto unlock_ret; + + qh = ERR_PTR(-EEXIST); + if (!(flags & KNAV_QUEUE_SHARED) && knav_queue_is_busy(inst)) + goto unlock_ret; + + qh = ERR_PTR(-EBUSY); + if ((flags & KNAV_QUEUE_SHARED) && + (knav_queue_is_busy(inst) && !knav_queue_is_shared(inst))) + goto unlock_ret; + + qh = __knav_queue_open(inst, name, flags); + +unlock_ret: + mutex_unlock(&knav_dev_lock); + + return qh; +} + +static struct knav_queue *knav_queue_open_by_type(const char *name, + unsigned type, unsigned flags) +{ + struct knav_queue_inst *inst; + struct knav_queue *qh = ERR_PTR(-EINVAL); + int idx; + + mutex_lock(&knav_dev_lock); + + for_each_instance(idx, inst, kdev) { + if (knav_queue_is_reserved(inst)) + continue; + if (!knav_queue_match_type(inst, type)) + continue; + if (knav_queue_is_busy(inst)) + continue; + qh = __knav_queue_open(inst, name, flags); + goto unlock_ret; + } + +unlock_ret: + mutex_unlock(&knav_dev_lock); + return qh; +} + +static void knav_queue_set_notify(struct knav_queue_inst *inst, bool enabled) +{ + struct knav_range_info *range = inst->range; + + if (range->ops && range->ops->set_notify) + range->ops->set_notify(range, inst, enabled); +} + +static int knav_queue_enable_notifier(struct knav_queue *qh) +{ + struct knav_queue_inst *inst = qh->inst; + bool first; + + if (WARN_ON(!qh->notifier_fn)) + return -EINVAL; + + /* Adjust the per handle notifier count */ + first = (atomic_inc_return(&qh->notifier_enabled) == 1); + if (!first) + return 0; /* nothing to do */ + + /* Now adjust the per instance notifier count */ + first = (atomic_inc_return(&inst->num_notifiers) == 1); + if (first) + knav_queue_set_notify(inst, true); + + return 0; +} + +static int knav_queue_disable_notifier(struct knav_queue *qh) +{ + struct knav_queue_inst *inst = qh->inst; + bool last; + + last = (atomic_dec_return(&qh->notifier_enabled) == 0); + if (!last) + return 0; /* nothing to do */ + + last = (atomic_dec_return(&inst->num_notifiers) == 0); + if (last) + knav_queue_set_notify(inst, false); + + return 0; +} + +static int knav_queue_set_notifier(struct knav_queue *qh, + struct knav_queue_notify_config *cfg) +{ + knav_queue_notify_fn old_fn = qh->notifier_fn; + + if (!cfg) + return -EINVAL; + + if (!(qh->inst->range->flags & (RANGE_HAS_ACCUMULATOR | RANGE_HAS_IRQ))) + return -ENOTSUPP; + + if (!cfg->fn && old_fn) + knav_queue_disable_notifier(qh); + + qh->notifier_fn = cfg->fn; + qh->notifier_fn_arg = cfg->fn_arg; + + if (cfg->fn && !old_fn) + knav_queue_enable_notifier(qh); + + return 0; +} + +static int knav_gp_set_notify(struct knav_range_info *range, + struct knav_queue_inst *inst, + bool enabled) +{ + unsigned queue; + + if (range->flags & RANGE_HAS_IRQ) { + queue = inst->id - range->queue_base; + if (enabled) + enable_irq(range->irqs[queue].irq); + else + disable_irq_nosync(range->irqs[queue].irq); + } + return 0; +} + +static int knav_gp_open_queue(struct knav_range_info *range, + struct knav_queue_inst *inst, unsigned flags) +{ + return knav_queue_setup_irq(range, inst); +} + +static int knav_gp_close_queue(struct knav_range_info *range, + struct knav_queue_inst *inst) +{ + knav_queue_free_irq(inst); + return 0; +} + +struct knav_range_ops knav_gp_range_ops = { + .set_notify = knav_gp_set_notify, + .open_queue = knav_gp_open_queue, + .close_queue = knav_gp_close_queue, +}; + + +static int knav_queue_get_count(void *qhandle) +{ + struct knav_queue *qh = qhandle; + struct knav_queue_inst *inst = qh->inst; + + return readl_relaxed(&qh->reg_peek[0].entry_count) + + atomic_read(&inst->desc_count); +} + +static void knav_queue_debug_show_instance(struct seq_file *s, + struct knav_queue_inst *inst) +{ + struct knav_device *kdev = inst->kdev; + struct knav_queue *qh; + + if (!knav_queue_is_busy(inst)) + return; + + seq_printf(s, "\tqueue id %d (%s)\n", + kdev->base_id + inst->id, inst->name); + for_each_handle_rcu(qh, inst) { + seq_printf(s, "\t\thandle %p: ", qh); + seq_printf(s, "pushes %8d, ", + atomic_read(&qh->stats.pushes)); + seq_printf(s, "pops %8d, ", + atomic_read(&qh->stats.pops)); + seq_printf(s, "count %8d, ", + knav_queue_get_count(qh)); + seq_printf(s, "notifies %8d, ", + atomic_read(&qh->stats.notifies)); + seq_printf(s, "push errors %8d, ", + atomic_read(&qh->stats.push_errors)); + seq_printf(s, "pop errors %8d\n", + atomic_read(&qh->stats.pop_errors)); + } +} + +static int knav_queue_debug_show(struct seq_file *s, void *v) +{ + struct knav_queue_inst *inst; + int idx; + + mutex_lock(&knav_dev_lock); + seq_printf(s, "%s: %u-%u\n", + dev_name(kdev->dev), kdev->base_id, + kdev->base_id + kdev->num_queues - 1); + for_each_instance(idx, inst, kdev) + knav_queue_debug_show_instance(s, inst); + mutex_unlock(&knav_dev_lock); + + return 0; +} + +static int knav_queue_debug_open(struct inode *inode, struct file *file) +{ + return single_open(file, knav_queue_debug_show, NULL); +} + +static const struct file_operations knav_queue_debug_ops = { + .open = knav_queue_debug_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; + +static inline int knav_queue_pdsp_wait(u32 * __iomem addr, unsigned timeout, + u32 flags) +{ + unsigned long end; + u32 val = 0; + + end = jiffies + msecs_to_jiffies(timeout); + while (time_after(end, jiffies)) { + val = readl_relaxed(addr); + if (flags) + val &= flags; + if (!val) + break; + cpu_relax(); + } + return val ? -ETIMEDOUT : 0; +} + + +static int knav_queue_flush(struct knav_queue *qh) +{ + struct knav_queue_inst *inst = qh->inst; + unsigned id = inst->id - inst->qmgr->start_queue; + + atomic_set(&inst->desc_count, 0); + writel_relaxed(0, &inst->qmgr->reg_push[id].ptr_size_thresh); + return 0; +} + +/** + * knav_queue_open() - open a hardware queue + * @name - name to give the queue handle + * @id - desired queue number if any or specifes the type + * of queue + * @flags - the following flags are applicable to queues: + * KNAV_QUEUE_SHARED - allow the queue to be shared. Queues are + * exclusive by default. + * Subsequent attempts to open a shared queue should + * also have this flag. + * + * Returns a handle to the open hardware queue if successful. Use IS_ERR() + * to check the returned value for error codes. + */ +void *knav_queue_open(const char *name, unsigned id, + unsigned flags) +{ + struct knav_queue *qh = ERR_PTR(-EINVAL); + + switch (id) { + case KNAV_QUEUE_QPEND: + case KNAV_QUEUE_ACC: + case KNAV_QUEUE_GP: + qh = knav_queue_open_by_type(name, id, flags); + break; + + default: + qh = knav_queue_open_by_id(name, id, flags); + break; + } + return qh; +} +EXPORT_SYMBOL_GPL(knav_queue_open); + +/** + * knav_queue_close() - close a hardware queue handle + * @qh - handle to close + */ +void knav_queue_close(void *qhandle) +{ + struct knav_queue *qh = qhandle; + struct knav_queue_inst *inst = qh->inst; + + while (atomic_read(&qh->notifier_enabled) > 0) + knav_queue_disable_notifier(qh); + + mutex_lock(&knav_dev_lock); + list_del_rcu(&qh->list); + mutex_unlock(&knav_dev_lock); + synchronize_rcu(); + if (!knav_queue_is_busy(inst)) { + struct knav_range_info *range = inst->range; + + if (range->ops && range->ops->close_queue) + range->ops->close_queue(range, inst); + } + devm_kfree(inst->kdev->dev, qh); +} +EXPORT_SYMBOL_GPL(knav_queue_close); + +/** + * knav_queue_device_control() - Perform control operations on a queue + * @qh - queue handle + * @cmd - control commands + * @arg - command argument + * + * Returns 0 on success, errno otherwise. + */ +int knav_queue_device_control(void *qhandle, enum knav_queue_ctrl_cmd cmd, + unsigned long arg) +{ + struct knav_queue *qh = qhandle; + struct knav_queue_notify_config *cfg; + int ret; + + switch ((int)cmd) { + case KNAV_QUEUE_GET_ID: + ret = qh->inst->kdev->base_id + qh->inst->id; + break; + + case KNAV_QUEUE_FLUSH: + ret = knav_queue_flush(qh); + break; + + case KNAV_QUEUE_SET_NOTIFIER: + cfg = (void *)arg; + ret = knav_queue_set_notifier(qh, cfg); + break; + + case KNAV_QUEUE_ENABLE_NOTIFY: + ret = knav_queue_enable_notifier(qh); + break; + + case KNAV_QUEUE_DISABLE_NOTIFY: + ret = knav_queue_disable_notifier(qh); + break; + + case KNAV_QUEUE_GET_COUNT: + ret = knav_queue_get_count(qh); + break; + + default: + ret = -ENOTSUPP; + break; + } + return ret; +} +EXPORT_SYMBOL_GPL(knav_queue_device_control); + + + +/** + * knav_queue_push() - push data (or descriptor) to the tail of a queue + * @qh - hardware queue handle + * @data - data to push + * @size - size of data to push + * @flags - can be used to pass additional information + * + * Returns 0 on success, errno otherwise. + */ +int knav_queue_push(void *qhandle, dma_addr_t dma, + unsigned size, unsigned flags) +{ + struct knav_queue *qh = qhandle; + u32 val; + + val = (u32)dma | ((size / 16) - 1); + writel_relaxed(val, &qh->reg_push[0].ptr_size_thresh); + + atomic_inc(&qh->stats.pushes); + return 0; +} + +/** + * knav_queue_pop() - pop data (or descriptor) from the head of a queue + * @qh - hardware queue handle + * @size - (optional) size of the data pop'ed. + * + * Returns a DMA address on success, 0 on failure. + */ +dma_addr_t knav_queue_pop(void *qhandle, unsigned *size) +{ + struct knav_queue *qh = qhandle; + struct knav_queue_inst *inst = qh->inst; + dma_addr_t dma; + u32 val, idx; + + /* are we accumulated? */ + if (inst->descs) { + if (unlikely(atomic_dec_return(&inst->desc_count) < 0)) { + atomic_inc(&inst->desc_count); + return 0; + } + idx = atomic_inc_return(&inst->desc_head); + idx &= ACC_DESCS_MASK; + val = inst->descs[idx]; + } else { + val = readl_relaxed(&qh->reg_pop[0].ptr_size_thresh); + if (unlikely(!val)) + return 0; + } + + dma = val & DESC_PTR_MASK; + if (size) + *size = ((val & DESC_SIZE_MASK) + 1) * 16; + + atomic_inc(&qh->stats.pops); + return dma; +} + +/* carve out descriptors and push into queue */ +static void kdesc_fill_pool(struct knav_pool *pool) +{ + struct knav_region *region; + int i; + + region = pool->region; + pool->desc_size = region->desc_size; + for (i = 0; i < pool->num_desc; i++) { + int index = pool->region_offset + i; + dma_addr_t dma_addr; + unsigned dma_size; + dma_addr = region->dma_start + (region->desc_size * index); + dma_size = ALIGN(pool->desc_size, SMP_CACHE_BYTES); + dma_sync_single_for_device(pool->dev, dma_addr, dma_size, + DMA_TO_DEVICE); + knav_queue_push(pool->queue, dma_addr, dma_size, 0); + } +} + +/* pop out descriptors and close the queue */ +static void kdesc_empty_pool(struct knav_pool *pool) +{ + dma_addr_t dma; + unsigned size; + void *desc; + int i; + + if (!pool->queue) + return; + + for (i = 0;; i++) { + dma = knav_queue_pop(pool->queue, &size); + if (!dma) + break; + desc = knav_pool_desc_dma_to_virt(pool, dma); + if (!desc) { + dev_dbg(pool->kdev->dev, + "couldn't unmap desc, continuing\n"); + continue; + } + } + WARN_ON(i != pool->num_desc); + knav_queue_close(pool->queue); +} + + +/* Get the DMA address of a descriptor */ +dma_addr_t knav_pool_desc_virt_to_dma(void *ph, void *virt) +{ + struct knav_pool *pool = ph; + return pool->region->dma_start + (virt - pool->region->virt_start); +} + +void *knav_pool_desc_dma_to_virt(void *ph, dma_addr_t dma) +{ + struct knav_pool *pool = ph; + return pool->region->virt_start + (dma - pool->region->dma_start); +} + +/** + * knav_pool_create() - Create a pool of descriptors + * @name - name to give the pool handle + * @num_desc - numbers of descriptors in the pool + * @region_id - QMSS region id from which the descriptors are to be + * allocated. + * + * Returns a pool handle on success. + * Use IS_ERR_OR_NULL() to identify error values on return. + */ +void *knav_pool_create(const char *name, + int num_desc, int region_id) +{ + struct knav_region *reg_itr, *region = NULL; + struct knav_pool *pool, *pi; + struct list_head *node; + unsigned last_offset; + bool slot_found; + int ret; + + if (!kdev->dev) + return ERR_PTR(-ENODEV); + + pool = devm_kzalloc(kdev->dev, sizeof(*pool), GFP_KERNEL); + if (!pool) { + dev_err(kdev->dev, "out of memory allocating pool\n"); + return ERR_PTR(-ENOMEM); + } + + for_each_region(kdev, reg_itr) { + if (reg_itr->id != region_id) + continue; + region = reg_itr; + break; + } + + if (!region) { + dev_err(kdev->dev, "region-id(%d) not found\n", region_id); + ret = -EINVAL; + goto err; + } + + pool->queue = knav_queue_open(name, KNAV_QUEUE_GP, 0); + if (IS_ERR_OR_NULL(pool->queue)) { + dev_err(kdev->dev, + "failed to open queue for pool(%s), error %ld\n", + name, PTR_ERR(pool->queue)); + ret = PTR_ERR(pool->queue); + goto err; + } + + pool->name = kstrndup(name, KNAV_NAME_SIZE, GFP_KERNEL); + pool->kdev = kdev; + pool->dev = kdev->dev; + + mutex_lock(&knav_dev_lock); + + if (num_desc > (region->num_desc - region->used_desc)) { + dev_err(kdev->dev, "out of descs in region(%d) for pool(%s)\n", + region_id, name); + ret = -ENOMEM; + goto err; + } + + /* Region maintains a sorted (by region offset) list of pools + * use the first free slot which is large enough to accomodate + * the request + */ + last_offset = 0; + slot_found = false; + node = ®ion->pools; + list_for_each_entry(pi, ®ion->pools, region_inst) { + if ((pi->region_offset - last_offset) >= num_desc) { + slot_found = true; + break; + } + last_offset = pi->region_offset + pi->num_desc; + } + node = &pi->region_inst; + + if (slot_found) { + pool->region = region; + pool->num_desc = num_desc; + pool->region_offset = last_offset; + region->used_desc += num_desc; + list_add_tail(&pool->list, &kdev->pools); + list_add_tail(&pool->region_inst, node); + } else { + dev_err(kdev->dev, "pool(%s) create failed: fragmented desc pool in region(%d)\n", + name, region_id); + ret = -ENOMEM; + goto err; + } + + mutex_unlock(&knav_dev_lock); + kdesc_fill_pool(pool); + return pool; + +err: + mutex_unlock(&knav_dev_lock); + kfree(pool->name); + devm_kfree(kdev->dev, pool); + return ERR_PTR(ret); +} +EXPORT_SYMBOL_GPL(knav_pool_create); + +/** + * knav_pool_destroy() - Free a pool of descriptors + * @pool - pool handle + */ +void knav_pool_destroy(void *ph) +{ + struct knav_pool *pool = ph; + + if (!pool) + return; + + if (!pool->region) + return; + + kdesc_empty_pool(pool); + mutex_lock(&knav_dev_lock); + + pool->region->used_desc -= pool->num_desc; + list_del(&pool->region_inst); + list_del(&pool->list); + + mutex_unlock(&knav_dev_lock); + kfree(pool->name); + devm_kfree(kdev->dev, pool); +} +EXPORT_SYMBOL_GPL(knav_pool_destroy); + + +/** + * knav_pool_desc_get() - Get a descriptor from the pool + * @pool - pool handle + * + * Returns descriptor from the pool. + */ +void *knav_pool_desc_get(void *ph) +{ + struct knav_pool *pool = ph; + dma_addr_t dma; + unsigned size; + void *data; + + dma = knav_queue_pop(pool->queue, &size); + if (unlikely(!dma)) + return ERR_PTR(-ENOMEM); + data = knav_pool_desc_dma_to_virt(pool, dma); + return data; +} + +/** + * knav_pool_desc_put() - return a descriptor to the pool + * @pool - pool handle + */ +void knav_pool_desc_put(void *ph, void *desc) +{ + struct knav_pool *pool = ph; + dma_addr_t dma; + dma = knav_pool_desc_virt_to_dma(pool, desc); + knav_queue_push(pool->queue, dma, pool->region->desc_size, 0); +} + +/** + * knav_pool_desc_map() - Map descriptor for DMA transfer + * @pool - pool handle + * @desc - address of descriptor to map + * @size - size of descriptor to map + * @dma - DMA address return pointer + * @dma_sz - adjusted return pointer + * + * Returns 0 on success, errno otherwise. + */ +int knav_pool_desc_map(void *ph, void *desc, unsigned size, + dma_addr_t *dma, unsigned *dma_sz) +{ + struct knav_pool *pool = ph; + *dma = knav_pool_desc_virt_to_dma(pool, desc); + size = min(size, pool->region->desc_size); + size = ALIGN(size, SMP_CACHE_BYTES); + *dma_sz = size; + dma_sync_single_for_device(pool->dev, *dma, size, DMA_TO_DEVICE); + + /* Ensure the descriptor reaches to the memory */ + __iowmb(); + + return 0; +} + +/** + * knav_pool_desc_unmap() - Unmap descriptor after DMA transfer + * @pool - pool handle + * @dma - DMA address of descriptor to unmap + * @dma_sz - size of descriptor to unmap + * + * Returns descriptor address on success, Use IS_ERR_OR_NULL() to identify + * error values on return. + */ +void *knav_pool_desc_unmap(void *ph, dma_addr_t dma, unsigned dma_sz) +{ + struct knav_pool *pool = ph; + unsigned desc_sz; + void *desc; + + desc_sz = min(dma_sz, pool->region->desc_size); + desc = knav_pool_desc_dma_to_virt(pool, dma); + dma_sync_single_for_cpu(pool->dev, dma, desc_sz, DMA_FROM_DEVICE); + prefetch(desc); + return desc; +} + +/** + * knav_pool_count() - Get the number of descriptors in pool. + * @pool - pool handle + * Returns number of elements in the pool. + */ +int knav_pool_count(void *ph) +{ + struct knav_pool *pool = ph; + return knav_queue_get_count(pool->queue); +} + +static void knav_queue_setup_region(struct knav_device *kdev, + struct knav_region *region) +{ + unsigned hw_num_desc, hw_desc_size, size; + struct knav_reg_region __iomem *regs; + struct knav_qmgr_info *qmgr; + struct knav_pool *pool; + int id = region->id; + struct page *page; + + /* unused region? */ + if (!region->num_desc) { + dev_warn(kdev->dev, "unused region %s\n", region->name); + return; + } + + /* get hardware descriptor value */ + hw_num_desc = ilog2(region->num_desc - 1) + 1; + + /* did we force fit ourselves into nothingness? */ + if (region->num_desc < 32) { + region->num_desc = 0; + dev_warn(kdev->dev, "too few descriptors in region %s\n", + region->name); + return; + } + + size = region->num_desc * region->desc_size; + region->virt_start = alloc_pages_exact(size, GFP_KERNEL | GFP_DMA | + GFP_DMA32); + if (!region->virt_start) { + region->num_desc = 0; + dev_err(kdev->dev, "memory alloc failed for region %s\n", + region->name); + return; + } + region->virt_end = region->virt_start + size; + page = virt_to_page(region->virt_start); + + region->dma_start = dma_map_page(kdev->dev, page, 0, size, + DMA_BIDIRECTIONAL); + if (dma_mapping_error(kdev->dev, region->dma_start)) { + dev_err(kdev->dev, "dma map failed for region %s\n", + region->name); + goto fail; + } + region->dma_end = region->dma_start + size; + + pool = devm_kzalloc(kdev->dev, sizeof(*pool), GFP_KERNEL); + if (!pool) { + dev_err(kdev->dev, "out of memory allocating dummy pool\n"); + goto fail; + } + pool->num_desc = 0; + pool->region_offset = region->num_desc; + list_add(&pool->region_inst, ®ion->pools); + + dev_dbg(kdev->dev, + "region %s (%d): size:%d, link:%d@%d, phys:%08x-%08x, virt:%p-%p\n", + region->name, id, region->desc_size, region->num_desc, + region->link_index, region->dma_start, region->dma_end, + region->virt_start, region->virt_end); + + hw_desc_size = (region->desc_size / 16) - 1; + hw_num_desc -= 5; + + for_each_qmgr(kdev, qmgr) { + regs = qmgr->reg_region + id; + writel_relaxed(region->dma_start, ®s->base); + writel_relaxed(region->link_index, ®s->start_index); + writel_relaxed(hw_desc_size << 16 | hw_num_desc, + ®s->size_count); + } + return; + +fail: + if (region->dma_start) + dma_unmap_page(kdev->dev, region->dma_start, size, + DMA_BIDIRECTIONAL); + if (region->virt_start) + free_pages_exact(region->virt_start, size); + region->num_desc = 0; + return; +} + +static const char *knav_queue_find_name(struct device_node *node) +{ + const char *name; + + if (of_property_read_string(node, "label", &name) < 0) + name = node->name; + if (!name) + name = "unknown"; + return name; +} + +static int knav_queue_setup_regions(struct knav_device *kdev, + struct device_node *regions) +{ + struct device *dev = kdev->dev; + struct knav_region *region; + struct device_node *child; + u32 temp[2]; + int ret; + + for_each_child_of_node(regions, child) { + region = devm_kzalloc(dev, sizeof(*region), GFP_KERNEL); + if (!region) { + dev_err(dev, "out of memory allocating region\n"); + return -ENOMEM; + } + + region->name = knav_queue_find_name(child); + of_property_read_u32(child, "id", ®ion->id); + ret = of_property_read_u32_array(child, "region-spec", temp, 2); + if (!ret) { + region->num_desc = temp[0]; + region->desc_size = temp[1]; + } else { + dev_err(dev, "invalid region info %s\n", region->name); + devm_kfree(dev, region); + continue; + } + + if (!of_get_property(child, "link-index", NULL)) { + dev_err(dev, "No link info for %s\n", region->name); + devm_kfree(dev, region); + continue; + } + ret = of_property_read_u32(child, "link-index", + ®ion->link_index); + if (ret) { + dev_err(dev, "link index not found for %s\n", + region->name); + devm_kfree(dev, region); + continue; + } + + INIT_LIST_HEAD(®ion->pools); + list_add_tail(®ion->list, &kdev->regions); + } + if (list_empty(&kdev->regions)) { + dev_err(dev, "no valid region information found\n"); + return -ENODEV; + } + + /* Next, we run through the regions and set things up */ + for_each_region(kdev, region) + knav_queue_setup_region(kdev, region); + + return 0; +} + +static int knav_get_link_ram(struct knav_device *kdev, + const char *name, + struct knav_link_ram_block *block) +{ + struct platform_device *pdev = to_platform_device(kdev->dev); + struct device_node *node = pdev->dev.of_node; + u32 temp[2]; + + /* + * Note: link ram resources are specified in "entry" sized units. In + * reality, although entries are ~40bits in hardware, we treat them as + * 64-bit entities here. + * + * For example, to specify the internal link ram for Keystone-I class + * devices, we would set the linkram0 resource to 0x80000-0x83fff. + * + * This gets a bit weird when other link rams are used. For example, + * if the range specified is 0x0c000000-0x0c003fff (i.e., 16K entries + * in MSMC SRAM), the actual memory used is 0x0c000000-0x0c020000, + * which accounts for 64-bits per entry, for 16K entries. + */ + if (!of_property_read_u32_array(node, name , temp, 2)) { + if (temp[0]) { + /* + * queue_base specified => using internal or onchip + * link ram WARNING - we do not "reserve" this block + */ + block->phys = (dma_addr_t)temp[0]; + block->virt = NULL; + block->size = temp[1]; + } else { + block->size = temp[1]; + /* queue_base not specific => allocate requested size */ + block->virt = dmam_alloc_coherent(kdev->dev, + 8 * block->size, &block->phys, + GFP_KERNEL); + if (!block->virt) { + dev_err(kdev->dev, "failed to alloc linkram\n"); + return -ENOMEM; + } + } + } else { + return -ENODEV; + } + return 0; +} + +static int knav_queue_setup_link_ram(struct knav_device *kdev) +{ + struct knav_link_ram_block *block; + struct knav_qmgr_info *qmgr; + + for_each_qmgr(kdev, qmgr) { + block = &kdev->link_rams[0]; + dev_dbg(kdev->dev, "linkram0: phys:%x, virt:%p, size:%x\n", + block->phys, block->virt, block->size); + writel_relaxed(block->phys, &qmgr->reg_config->link_ram_base0); + writel_relaxed(block->size, &qmgr->reg_config->link_ram_size0); + + block++; + if (!block->size) + return 0; + + dev_dbg(kdev->dev, "linkram1: phys:%x, virt:%p, size:%x\n", + block->phys, block->virt, block->size); + writel_relaxed(block->phys, &qmgr->reg_config->link_ram_base1); + } + + return 0; +} + +static int knav_setup_queue_range(struct knav_device *kdev, + struct device_node *node) +{ + struct device *dev = kdev->dev; + struct knav_range_info *range; + struct knav_qmgr_info *qmgr; + u32 temp[2], start, end, id, index; + int ret, i; + + range = devm_kzalloc(dev, sizeof(*range), GFP_KERNEL); + if (!range) { + dev_err(dev, "out of memory allocating range\n"); + return -ENOMEM; + } + + range->kdev = kdev; + range->name = knav_queue_find_name(node); + ret = of_property_read_u32_array(node, "qrange", temp, 2); + if (!ret) { + range->queue_base = temp[0] - kdev->base_id; + range->num_queues = temp[1]; + } else { + dev_err(dev, "invalid queue range %s\n", range->name); + devm_kfree(dev, range); + return -EINVAL; + } + + for (i = 0; i < RANGE_MAX_IRQS; i++) { + struct of_phandle_args oirq; + + if (of_irq_parse_one(node, i, &oirq)) + break; + + range->irqs[i].irq = irq_create_of_mapping(&oirq); + if (range->irqs[i].irq == IRQ_NONE) + break; + + range->num_irqs++; + + if (oirq.args_count == 3) + range->irqs[i].cpu_map = + (oirq.args[2] & 0x0000ff00) >> 8; + } + + range->num_irqs = min(range->num_irqs, range->num_queues); + if (range->num_irqs) + range->flags |= RANGE_HAS_IRQ; + + if (of_get_property(node, "qalloc-by-id", NULL)) + range->flags |= RANGE_RESERVED; + + if (of_get_property(node, "accumulator", NULL)) { + ret = knav_init_acc_range(kdev, node, range); + if (ret < 0) { + devm_kfree(dev, range); + return ret; + } + } else { + range->ops = &knav_gp_range_ops; + } + + /* set threshold to 1, and flush out the queues */ + for_each_qmgr(kdev, qmgr) { + start = max(qmgr->start_queue, range->queue_base); + end = min(qmgr->start_queue + qmgr->num_queues, + range->queue_base + range->num_queues); + for (id = start; id < end; id++) { + index = id - qmgr->start_queue; + writel_relaxed(THRESH_GTE | 1, + &qmgr->reg_peek[index].ptr_size_thresh); + writel_relaxed(0, + &qmgr->reg_push[index].ptr_size_thresh); + } + } + + list_add_tail(&range->list, &kdev->queue_ranges); + dev_dbg(dev, "added range %s: %d-%d, %d irqs%s%s%s\n", + range->name, range->queue_base, + range->queue_base + range->num_queues - 1, + range->num_irqs, + (range->flags & RANGE_HAS_IRQ) ? ", has irq" : "", + (range->flags & RANGE_RESERVED) ? ", reserved" : "", + (range->flags & RANGE_HAS_ACCUMULATOR) ? ", acc" : ""); + kdev->num_queues_in_use += range->num_queues; + return 0; +} + +static int knav_setup_queue_pools(struct knav_device *kdev, + struct device_node *queue_pools) +{ + struct device_node *type, *range; + int ret; + + for_each_child_of_node(queue_pools, type) { + for_each_child_of_node(type, range) { + ret = knav_setup_queue_range(kdev, range); + /* return value ignored, we init the rest... */ + } + } + + /* ... and barf if they all failed! */ + if (list_empty(&kdev->queue_ranges)) { + dev_err(kdev->dev, "no valid queue range found\n"); + return -ENODEV; + } + return 0; +} + +static void knav_free_queue_range(struct knav_device *kdev, + struct knav_range_info *range) +{ + if (range->ops && range->ops->free_range) + range->ops->free_range(range); + list_del(&range->list); + devm_kfree(kdev->dev, range); +} + +static void knav_free_queue_ranges(struct knav_device *kdev) +{ + struct knav_range_info *range; + + for (;;) { + range = first_queue_range(kdev); + if (!range) + break; + knav_free_queue_range(kdev, range); + } +} + +static void knav_queue_free_regions(struct knav_device *kdev) +{ + struct knav_region *region; + struct knav_pool *pool; + unsigned size; + + for (;;) { + region = first_region(kdev); + if (!region) + break; + list_for_each_entry(pool, ®ion->pools, region_inst) + knav_pool_destroy(pool); + + size = region->virt_end - region->virt_start; + if (size) + free_pages_exact(region->virt_start, size); + list_del(®ion->list); + devm_kfree(kdev->dev, region); + } +} + +static void __iomem *knav_queue_map_reg(struct knav_device *kdev, + struct device_node *node, int index) +{ + struct resource res; + void __iomem *regs; + int ret; + + ret = of_address_to_resource(node, index, &res); + if (ret) { + dev_err(kdev->dev, "Can't translate of node(%s) address for index(%d)\n", + node->name, index); + return ERR_PTR(ret); + } + + regs = devm_ioremap_resource(kdev->dev, &res); + if (IS_ERR(regs)) + dev_err(kdev->dev, "Failed to map register base for index(%d) node(%s)\n", + index, node->name); + return regs; +} + +static int knav_queue_init_qmgrs(struct knav_device *kdev, + struct device_node *qmgrs) +{ + struct device *dev = kdev->dev; + struct knav_qmgr_info *qmgr; + struct device_node *child; + u32 temp[2]; + int ret; + + for_each_child_of_node(qmgrs, child) { + qmgr = devm_kzalloc(dev, sizeof(*qmgr), GFP_KERNEL); + if (!qmgr) { + dev_err(dev, "out of memory allocating qmgr\n"); + return -ENOMEM; + } + + ret = of_property_read_u32_array(child, "managed-queues", + temp, 2); + if (!ret) { + qmgr->start_queue = temp[0]; + qmgr->num_queues = temp[1]; + } else { + dev_err(dev, "invalid qmgr queue range\n"); + devm_kfree(dev, qmgr); + continue; + } + + dev_info(dev, "qmgr start queue %d, number of queues %d\n", + qmgr->start_queue, qmgr->num_queues); + + qmgr->reg_peek = + knav_queue_map_reg(kdev, child, + KNAV_QUEUE_PEEK_REG_INDEX); + qmgr->reg_status = + knav_queue_map_reg(kdev, child, + KNAV_QUEUE_STATUS_REG_INDEX); + qmgr->reg_config = + knav_queue_map_reg(kdev, child, + KNAV_QUEUE_CONFIG_REG_INDEX); + qmgr->reg_region = + knav_queue_map_reg(kdev, child, + KNAV_QUEUE_REGION_REG_INDEX); + qmgr->reg_push = + knav_queue_map_reg(kdev, child, + KNAV_QUEUE_PUSH_REG_INDEX); + qmgr->reg_pop = + knav_queue_map_reg(kdev, child, + KNAV_QUEUE_POP_REG_INDEX); + + if (IS_ERR(qmgr->reg_peek) || IS_ERR(qmgr->reg_status) || + IS_ERR(qmgr->reg_config) || IS_ERR(qmgr->reg_region) || + IS_ERR(qmgr->reg_push) || IS_ERR(qmgr->reg_pop)) { + dev_err(dev, "failed to map qmgr regs\n"); + if (!IS_ERR(qmgr->reg_peek)) + devm_iounmap(dev, qmgr->reg_peek); + if (!IS_ERR(qmgr->reg_status)) + devm_iounmap(dev, qmgr->reg_status); + if (!IS_ERR(qmgr->reg_config)) + devm_iounmap(dev, qmgr->reg_config); + if (!IS_ERR(qmgr->reg_region)) + devm_iounmap(dev, qmgr->reg_region); + if (!IS_ERR(qmgr->reg_push)) + devm_iounmap(dev, qmgr->reg_push); + if (!IS_ERR(qmgr->reg_pop)) + devm_iounmap(dev, qmgr->reg_pop); + devm_kfree(dev, qmgr); + continue; + } + + list_add_tail(&qmgr->list, &kdev->qmgrs); + dev_info(dev, "added qmgr start queue %d, num of queues %d, reg_peek %p, reg_status %p, reg_config %p, reg_region %p, reg_push %p, reg_pop %p\n", + qmgr->start_queue, qmgr->num_queues, + qmgr->reg_peek, qmgr->reg_status, + qmgr->reg_config, qmgr->reg_region, + qmgr->reg_push, qmgr->reg_pop); + } + return 0; +} + +static int knav_queue_init_pdsps(struct knav_device *kdev, + struct device_node *pdsps) +{ + struct device *dev = kdev->dev; + struct knav_pdsp_info *pdsp; + struct device_node *child; + int ret; + + for_each_child_of_node(pdsps, child) { + pdsp = devm_kzalloc(dev, sizeof(*pdsp), GFP_KERNEL); + if (!pdsp) { + dev_err(dev, "out of memory allocating pdsp\n"); + return -ENOMEM; + } + pdsp->name = knav_queue_find_name(child); + ret = of_property_read_string(child, "firmware", + &pdsp->firmware); + if (ret < 0 || !pdsp->firmware) { + dev_err(dev, "unknown firmware for pdsp %s\n", + pdsp->name); + devm_kfree(dev, pdsp); + continue; + } + dev_dbg(dev, "pdsp name %s fw name :%s\n", pdsp->name, + pdsp->firmware); + + pdsp->iram = + knav_queue_map_reg(kdev, child, + KNAV_QUEUE_PDSP_IRAM_REG_INDEX); + pdsp->regs = + knav_queue_map_reg(kdev, child, + KNAV_QUEUE_PDSP_REGS_REG_INDEX); + pdsp->intd = + knav_queue_map_reg(kdev, child, + KNAV_QUEUE_PDSP_INTD_REG_INDEX); + pdsp->command = + knav_queue_map_reg(kdev, child, + KNAV_QUEUE_PDSP_CMD_REG_INDEX); + + if (IS_ERR(pdsp->command) || IS_ERR(pdsp->iram) || + IS_ERR(pdsp->regs) || IS_ERR(pdsp->intd)) { + dev_err(dev, "failed to map pdsp %s regs\n", + pdsp->name); + if (!IS_ERR(pdsp->command)) + devm_iounmap(dev, pdsp->command); + if (!IS_ERR(pdsp->iram)) + devm_iounmap(dev, pdsp->iram); + if (!IS_ERR(pdsp->regs)) + devm_iounmap(dev, pdsp->regs); + if (!IS_ERR(pdsp->intd)) + devm_iounmap(dev, pdsp->intd); + devm_kfree(dev, pdsp); + continue; + } + of_property_read_u32(child, "id", &pdsp->id); + list_add_tail(&pdsp->list, &kdev->pdsps); + dev_dbg(dev, "added pdsp %s: command %p, iram %p, regs %p, intd %p, firmware %s\n", + pdsp->name, pdsp->command, pdsp->iram, pdsp->regs, + pdsp->intd, pdsp->firmware); + } + return 0; +} + +static int knav_queue_stop_pdsp(struct knav_device *kdev, + struct knav_pdsp_info *pdsp) +{ + u32 val, timeout = 1000; + int ret; + + val = readl_relaxed(&pdsp->regs->control) & ~PDSP_CTRL_ENABLE; + writel_relaxed(val, &pdsp->regs->control); + ret = knav_queue_pdsp_wait(&pdsp->regs->control, timeout, + PDSP_CTRL_RUNNING); + if (ret < 0) { + dev_err(kdev->dev, "timed out on pdsp %s stop\n", pdsp->name); + return ret; + } + return 0; +} + +static int knav_queue_load_pdsp(struct knav_device *kdev, + struct knav_pdsp_info *pdsp) +{ + int i, ret, fwlen; + const struct firmware *fw; + u32 *fwdata; + + ret = request_firmware(&fw, pdsp->firmware, kdev->dev); + if (ret) { + dev_err(kdev->dev, "failed to get firmware %s for pdsp %s\n", + pdsp->firmware, pdsp->name); + return ret; + } + writel_relaxed(pdsp->id + 1, pdsp->command + 0x18); + /* download the firmware */ + fwdata = (u32 *)fw->data; + fwlen = (fw->size + sizeof(u32) - 1) / sizeof(u32); + for (i = 0; i < fwlen; i++) + writel_relaxed(be32_to_cpu(fwdata[i]), pdsp->iram + i); + + release_firmware(fw); + return 0; +} + +static int knav_queue_start_pdsp(struct knav_device *kdev, + struct knav_pdsp_info *pdsp) +{ + u32 val, timeout = 1000; + int ret; + + /* write a command for sync */ + writel_relaxed(0xffffffff, pdsp->command); + while (readl_relaxed(pdsp->command) != 0xffffffff) + cpu_relax(); + + /* soft reset the PDSP */ + val = readl_relaxed(&pdsp->regs->control); + val &= ~(PDSP_CTRL_PC_MASK | PDSP_CTRL_SOFT_RESET); + writel_relaxed(val, &pdsp->regs->control); + + /* enable pdsp */ + val = readl_relaxed(&pdsp->regs->control) | PDSP_CTRL_ENABLE; + writel_relaxed(val, &pdsp->regs->control); + + /* wait for command register to clear */ + ret = knav_queue_pdsp_wait(pdsp->command, timeout, 0); + if (ret < 0) { + dev_err(kdev->dev, + "timed out on pdsp %s command register wait\n", + pdsp->name); + return ret; + } + return 0; +} + +static void knav_queue_stop_pdsps(struct knav_device *kdev) +{ + struct knav_pdsp_info *pdsp; + + /* disable all pdsps */ + for_each_pdsp(kdev, pdsp) + knav_queue_stop_pdsp(kdev, pdsp); +} + +static int knav_queue_start_pdsps(struct knav_device *kdev) +{ + struct knav_pdsp_info *pdsp; + int ret; + + knav_queue_stop_pdsps(kdev); + /* now load them all */ + for_each_pdsp(kdev, pdsp) { + ret = knav_queue_load_pdsp(kdev, pdsp); + if (ret < 0) + return ret; + } + + for_each_pdsp(kdev, pdsp) { + ret = knav_queue_start_pdsp(kdev, pdsp); + WARN_ON(ret); + } + return 0; +} + +static inline struct knav_qmgr_info *knav_find_qmgr(unsigned id) +{ + struct knav_qmgr_info *qmgr; + + for_each_qmgr(kdev, qmgr) { + if ((id >= qmgr->start_queue) && + (id < qmgr->start_queue + qmgr->num_queues)) + return qmgr; + } + return NULL; +} + +static int knav_queue_init_queue(struct knav_device *kdev, + struct knav_range_info *range, + struct knav_queue_inst *inst, + unsigned id) +{ + char irq_name[KNAV_NAME_SIZE]; + inst->qmgr = knav_find_qmgr(id); + if (!inst->qmgr) + return -1; + + INIT_LIST_HEAD(&inst->handles); + inst->kdev = kdev; + inst->range = range; + inst->irq_num = -1; + inst->id = id; + scnprintf(irq_name, sizeof(irq_name), "hwqueue-%d", id); + inst->irq_name = kstrndup(irq_name, sizeof(irq_name), GFP_KERNEL); + + if (range->ops && range->ops->init_queue) + return range->ops->init_queue(range, inst); + else + return 0; +} + +static int knav_queue_init_queues(struct knav_device *kdev) +{ + struct knav_range_info *range; + int size, id, base_idx; + int idx = 0, ret = 0; + + /* how much do we need for instance data? */ + size = sizeof(struct knav_queue_inst); + + /* round this up to a power of 2, keep the index to instance + * arithmetic fast. + * */ + kdev->inst_shift = order_base_2(size); + size = (1 << kdev->inst_shift) * kdev->num_queues_in_use; + kdev->instances = devm_kzalloc(kdev->dev, size, GFP_KERNEL); + if (!kdev->instances) + return -1; + + for_each_queue_range(kdev, range) { + if (range->ops && range->ops->init_range) + range->ops->init_range(range); + base_idx = idx; + for (id = range->queue_base; + id < range->queue_base + range->num_queues; id++, idx++) { + ret = knav_queue_init_queue(kdev, range, + knav_queue_idx_to_inst(kdev, idx), id); + if (ret < 0) + return ret; + } + range->queue_base_inst = + knav_queue_idx_to_inst(kdev, base_idx); + } + return 0; +} + +static int knav_queue_probe(struct platform_device *pdev) +{ + struct device_node *node = pdev->dev.of_node; + struct device_node *qmgrs, *queue_pools, *regions, *pdsps; + struct device *dev = &pdev->dev; + u32 temp[2]; + int ret; + + if (!node) { + dev_err(dev, "device tree info unavailable\n"); + return -ENODEV; + } + + kdev = devm_kzalloc(dev, sizeof(struct knav_device), GFP_KERNEL); + if (!kdev) { + dev_err(dev, "memory allocation failed\n"); + return -ENOMEM; + } + + platform_set_drvdata(pdev, kdev); + kdev->dev = dev; + INIT_LIST_HEAD(&kdev->queue_ranges); + INIT_LIST_HEAD(&kdev->qmgrs); + INIT_LIST_HEAD(&kdev->pools); + INIT_LIST_HEAD(&kdev->regions); + INIT_LIST_HEAD(&kdev->pdsps); + + pm_runtime_enable(&pdev->dev); + ret = pm_runtime_get_sync(&pdev->dev); + if (ret < 0) { + dev_err(dev, "Failed to enable QMSS\n"); + return ret; + } + + if (of_property_read_u32_array(node, "queue-range", temp, 2)) { + dev_err(dev, "queue-range not specified\n"); + ret = -ENODEV; + goto err; + } + kdev->base_id = temp[0]; + kdev->num_queues = temp[1]; + + /* Initialize queue managers using device tree configuration */ + qmgrs = of_get_child_by_name(node, "qmgrs"); + if (!qmgrs) { + dev_err(dev, "queue manager info not specified\n"); + ret = -ENODEV; + goto err; + } + ret = knav_queue_init_qmgrs(kdev, qmgrs); + of_node_put(qmgrs); + if (ret) + goto err; + + /* get pdsp configuration values from device tree */ + pdsps = of_get_child_by_name(node, "pdsps"); + if (pdsps) { + ret = knav_queue_init_pdsps(kdev, pdsps); + if (ret) + goto err; + + ret = knav_queue_start_pdsps(kdev); + if (ret) + goto err; + } + of_node_put(pdsps); + + /* get usable queue range values from device tree */ + queue_pools = of_get_child_by_name(node, "queue-pools"); + if (!queue_pools) { + dev_err(dev, "queue-pools not specified\n"); + ret = -ENODEV; + goto err; + } + ret = knav_setup_queue_pools(kdev, queue_pools); + of_node_put(queue_pools); + if (ret) + goto err; + + ret = knav_get_link_ram(kdev, "linkram0", &kdev->link_rams[0]); + if (ret) { + dev_err(kdev->dev, "could not setup linking ram\n"); + goto err; + } + + ret = knav_get_link_ram(kdev, "linkram1", &kdev->link_rams[1]); + if (ret) { + /* + * nothing really, we have one linking ram already, so we just + * live within our means + */ + } + + ret = knav_queue_setup_link_ram(kdev); + if (ret) + goto err; + + regions = of_get_child_by_name(node, "descriptor-regions"); + if (!regions) { + dev_err(dev, "descriptor-regions not specified\n"); + goto err; + } + ret = knav_queue_setup_regions(kdev, regions); + of_node_put(regions); + if (ret) + goto err; + + ret = knav_queue_init_queues(kdev); + if (ret < 0) { + dev_err(dev, "hwqueue initialization failed\n"); + goto err; + } + + debugfs_create_file("qmss", S_IFREG | S_IRUGO, NULL, NULL, + &knav_queue_debug_ops); + return 0; + +err: + knav_queue_stop_pdsps(kdev); + knav_queue_free_regions(kdev); + knav_free_queue_ranges(kdev); + pm_runtime_put_sync(&pdev->dev); + pm_runtime_disable(&pdev->dev); + return ret; +} + +static int knav_queue_remove(struct platform_device *pdev) +{ + /* TODO: Free resources */ + pm_runtime_put_sync(&pdev->dev); + pm_runtime_disable(&pdev->dev); + return 0; +} + +/* Match table for of_platform binding */ +static struct of_device_id keystone_qmss_of_match[] = { + { .compatible = "ti,keystone-navigator-qmss", }, + {}, +}; +MODULE_DEVICE_TABLE(of, keystone_qmss_of_match); + +static struct platform_driver keystone_qmss_driver = { + .probe = knav_queue_probe, + .remove = knav_queue_remove, + .driver = { + .name = "keystone-navigator-qmss", + .owner = THIS_MODULE, + .of_match_table = keystone_qmss_of_match, + }, +}; +module_platform_driver(keystone_qmss_driver); + +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("TI QMSS driver for Keystone SOCs"); +MODULE_AUTHOR("Sandeep Nair "); +MODULE_AUTHOR("Santosh Shilimkar "); diff --git a/include/linux/soc/ti/knav_qmss.h b/include/linux/soc/ti/knav_qmss.h new file mode 100644 index 000000000000..9f0ebb3bad27 --- /dev/null +++ b/include/linux/soc/ti/knav_qmss.h @@ -0,0 +1,90 @@ +/* + * Keystone Navigator Queue Management Sub-System header + * + * Copyright (C) 2014 Texas Instruments Incorporated - http://www.ti.com + * Author: Sandeep Nair + * Cyril Chemparathy + * Santosh Shilimkar + * + * 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. + */ + +#ifndef __SOC_TI_KNAV_QMSS_H__ +#define __SOC_TI_KNAV_QMSS_H__ + +#include +#include +#include +#include +#include +#include + +/* queue types */ +#define KNAV_QUEUE_QPEND ((unsigned)-2) /* interruptible qpend queue */ +#define KNAV_QUEUE_ACC ((unsigned)-3) /* Accumulated queue */ +#define KNAV_QUEUE_GP ((unsigned)-4) /* General purpose queue */ + +/* queue flags */ +#define KNAV_QUEUE_SHARED 0x0001 /* Queue can be shared */ + +/** + * enum knav_queue_ctrl_cmd - queue operations. + * @KNAV_QUEUE_GET_ID: Get the ID number for an open queue + * @KNAV_QUEUE_FLUSH: forcibly empty a queue if possible + * @KNAV_QUEUE_SET_NOTIFIER: Set a notifier callback to a queue handle. + * @KNAV_QUEUE_ENABLE_NOTIFY: Enable notifier callback for a queue handle. + * @KNAV_QUEUE_DISABLE_NOTIFY: Disable notifier callback for a queue handle. + * @KNAV_QUEUE_GET_COUNT: Get number of queues. + */ +enum knav_queue_ctrl_cmd { + KNAV_QUEUE_GET_ID, + KNAV_QUEUE_FLUSH, + KNAV_QUEUE_SET_NOTIFIER, + KNAV_QUEUE_ENABLE_NOTIFY, + KNAV_QUEUE_DISABLE_NOTIFY, + KNAV_QUEUE_GET_COUNT +}; + +/* Queue notifier callback prototype */ +typedef void (*knav_queue_notify_fn)(void *arg); + +/** + * struct knav_queue_notify_config: Notifier configuration + * @fn: Notifier function + * @fn_arg: Notifier function arguments + */ +struct knav_queue_notify_config { + knav_queue_notify_fn fn; + void *fn_arg; +}; + +void *knav_queue_open(const char *name, unsigned id, + unsigned flags); +void knav_queue_close(void *qhandle); +int knav_queue_device_control(void *qhandle, + enum knav_queue_ctrl_cmd cmd, + unsigned long arg); +dma_addr_t knav_queue_pop(void *qhandle, unsigned *size); +int knav_queue_push(void *qhandle, dma_addr_t dma, + unsigned size, unsigned flags); + +void *knav_pool_create(const char *name, + int num_desc, int region_id); +void knav_pool_destroy(void *ph); +int knav_pool_count(void *ph); +void *knav_pool_desc_get(void *ph); +void knav_pool_desc_put(void *ph, void *desc); +int knav_pool_desc_map(void *ph, void *desc, unsigned size, + dma_addr_t *dma, unsigned *dma_sz); +void *knav_pool_desc_unmap(void *ph, dma_addr_t dma, unsigned dma_sz); +dma_addr_t knav_pool_desc_virt_to_dma(void *ph, void *virt); +void *knav_pool_desc_dma_to_virt(void *ph, dma_addr_t dma); + +#endif /* __SOC_TI_KNAV_QMSS_H__ */ -- cgit v1.2.1 From 8172296d8717be1951da4bb4feb2700a60e8cdde Mon Sep 17 00:00:00 2001 From: Santosh Shilimkar Date: Sun, 30 Mar 2014 17:29:04 -0400 Subject: Documentation: dt: soc: add Keystone Navigator DMA bindings The Keystone Navigator DMA driver sets up the dma channels and flows for the QMSS(Queue Manager SubSystem) who triggers the actual data movements across clients using destination queues. Every client modules like NETCP(Network Coprocessor), SRIO(Serial Rapid IO) and CRYPTO Engines has its own instance of packet dma hardware. QMSS has also an internal packet DMA module which is used as an infrastructure DMA with zero copy. Initially this driver was proposed as DMA engine driver but since the hardware is not typical DMA engine and hence doesn't comply with typical DMA engine driver needs, that approach was naked. Link to that discussion - https://lkml.org/lkml/2014/3/18/340 As aligned, now we pair the Navigator DMA with its companion Navigator QMSS subsystem driver. Cc: Greg Kroah-Hartman Cc: Kumar Gala Cc: Olof Johansson Cc: Arnd Bergmann Cc: Grant Likely Cc: Rob Herring Cc: Mark Rutland Signed-off-by: Sandeep Nair Signed-off-by: Santosh Shilimkar --- .../bindings/soc/ti/keystone-navigator-dma.txt | 111 +++++++++++++++++++++ 1 file changed, 111 insertions(+) create mode 100644 Documentation/devicetree/bindings/soc/ti/keystone-navigator-dma.txt diff --git a/Documentation/devicetree/bindings/soc/ti/keystone-navigator-dma.txt b/Documentation/devicetree/bindings/soc/ti/keystone-navigator-dma.txt new file mode 100644 index 000000000000..337c4ea5c57b --- /dev/null +++ b/Documentation/devicetree/bindings/soc/ti/keystone-navigator-dma.txt @@ -0,0 +1,111 @@ +Keystone Navigator DMA Controller + +This document explains the device tree bindings for the packet dma +on keystone devices. The Keystone Navigator DMA driver sets up the dma +channels and flows for the QMSS(Queue Manager SubSystem) who triggers +the actual data movements across clients using destination queues. Every +client modules like NETCP(Network Coprocessor), SRIO(Serial Rapid IO), +CRYPTO Engines etc has its own instance of dma hardware. QMSS has also +an internal packet DMA module which is used as an infrastructure DMA +with zero copy. + +Navigator DMA cloud layout: + ------------------ + | Navigator DMAs | + ------------------ + | + |-> DMA instance #0 + | + |-> DMA instance #1 + . + . + | + |-> DMA instance #n + +Navigator DMA properties: +Required properties: + - compatible: Should be "ti,keystone-navigator-dma" + - clocks: phandle to dma instances clocks. The clock handles can be as + many as the dma instances. The order should be maintained as per + the dma instances. + - ti,navigator-cloud-address: Should contain base address for the multi-core + navigator cloud and number of addresses depends on SOC integration + configuration.. Navigator cloud global address needs to be programmed + into DMA and the DMA uses it as the physical addresses to reach queue + managers. Note that these addresses though points to queue managers, + they are relevant only from DMA perspective. The QMSS may not choose to + use them since it has a different address space view to reach all + its components. + +DMA instance properties: +Required properties: + - reg: Should contain register location and length of the following dma + register regions. Register regions should be specified in the following + order. + - Global control register region (global). + - Tx DMA channel configuration register region (txchan). + - Rx DMA channel configuration register region (rxchan). + - Tx DMA channel Scheduler configuration register region (txsched). + - Rx DMA flow configuration register region (rxflow). + +Optional properties: + - reg-names: Names for the register regions. + - ti,enable-all: Enable all DMA channels vs clients opening specific channels + what they need. This property is useful for the userspace fast path + case where the linux drivers enables the channels used by userland + stack. + - ti,loop-back: To loopback Tx streaming I/F to Rx streaming I/F. Used for + infrastructure transfers. + - ti,rx-retry-timeout: Number of dma cycles to wait before retry on buffer + starvation. + +Example: + + knav_dmas: knav_dmas@0 { + compatible = "ti,keystone-navigator-dma"; + clocks = <&papllclk>, <&clkxge>; + #address-cells = <1>; + #size-cells = <1>; + ranges; + ti,navigator-cloud-address = <0x23a80000 0x23a90000 + 0x23aa0000 0x23ab0000>; + + dma_gbe: dma_gbe@0 { + reg = <0x2004000 0x100>, + <0x2004400 0x120>, + <0x2004800 0x300>, + <0x2004c00 0x120>, + <0x2005000 0x400>; + reg-names = "global", "txchan", "rxchan", + "txsched", "rxflow"; + }; + + dma_xgbe: dma_xgbe@0 { + reg = <0x2fa1000 0x100>, + <0x2fa1400 0x200>, + <0x2fa1800 0x200>, + <0x2fa1c00 0x200>, + <0x2fa2000 0x400>; + reg-names = "global", "txchan", "rxchan", + "txsched", "rxflow"; + }; + }; + +Navigator DMA client: +Required properties: + - ti,navigator-dmas: List of one or more DMA specifiers, each consisting of + - A phandle pointing to DMA instance node + - A DMA channel number as a phandle arg. + - ti,navigator-dma-names: Contains dma channel name for each DMA specifier in + the 'ti,navigator-dmas' property. + +Example: + + netcp: netcp@2090000 { + .. + ti,navigator-dmas = <&dma_gbe 22>, + <&dma_gbe 23>, + <&dma_gbe 8>; + ti,navigator-dma-names = "netrx0", "netrx1", "nettx"; + .. + }; -- cgit v1.2.1 From 88139ed030583557751e279968e13e892ae10825 Mon Sep 17 00:00:00 2001 From: Santosh Shilimkar Date: Sun, 30 Mar 2014 17:29:04 -0400 Subject: soc: ti: add Keystone Navigator DMA support The Keystone Navigator DMA driver sets up the dma channels and flows for the QMSS(Queue Manager SubSystem) who triggers the actual data movements across clients using destination queues. Every client modules like NETCP(Network Coprocessor), SRIO(Serial Rapid IO) and CRYPTO Engines has its own instance of packet dma hardware. QMSS has also an internal packet DMA module which is used as an infrastructure DMA with zero copy. Initially this driver was proposed as DMA engine driver but since the hardware is not typical DMA engine and hence doesn't comply with typical DMA engine driver needs, that approach was naked. Link to that discussion - https://lkml.org/lkml/2014/3/18/340 As aligned, now we pair the Navigator DMA with its companion Navigator QMSS subsystem driver. Cc: Greg Kroah-Hartman Cc: Kumar Gala Cc: Olof Johansson Cc: Arnd Bergmann Cc: Grant Likely Cc: Rob Herring Cc: Mark Rutland Signed-off-by: Sandeep Nair Signed-off-by: Santosh Shilimkar --- drivers/soc/ti/Kconfig | 10 + drivers/soc/ti/Makefile | 1 + drivers/soc/ti/knav_dma.c | 815 ++++++++++++++++++++++++++++++++++++++++ include/linux/soc/ti/knav_dma.h | 175 +++++++++ 4 files changed, 1001 insertions(+) create mode 100644 drivers/soc/ti/knav_dma.c create mode 100644 include/linux/soc/ti/knav_dma.h diff --git a/drivers/soc/ti/Kconfig b/drivers/soc/ti/Kconfig index f73896f762e8..7266b2165183 100644 --- a/drivers/soc/ti/Kconfig +++ b/drivers/soc/ti/Kconfig @@ -18,4 +18,14 @@ config KEYSTONE_NAVIGATOR_QMSS If unsure, say N. +config KEYSTONE_NAVIGATOR_DMA + tristate "TI Keystone Navigator Packet DMA support" + depends on ARCH_KEYSTONE + help + Say y tp enable support for the Keystone Navigator Packet DMA on + on Keystone family of devices. It sets up the dma channels for the + Queue Manager Sub System. + + If unsure, say N. + endif # SOC_TI diff --git a/drivers/soc/ti/Makefile b/drivers/soc/ti/Makefile index bf85cacd5b85..6bed611e1934 100644 --- a/drivers/soc/ti/Makefile +++ b/drivers/soc/ti/Makefile @@ -2,3 +2,4 @@ # TI Keystone SOC drivers # obj-$(CONFIG_KEYSTONE_NAVIGATOR_QMSS) += knav_qmss_queue.o knav_qmss_acc.o +obj-$(CONFIG_KEYSTONE_NAVIGATOR_DMA) += knav_dma.o diff --git a/drivers/soc/ti/knav_dma.c b/drivers/soc/ti/knav_dma.c new file mode 100644 index 000000000000..17264275f32b --- /dev/null +++ b/drivers/soc/ti/knav_dma.c @@ -0,0 +1,815 @@ +/* + * Copyright (C) 2014 Texas Instruments Incorporated + * Authors: Santosh Shilimkar + * Sandeep Nair + * Cyril Chemparathy + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include + +#define REG_MASK 0xffffffff + +#define DMA_LOOPBACK BIT(31) +#define DMA_ENABLE BIT(31) +#define DMA_TEARDOWN BIT(30) + +#define DMA_TX_FILT_PSWORDS BIT(29) +#define DMA_TX_FILT_EINFO BIT(30) +#define DMA_TX_PRIO_SHIFT 0 +#define DMA_RX_PRIO_SHIFT 16 +#define DMA_PRIO_MASK GENMASK(3, 0) +#define DMA_PRIO_DEFAULT 0 +#define DMA_RX_TIMEOUT_DEFAULT 17500 /* cycles */ +#define DMA_RX_TIMEOUT_MASK GENMASK(16, 0) +#define DMA_RX_TIMEOUT_SHIFT 0 + +#define CHAN_HAS_EPIB BIT(30) +#define CHAN_HAS_PSINFO BIT(29) +#define CHAN_ERR_RETRY BIT(28) +#define CHAN_PSINFO_AT_SOP BIT(25) +#define CHAN_SOP_OFF_SHIFT 16 +#define CHAN_SOP_OFF_MASK GENMASK(9, 0) +#define DESC_TYPE_SHIFT 26 +#define DESC_TYPE_MASK GENMASK(2, 0) + +/* + * QMGR & QNUM together make up 14 bits with QMGR as the 2 MSb's in the logical + * navigator cloud mapping scheme. + * using the 14bit physical queue numbers directly maps into this scheme. + */ +#define CHAN_QNUM_MASK GENMASK(14, 0) +#define DMA_MAX_QMS 4 +#define DMA_TIMEOUT 1 /* msecs */ +#define DMA_INVALID_ID 0xffff + +struct reg_global { + u32 revision; + u32 perf_control; + u32 emulation_control; + u32 priority_control; + u32 qm_base_address[DMA_MAX_QMS]; +}; + +struct reg_chan { + u32 control; + u32 mode; + u32 __rsvd[6]; +}; + +struct reg_tx_sched { + u32 prio; +}; + +struct reg_rx_flow { + u32 control; + u32 tags; + u32 tag_sel; + u32 fdq_sel[2]; + u32 thresh[3]; +}; + +struct knav_dma_pool_device { + struct device *dev; + struct list_head list; +}; + +struct knav_dma_device { + bool loopback, enable_all; + unsigned tx_priority, rx_priority, rx_timeout; + unsigned logical_queue_managers; + unsigned qm_base_address[DMA_MAX_QMS]; + struct reg_global __iomem *reg_global; + struct reg_chan __iomem *reg_tx_chan; + struct reg_rx_flow __iomem *reg_rx_flow; + struct reg_chan __iomem *reg_rx_chan; + struct reg_tx_sched __iomem *reg_tx_sched; + unsigned max_rx_chan, max_tx_chan; + unsigned max_rx_flow; + char name[32]; + atomic_t ref_count; + struct list_head list; + struct list_head chan_list; + spinlock_t lock; +}; + +struct knav_dma_chan { + enum dma_transfer_direction direction; + struct knav_dma_device *dma; + atomic_t ref_count; + + /* registers */ + struct reg_chan __iomem *reg_chan; + struct reg_tx_sched __iomem *reg_tx_sched; + struct reg_rx_flow __iomem *reg_rx_flow; + + /* configuration stuff */ + unsigned channel, flow; + struct knav_dma_cfg cfg; + struct list_head list; + spinlock_t lock; +}; + +#define chan_number(ch) ((ch->direction == DMA_MEM_TO_DEV) ? \ + ch->channel : ch->flow) + +static struct knav_dma_pool_device *kdev; + +static bool check_config(struct knav_dma_chan *chan, struct knav_dma_cfg *cfg) +{ + if (!memcmp(&chan->cfg, cfg, sizeof(*cfg))) + return true; + else + return false; +} + +static int chan_start(struct knav_dma_chan *chan, + struct knav_dma_cfg *cfg) +{ + u32 v = 0; + + spin_lock(&chan->lock); + if ((chan->direction == DMA_MEM_TO_DEV) && chan->reg_chan) { + if (cfg->u.tx.filt_pswords) + v |= DMA_TX_FILT_PSWORDS; + if (cfg->u.tx.filt_einfo) + v |= DMA_TX_FILT_EINFO; + writel_relaxed(v, &chan->reg_chan->mode); + writel_relaxed(DMA_ENABLE, &chan->reg_chan->control); + } + + if (chan->reg_tx_sched) + writel_relaxed(cfg->u.tx.priority, &chan->reg_tx_sched->prio); + + if (chan->reg_rx_flow) { + v = 0; + + if (cfg->u.rx.einfo_present) + v |= CHAN_HAS_EPIB; + if (cfg->u.rx.psinfo_present) + v |= CHAN_HAS_PSINFO; + if (cfg->u.rx.err_mode == DMA_RETRY) + v |= CHAN_ERR_RETRY; + v |= (cfg->u.rx.desc_type & DESC_TYPE_MASK) << DESC_TYPE_SHIFT; + if (cfg->u.rx.psinfo_at_sop) + v |= CHAN_PSINFO_AT_SOP; + v |= (cfg->u.rx.sop_offset & CHAN_SOP_OFF_MASK) + << CHAN_SOP_OFF_SHIFT; + v |= cfg->u.rx.dst_q & CHAN_QNUM_MASK; + + writel_relaxed(v, &chan->reg_rx_flow->control); + writel_relaxed(0, &chan->reg_rx_flow->tags); + writel_relaxed(0, &chan->reg_rx_flow->tag_sel); + + v = cfg->u.rx.fdq[0] << 16; + v |= cfg->u.rx.fdq[1] & CHAN_QNUM_MASK; + writel_relaxed(v, &chan->reg_rx_flow->fdq_sel[0]); + + v = cfg->u.rx.fdq[2] << 16; + v |= cfg->u.rx.fdq[3] & CHAN_QNUM_MASK; + writel_relaxed(v, &chan->reg_rx_flow->fdq_sel[1]); + + writel_relaxed(0, &chan->reg_rx_flow->thresh[0]); + writel_relaxed(0, &chan->reg_rx_flow->thresh[1]); + writel_relaxed(0, &chan->reg_rx_flow->thresh[2]); + } + + /* Keep a copy of the cfg */ + memcpy(&chan->cfg, cfg, sizeof(*cfg)); + spin_unlock(&chan->lock); + + return 0; +} + +static int chan_teardown(struct knav_dma_chan *chan) +{ + unsigned long end, value; + + if (!chan->reg_chan) + return 0; + + /* indicate teardown */ + writel_relaxed(DMA_TEARDOWN, &chan->reg_chan->control); + + /* wait for the dma to shut itself down */ + end = jiffies + msecs_to_jiffies(DMA_TIMEOUT); + do { + value = readl_relaxed(&chan->reg_chan->control); + if ((value & DMA_ENABLE) == 0) + break; + } while (time_after(end, jiffies)); + + if (readl_relaxed(&chan->reg_chan->control) & DMA_ENABLE) { + dev_err(kdev->dev, "timeout waiting for teardown\n"); + return -ETIMEDOUT; + } + + return 0; +} + +static void chan_stop(struct knav_dma_chan *chan) +{ + spin_lock(&chan->lock); + if (chan->reg_rx_flow) { + /* first detach fdqs, starve out the flow */ + writel_relaxed(0, &chan->reg_rx_flow->fdq_sel[0]); + writel_relaxed(0, &chan->reg_rx_flow->fdq_sel[1]); + writel_relaxed(0, &chan->reg_rx_flow->thresh[0]); + writel_relaxed(0, &chan->reg_rx_flow->thresh[1]); + writel_relaxed(0, &chan->reg_rx_flow->thresh[2]); + } + + /* teardown the dma channel */ + chan_teardown(chan); + + /* then disconnect the completion side */ + if (chan->reg_rx_flow) { + writel_relaxed(0, &chan->reg_rx_flow->control); + writel_relaxed(0, &chan->reg_rx_flow->tags); + writel_relaxed(0, &chan->reg_rx_flow->tag_sel); + } + + memset(&chan->cfg, 0, sizeof(struct knav_dma_cfg)); + spin_unlock(&chan->lock); + + dev_dbg(kdev->dev, "channel stopped\n"); +} + +static void dma_hw_enable_all(struct knav_dma_device *dma) +{ + int i; + + for (i = 0; i < dma->max_tx_chan; i++) { + writel_relaxed(0, &dma->reg_tx_chan[i].mode); + writel_relaxed(DMA_ENABLE, &dma->reg_tx_chan[i].control); + } +} + + +static void knav_dma_hw_init(struct knav_dma_device *dma) +{ + unsigned v; + int i; + + spin_lock(&dma->lock); + v = dma->loopback ? DMA_LOOPBACK : 0; + writel_relaxed(v, &dma->reg_global->emulation_control); + + v = readl_relaxed(&dma->reg_global->perf_control); + v |= ((dma->rx_timeout & DMA_RX_TIMEOUT_MASK) << DMA_RX_TIMEOUT_SHIFT); + writel_relaxed(v, &dma->reg_global->perf_control); + + v = ((dma->tx_priority << DMA_TX_PRIO_SHIFT) | + (dma->rx_priority << DMA_RX_PRIO_SHIFT)); + + writel_relaxed(v, &dma->reg_global->priority_control); + + /* Always enable all Rx channels. Rx paths are managed using flows */ + for (i = 0; i < dma->max_rx_chan; i++) + writel_relaxed(DMA_ENABLE, &dma->reg_rx_chan[i].control); + + for (i = 0; i < dma->logical_queue_managers; i++) + writel_relaxed(dma->qm_base_address[i], + &dma->reg_global->qm_base_address[i]); + spin_unlock(&dma->lock); +} + +static void knav_dma_hw_destroy(struct knav_dma_device *dma) +{ + int i; + unsigned v; + + spin_lock(&dma->lock); + v = ~DMA_ENABLE & REG_MASK; + + for (i = 0; i < dma->max_rx_chan; i++) + writel_relaxed(v, &dma->reg_rx_chan[i].control); + + for (i = 0; i < dma->max_tx_chan; i++) + writel_relaxed(v, &dma->reg_tx_chan[i].control); + spin_unlock(&dma->lock); +} + +static void dma_debug_show_channels(struct seq_file *s, + struct knav_dma_chan *chan) +{ + int i; + + seq_printf(s, "\t%s %d:\t", + ((chan->direction == DMA_MEM_TO_DEV) ? "tx chan" : "rx flow"), + chan_number(chan)); + + if (chan->direction == DMA_MEM_TO_DEV) { + seq_printf(s, "einfo - %d, pswords - %d, priority - %d\n", + chan->cfg.u.tx.filt_einfo, + chan->cfg.u.tx.filt_pswords, + chan->cfg.u.tx.priority); + } else { + seq_printf(s, "einfo - %d, psinfo - %d, desc_type - %d\n", + chan->cfg.u.rx.einfo_present, + chan->cfg.u.rx.psinfo_present, + chan->cfg.u.rx.desc_type); + seq_printf(s, "\t\t\tdst_q: [%d], thresh: %d fdq: ", + chan->cfg.u.rx.dst_q, + chan->cfg.u.rx.thresh); + for (i = 0; i < KNAV_DMA_FDQ_PER_CHAN; i++) + seq_printf(s, "[%d]", chan->cfg.u.rx.fdq[i]); + seq_printf(s, "\n"); + } +} + +static void dma_debug_show_devices(struct seq_file *s, + struct knav_dma_device *dma) +{ + struct knav_dma_chan *chan; + + list_for_each_entry(chan, &dma->chan_list, list) { + if (atomic_read(&chan->ref_count)) + dma_debug_show_channels(s, chan); + } +} + +static int dma_debug_show(struct seq_file *s, void *v) +{ + struct knav_dma_device *dma; + + list_for_each_entry(dma, &kdev->list, list) { + if (atomic_read(&dma->ref_count)) { + seq_printf(s, "%s : max_tx_chan: (%d), max_rx_flows: (%d)\n", + dma->name, dma->max_tx_chan, dma->max_rx_flow); + dma_debug_show_devices(s, dma); + } + } + + return 0; +} + +static int knav_dma_debug_open(struct inode *inode, struct file *file) +{ + return single_open(file, dma_debug_show, NULL); +} + +static const struct file_operations knav_dma_debug_ops = { + .open = knav_dma_debug_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; + +static int of_channel_match_helper(struct device_node *np, const char *name, + const char **dma_instance) +{ + struct of_phandle_args args; + struct device_node *dma_node; + int index; + + dma_node = of_parse_phandle(np, "ti,navigator-dmas", 0); + if (!dma_node) + return -ENODEV; + + *dma_instance = dma_node->name; + index = of_property_match_string(np, "ti,navigator-dma-names", name); + if (index < 0) { + dev_err(kdev->dev, "No 'ti,navigator-dma-names' propery\n"); + return -ENODEV; + } + + if (of_parse_phandle_with_fixed_args(np, "ti,navigator-dmas", + 1, index, &args)) { + dev_err(kdev->dev, "Missing the pahndle args name %s\n", name); + return -ENODEV; + } + + if (args.args[0] < 0) { + dev_err(kdev->dev, "Missing args for %s\n", name); + return -ENODEV; + } + + return args.args[0]; +} + +/** + * knav_dma_open_channel() - try to setup an exclusive slave channel + * @dev: pointer to client device structure + * @name: slave channel name + * @config: dma configuration parameters + * + * Returns pointer to appropriate DMA channel on success or NULL. + */ +void *knav_dma_open_channel(struct device *dev, const char *name, + struct knav_dma_cfg *config) +{ + struct knav_dma_chan *chan; + struct knav_dma_device *dma; + bool found = false; + int chan_num = -1; + const char *instance; + + if (!kdev) { + pr_err("keystone-navigator-dma driver not registered\n"); + return (void *)-EINVAL; + } + + chan_num = of_channel_match_helper(dev->of_node, name, &instance); + if (chan_num < 0) { + dev_err(kdev->dev, "No DMA instace with name %s\n", name); + return (void *)-EINVAL; + } + + dev_dbg(kdev->dev, "initializing %s channel %d from DMA %s\n", + config->direction == DMA_MEM_TO_DEV ? "transmit" : + config->direction == DMA_DEV_TO_MEM ? "receive" : + "unknown", chan_num, instance); + + if (config->direction != DMA_MEM_TO_DEV && + config->direction != DMA_DEV_TO_MEM) { + dev_err(kdev->dev, "bad direction\n"); + return (void *)-EINVAL; + } + + /* Look for correct dma instance */ + list_for_each_entry(dma, &kdev->list, list) { + if (!strcmp(dma->name, instance)) { + found = true; + break; + } + } + if (!found) { + dev_err(kdev->dev, "No DMA instace with name %s\n", instance); + return (void *)-EINVAL; + } + + /* Look for correct dma channel from dma instance */ + found = false; + list_for_each_entry(chan, &dma->chan_list, list) { + if (config->direction == DMA_MEM_TO_DEV) { + if (chan->channel == chan_num) { + found = true; + break; + } + } else { + if (chan->flow == chan_num) { + found = true; + break; + } + } + } + if (!found) { + dev_err(kdev->dev, "channel %d is not in DMA %s\n", + chan_num, instance); + return (void *)-EINVAL; + } + + if (atomic_read(&chan->ref_count) >= 1) { + if (!check_config(chan, config)) { + dev_err(kdev->dev, "channel %d config miss-match\n", + chan_num); + return (void *)-EINVAL; + } + } + + if (atomic_inc_return(&chan->dma->ref_count) <= 1) + knav_dma_hw_init(chan->dma); + + if (atomic_inc_return(&chan->ref_count) <= 1) + chan_start(chan, config); + + dev_dbg(kdev->dev, "channel %d opened from DMA %s\n", + chan_num, instance); + + return chan; +} +EXPORT_SYMBOL_GPL(knav_dma_open_channel); + +/** + * knav_dma_close_channel() - Destroy a dma channel + * + * channel: dma channel handle + * + */ +void knav_dma_close_channel(void *channel) +{ + struct knav_dma_chan *chan = channel; + + if (!kdev) { + pr_err("keystone-navigator-dma driver not registered\n"); + return; + } + + if (atomic_dec_return(&chan->ref_count) <= 0) + chan_stop(chan); + + if (atomic_dec_return(&chan->dma->ref_count) <= 0) + knav_dma_hw_destroy(chan->dma); + + dev_dbg(kdev->dev, "channel %d or flow %d closed from DMA %s\n", + chan->channel, chan->flow, chan->dma->name); +} +EXPORT_SYMBOL_GPL(knav_dma_close_channel); + +static void __iomem *pktdma_get_regs(struct knav_dma_device *dma, + struct device_node *node, + unsigned index, resource_size_t *_size) +{ + struct device *dev = kdev->dev; + struct resource res; + void __iomem *regs; + int ret; + + ret = of_address_to_resource(node, index, &res); + if (ret) { + dev_err(dev, "Can't translate of node(%s) address for index(%d)\n", + node->name, index); + return ERR_PTR(ret); + } + + regs = devm_ioremap_resource(kdev->dev, &res); + if (IS_ERR(regs)) + dev_err(dev, "Failed to map register base for index(%d) node(%s)\n", + index, node->name); + if (_size) + *_size = resource_size(&res); + + return regs; +} + +static int pktdma_init_rx_chan(struct knav_dma_chan *chan, u32 flow) +{ + struct knav_dma_device *dma = chan->dma; + + chan->flow = flow; + chan->reg_rx_flow = dma->reg_rx_flow + flow; + chan->channel = DMA_INVALID_ID; + dev_dbg(kdev->dev, "rx flow(%d) (%p)\n", chan->flow, chan->reg_rx_flow); + + return 0; +} + +static int pktdma_init_tx_chan(struct knav_dma_chan *chan, u32 channel) +{ + struct knav_dma_device *dma = chan->dma; + + chan->channel = channel; + chan->reg_chan = dma->reg_tx_chan + channel; + chan->reg_tx_sched = dma->reg_tx_sched + channel; + chan->flow = DMA_INVALID_ID; + dev_dbg(kdev->dev, "tx channel(%d) (%p)\n", chan->channel, chan->reg_chan); + + return 0; +} + +static int pktdma_init_chan(struct knav_dma_device *dma, + enum dma_transfer_direction dir, + unsigned chan_num) +{ + struct device *dev = kdev->dev; + struct knav_dma_chan *chan; + int ret = -EINVAL; + + chan = devm_kzalloc(dev, sizeof(*chan), GFP_KERNEL); + if (!chan) + return -ENOMEM; + + INIT_LIST_HEAD(&chan->list); + chan->dma = dma; + chan->direction = DMA_NONE; + atomic_set(&chan->ref_count, 0); + spin_lock_init(&chan->lock); + + if (dir == DMA_MEM_TO_DEV) { + chan->direction = dir; + ret = pktdma_init_tx_chan(chan, chan_num); + } else if (dir == DMA_DEV_TO_MEM) { + chan->direction = dir; + ret = pktdma_init_rx_chan(chan, chan_num); + } else { + dev_err(dev, "channel(%d) direction unknown\n", chan_num); + } + + list_add_tail(&chan->list, &dma->chan_list); + + return ret; +} + +static int dma_init(struct device_node *cloud, struct device_node *dma_node) +{ + unsigned max_tx_chan, max_rx_chan, max_rx_flow, max_tx_sched; + struct device_node *node = dma_node; + struct knav_dma_device *dma; + int ret, len, num_chan = 0; + resource_size_t size; + u32 timeout; + u32 i; + + dma = devm_kzalloc(kdev->dev, sizeof(*dma), GFP_KERNEL); + if (!dma) { + dev_err(kdev->dev, "could not allocate driver mem\n"); + return -ENOMEM; + } + INIT_LIST_HEAD(&dma->list); + INIT_LIST_HEAD(&dma->chan_list); + + if (!of_find_property(cloud, "ti,navigator-cloud-address", &len)) { + dev_err(kdev->dev, "unspecified navigator cloud addresses\n"); + return -ENODEV; + } + + dma->logical_queue_managers = len / sizeof(u32); + if (dma->logical_queue_managers > DMA_MAX_QMS) { + dev_warn(kdev->dev, "too many queue mgrs(>%d) rest ignored\n", + dma->logical_queue_managers); + dma->logical_queue_managers = DMA_MAX_QMS; + } + + ret = of_property_read_u32_array(cloud, "ti,navigator-cloud-address", + dma->qm_base_address, + dma->logical_queue_managers); + if (ret) { + dev_err(kdev->dev, "invalid navigator cloud addresses\n"); + return -ENODEV; + } + + dma->reg_global = pktdma_get_regs(dma, node, 0, &size); + if (!dma->reg_global) + return -ENODEV; + if (size < sizeof(struct reg_global)) { + dev_err(kdev->dev, "bad size %pa for global regs\n", &size); + return -ENODEV; + } + + dma->reg_tx_chan = pktdma_get_regs(dma, node, 1, &size); + if (!dma->reg_tx_chan) + return -ENODEV; + + max_tx_chan = size / sizeof(struct reg_chan); + dma->reg_rx_chan = pktdma_get_regs(dma, node, 2, &size); + if (!dma->reg_rx_chan) + return -ENODEV; + + max_rx_chan = size / sizeof(struct reg_chan); + dma->reg_tx_sched = pktdma_get_regs(dma, node, 3, &size); + if (!dma->reg_tx_sched) + return -ENODEV; + + max_tx_sched = size / sizeof(struct reg_tx_sched); + dma->reg_rx_flow = pktdma_get_regs(dma, node, 4, &size); + if (!dma->reg_rx_flow) + return -ENODEV; + + max_rx_flow = size / sizeof(struct reg_rx_flow); + dma->rx_priority = DMA_PRIO_DEFAULT; + dma->tx_priority = DMA_PRIO_DEFAULT; + + dma->enable_all = (of_get_property(node, "ti,enable-all", NULL) != NULL); + dma->loopback = (of_get_property(node, "ti,loop-back", NULL) != NULL); + + ret = of_property_read_u32(node, "ti,rx-retry-timeout", &timeout); + if (ret < 0) { + dev_dbg(kdev->dev, "unspecified rx timeout using value %d\n", + DMA_RX_TIMEOUT_DEFAULT); + timeout = DMA_RX_TIMEOUT_DEFAULT; + } + + dma->rx_timeout = timeout; + dma->max_rx_chan = max_rx_chan; + dma->max_rx_flow = max_rx_flow; + dma->max_tx_chan = min(max_tx_chan, max_tx_sched); + atomic_set(&dma->ref_count, 0); + strcpy(dma->name, node->name); + spin_lock_init(&dma->lock); + + for (i = 0; i < dma->max_tx_chan; i++) { + if (pktdma_init_chan(dma, DMA_MEM_TO_DEV, i) >= 0) + num_chan++; + } + + for (i = 0; i < dma->max_rx_flow; i++) { + if (pktdma_init_chan(dma, DMA_DEV_TO_MEM, i) >= 0) + num_chan++; + } + + list_add_tail(&dma->list, &kdev->list); + + /* + * For DSP software usecases or userpace transport software, setup all + * the DMA hardware resources. + */ + if (dma->enable_all) { + atomic_inc(&dma->ref_count); + knav_dma_hw_init(dma); + dma_hw_enable_all(dma); + } + + dev_info(kdev->dev, "DMA %s registered %d logical channels, flows %d, tx chans: %d, rx chans: %d%s\n", + dma->name, num_chan, dma->max_rx_flow, + dma->max_tx_chan, dma->max_rx_chan, + dma->loopback ? ", loopback" : ""); + + return 0; +} + +static int knav_dma_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct device_node *node = pdev->dev.of_node; + struct device_node *child; + int ret = 0; + + if (!node) { + dev_err(&pdev->dev, "could not find device info\n"); + return -EINVAL; + } + + kdev = devm_kzalloc(dev, + sizeof(struct knav_dma_pool_device), GFP_KERNEL); + if (!kdev) { + dev_err(dev, "could not allocate driver mem\n"); + return -ENOMEM; + } + + kdev->dev = dev; + INIT_LIST_HEAD(&kdev->list); + + pm_runtime_enable(kdev->dev); + ret = pm_runtime_get_sync(kdev->dev); + if (ret < 0) { + dev_err(kdev->dev, "unable to enable pktdma, err %d\n", ret); + return ret; + } + + /* Initialise all packet dmas */ + for_each_child_of_node(node, child) { + ret = dma_init(node, child); + if (ret) { + dev_err(&pdev->dev, "init failed with %d\n", ret); + break; + } + } + + if (list_empty(&kdev->list)) { + dev_err(dev, "no valid dma instance\n"); + return -ENODEV; + } + + debugfs_create_file("knav_dma", S_IFREG | S_IRUGO, NULL, NULL, + &knav_dma_debug_ops); + + return ret; +} + +static int knav_dma_remove(struct platform_device *pdev) +{ + struct knav_dma_device *dma; + + list_for_each_entry(dma, &kdev->list, list) { + if (atomic_dec_return(&dma->ref_count) == 0) + knav_dma_hw_destroy(dma); + } + + pm_runtime_put_sync(&pdev->dev); + pm_runtime_disable(&pdev->dev); + + return 0; +} + +static struct of_device_id of_match[] = { + { .compatible = "ti,keystone-navigator-dma", }, + {}, +}; + +MODULE_DEVICE_TABLE(of, of_match); + +static struct platform_driver knav_dma_driver = { + .probe = knav_dma_probe, + .remove = knav_dma_remove, + .driver = { + .name = "keystone-navigator-dma", + .owner = THIS_MODULE, + .of_match_table = of_match, + }, +}; +module_platform_driver(knav_dma_driver); + +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("TI Keystone Navigator Packet DMA driver"); +MODULE_AUTHOR("Sandeep Nair "); +MODULE_AUTHOR("Santosh Shilimkar "); diff --git a/include/linux/soc/ti/knav_dma.h b/include/linux/soc/ti/knav_dma.h new file mode 100644 index 000000000000..e864a3eb9ac4 --- /dev/null +++ b/include/linux/soc/ti/knav_dma.h @@ -0,0 +1,175 @@ +/* + * Copyright (C) 2014 Texas Instruments Incorporated + * Authors: Sandeep Nair + * + * 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. + */ + +#ifndef __SOC_TI_KEYSTONE_NAVIGATOR_DMA_H__ +#define __SOC_TI_KEYSTONE_NAVIGATOR_DMA_H__ + +/* + * PKTDMA descriptor manipulation macros for host packet descriptor + */ +#define MASK(x) (BIT(x) - 1) +#define KNAV_DMA_DESC_PKT_LEN_MASK MASK(22) +#define KNAV_DMA_DESC_PKT_LEN_SHIFT 0 +#define KNAV_DMA_DESC_PS_INFO_IN_SOP BIT(22) +#define KNAV_DMA_DESC_PS_INFO_IN_DESC 0 +#define KNAV_DMA_DESC_TAG_MASK MASK(8) +#define KNAV_DMA_DESC_SAG_HI_SHIFT 24 +#define KNAV_DMA_DESC_STAG_LO_SHIFT 16 +#define KNAV_DMA_DESC_DTAG_HI_SHIFT 8 +#define KNAV_DMA_DESC_DTAG_LO_SHIFT 0 +#define KNAV_DMA_DESC_HAS_EPIB BIT(31) +#define KNAV_DMA_DESC_NO_EPIB 0 +#define KNAV_DMA_DESC_PSLEN_SHIFT 24 +#define KNAV_DMA_DESC_PSLEN_MASK MASK(6) +#define KNAV_DMA_DESC_ERR_FLAG_SHIFT 20 +#define KNAV_DMA_DESC_ERR_FLAG_MASK MASK(4) +#define KNAV_DMA_DESC_PSFLAG_SHIFT 16 +#define KNAV_DMA_DESC_PSFLAG_MASK MASK(4) +#define KNAV_DMA_DESC_RETQ_SHIFT 0 +#define KNAV_DMA_DESC_RETQ_MASK MASK(14) +#define KNAV_DMA_DESC_BUF_LEN_MASK MASK(22) + +#define KNAV_DMA_NUM_EPIB_WORDS 4 +#define KNAV_DMA_NUM_PS_WORDS 16 +#define KNAV_DMA_FDQ_PER_CHAN 4 + +/* Tx channel scheduling priority */ +enum knav_dma_tx_priority { + DMA_PRIO_HIGH = 0, + DMA_PRIO_MED_H, + DMA_PRIO_MED_L, + DMA_PRIO_LOW +}; + +/* Rx channel error handling mode during buffer starvation */ +enum knav_dma_rx_err_mode { + DMA_DROP = 0, + DMA_RETRY +}; + +/* Rx flow size threshold configuration */ +enum knav_dma_rx_thresholds { + DMA_THRESH_NONE = 0, + DMA_THRESH_0 = 1, + DMA_THRESH_0_1 = 3, + DMA_THRESH_0_1_2 = 7 +}; + +/* Descriptor type */ +enum knav_dma_desc_type { + DMA_DESC_HOST = 0, + DMA_DESC_MONOLITHIC = 2 +}; + +/** + * struct knav_dma_tx_cfg: Tx channel configuration + * @filt_einfo: Filter extended packet info + * @filt_pswords: Filter PS words present + * @knav_dma_tx_priority: Tx channel scheduling priority + */ +struct knav_dma_tx_cfg { + bool filt_einfo; + bool filt_pswords; + enum knav_dma_tx_priority priority; +}; + +/** + * struct knav_dma_rx_cfg: Rx flow configuration + * @einfo_present: Extended packet info present + * @psinfo_present: PS words present + * @knav_dma_rx_err_mode: Error during buffer starvation + * @knav_dma_desc_type: Host or Monolithic desc + * @psinfo_at_sop: PS word located at start of packet + * @sop_offset: Start of packet offset + * @dst_q: Destination queue for a given flow + * @thresh: Rx flow size threshold + * @fdq[]: Free desc Queue array + * @sz_thresh0: RX packet size threshold 0 + * @sz_thresh1: RX packet size threshold 1 + * @sz_thresh2: RX packet size threshold 2 + */ +struct knav_dma_rx_cfg { + bool einfo_present; + bool psinfo_present; + enum knav_dma_rx_err_mode err_mode; + enum knav_dma_desc_type desc_type; + bool psinfo_at_sop; + unsigned int sop_offset; + unsigned int dst_q; + enum knav_dma_rx_thresholds thresh; + unsigned int fdq[KNAV_DMA_FDQ_PER_CHAN]; + unsigned int sz_thresh0; + unsigned int sz_thresh1; + unsigned int sz_thresh2; +}; + +/** + * struct knav_dma_cfg: Pktdma channel configuration + * @sl_cfg: Slave configuration + * @tx: Tx channel configuration + * @rx: Rx flow configuration + */ +struct knav_dma_cfg { + enum dma_transfer_direction direction; + union { + struct knav_dma_tx_cfg tx; + struct knav_dma_rx_cfg rx; + } u; +}; + +/** + * struct knav_dma_desc: Host packet descriptor layout + * @desc_info: Descriptor information like id, type, length + * @tag_info: Flow tag info written in during RX + * @packet_info: Queue Manager, policy, flags etc + * @buff_len: Buffer length in bytes + * @buff: Buffer pointer + * @next_desc: For chaining the descriptors + * @orig_len: length since 'buff_len' can be overwritten + * @orig_buff: buff pointer since 'buff' can be overwritten + * @epib: Extended packet info block + * @psdata: Protocol specific + */ +struct knav_dma_desc { + u32 desc_info; + u32 tag_info; + u32 packet_info; + u32 buff_len; + u32 buff; + u32 next_desc; + u32 orig_len; + u32 orig_buff; + u32 epib[KNAV_DMA_NUM_EPIB_WORDS]; + u32 psdata[KNAV_DMA_NUM_PS_WORDS]; + u32 pad[4]; +} ____cacheline_aligned; + +#ifdef CONFIG_KEYSTONE_NAVIGATOR_DMA +void *knav_dma_open_channel(struct device *dev, const char *name, + struct knav_dma_cfg *config); +void knav_dma_close_channel(void *channel); +#else +static inline void *knav_dma_open_channel(struct device *dev, const char *name, + struct knav_dma_cfg *config) +{ + return (void *) NULL; +} +static inline void knav_dma_close_channel(void *channel) +{} + +#endif + +#endif /* __SOC_TI_KEYSTONE_NAVIGATOR_DMA_H__ */ -- cgit v1.2.1 From e0c524049f8279d00d2fbd4748b03234a2726fdd Mon Sep 17 00:00:00 2001 From: Santosh Shilimkar Date: Thu, 10 Jul 2014 11:30:08 -0400 Subject: MAINTAINERS: Add Keystone Multicore Navigator drivers entry Signed-off-by: Santosh Shilimkar --- MAINTAINERS | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/MAINTAINERS b/MAINTAINERS index aefa94841ff3..119bc9d67512 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -9086,6 +9086,15 @@ F: drivers/misc/tifm* F: drivers/mmc/host/tifm_sd.c F: include/linux/tifm.h +TI KEYSTONE MULTICORE NAVIGATOR DRIVERS +M: Santosh Shilimkar +L: linux-kernel@vger.kernel.org +L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers) +S: Maintained +F: drivers/soc/ti/* +T: git git://git.kernel.org/pub/scm/linux/kernel/git/ssantosh/linux-keystone.git + + TI LM49xxx FAMILY ASoC CODEC DRIVERS M: M R Swami Reddy M: Vishwas A Deshpande -- cgit v1.2.1 From b2fc3f3c6d397d434174147eca3db1ec778195ce Mon Sep 17 00:00:00 2001 From: Olof Johansson Date: Wed, 24 Sep 2014 11:42:38 -0700 Subject: drivers/soc: ti: fix build break with modules Fixes below build break by not switching to stubs when the driver is a module: drivers/soc/ti/knav_dma.c:418:7: error: redefinition of 'knav_dma_open_channel' void *knav_dma_open_channel(struct device *dev, const char *name, ^ In file included from drivers/soc/ti/knav_dma.c:26:0: include/linux/soc/ti/knav_dma.h:165:21: note: previous definition of 'knav_dma_open_channel' was here static inline void *knav_dma_open_channel(struct device *dev, const char *name, ^ Cc: Santosh Shilimkar Signed-off-by: Olof Johansson --- include/linux/soc/ti/knav_dma.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/linux/soc/ti/knav_dma.h b/include/linux/soc/ti/knav_dma.h index e864a3eb9ac4..dad035c16d94 100644 --- a/include/linux/soc/ti/knav_dma.h +++ b/include/linux/soc/ti/knav_dma.h @@ -157,7 +157,7 @@ struct knav_dma_desc { u32 pad[4]; } ____cacheline_aligned; -#ifdef CONFIG_KEYSTONE_NAVIGATOR_DMA +#if IS_ENABLED(CONFIG_KEYSTONE_NAVIGATOR_DMA) void *knav_dma_open_channel(struct device *dev, const char *name, struct knav_dma_cfg *config); void knav_dma_close_channel(void *channel); -- cgit v1.2.1 From 535f09cc1ca7788ecdaf27466b23887a4932ae73 Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Thu, 22 May 2014 00:34:16 +0200 Subject: leds: add a driver for syscon-based LEDs This makes it possible to create a set of LEDs from a syscon MFD instance, which is lean mean and clean on the ARM reference designs and can replace the Versatile LEDs driver in the long run, as well as other custom syscon LEDs drivers. Cc: Bryan Wu Cc: Richard Purdie [Fixed cocinelle warnings] Signed-off-by: Fengguang Wu Signed-off-by: Linus Walleij Signed-off-by: Arnd Bergmann --- drivers/leds/Kconfig | 10 +++ drivers/leds/Makefile | 1 + drivers/leds/leds-syscon.c | 166 +++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 177 insertions(+) create mode 100644 drivers/leds/leds-syscon.c diff --git a/drivers/leds/Kconfig b/drivers/leds/Kconfig index 8c96e2ddf43b..dc588b4f5905 100644 --- a/drivers/leds/Kconfig +++ b/drivers/leds/Kconfig @@ -478,6 +478,16 @@ config LEDS_BLINKM This option enables support for the BlinkM RGB LED connected through I2C. Say Y to enable support for the BlinkM LED. +config LEDS_SYSCON + bool "LED support for LEDs on system controllers" + depends on LEDS_CLASS=y + depends on MFD_SYSCON + depends on OF + help + This option enabled support for the LEDs on syscon type + devices. This will only work with device tree enabled + devices. + config LEDS_VERSATILE tristate "LED support for the ARM Versatile and RealView" depends on ARCH_REALVIEW || ARCH_VERSATILE diff --git a/drivers/leds/Makefile b/drivers/leds/Makefile index d8cc5f2777de..822dd83ef97a 100644 --- a/drivers/leds/Makefile +++ b/drivers/leds/Makefile @@ -53,6 +53,7 @@ obj-$(CONFIG_LEDS_ASIC3) += leds-asic3.o obj-$(CONFIG_LEDS_MAX8997) += leds-max8997.o obj-$(CONFIG_LEDS_LM355x) += leds-lm355x.o obj-$(CONFIG_LEDS_BLINKM) += leds-blinkm.o +obj-$(CONFIG_LEDS_SYSCON) += leds-syscon.o obj-$(CONFIG_LEDS_VERSATILE) += leds-versatile.o # LED SPI Drivers diff --git a/drivers/leds/leds-syscon.c b/drivers/leds/leds-syscon.c new file mode 100644 index 000000000000..3afec79c43f4 --- /dev/null +++ b/drivers/leds/leds-syscon.c @@ -0,0 +1,166 @@ +/* + * Generic Syscon LEDs Driver + * + * Copyright (c) 2014, Linaro Limited + * Author: Linus Walleij + * + * 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. + * + * 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 + * + * This driver provides system reboot functionality for APM X-Gene SoC. + * For system shutdown, this is board specify. If a board designer + * implements GPIO shutdown, use the gpio-poweroff.c driver. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/** + * struct syscon_led - state container for syscon based LEDs + * @cdev: LED class device for this LED + * @map: regmap to access the syscon device backing this LED + * @offset: the offset into the syscon regmap for the LED register + * @mask: the bit in the register corresponding to the LED + * @state: current state of the LED + */ +struct syscon_led { + struct led_classdev cdev; + struct regmap *map; + u32 offset; + u32 mask; + bool state; +}; + +static void syscon_led_set(struct led_classdev *led_cdev, + enum led_brightness value) +{ + struct syscon_led *sled = + container_of(led_cdev, struct syscon_led, cdev); + u32 val; + int ret; + + if (value == LED_OFF) { + val = 0; + sled->state = false; + } else { + val = sled->mask; + sled->state = true; + } + + ret = regmap_update_bits(sled->map, sled->offset, sled->mask, val); + if (ret < 0) + dev_err(sled->cdev.dev, "error updating LED status\n"); +} + +static const struct of_device_id syscon_match[] = { + { .compatible = "syscon", }, + {}, +}; + +static int __init syscon_leds_init(void) +{ + const struct of_device_id *devid; + struct device_node *np; + struct device_node *child; + struct regmap *map; + struct platform_device *pdev; + struct device *dev; + int ret; + + np = of_find_matching_node_and_match(NULL, syscon_match, + &devid); + if (!np) + return -ENODEV; + + map = syscon_node_to_regmap(np); + if (IS_ERR(map)) + return PTR_ERR(map); + + /* + * If the map is there, the device should be there, we allocate + * memory on the syscon device's behalf here. + */ + pdev = of_find_device_by_node(np); + if (!pdev) + return -ENODEV; + dev = &pdev->dev; + + for_each_available_child_of_node(np, child) { + struct syscon_led *sled; + const char *state; + + /* Only check for register-bit-leds */ + if (of_property_match_string(child, "compatible", + "register-bit-led") < 0) + continue; + + sled = devm_kzalloc(dev, sizeof(*sled), GFP_KERNEL); + if (!sled) + return -ENOMEM; + + sled->map = map; + + if (of_property_read_u32(child, "offset", &sled->offset)) + return -EINVAL; + if (of_property_read_u32(child, "mask", &sled->mask)) + return -EINVAL; + sled->cdev.name = + of_get_property(child, "label", NULL) ? : child->name; + sled->cdev.default_trigger = + of_get_property(child, "linux,default-trigger", NULL); + + state = of_get_property(child, "default-state", NULL); + if (state) { + if (!strcmp(state, "keep")) { + u32 val; + + ret = regmap_read(map, sled->offset, &val); + if (ret < 0) + return ret; + sled->state = !!(val & sled->mask); + } else if (!strcmp(state, "on")) { + sled->state = true; + ret = regmap_update_bits(map, sled->offset, + sled->mask, + sled->mask); + if (ret < 0) + return ret; + } else { + sled->state = false; + ret = regmap_update_bits(map, sled->offset, + sled->mask, 0); + if (ret < 0) + return ret; + } + + } + sled->cdev.brightness_set = syscon_led_set; + + ret = led_classdev_register(dev, &sled->cdev); + if (ret < 0) + return ret; + + dev_info(dev, "registered LED %s\n", sled->cdev.name); + } + + return 0; +} +device_initcall(syscon_leds_init); -- cgit v1.2.1 From 0e545f57b708630c54c8c5c24ea7f7034f6c40d9 Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Thu, 22 May 2014 10:20:38 +0200 Subject: power: reset: driver for the Versatile syscon reboot This driver enabled us to drive the reboot of the Versatile family of ARM reference boards. Even though only the RealView boards are supported initially, these boards all have the same procedure for reboot: - Write a magic value into an unlocking register - Write another magic value into a reset control register The driver will be reusable for Versatile and possibly also the Integrator family of reference boards. Cc: Dmitry Eremin-Solenikov Cc: David Woodhouse Acked-By: Sebastian Reichel Signed-off-by: Linus Walleij Signed-off-by: Arnd Bergmann --- drivers/power/reset/Kconfig | 9 +++ drivers/power/reset/Makefile | 1 + drivers/power/reset/arm-versatile-reboot.c | 111 +++++++++++++++++++++++++++++ 3 files changed, 121 insertions(+) create mode 100644 drivers/power/reset/arm-versatile-reboot.c diff --git a/drivers/power/reset/Kconfig b/drivers/power/reset/Kconfig index 81b445122eea..527a0f47ef44 100644 --- a/drivers/power/reset/Kconfig +++ b/drivers/power/reset/Kconfig @@ -92,6 +92,15 @@ config POWER_RESET_SUN6I help Reboot support for the Allwinner A31 SoCs. +config POWER_RESET_VERSATILE + bool "ARM Versatile family reboot driver" + depends on ARM + depends on MFD_SYSCON + depends on OF + help + Power off and restart support for ARM Versatile family of + reference boards. + config POWER_RESET_VEXPRESS bool "ARM Versatile Express power-off and reset driver" depends on ARM || ARM64 diff --git a/drivers/power/reset/Makefile b/drivers/power/reset/Makefile index 4433a753fd93..73221009f2bf 100644 --- a/drivers/power/reset/Makefile +++ b/drivers/power/reset/Makefile @@ -9,6 +9,7 @@ obj-$(CONFIG_POWER_RESET_MSM) += msm-poweroff.o obj-$(CONFIG_POWER_RESET_QNAP) += qnap-poweroff.o obj-$(CONFIG_POWER_RESET_RESTART) += restart-poweroff.o obj-$(CONFIG_POWER_RESET_SUN6I) += sun6i-reboot.o +obj-$(CONFIG_POWER_RESET_VERSATILE) += arm-versatile-reboot.o obj-$(CONFIG_POWER_RESET_VEXPRESS) += vexpress-poweroff.o obj-$(CONFIG_POWER_RESET_XGENE) += xgene-reboot.o obj-$(CONFIG_POWER_RESET_KEYSTONE) += keystone-reset.o diff --git a/drivers/power/reset/arm-versatile-reboot.c b/drivers/power/reset/arm-versatile-reboot.c new file mode 100644 index 000000000000..5b08bffcf1a8 --- /dev/null +++ b/drivers/power/reset/arm-versatile-reboot.c @@ -0,0 +1,111 @@ +/* + * Copyright (C) 2014 Linaro Ltd. + * + * Author: Linus Walleij + * + * 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 + +#define REALVIEW_SYS_LOCK_OFFSET 0x20 +#define REALVIEW_SYS_LOCK_VAL 0xA05F +#define REALVIEW_SYS_RESETCTL_OFFSET 0x40 + +/* + * We detect the different syscon types from the compatible strings. + */ +enum versatile_reboot { + REALVIEW_REBOOT_EB, + REALVIEW_REBOOT_PB1176, + REALVIEW_REBOOT_PB11MP, + REALVIEW_REBOOT_PBA8, + REALVIEW_REBOOT_PBX, +}; + +/* Pointer to the system controller */ +static struct regmap *syscon_regmap; +static enum versatile_reboot versatile_reboot_type; + +static const struct of_device_id versatile_reboot_of_match[] = { + { + .compatible = "arm,realview-eb-syscon", + .data = (void *)REALVIEW_REBOOT_EB, + }, + { + .compatible = "arm,realview-pb1176-syscon", + .data = (void *)REALVIEW_REBOOT_PB1176, + }, + { + .compatible = "arm,realview-pb11mp-syscon", + .data = (void *)REALVIEW_REBOOT_PB11MP, + }, + { + .compatible = "arm,realview-pba8-syscon", + .data = (void *)REALVIEW_REBOOT_PBA8, + }, + { + .compatible = "arm,realview-pbx-syscon", + .data = (void *)REALVIEW_REBOOT_PBX, + }, +}; + +static void versatile_reboot(enum reboot_mode mode, const char *cmd) +{ + /* Unlock the reset register */ + regmap_write(syscon_regmap, REALVIEW_SYS_LOCK_OFFSET, + REALVIEW_SYS_LOCK_VAL); + /* Then hit reset on the different machines */ + switch (versatile_reboot_type) { + case REALVIEW_REBOOT_EB: + regmap_write(syscon_regmap, + REALVIEW_SYS_RESETCTL_OFFSET, 0x0008); + break; + case REALVIEW_REBOOT_PB1176: + regmap_write(syscon_regmap, + REALVIEW_SYS_RESETCTL_OFFSET, 0x0100); + break; + case REALVIEW_REBOOT_PB11MP: + case REALVIEW_REBOOT_PBA8: + regmap_write(syscon_regmap, REALVIEW_SYS_RESETCTL_OFFSET, + 0x0000); + regmap_write(syscon_regmap, REALVIEW_SYS_RESETCTL_OFFSET, + 0x0004); + break; + case REALVIEW_REBOOT_PBX: + regmap_write(syscon_regmap, REALVIEW_SYS_RESETCTL_OFFSET, + 0x00f0); + regmap_write(syscon_regmap, REALVIEW_SYS_RESETCTL_OFFSET, + 0x00f4); + break; + } + dsb(); +} + +static int __init versatile_reboot_probe(void) +{ + const struct of_device_id *reboot_id; + struct device_node *np; + + np = of_find_matching_node_and_match(NULL, versatile_reboot_of_match, + &reboot_id); + if (!np) + return -ENODEV; + versatile_reboot_type = (enum versatile_reboot)reboot_id->data; + + syscon_regmap = syscon_node_to_regmap(np); + if (IS_ERR(syscon_regmap)) + return PTR_ERR(syscon_regmap); + + arm_pm_restart = versatile_reboot; + pr_info("versatile reboot driver registered\n"); + return 0; +} +device_initcall(versatile_reboot_probe); -- cgit v1.2.1 From a2974c9c1f83e7163e855eb80c772db45bc9a291 Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Fri, 25 Jul 2014 15:04:52 +0200 Subject: soc: add driver for the ARM RealView This adds a SoC driver to be used by the ARM RealView reference boards. We create the "versatile" directory to hold the different ARM reference designs as per the pattern of the clk directory layout. The driver utilze the syscon to get to the register needed. After this we can use sysfs to get at some SoC properties on RealView DT variants like this: > cd /sysbus/soc/devices/soc0 > ls board family machine power subsystem build fpga manufacturer soc_id uevent > cat family Versatile > cat fpga Multi-layer AXI > cat board HBI-0147 > cat build 03 Signed-off-by: Linus Walleij Signed-off-by: Arnd Bergmann Conflicts: drivers/soc/Kconfig drivers/soc/Makefile --- drivers/soc/Kconfig | 1 + drivers/soc/Makefile | 1 + drivers/soc/versatile/Kconfig | 10 +++ drivers/soc/versatile/Makefile | 1 + drivers/soc/versatile/soc-realview.c | 144 +++++++++++++++++++++++++++++++++++ 5 files changed, 157 insertions(+) create mode 100644 drivers/soc/versatile/Kconfig create mode 100644 drivers/soc/versatile/Makefile create mode 100644 drivers/soc/versatile/soc-realview.c diff --git a/drivers/soc/Kconfig b/drivers/soc/Kconfig index 49e3f0cc71af..76d6bd4da138 100644 --- a/drivers/soc/Kconfig +++ b/drivers/soc/Kconfig @@ -2,5 +2,6 @@ menu "SOC (System On Chip) specific Drivers" source "drivers/soc/qcom/Kconfig" source "drivers/soc/ti/Kconfig" +source "drivers/soc/versatile/Kconfig" endmenu diff --git a/drivers/soc/Makefile b/drivers/soc/Makefile index 0d6e35dfea8c..063113d0bd38 100644 --- a/drivers/soc/Makefile +++ b/drivers/soc/Makefile @@ -5,3 +5,4 @@ obj-$(CONFIG_ARCH_QCOM) += qcom/ obj-$(CONFIG_ARCH_TEGRA) += tegra/ obj-$(CONFIG_SOC_TI) += ti/ +obj-$(CONFIG_PLAT_VERSATILE) += versatile/ diff --git a/drivers/soc/versatile/Kconfig b/drivers/soc/versatile/Kconfig new file mode 100644 index 000000000000..bf5ee9c85330 --- /dev/null +++ b/drivers/soc/versatile/Kconfig @@ -0,0 +1,10 @@ +# +# ARM Versatile SoC drivers +# +config SOC_REALVIEW + bool "SoC bus device for the ARM RealView platforms" + depends on ARCH_REALVIEW + select SOC_BUS + 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/versatile/Makefile b/drivers/soc/versatile/Makefile new file mode 100644 index 000000000000..ad547435648e --- /dev/null +++ b/drivers/soc/versatile/Makefile @@ -0,0 +1 @@ +obj-$(CONFIG_SOC_REALVIEW) += soc-realview.o diff --git a/drivers/soc/versatile/soc-realview.c b/drivers/soc/versatile/soc-realview.c new file mode 100644 index 000000000000..cea8ea3491d2 --- /dev/null +++ b/drivers/soc/versatile/soc-realview.c @@ -0,0 +1,144 @@ +/* + * Copyright (C) 2014 Linaro Ltd. + * + * Author: Linus Walleij + * + * 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 + +/* System ID in syscon */ +#define REALVIEW_SYS_ID_OFFSET 0x00 + +static const struct of_device_id realview_soc_of_match[] = { + { .compatible = "arm,realview-eb-soc", }, + { .compatible = "arm,realview-pb1176-soc", }, + { .compatible = "arm,realview-pb11mp-soc", }, + { .compatible = "arm,realview-pba8-soc", }, + { .compatible = "arm,realview-pbx-soc", }, +}; + +static u32 realview_coreid; + +static const char *realview_board_str(u32 id) +{ + switch ((id >> 16) & 0xfff) { + case 0x0147: + return "HBI-0147"; + default: + return "Unknown"; + } +} + +static const char *realview_arch_str(u32 id) +{ + switch ((id >> 8) & 0xf) { + case 0x05: + return "Multi-layer AXI"; + default: + return "Unknown"; + } +} + +static ssize_t realview_get_manf(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + return sprintf(buf, "%02x\n", realview_coreid >> 24); +} + +static struct device_attribute realview_manf_attr = + __ATTR(manufacturer, S_IRUGO, realview_get_manf, NULL); + +static ssize_t realview_get_board(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + return sprintf(buf, "%s\n", realview_board_str(realview_coreid)); +} + +static struct device_attribute realview_board_attr = + __ATTR(board, S_IRUGO, realview_get_board, NULL); + +static ssize_t realview_get_arch(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + return sprintf(buf, "%s\n", realview_arch_str(realview_coreid)); +} + +static struct device_attribute realview_arch_attr = + __ATTR(fpga, S_IRUGO, realview_get_arch, NULL); + +static ssize_t realview_get_build(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + return sprintf(buf, "%02x\n", (realview_coreid & 0xFF)); +} + +static struct device_attribute realview_build_attr = + __ATTR(build, S_IRUGO, realview_get_build, NULL); + +static int realview_soc_probe(struct platform_device *pdev) +{ + static struct regmap *syscon_regmap; + struct soc_device *soc_dev; + struct soc_device_attribute *soc_dev_attr; + struct device_node *np = pdev->dev.of_node; + int ret; + + syscon_regmap = syscon_regmap_lookup_by_phandle(np, "regmap"); + if (IS_ERR(syscon_regmap)) + return PTR_ERR(syscon_regmap); + + soc_dev_attr = kzalloc(sizeof(*soc_dev_attr), GFP_KERNEL); + if (!soc_dev_attr) + return -ENOMEM; + + ret = of_property_read_string(np, "compatible", + &soc_dev_attr->soc_id); + if (ret) + return -EINVAL; + + soc_dev_attr->machine = "RealView"; + soc_dev_attr->family = "Versatile"; + soc_dev = soc_device_register(soc_dev_attr); + if (IS_ERR(soc_dev)) { + kfree(soc_dev_attr); + return -ENODEV; + } + ret = regmap_read(syscon_regmap, REALVIEW_SYS_ID_OFFSET, + &realview_coreid); + if (ret) + return -ENODEV; + + device_create_file(soc_device_to_device(soc_dev), &realview_manf_attr); + device_create_file(soc_device_to_device(soc_dev), &realview_board_attr); + device_create_file(soc_device_to_device(soc_dev), &realview_arch_attr); + device_create_file(soc_device_to_device(soc_dev), &realview_build_attr); + + dev_info(&pdev->dev, "RealView Syscon Core ID: 0x%08x\n", + realview_coreid); + /* FIXME: add attributes for SoC to sysfs */ + return 0; +} + +static struct platform_driver realview_soc_driver = { + .probe = realview_soc_probe, + .driver = { + .name = "realview-soc", + .of_match_table = realview_soc_of_match, + }, +}; +module_platform_driver(realview_soc_driver); -- cgit v1.2.1 From 741c798a0229c9523c1f68bc8c83a79fefad4333 Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Thu, 22 May 2014 00:35:37 +0200 Subject: leds: add device tree bindings for register bit LEDs This adds the device tree bindings used by register bit LEDs. Cc: devicetree@vger.kernel.org Cc: Bryan Wu Cc: Richard Purdie Signed-off-by: Linus Walleij --- .../devicetree/bindings/leds/register-bit-led.txt | 99 ++++++++++++++++++++++ 1 file changed, 99 insertions(+) create mode 100644 Documentation/devicetree/bindings/leds/register-bit-led.txt diff --git a/Documentation/devicetree/bindings/leds/register-bit-led.txt b/Documentation/devicetree/bindings/leds/register-bit-led.txt new file mode 100644 index 000000000000..379cefdc0bda --- /dev/null +++ b/Documentation/devicetree/bindings/leds/register-bit-led.txt @@ -0,0 +1,99 @@ +Device Tree Bindings for Register Bit LEDs + +Register bit leds are used with syscon multifunctional devices +where single bits in a certain register can turn on/off a +single LED. The register bit LEDs appear as children to the +syscon device, with the proper compatible string. For the +syscon bindings see: +Documentation/devicetree/bindings/mfd/syscon.txt + +Each LED is represented as a sub-node of the syscon device. Each +node's name represents the name of the corresponding LED. + +LED sub-node properties: + +Required properties: +- compatible : must be "register-bit-led" +- offset : register offset to the register controlling this LED +- mask : bit mask for the bit controlling this LED in the register + typically 0x01, 0x02, 0x04 ... + +Optional properties: +- label : (optional) + see Documentation/devicetree/bindings/leds/common.txt +- linux,default-trigger : (optional) + see Documentation/devicetree/bindings/leds/common.txt +- default-state: (optional) The initial state of the LED. Valid + values are "on", "off", and "keep". If the LED is already on or off + and the default-state property is set the to same value, then no + glitch should be produced where the LED momentarily turns off (or + on). The "keep" setting will keep the LED at whatever its current + state is, without producing a glitch. The default is off if this + property is not present. + +Example: + +syscon: syscon@10000000 { + compatible = "arm,realview-pb1176-syscon", "syscon"; + reg = <0x10000000 0x1000>; + + led@08.0 { + compatible = "register-bit-led"; + offset = <0x08>; + mask = <0x01>; + label = "versatile:0"; + linux,default-trigger = "heartbeat"; + default-state = "on"; + }; + led@08.1 { + compatible = "register-bit-led"; + offset = <0x08>; + mask = <0x02>; + label = "versatile:1"; + linux,default-trigger = "mmc0"; + default-state = "off"; + }; + led@08.2 { + compatible = "register-bit-led"; + offset = <0x08>; + mask = <0x04>; + label = "versatile:2"; + linux,default-trigger = "cpu0"; + default-state = "off"; + }; + led@08.3 { + compatible = "register-bit-led"; + offset = <0x08>; + mask = <0x08>; + label = "versatile:3"; + default-state = "off"; + }; + led@08.4 { + compatible = "register-bit-led"; + offset = <0x08>; + mask = <0x10>; + label = "versatile:4"; + default-state = "off"; + }; + led@08.5 { + compatible = "register-bit-led"; + offset = <0x08>; + mask = <0x20>; + label = "versatile:5"; + default-state = "off"; + }; + led@08.6 { + compatible = "register-bit-led"; + offset = <0x08>; + mask = <0x40>; + label = "versatile:6"; + default-state = "off"; + }; + led@08.7 { + compatible = "register-bit-led"; + offset = <0x08>; + mask = <0x80>; + label = "versatile:7"; + default-state = "off"; + }; +}; -- cgit v1.2.1 From fa637bf0595ee1796d728a0d33b6b7fff12e1f3d Mon Sep 17 00:00:00 2001 From: Pawel Moll Date: Mon, 15 Sep 2014 15:33:48 +0100 Subject: bus: arm-ccn: Fix spurious warning message Because CCN's cycle counter always runs, it will generate an interrupt on overflow even if the relevant perf event was not requested, causing a spurious warning message. Fixed now by warning on only normal counter unwanted overflows. Also cleaning the overflow mask at init now, not to warn on event previously requested by firmware. Signed-off-by: Pawel Moll Signed-off-by: Olof Johansson Signed-off-by: Arnd Bergmann --- drivers/bus/arm-ccn.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/drivers/bus/arm-ccn.c b/drivers/bus/arm-ccn.c index 6f550d9e7a2d..f5bca3113bfc 100644 --- a/drivers/bus/arm-ccn.c +++ b/drivers/bus/arm-ccn.c @@ -57,6 +57,7 @@ #define CCN_DT_PMCCNTRSR 0x0190 #define CCN_DT_PMOVSR 0x0198 #define CCN_DT_PMOVSR_CLR 0x01a0 +#define CCN_DT_PMOVSR_CLR__MASK 0x1f #define CCN_DT_PMCR 0x01a8 #define CCN_DT_PMCR__OVFL_INTR_EN (1 << 6) #define CCN_DT_PMCR__PMU_EN (1 << 0) @@ -1052,7 +1053,8 @@ static irqreturn_t arm_ccn_pmu_overflow_handler(struct arm_ccn_dt *dt) struct perf_event *event = dt->pmu_counters[idx].event; int overflowed = pmovsr & BIT(idx); - WARN_ON_ONCE(overflowed && !event); + WARN_ON_ONCE(overflowed && !event && + idx != CCN_IDX_PMU_CYCLE_COUNTER); if (!event || !overflowed) continue; @@ -1088,6 +1090,7 @@ static int arm_ccn_pmu_init(struct arm_ccn *ccn) /* Initialize DT subsystem */ ccn->dt.base = ccn->base + CCN_REGION_SIZE; spin_lock_init(&ccn->dt.config_lock); + writel(CCN_DT_PMOVSR_CLR__MASK, ccn->dt.base + CCN_DT_PMOVSR_CLR); writel(CCN_DT_CTL__DT_EN, ccn->dt.base + CCN_DT_CTL); writel(CCN_DT_PMCR__OVFL_INTR_EN | CCN_DT_PMCR__PMU_EN, ccn->dt.base + CCN_DT_PMCR); -- cgit v1.2.1