From e98bbbf3bd59d5283750e8f56031bd1cc2feafd8 Mon Sep 17 00:00:00 2001 From: David Lechner Date: Tue, 23 Jan 2018 12:14:43 -0600 Subject: phy: da8xx-usb: rename clock con_ids This renames the clock con_ids in the DA8XX USB PHY driver as well as the matching names in the mach clock registration code. This is in preparation for using device tree clocks where these names will become part of the device tree bindings. The new names more closely match the names used in the USB clock diagram in the SoC TRM. Acked-by: Kishon Vijay Abraham I Signed-off-by: David Lechner Signed-off-by: Sekhar Nori --- drivers/phy/ti/phy-da8xx-usb.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'drivers') diff --git a/drivers/phy/ti/phy-da8xx-usb.c b/drivers/phy/ti/phy-da8xx-usb.c index 1b82bff6330f..5bd33d06df95 100644 --- a/drivers/phy/ti/phy-da8xx-usb.c +++ b/drivers/phy/ti/phy-da8xx-usb.c @@ -162,15 +162,15 @@ static int da8xx_usb_phy_probe(struct platform_device *pdev) return PTR_ERR(d_phy->regmap); } - d_phy->usb11_clk = devm_clk_get(dev, "usb11_phy"); + d_phy->usb11_clk = devm_clk_get(dev, "usb1_clk48"); if (IS_ERR(d_phy->usb11_clk)) { - dev_err(dev, "Failed to get usb11_phy clock\n"); + dev_err(dev, "Failed to get usb1_clk48\n"); return PTR_ERR(d_phy->usb11_clk); } - d_phy->usb20_clk = devm_clk_get(dev, "usb20_phy"); + d_phy->usb20_clk = devm_clk_get(dev, "usb0_clk48"); if (IS_ERR(d_phy->usb20_clk)) { - dev_err(dev, "Failed to get usb20_phy clock\n"); + dev_err(dev, "Failed to get usb0_clk48\n"); return PTR_ERR(d_phy->usb20_clk); } -- cgit v1.2.3 From 707aa45d2612778e2f3b6c5e1950ed9fa48974aa Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Tue, 19 Dec 2017 16:54:44 +0100 Subject: soc: renesas: rcar-sysc: Mark rcar_sysc_matches[] __initconst rcar_sysc_matches[] is used only by rcar_sysc_pd_init(), which is __init. Hence mark rcar_sysc_matches[] __initconst. This frees another 1764 bytes (arm32/shmobile_defconfig) or 1000 bytes (arm64/renesas_defconfig) of memory after kernel init. Signed-off-by: Geert Uytterhoeven Signed-off-by: Simon Horman --- drivers/soc/renesas/rcar-sysc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/soc/renesas/rcar-sysc.c b/drivers/soc/renesas/rcar-sysc.c index 52c25a5e2646..636872bc2416 100644 --- a/drivers/soc/renesas/rcar-sysc.c +++ b/drivers/soc/renesas/rcar-sysc.c @@ -254,7 +254,7 @@ finalize: pm_genpd_init(genpd, gov, false); } -static const struct of_device_id rcar_sysc_matches[] = { +static const struct of_device_id rcar_sysc_matches[] __initconst = { #ifdef CONFIG_SYSC_R8A7743 { .compatible = "renesas,r8a7743-sysc", .data = &r8a7743_sysc_info }, #endif -- cgit v1.2.3 From 8447756d1e582a401cdae33c9ed5d68fdb6e0410 Mon Sep 17 00:00:00 2001 From: Sergei Shtylyov Date: Fri, 2 Feb 2018 21:22:14 +0300 Subject: soc: renesas: identify R-Car V3H Add support for identifying the R-Car V3H (R8A77980) SoC. Signed-off-by: Sergei Shtylyov Reviewed-by: Geert Uytterhoeven Signed-off-by: Simon Horman --- drivers/soc/renesas/renesas-soc.c | 8 ++++++++ 1 file changed, 8 insertions(+) (limited to 'drivers') diff --git a/drivers/soc/renesas/renesas-soc.c b/drivers/soc/renesas/renesas-soc.c index 926b7fd6db2d..000834321774 100644 --- a/drivers/soc/renesas/renesas-soc.c +++ b/drivers/soc/renesas/renesas-soc.c @@ -149,6 +149,11 @@ static const struct renesas_soc soc_rcar_v3m __initconst __maybe_unused = { .id = 0x54, }; +static const struct renesas_soc soc_rcar_v3h __initconst __maybe_unused = { + .family = &fam_rcar_gen3, + .id = 0x56, +}; + static const struct renesas_soc soc_rcar_d3 __initconst __maybe_unused = { .family = &fam_rcar_gen3, .id = 0x58, @@ -212,6 +217,9 @@ static const struct of_device_id renesas_socs[] __initconst = { #ifdef CONFIG_ARCH_R8A77970 { .compatible = "renesas,r8a77970", .data = &soc_rcar_v3m }, #endif +#ifdef CONFIG_ARCH_R8A77980 + { .compatible = "renesas,r8a77980", .data = &soc_rcar_v3h }, +#endif #ifdef CONFIG_ARCH_R8A77995 { .compatible = "renesas,r8a77995", .data = &soc_rcar_d3 }, #endif -- cgit v1.2.3 From 7d7b619e16d420b723d6618c60a0aaf0ba4e3666 Mon Sep 17 00:00:00 2001 From: Sergei Shtylyov Date: Fri, 2 Feb 2018 21:27:01 +0300 Subject: soc: renesas: rcar-rst: add R8A77980 support Add support for R-Car V3H (R8A77980) to the R-Car RST driver -- this driver is needed for the clock driver to work. Signed-off-by: Sergei Shtylyov Reviewed-by: Geert Uytterhoeven Signed-off-by: Simon Horman --- Documentation/devicetree/bindings/reset/renesas,rst.txt | 1 + drivers/soc/renesas/Kconfig | 2 +- drivers/soc/renesas/rcar-rst.c | 1 + 3 files changed, 3 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/Documentation/devicetree/bindings/reset/renesas,rst.txt b/Documentation/devicetree/bindings/reset/renesas,rst.txt index a8014f3ab8ba..a55b88658446 100644 --- a/Documentation/devicetree/bindings/reset/renesas,rst.txt +++ b/Documentation/devicetree/bindings/reset/renesas,rst.txt @@ -27,6 +27,7 @@ Required properties: - "renesas,r8a7795-rst" (R-Car H3) - "renesas,r8a7796-rst" (R-Car M3-W) - "renesas,r8a77970-rst" (R-Car V3M) + - "renesas,r8a77980-rst" (R-Car V3H) - "renesas,r8a77995-rst" (R-Car D3) - reg: Address start and address range for the device. diff --git a/drivers/soc/renesas/Kconfig b/drivers/soc/renesas/Kconfig index 09550b1da56d..6efd7bef8577 100644 --- a/drivers/soc/renesas/Kconfig +++ b/drivers/soc/renesas/Kconfig @@ -4,7 +4,7 @@ config SOC_RENESAS select SOC_BUS select RST_RCAR if ARCH_RCAR_GEN1 || ARCH_RCAR_GEN2 || \ ARCH_R8A7795 || ARCH_R8A7796 || ARCH_R8A77970 || \ - ARCH_R8A77995 + ARCH_R8A77980 || ARCH_R8A77995 select SYSC_R8A7743 if ARCH_R8A7743 select SYSC_R8A7745 if ARCH_R8A7745 select SYSC_R8A7779 if ARCH_R8A7779 diff --git a/drivers/soc/renesas/rcar-rst.c b/drivers/soc/renesas/rcar-rst.c index 3316b028f231..e2340eb9ea9c 100644 --- a/drivers/soc/renesas/rcar-rst.c +++ b/drivers/soc/renesas/rcar-rst.c @@ -42,6 +42,7 @@ static const struct of_device_id rcar_rst_matches[] __initconst = { { .compatible = "renesas,r8a7795-rst", .data = &rcar_rst_gen2 }, { .compatible = "renesas,r8a7796-rst", .data = &rcar_rst_gen2 }, { .compatible = "renesas,r8a77970-rst", .data = &rcar_rst_gen2 }, + { .compatible = "renesas,r8a77980-rst", .data = &rcar_rst_gen2 }, { .compatible = "renesas,r8a77995-rst", .data = &rcar_rst_gen2 }, { /* sentinel */ } }; -- cgit v1.2.3 From 69e0d1b8db8f8cc319f966ec3eb2fffce28c4f28 Mon Sep 17 00:00:00 2001 From: Fabrizio Castro Date: Tue, 13 Feb 2018 13:02:44 +0000 Subject: soc: renesas: rcar-rst: Enable watchdog as reset trigger for Gen2 This patch allows for platform specific quirks as some of the SoC need further customization for the watchdog to work properly, like for R-Car Gen2 and for RZ/G. Signed-off-by: Fabrizio Castro Signed-off-by: Ramesh Shanmugasundaram Signed-off-by: Simon Horman --- drivers/soc/renesas/rcar-rst.c | 37 ++++++++++++++++++++++++++++++------- 1 file changed, 30 insertions(+), 7 deletions(-) (limited to 'drivers') diff --git a/drivers/soc/renesas/rcar-rst.c b/drivers/soc/renesas/rcar-rst.c index e2340eb9ea9c..34136664ece4 100644 --- a/drivers/soc/renesas/rcar-rst.c +++ b/drivers/soc/renesas/rcar-rst.c @@ -13,8 +13,18 @@ #include #include +#define WDTRSTCR_RESET 0xA55A0002 +#define WDTRSTCR 0x0054 + +static int rcar_rst_enable_wdt_reset(void __iomem *base) +{ + iowrite32(WDTRSTCR_RESET, base + WDTRSTCR); + return 0; +} + struct rst_config { - unsigned int modemr; /* Mode Monitoring Register Offset */ + unsigned int modemr; /* Mode Monitoring Register Offset */ + int (*configure)(void *base); /* Platform specific configuration */ }; static const struct rst_config rcar_rst_gen1 __initconst = { @@ -23,6 +33,11 @@ static const struct rst_config rcar_rst_gen1 __initconst = { static const struct rst_config rcar_rst_gen2 __initconst = { .modemr = 0x60, + .configure = rcar_rst_enable_wdt_reset, +}; + +static const struct rst_config rcar_rst_gen3 __initconst = { + .modemr = 0x60, }; static const struct of_device_id rcar_rst_matches[] __initconst = { @@ -38,12 +53,12 @@ static const struct of_device_id rcar_rst_matches[] __initconst = { { .compatible = "renesas,r8a7792-rst", .data = &rcar_rst_gen2 }, { .compatible = "renesas,r8a7793-rst", .data = &rcar_rst_gen2 }, { .compatible = "renesas,r8a7794-rst", .data = &rcar_rst_gen2 }, - /* R-Car Gen3 is handled like R-Car Gen2 */ - { .compatible = "renesas,r8a7795-rst", .data = &rcar_rst_gen2 }, - { .compatible = "renesas,r8a7796-rst", .data = &rcar_rst_gen2 }, - { .compatible = "renesas,r8a77970-rst", .data = &rcar_rst_gen2 }, - { .compatible = "renesas,r8a77980-rst", .data = &rcar_rst_gen2 }, - { .compatible = "renesas,r8a77995-rst", .data = &rcar_rst_gen2 }, + /* R-Car Gen3 */ + { .compatible = "renesas,r8a7795-rst", .data = &rcar_rst_gen3 }, + { .compatible = "renesas,r8a7796-rst", .data = &rcar_rst_gen3 }, + { .compatible = "renesas,r8a77970-rst", .data = &rcar_rst_gen3 }, + { .compatible = "renesas,r8a77980-rst", .data = &rcar_rst_gen3 }, + { .compatible = "renesas,r8a77995-rst", .data = &rcar_rst_gen3 }, { /* sentinel */ } }; @@ -72,6 +87,14 @@ static int __init rcar_rst_init(void) rcar_rst_base = base; cfg = match->data; saved_mode = ioread32(base + cfg->modemr); + if (cfg->configure) { + error = cfg->configure(base); + if (error) { + pr_warn("%pOF: Cannot run SoC specific configuration\n", + np); + goto out_put; + } + } pr_debug("%pOF: MODE = 0x%08x\n", np, saved_mode); -- cgit v1.2.3 From 40d5c8e94751f4a4fa4e0e27e3805e201a4e79c0 Mon Sep 17 00:00:00 2001 From: Sergei Shtylyov Date: Fri, 16 Feb 2018 23:09:48 +0300 Subject: soc: renesas: r8a77970-sysc: fix power area parents MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit According to the figure 9.2(b) of the R-Car Series, 3rd Generation User’s Manual: Hardware Rev. 0.80 the A2IRn and A2SCn power areas in R8A77970 have the A3IR area as a parent, thus the SYSC driver has those parents wrong... Fixes: bab9b2a74fe9 ("soc: renesas: rcar-sysc: add R8A77970 support") Signed-off-by: Sergei Shtylyov Reviewed-by: Geert Uytterhoeven Signed-off-by: Simon Horman --- drivers/soc/renesas/r8a77970-sysc.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) (limited to 'drivers') diff --git a/drivers/soc/renesas/r8a77970-sysc.c b/drivers/soc/renesas/r8a77970-sysc.c index 8c614164718e..caf894f193ed 100644 --- a/drivers/soc/renesas/r8a77970-sysc.c +++ b/drivers/soc/renesas/r8a77970-sysc.c @@ -25,12 +25,12 @@ static const struct rcar_sysc_area r8a77970_areas[] __initconst = { PD_CPU_NOCR }, { "cr7", 0x240, 0, R8A77970_PD_CR7, R8A77970_PD_ALWAYS_ON }, { "a3ir", 0x180, 0, R8A77970_PD_A3IR, R8A77970_PD_ALWAYS_ON }, - { "a2ir0", 0x400, 0, R8A77970_PD_A2IR0, R8A77970_PD_ALWAYS_ON }, - { "a2ir1", 0x400, 1, R8A77970_PD_A2IR1, R8A77970_PD_A2IR0 }, - { "a2ir2", 0x400, 2, R8A77970_PD_A2IR2, R8A77970_PD_A2IR0 }, - { "a2ir3", 0x400, 3, R8A77970_PD_A2IR3, R8A77970_PD_A2IR0 }, - { "a2sc0", 0x400, 4, R8A77970_PD_A2SC0, R8A77970_PD_ALWAYS_ON }, - { "a2sc1", 0x400, 5, R8A77970_PD_A2SC1, R8A77970_PD_A2SC0 }, + { "a2ir0", 0x400, 0, R8A77970_PD_A2IR0, R8A77970_PD_A3IR }, + { "a2ir1", 0x400, 1, R8A77970_PD_A2IR1, R8A77970_PD_A3IR }, + { "a2ir2", 0x400, 2, R8A77970_PD_A2IR2, R8A77970_PD_A3IR }, + { "a2ir3", 0x400, 3, R8A77970_PD_A2IR3, R8A77970_PD_A3IR }, + { "a2sc0", 0x400, 4, R8A77970_PD_A2SC0, R8A77970_PD_A3IR }, + { "a2sc1", 0x400, 5, R8A77970_PD_A2SC1, R8A77970_PD_A3IR }, }; const struct rcar_sysc_info r8a77970_sysc_info __initconst = { -- cgit v1.2.3 From bdec5a6b57896da81bc47262868468717a06bb69 Mon Sep 17 00:00:00 2001 From: David Lechner Date: Sat, 17 Feb 2018 21:22:24 -0600 Subject: ARM: da8xx: use platform data for CFGCHIP syscon regmap This converts from using a platform device for the CFGCHIP syscon regmap to using platform data to pass the regmap to consumers. A lazy getter function is used so that the regmap will only be created if it is actually used. This function will also be used in the clock init when we convert to the common clock framework. The USB PHY driver is currently the only consumer. This driver is updated to use platform data to get the CFGCHIP regmap instead of syscon_regmap_lookup_by_pdevname(). Signed-off-by: David Lechner Acked-by: Kishon Vijay Abraham I Signed-off-by: Sekhar Nori --- arch/arm/mach-davinci/board-da830-evm.c | 4 --- arch/arm/mach-davinci/board-da850-evm.c | 4 --- arch/arm/mach-davinci/board-mityomapl138.c | 4 --- arch/arm/mach-davinci/board-omapl138-hawk.c | 4 --- arch/arm/mach-davinci/devices-da8xx.c | 45 +++++++++++++++-------------- arch/arm/mach-davinci/include/mach/da8xx.h | 3 +- arch/arm/mach-davinci/usb-da8xx.c | 6 ++++ drivers/phy/ti/phy-da8xx-usb.c | 8 +++-- include/linux/platform_data/phy-da8xx-usb.h | 21 ++++++++++++++ 9 files changed, 58 insertions(+), 41 deletions(-) create mode 100644 include/linux/platform_data/phy-da8xx-usb.h (limited to 'drivers') diff --git a/arch/arm/mach-davinci/board-da830-evm.c b/arch/arm/mach-davinci/board-da830-evm.c index f673cd7a6766..f960cbef6538 100644 --- a/arch/arm/mach-davinci/board-da830-evm.c +++ b/arch/arm/mach-davinci/board-da830-evm.c @@ -551,10 +551,6 @@ static __init void da830_evm_init(void) struct davinci_soc_info *soc_info = &davinci_soc_info; int ret; - ret = da8xx_register_cfgchip(); - if (ret) - pr_warn("%s: CFGCHIP registration failed: %d\n", __func__, ret); - ret = da830_register_gpio(); if (ret) pr_warn("%s: GPIO init failed: %d\n", __func__, ret); diff --git a/arch/arm/mach-davinci/board-da850-evm.c b/arch/arm/mach-davinci/board-da850-evm.c index d898a94f6eae..26bdb10a8927 100644 --- a/arch/arm/mach-davinci/board-da850-evm.c +++ b/arch/arm/mach-davinci/board-da850-evm.c @@ -1334,10 +1334,6 @@ static __init void da850_evm_init(void) { int ret; - ret = da8xx_register_cfgchip(); - if (ret) - pr_warn("%s: CFGCHIP registration failed: %d\n", __func__, ret); - ret = da850_register_gpio(); if (ret) pr_warn("%s: GPIO init failed: %d\n", __func__, ret); diff --git a/arch/arm/mach-davinci/board-mityomapl138.c b/arch/arm/mach-davinci/board-mityomapl138.c index b73ce7bae81f..9e7388ba413c 100644 --- a/arch/arm/mach-davinci/board-mityomapl138.c +++ b/arch/arm/mach-davinci/board-mityomapl138.c @@ -502,10 +502,6 @@ static void __init mityomapl138_init(void) { int ret; - ret = da8xx_register_cfgchip(); - if (ret) - pr_warn("%s: CFGCHIP registration failed: %d\n", __func__, ret); - /* for now, no special EDMA channels are reserved */ ret = da850_register_edma(NULL); if (ret) diff --git a/arch/arm/mach-davinci/board-omapl138-hawk.c b/arch/arm/mach-davinci/board-omapl138-hawk.c index a3e78074be70..baab7eb61632 100644 --- a/arch/arm/mach-davinci/board-omapl138-hawk.c +++ b/arch/arm/mach-davinci/board-omapl138-hawk.c @@ -281,10 +281,6 @@ static __init void omapl138_hawk_init(void) { int ret; - ret = da8xx_register_cfgchip(); - if (ret) - pr_warn("%s: CFGCHIP registration failed: %d\n", __func__, ret); - ret = da850_register_gpio(); if (ret) pr_warn("%s: GPIO init failed: %d\n", __func__, ret); diff --git a/arch/arm/mach-davinci/devices-da8xx.c b/arch/arm/mach-davinci/devices-da8xx.c index e1c40e73d30a..166bf29b1296 100644 --- a/arch/arm/mach-davinci/devices-da8xx.c +++ b/arch/arm/mach-davinci/devices-da8xx.c @@ -11,7 +11,6 @@ * (at your option) any later version. */ #include -#include #include #include #include @@ -1118,29 +1117,33 @@ int __init da850_register_sata(unsigned long refclkpn) } #endif -static struct syscon_platform_data da8xx_cfgchip_platform_data = { - .label = "cfgchip", -}; +static struct regmap *da8xx_cfgchip; -static struct resource da8xx_cfgchip_resources[] = { - { - .start = DA8XX_SYSCFG0_BASE + DA8XX_CFGCHIP0_REG, - .end = DA8XX_SYSCFG0_BASE + DA8XX_CFGCHIP4_REG + 3, - .flags = IORESOURCE_MEM, - }, -}; +/* regmap doesn't make a copy of this, so we need to keep the pointer around */ +static const char da8xx_cfgchip_name[] = "cfgchip"; -static struct platform_device da8xx_cfgchip_device = { - .name = "syscon", - .id = -1, - .dev = { - .platform_data = &da8xx_cfgchip_platform_data, - }, - .num_resources = ARRAY_SIZE(da8xx_cfgchip_resources), - .resource = da8xx_cfgchip_resources, +static const struct regmap_config da8xx_cfgchip_config __initconst = { + .name = da8xx_cfgchip_name, + .reg_bits = 32, + .val_bits = 32, + .reg_stride = 4, + .max_register = DA8XX_CFGCHIP4_REG - DA8XX_CFGCHIP0_REG, }; -int __init da8xx_register_cfgchip(void) +/** + * da8xx_get_cfgchip - Lazy gets CFGCHIP as regmap + * + * This is for use on non-DT boards only. For DT boards, use + * syscon_regmap_lookup_by_compatible("ti,da830-cfgchip") + * + * Returns: Pointer to the CFGCHIP regmap or negative error code. + */ +struct regmap * __init da8xx_get_cfgchip(void) { - return platform_device_register(&da8xx_cfgchip_device); + if (IS_ERR_OR_NULL(da8xx_cfgchip)) + da8xx_cfgchip = regmap_init_mmio(NULL, + DA8XX_SYSCFG0_VIRT(DA8XX_CFGCHIP0_REG), + &da8xx_cfgchip_config); + + return da8xx_cfgchip; } diff --git a/arch/arm/mach-davinci/include/mach/da8xx.h b/arch/arm/mach-davinci/include/mach/da8xx.h index 93ff1569cee5..03f37ef4297f 100644 --- a/arch/arm/mach-davinci/include/mach/da8xx.h +++ b/arch/arm/mach-davinci/include/mach/da8xx.h @@ -18,6 +18,7 @@ #include #include #include +#include #include #include @@ -123,7 +124,7 @@ void da8xx_rproc_reserve_cma(void); int da8xx_register_rproc(void); int da850_register_gpio(void); int da830_register_gpio(void); -int da8xx_register_cfgchip(void); +struct regmap *da8xx_get_cfgchip(void); extern struct platform_device da8xx_serial_device[]; extern struct emac_platform_data da8xx_emac_pdata; diff --git a/arch/arm/mach-davinci/usb-da8xx.c b/arch/arm/mach-davinci/usb-da8xx.c index fb31f6eeba96..4d89d86ce7e5 100644 --- a/arch/arm/mach-davinci/usb-da8xx.c +++ b/arch/arm/mach-davinci/usb-da8xx.c @@ -8,6 +8,7 @@ #include #include #include +#include #include #include #include @@ -40,6 +41,11 @@ static struct platform_device da8xx_usb_phy = { int __init da8xx_register_usb_phy(void) { + struct da8xx_usb_phy_platform_data pdata; + + pdata.cfgchip = da8xx_get_cfgchip(); + da8xx_usb_phy.dev.platform_data = &pdata; + return platform_device_register(&da8xx_usb_phy); } diff --git a/drivers/phy/ti/phy-da8xx-usb.c b/drivers/phy/ti/phy-da8xx-usb.c index 5bd33d06df95..befb886ff121 100644 --- a/drivers/phy/ti/phy-da8xx-usb.c +++ b/drivers/phy/ti/phy-da8xx-usb.c @@ -20,6 +20,7 @@ #include #include #include +#include #include #include @@ -145,6 +146,7 @@ static struct phy *da8xx_usb_phy_of_xlate(struct device *dev, static int da8xx_usb_phy_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; + struct da8xx_usb_phy_platform_data *pdata = dev->platform_data; struct device_node *node = dev->of_node; struct da8xx_usb_phy *d_phy; @@ -152,11 +154,11 @@ static int da8xx_usb_phy_probe(struct platform_device *pdev) if (!d_phy) return -ENOMEM; - if (node) + if (pdata) + d_phy->regmap = pdata->cfgchip; + else d_phy->regmap = syscon_regmap_lookup_by_compatible( "ti,da830-cfgchip"); - else - d_phy->regmap = syscon_regmap_lookup_by_pdevname("syscon"); if (IS_ERR(d_phy->regmap)) { dev_err(dev, "Failed to get syscon\n"); return PTR_ERR(d_phy->regmap); diff --git a/include/linux/platform_data/phy-da8xx-usb.h b/include/linux/platform_data/phy-da8xx-usb.h new file mode 100644 index 000000000000..85c2b99381b2 --- /dev/null +++ b/include/linux/platform_data/phy-da8xx-usb.h @@ -0,0 +1,21 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * phy-da8xx-usb - TI DaVinci DA8xx USB PHY driver + * + * Copyright (C) 2018 David Lechner + */ + +#ifndef __LINUX_PLATFORM_DATA_PHY_DA8XX_USB_H__ +#define __LINUX_PLATFORM_DATA_PHY_DA8XX_USB_H__ + +#include + +/** + * da8xx_usb_phy_platform_data + * @cfgchip: CFGCHIP syscon regmap + */ +struct da8xx_usb_phy_platform_data { + struct regmap *cfgchip; +}; + +#endif /* __LINUX_PLATFORM_DATA_PHY_DA8XX_USB_H__ */ -- cgit v1.2.3 From 41d6d8bd8ae94ca9ee53720cd530168aca557db7 Mon Sep 17 00:00:00 2001 From: Sergei Shtylyov Date: Fri, 16 Feb 2018 21:28:02 +0300 Subject: soc: renesas: rcar-sysc: add R8A77980 support Add support for R-Car V3H (R8A77980) SoC power areas to the R-Car SYSC driver. Based on the original (and large) patch by Vladimir Barinov. Signed-off-by: Vladimir Barinov Signed-off-by: Sergei Shtylyov Reviewed-by: Geert Uytterhoeven Signed-off-by: Simon Horman --- .../bindings/power/renesas,rcar-sysc.txt | 1 + drivers/soc/renesas/Kconfig | 5 +++ drivers/soc/renesas/Makefile | 1 + drivers/soc/renesas/r8a77980-sysc.c | 52 ++++++++++++++++++++++ drivers/soc/renesas/rcar-sysc.c | 3 ++ drivers/soc/renesas/rcar-sysc.h | 1 + 6 files changed, 63 insertions(+) create mode 100644 drivers/soc/renesas/r8a77980-sysc.c (limited to 'drivers') diff --git a/Documentation/devicetree/bindings/power/renesas,rcar-sysc.txt b/Documentation/devicetree/bindings/power/renesas,rcar-sysc.txt index 8690f10426a3..6284a9550b3c 100644 --- a/Documentation/devicetree/bindings/power/renesas,rcar-sysc.txt +++ b/Documentation/devicetree/bindings/power/renesas,rcar-sysc.txt @@ -18,6 +18,7 @@ Required properties: - "renesas,r8a7795-sysc" (R-Car H3) - "renesas,r8a7796-sysc" (R-Car M3-W) - "renesas,r8a77970-sysc" (R-Car V3M) + - "renesas,r8a77980-sysc" (R-Car V3H) - "renesas,r8a77995-sysc" (R-Car D3) - reg: Address start and address range for the device. - #power-domain-cells: Must be 1. diff --git a/drivers/soc/renesas/Kconfig b/drivers/soc/renesas/Kconfig index 6efd7bef8577..6caa393e3a2d 100644 --- a/drivers/soc/renesas/Kconfig +++ b/drivers/soc/renesas/Kconfig @@ -15,6 +15,7 @@ config SOC_RENESAS select SYSC_R8A7795 if ARCH_R8A7795 select SYSC_R8A7796 if ARCH_R8A7796 select SYSC_R8A77970 if ARCH_R8A77970 + select SYSC_R8A77980 if ARCH_R8A77980 select SYSC_R8A77995 if ARCH_R8A77995 if SOC_RENESAS @@ -60,6 +61,10 @@ config SYSC_R8A77970 bool "R-Car V3M System Controller support" if COMPILE_TEST select SYSC_RCAR +config SYSC_R8A77980 + bool "R-Car V3H System Controller support" if COMPILE_TEST + select SYSC_RCAR + config SYSC_R8A77995 bool "R-Car D3 System Controller support" if COMPILE_TEST select SYSC_RCAR diff --git a/drivers/soc/renesas/Makefile b/drivers/soc/renesas/Makefile index 845d62a08ce1..d3b7bb3284c0 100644 --- a/drivers/soc/renesas/Makefile +++ b/drivers/soc/renesas/Makefile @@ -13,6 +13,7 @@ obj-$(CONFIG_SYSC_R8A7794) += r8a7794-sysc.o obj-$(CONFIG_SYSC_R8A7795) += r8a7795-sysc.o obj-$(CONFIG_SYSC_R8A7796) += r8a7796-sysc.o obj-$(CONFIG_SYSC_R8A77970) += r8a77970-sysc.o +obj-$(CONFIG_SYSC_R8A77980) += r8a77980-sysc.o obj-$(CONFIG_SYSC_R8A77995) += r8a77995-sysc.o # Family diff --git a/drivers/soc/renesas/r8a77980-sysc.c b/drivers/soc/renesas/r8a77980-sysc.c new file mode 100644 index 000000000000..9265fb525ef3 --- /dev/null +++ b/drivers/soc/renesas/r8a77980-sysc.c @@ -0,0 +1,52 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Renesas R-Car V3H System Controller + * + * Copyright (C) 2018 Renesas Electronics Corp. + * Copyright (C) 2018 Cogent Embedded, Inc. + */ + +#include +#include + +#include + +#include "rcar-sysc.h" + +static const struct rcar_sysc_area r8a77980_areas[] __initconst = { + { "always-on", 0, 0, R8A77980_PD_ALWAYS_ON, -1, PD_ALWAYS_ON }, + { "ca53-scu", 0x140, 0, R8A77980_PD_CA53_SCU, R8A77980_PD_ALWAYS_ON, + PD_SCU }, + { "ca53-cpu0", 0x200, 0, R8A77980_PD_CA53_CPU0, R8A77980_PD_CA53_SCU, + PD_CPU_NOCR }, + { "ca53-cpu1", 0x200, 1, R8A77980_PD_CA53_CPU1, R8A77980_PD_CA53_SCU, + PD_CPU_NOCR }, + { "ca53-cpu2", 0x200, 2, R8A77980_PD_CA53_CPU2, R8A77980_PD_CA53_SCU, + PD_CPU_NOCR }, + { "ca53-cpu3", 0x200, 3, R8A77980_PD_CA53_CPU3, R8A77980_PD_CA53_SCU, + PD_CPU_NOCR }, + { "cr7", 0x240, 0, R8A77980_PD_CR7, R8A77980_PD_ALWAYS_ON }, + { "a3ir", 0x180, 0, R8A77980_PD_A3IR, R8A77980_PD_ALWAYS_ON }, + { "a2ir0", 0x400, 0, R8A77980_PD_A2IR0, R8A77980_PD_A3IR }, + { "a2ir1", 0x400, 1, R8A77980_PD_A2IR1, R8A77980_PD_A3IR }, + { "a2ir2", 0x400, 2, R8A77980_PD_A2IR2, R8A77980_PD_A3IR }, + { "a2ir3", 0x400, 3, R8A77980_PD_A2IR3, R8A77980_PD_A3IR }, + { "a2ir4", 0x400, 4, R8A77980_PD_A2IR4, R8A77980_PD_A3IR }, + { "a2ir5", 0x400, 5, R8A77980_PD_A2IR5, R8A77980_PD_A3IR }, + { "a2sc0", 0x400, 6, R8A77980_PD_A2SC0, R8A77980_PD_A3IR }, + { "a2sc1", 0x400, 7, R8A77980_PD_A2SC1, R8A77980_PD_A3IR }, + { "a2sc2", 0x400, 8, R8A77980_PD_A2SC2, R8A77980_PD_A3IR }, + { "a2sc3", 0x400, 9, R8A77980_PD_A2SC3, R8A77980_PD_A3IR }, + { "a2sc4", 0x400, 10, R8A77980_PD_A2SC4, R8A77980_PD_A3IR }, + { "a2pd0", 0x400, 11, R8A77980_PD_A2PD0, R8A77980_PD_A3IR }, + { "a2pd1", 0x400, 12, R8A77980_PD_A2PD1, R8A77980_PD_A3IR }, + { "a2cn", 0x400, 13, R8A77980_PD_A2CN, R8A77980_PD_A3IR }, + { "a3vip", 0x2c0, 0, R8A77980_PD_A3VIP, R8A77980_PD_ALWAYS_ON }, + { "a3vip1", 0x300, 0, R8A77980_PD_A3VIP1, R8A77980_PD_A3VIP }, + { "a3vip2", 0x280, 0, R8A77980_PD_A3VIP2, R8A77980_PD_A3VIP }, +}; + +const struct rcar_sysc_info r8a77980_sysc_info __initconst = { + .areas = r8a77980_areas, + .num_areas = ARRAY_SIZE(r8a77980_areas), +}; diff --git a/drivers/soc/renesas/rcar-sysc.c b/drivers/soc/renesas/rcar-sysc.c index 636872bc2416..72b0f4a9ad4e 100644 --- a/drivers/soc/renesas/rcar-sysc.c +++ b/drivers/soc/renesas/rcar-sysc.c @@ -287,6 +287,9 @@ static const struct of_device_id rcar_sysc_matches[] __initconst = { #ifdef CONFIG_SYSC_R8A77970 { .compatible = "renesas,r8a77970-sysc", .data = &r8a77970_sysc_info }, #endif +#ifdef CONFIG_SYSC_R8A77980 + { .compatible = "renesas,r8a77980-sysc", .data = &r8a77980_sysc_info }, +#endif #ifdef CONFIG_SYSC_R8A77995 { .compatible = "renesas,r8a77995-sysc", .data = &r8a77995_sysc_info }, #endif diff --git a/drivers/soc/renesas/rcar-sysc.h b/drivers/soc/renesas/rcar-sysc.h index 9d9daf9eb91b..974b18619c08 100644 --- a/drivers/soc/renesas/rcar-sysc.h +++ b/drivers/soc/renesas/rcar-sysc.h @@ -59,6 +59,7 @@ extern const struct rcar_sysc_info r8a7794_sysc_info; extern const struct rcar_sysc_info r8a7795_sysc_info; extern const struct rcar_sysc_info r8a7796_sysc_info; extern const struct rcar_sysc_info r8a77970_sysc_info; +extern const struct rcar_sysc_info r8a77980_sysc_info; extern const struct rcar_sysc_info r8a77995_sysc_info; -- cgit v1.2.3 From bfd8398339ccd824a3c5dfaee2a2b489a85b132f Mon Sep 17 00:00:00 2001 From: Jacopo Mondi Date: Tue, 20 Feb 2018 16:12:05 +0100 Subject: soc: renesas: Identify R-Car M3-N Add support for indentifying R-Car M3-N (R8A77965) SoC. Signed-off-by: Jacopo Mondi Reviewed-by: Geert Uytterhoeven Signed-off-by: Simon Horman --- drivers/soc/renesas/renesas-soc.c | 8 ++++++++ 1 file changed, 8 insertions(+) (limited to 'drivers') diff --git a/drivers/soc/renesas/renesas-soc.c b/drivers/soc/renesas/renesas-soc.c index 000834321774..ea71c413c926 100644 --- a/drivers/soc/renesas/renesas-soc.c +++ b/drivers/soc/renesas/renesas-soc.c @@ -144,6 +144,11 @@ static const struct renesas_soc soc_rcar_m3_w __initconst __maybe_unused = { .id = 0x52, }; +static const struct renesas_soc soc_rcar_m3_n __initconst __maybe_unused = { + .family = &fam_rcar_gen3, + .id = 0x55, +}; + static const struct renesas_soc soc_rcar_v3m __initconst __maybe_unused = { .family = &fam_rcar_gen3, .id = 0x54, @@ -214,6 +219,9 @@ static const struct of_device_id renesas_socs[] __initconst = { #ifdef CONFIG_ARCH_R8A7796 { .compatible = "renesas,r8a7796", .data = &soc_rcar_m3_w }, #endif +#ifdef CONFIG_ARCH_R8A77965 + { .compatible = "renesas,r8a77965", .data = &soc_rcar_m3_n }, +#endif #ifdef CONFIG_ARCH_R8A77970 { .compatible = "renesas,r8a77970", .data = &soc_rcar_v3m }, #endif -- cgit v1.2.3 From a527709b78b3c9979fb47ddd4c3d3fd96182b504 Mon Sep 17 00:00:00 2001 From: Jacopo Mondi Date: Tue, 20 Feb 2018 16:12:06 +0100 Subject: soc: renesas: rcar-sysc: Add R-Car M3-N support Add support for R-Car M3-N (R8A77965) power areas. Signed-off-by: Jacopo Mondi Reviewed-by: Geert Uytterhoeven Signed-off-by: Simon Horman --- .../bindings/power/renesas,rcar-sysc.txt | 1 + drivers/soc/renesas/Kconfig | 5 +++ drivers/soc/renesas/Makefile | 1 + drivers/soc/renesas/r8a77965-sysc.c | 37 ++++++++++++++++++++++ drivers/soc/renesas/rcar-sysc.c | 3 ++ drivers/soc/renesas/rcar-sysc.h | 1 + include/dt-bindings/power/r8a77965-sysc.h | 30 ++++++++++++++++++ 7 files changed, 78 insertions(+) create mode 100644 drivers/soc/renesas/r8a77965-sysc.c create mode 100644 include/dt-bindings/power/r8a77965-sysc.h (limited to 'drivers') diff --git a/Documentation/devicetree/bindings/power/renesas,rcar-sysc.txt b/Documentation/devicetree/bindings/power/renesas,rcar-sysc.txt index 6284a9550b3c..ab399e559257 100644 --- a/Documentation/devicetree/bindings/power/renesas,rcar-sysc.txt +++ b/Documentation/devicetree/bindings/power/renesas,rcar-sysc.txt @@ -17,6 +17,7 @@ Required properties: - "renesas,r8a7794-sysc" (R-Car E2) - "renesas,r8a7795-sysc" (R-Car H3) - "renesas,r8a7796-sysc" (R-Car M3-W) + - "renesas,r8a77965-sysc" (R-Car M3-N) - "renesas,r8a77970-sysc" (R-Car V3M) - "renesas,r8a77980-sysc" (R-Car V3H) - "renesas,r8a77995-sysc" (R-Car D3) diff --git a/drivers/soc/renesas/Kconfig b/drivers/soc/renesas/Kconfig index 6caa393e3a2d..7106a6330210 100644 --- a/drivers/soc/renesas/Kconfig +++ b/drivers/soc/renesas/Kconfig @@ -14,6 +14,7 @@ config SOC_RENESAS select SYSC_R8A7794 if ARCH_R8A7794 select SYSC_R8A7795 if ARCH_R8A7795 select SYSC_R8A7796 if ARCH_R8A7796 + select SYSC_R8A77965 if ARCH_R8A77965 select SYSC_R8A77970 if ARCH_R8A77970 select SYSC_R8A77980 if ARCH_R8A77980 select SYSC_R8A77995 if ARCH_R8A77995 @@ -57,6 +58,10 @@ config SYSC_R8A7796 bool "R-Car M3-W System Controller support" if COMPILE_TEST select SYSC_RCAR +config SYSC_R8A77965 + bool "R-Car M3-N System Controller support" if COMPILE_TEST + select SYSC_RCAR + config SYSC_R8A77970 bool "R-Car V3M System Controller support" if COMPILE_TEST select SYSC_RCAR diff --git a/drivers/soc/renesas/Makefile b/drivers/soc/renesas/Makefile index d3b7bb3284c0..ccb5ec57a262 100644 --- a/drivers/soc/renesas/Makefile +++ b/drivers/soc/renesas/Makefile @@ -12,6 +12,7 @@ obj-$(CONFIG_SYSC_R8A7792) += r8a7792-sysc.o obj-$(CONFIG_SYSC_R8A7794) += r8a7794-sysc.o obj-$(CONFIG_SYSC_R8A7795) += r8a7795-sysc.o obj-$(CONFIG_SYSC_R8A7796) += r8a7796-sysc.o +obj-$(CONFIG_SYSC_R8A77965) += r8a77965-sysc.o obj-$(CONFIG_SYSC_R8A77970) += r8a77970-sysc.o obj-$(CONFIG_SYSC_R8A77980) += r8a77980-sysc.o obj-$(CONFIG_SYSC_R8A77995) += r8a77995-sysc.o diff --git a/drivers/soc/renesas/r8a77965-sysc.c b/drivers/soc/renesas/r8a77965-sysc.c new file mode 100644 index 000000000000..d7f7928e3c07 --- /dev/null +++ b/drivers/soc/renesas/r8a77965-sysc.c @@ -0,0 +1,37 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Renesas R-Car M3-N System Controller + * Copyright (C) 2018 Jacopo Mondi + * + * Based on Renesas R-Car M3-W System Controller + * Copyright (C) 2016 Glider bvba + */ + +#include +#include + +#include + +#include "rcar-sysc.h" + +static const struct rcar_sysc_area r8a77965_areas[] __initconst = { + { "always-on", 0, 0, R8A77965_PD_ALWAYS_ON, -1, PD_ALWAYS_ON }, + { "ca57-scu", 0x1c0, 0, R8A77965_PD_CA57_SCU, R8A77965_PD_ALWAYS_ON, + PD_SCU }, + { "ca57-cpu0", 0x80, 0, R8A77965_PD_CA57_CPU0, R8A77965_PD_CA57_SCU, + PD_CPU_NOCR }, + { "ca57-cpu1", 0x80, 1, R8A77965_PD_CA57_CPU1, R8A77965_PD_CA57_SCU, + PD_CPU_NOCR }, + { "cr7", 0x240, 0, R8A77965_PD_CR7, R8A77965_PD_ALWAYS_ON }, + { "a3vc", 0x380, 0, R8A77965_PD_A3VC, R8A77965_PD_ALWAYS_ON }, + { "a3vp", 0x340, 0, R8A77965_PD_A3VP, R8A77965_PD_ALWAYS_ON }, + { "a2vc1", 0x3c0, 1, R8A77965_PD_A2VC1, R8A77965_PD_A3VC }, + { "3dg-a", 0x100, 0, R8A77965_PD_3DG_A, R8A77965_PD_ALWAYS_ON }, + { "3dg-b", 0x100, 1, R8A77965_PD_3DG_B, R8A77965_PD_3DG_A }, + { "a3ir", 0x180, 0, R8A77965_PD_A3IR, R8A77965_PD_ALWAYS_ON }, +}; + +const struct rcar_sysc_info r8a77965_sysc_info __initconst = { + .areas = r8a77965_areas, + .num_areas = ARRAY_SIZE(r8a77965_areas), +}; diff --git a/drivers/soc/renesas/rcar-sysc.c b/drivers/soc/renesas/rcar-sysc.c index 72b0f4a9ad4e..faf20e719361 100644 --- a/drivers/soc/renesas/rcar-sysc.c +++ b/drivers/soc/renesas/rcar-sysc.c @@ -284,6 +284,9 @@ static const struct of_device_id rcar_sysc_matches[] __initconst = { #ifdef CONFIG_SYSC_R8A7796 { .compatible = "renesas,r8a7796-sysc", .data = &r8a7796_sysc_info }, #endif +#ifdef CONFIG_SYSC_R8A77965 + { .compatible = "renesas,r8a77965-sysc", .data = &r8a77965_sysc_info }, +#endif #ifdef CONFIG_SYSC_R8A77970 { .compatible = "renesas,r8a77970-sysc", .data = &r8a77970_sysc_info }, #endif diff --git a/drivers/soc/renesas/rcar-sysc.h b/drivers/soc/renesas/rcar-sysc.h index 974b18619c08..dcdc9ec8eba7 100644 --- a/drivers/soc/renesas/rcar-sysc.h +++ b/drivers/soc/renesas/rcar-sysc.h @@ -58,6 +58,7 @@ extern const struct rcar_sysc_info r8a7792_sysc_info; extern const struct rcar_sysc_info r8a7794_sysc_info; extern const struct rcar_sysc_info r8a7795_sysc_info; extern const struct rcar_sysc_info r8a7796_sysc_info; +extern const struct rcar_sysc_info r8a77965_sysc_info; extern const struct rcar_sysc_info r8a77970_sysc_info; extern const struct rcar_sysc_info r8a77980_sysc_info; extern const struct rcar_sysc_info r8a77995_sysc_info; diff --git a/include/dt-bindings/power/r8a77965-sysc.h b/include/dt-bindings/power/r8a77965-sysc.h new file mode 100644 index 000000000000..05a4b5917314 --- /dev/null +++ b/include/dt-bindings/power/r8a77965-sysc.h @@ -0,0 +1,30 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (C) 2018 Jacopo Mondi + * Copyright (C) 2016 Glider bvba + */ + +#ifndef __DT_BINDINGS_POWER_R8A77965_SYSC_H__ +#define __DT_BINDINGS_POWER_R8A77965_SYSC_H__ + +/* + * These power domain indices match the numbers of the interrupt bits + * representing the power areas in the various Interrupt Registers + * (e.g. SYSCISR, Interrupt Status Register) + */ + +#define R8A77965_PD_CA57_CPU0 0 +#define R8A77965_PD_CA57_CPU1 1 +#define R8A77965_PD_A3VP 9 +#define R8A77965_PD_CA57_SCU 12 +#define R8A77965_PD_CR7 13 +#define R8A77965_PD_A3VC 14 +#define R8A77965_PD_3DG_A 17 +#define R8A77965_PD_3DG_B 18 +#define R8A77965_PD_A3IR 24 +#define R8A77965_PD_A2VC1 26 + +/* Always-on power area */ +#define R8A77965_PD_ALWAYS_ON 32 + +#endif /* __DT_BINDINGS_POWER_R8A77965_SYSC_H__ */ -- cgit v1.2.3 From af04aa856e932876e11e0c6d21d82281824e1b11 Mon Sep 17 00:00:00 2001 From: Keerthy Date: Thu, 15 Feb 2018 11:31:46 +0530 Subject: ARM: OMAP: Move dmtimer driver out of plat-omap to drivers under clocksource Move the dmtimer driver out of plat-omap to clocksource. So that non-omap devices also could use this. No Code changes done to the driver file only renamed to timer-ti-dm.c. Also removed the config dependencies for OMAP_DM_TIMER. Signed-off-by: Keerthy Reviewed-by: Sebastian Reichel Tested-by: Ladislav Michl [tony@atomide.com: add select omap_dm_timer for omap16xx] Signed-off-by: Tony Lindgren --- arch/arm/mach-omap1/Kconfig | 1 + arch/arm/plat-omap/Kconfig | 6 - arch/arm/plat-omap/Makefile | 1 - arch/arm/plat-omap/dmtimer.c | 973 -------------------------------------- drivers/clocksource/Kconfig | 3 + drivers/clocksource/Makefile | 1 + drivers/clocksource/timer-ti-dm.c | 973 ++++++++++++++++++++++++++++++++++++++ 7 files changed, 978 insertions(+), 980 deletions(-) delete mode 100644 arch/arm/plat-omap/dmtimer.c create mode 100644 drivers/clocksource/timer-ti-dm.c (limited to 'drivers') diff --git a/arch/arm/mach-omap1/Kconfig b/arch/arm/mach-omap1/Kconfig index 45c6b733c881..c4694f26b5c4 100644 --- a/arch/arm/mach-omap1/Kconfig +++ b/arch/arm/mach-omap1/Kconfig @@ -30,6 +30,7 @@ config ARCH_OMAP16XX bool "OMAP16xx Based System" select ARCH_OMAP_OTG select CPU_ARM926T + select OMAP_DM_TIMER config OMAP_MUX bool "OMAP multiplexing support" diff --git a/arch/arm/plat-omap/Kconfig b/arch/arm/plat-omap/Kconfig index 7276afee30b3..afc1a1d4f7a5 100644 --- a/arch/arm/plat-omap/Kconfig +++ b/arch/arm/plat-omap/Kconfig @@ -106,12 +106,6 @@ config OMAP3_L2_AUX_SECURE_SERVICE_SET_ID help PPA routine service ID for setting L2 auxiliary control register. -config OMAP_DM_TIMER - bool "Use dual-mode timer" - depends on ARCH_OMAP16XX || ARCH_OMAP2PLUS - help - Select this option if you want to use OMAP Dual-Mode timers. - config OMAP_SERIAL_WAKE bool "Enable wake-up events for serial ports" depends on ARCH_OMAP1 && OMAP_MUX diff --git a/arch/arm/plat-omap/Makefile b/arch/arm/plat-omap/Makefile index 47e186729d44..7215ada707e4 100644 --- a/arch/arm/plat-omap/Makefile +++ b/arch/arm/plat-omap/Makefile @@ -9,5 +9,4 @@ obj-y := sram.o dma.o counter_32k.o # omap_device support (OMAP2+ only at the moment) -obj-$(CONFIG_OMAP_DM_TIMER) += dmtimer.o obj-$(CONFIG_OMAP_DEBUG_LEDS) += debug-leds.o diff --git a/arch/arm/plat-omap/dmtimer.c b/arch/arm/plat-omap/dmtimer.c deleted file mode 100644 index 346898809276..000000000000 --- a/arch/arm/plat-omap/dmtimer.c +++ /dev/null @@ -1,973 +0,0 @@ -/* - * linux/arch/arm/plat-omap/dmtimer.c - * - * OMAP Dual-Mode Timers - * - * Copyright (C) 2010 Texas Instruments Incorporated - http://www.ti.com/ - * Tarun Kanti DebBarma - * Thara Gopinath - * - * dmtimer adaptation to platform_driver. - * - * Copyright (C) 2005 Nokia Corporation - * OMAP2 support by Juha Yrjola - * API improvements and OMAP2 clock framework support by Timo Teras - * - * Copyright (C) 2009 Texas Instruments - * Added OMAP4 support - 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; either version 2 of the License, or (at your - * option) any later version. - * - * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED - * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN - * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT - * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF - * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - * 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., - * 675 Mass Ave, Cambridge, MA 02139, USA. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include - -static u32 omap_reserved_systimers; -static LIST_HEAD(omap_timer_list); -static DEFINE_SPINLOCK(dm_timer_lock); - -enum { - REQUEST_ANY = 0, - REQUEST_BY_ID, - REQUEST_BY_CAP, - REQUEST_BY_NODE, -}; - -/** - * omap_dm_timer_read_reg - read timer registers in posted and non-posted mode - * @timer: timer pointer over which read operation to perform - * @reg: lowest byte holds the register offset - * - * The posted mode bit is encoded in reg. Note that in posted mode write - * pending bit must be checked. Otherwise a read of a non completed write - * will produce an error. - */ -static inline u32 omap_dm_timer_read_reg(struct omap_dm_timer *timer, u32 reg) -{ - WARN_ON((reg & 0xff) < _OMAP_TIMER_WAKEUP_EN_OFFSET); - return __omap_dm_timer_read(timer, reg, timer->posted); -} - -/** - * omap_dm_timer_write_reg - write timer registers in posted and non-posted mode - * @timer: timer pointer over which write operation is to perform - * @reg: lowest byte holds the register offset - * @value: data to write into the register - * - * The posted mode bit is encoded in reg. Note that in posted mode the write - * pending bit must be checked. Otherwise a write on a register which has a - * pending write will be lost. - */ -static void omap_dm_timer_write_reg(struct omap_dm_timer *timer, u32 reg, - u32 value) -{ - WARN_ON((reg & 0xff) < _OMAP_TIMER_WAKEUP_EN_OFFSET); - __omap_dm_timer_write(timer, reg, value, timer->posted); -} - -static void omap_timer_restore_context(struct omap_dm_timer *timer) -{ - omap_dm_timer_write_reg(timer, OMAP_TIMER_WAKEUP_EN_REG, - timer->context.twer); - omap_dm_timer_write_reg(timer, OMAP_TIMER_COUNTER_REG, - timer->context.tcrr); - omap_dm_timer_write_reg(timer, OMAP_TIMER_LOAD_REG, - timer->context.tldr); - omap_dm_timer_write_reg(timer, OMAP_TIMER_MATCH_REG, - timer->context.tmar); - omap_dm_timer_write_reg(timer, OMAP_TIMER_IF_CTRL_REG, - timer->context.tsicr); - writel_relaxed(timer->context.tier, timer->irq_ena); - omap_dm_timer_write_reg(timer, OMAP_TIMER_CTRL_REG, - timer->context.tclr); -} - -static int omap_dm_timer_reset(struct omap_dm_timer *timer) -{ - u32 l, timeout = 100000; - - if (timer->revision != 1) - return -EINVAL; - - omap_dm_timer_write_reg(timer, OMAP_TIMER_IF_CTRL_REG, 0x06); - - do { - l = __omap_dm_timer_read(timer, - OMAP_TIMER_V1_SYS_STAT_OFFSET, 0); - } while (!l && timeout--); - - if (!timeout) { - dev_err(&timer->pdev->dev, "Timer failed to reset\n"); - return -ETIMEDOUT; - } - - /* Configure timer for smart-idle mode */ - l = __omap_dm_timer_read(timer, OMAP_TIMER_OCP_CFG_OFFSET, 0); - l |= 0x2 << 0x3; - __omap_dm_timer_write(timer, OMAP_TIMER_OCP_CFG_OFFSET, l, 0); - - timer->posted = 0; - - return 0; -} - -static int omap_dm_timer_of_set_source(struct omap_dm_timer *timer) -{ - int ret; - struct clk *parent; - - /* - * FIXME: OMAP1 devices do not use the clock framework for dmtimers so - * do not call clk_get() for these devices. - */ - if (!timer->fclk) - return -ENODEV; - - parent = clk_get(&timer->pdev->dev, NULL); - if (IS_ERR(parent)) - return -ENODEV; - - ret = clk_set_parent(timer->fclk, parent); - if (ret < 0) - pr_err("%s: failed to set parent\n", __func__); - - clk_put(parent); - - return ret; -} - -static int omap_dm_timer_prepare(struct omap_dm_timer *timer) -{ - int rc; - - /* - * FIXME: OMAP1 devices do not use the clock framework for dmtimers so - * do not call clk_get() for these devices. - */ - if (!(timer->capability & OMAP_TIMER_NEEDS_RESET)) { - timer->fclk = clk_get(&timer->pdev->dev, "fck"); - if (WARN_ON_ONCE(IS_ERR(timer->fclk))) { - dev_err(&timer->pdev->dev, ": No fclk handle.\n"); - return -EINVAL; - } - } - - omap_dm_timer_enable(timer); - - if (timer->capability & OMAP_TIMER_NEEDS_RESET) { - rc = omap_dm_timer_reset(timer); - if (rc) { - omap_dm_timer_disable(timer); - return rc; - } - } - - __omap_dm_timer_enable_posted(timer); - omap_dm_timer_disable(timer); - - rc = omap_dm_timer_of_set_source(timer); - if (rc == -ENODEV) - return omap_dm_timer_set_source(timer, OMAP_TIMER_SRC_32_KHZ); - - return rc; -} - -static inline u32 omap_dm_timer_reserved_systimer(int id) -{ - return (omap_reserved_systimers & (1 << (id - 1))) ? 1 : 0; -} - -int omap_dm_timer_reserve_systimer(int id) -{ - if (omap_dm_timer_reserved_systimer(id)) - return -ENODEV; - - omap_reserved_systimers |= (1 << (id - 1)); - - return 0; -} - -static struct omap_dm_timer *_omap_dm_timer_request(int req_type, void *data) -{ - struct omap_dm_timer *timer = NULL, *t; - struct device_node *np = NULL; - unsigned long flags; - u32 cap = 0; - int id = 0; - - switch (req_type) { - case REQUEST_BY_ID: - id = *(int *)data; - break; - case REQUEST_BY_CAP: - cap = *(u32 *)data; - break; - case REQUEST_BY_NODE: - np = (struct device_node *)data; - break; - default: - /* REQUEST_ANY */ - break; - } - - spin_lock_irqsave(&dm_timer_lock, flags); - list_for_each_entry(t, &omap_timer_list, node) { - if (t->reserved) - continue; - - switch (req_type) { - case REQUEST_BY_ID: - if (id == t->pdev->id) { - timer = t; - timer->reserved = 1; - goto found; - } - break; - case REQUEST_BY_CAP: - if (cap == (t->capability & cap)) { - /* - * If timer is not NULL, we have already found - * one timer. But it was not an exact match - * because it had more capabilities than what - * was required. Therefore, unreserve the last - * timer found and see if this one is a better - * match. - */ - if (timer) - timer->reserved = 0; - timer = t; - timer->reserved = 1; - - /* Exit loop early if we find an exact match */ - if (t->capability == cap) - goto found; - } - break; - case REQUEST_BY_NODE: - if (np == t->pdev->dev.of_node) { - timer = t; - timer->reserved = 1; - goto found; - } - break; - default: - /* REQUEST_ANY */ - timer = t; - timer->reserved = 1; - goto found; - } - } -found: - spin_unlock_irqrestore(&dm_timer_lock, flags); - - if (timer && omap_dm_timer_prepare(timer)) { - timer->reserved = 0; - timer = NULL; - } - - if (!timer) - pr_debug("%s: timer request failed!\n", __func__); - - return timer; -} - -struct omap_dm_timer *omap_dm_timer_request(void) -{ - return _omap_dm_timer_request(REQUEST_ANY, NULL); -} - -struct omap_dm_timer *omap_dm_timer_request_specific(int id) -{ - /* Requesting timer by ID is not supported when device tree is used */ - if (of_have_populated_dt()) { - pr_warn("%s: Please use omap_dm_timer_request_by_cap/node()\n", - __func__); - return NULL; - } - - return _omap_dm_timer_request(REQUEST_BY_ID, &id); -} - -/** - * omap_dm_timer_request_by_cap - Request a timer by capability - * @cap: Bit mask of capabilities to match - * - * Find a timer based upon capabilities bit mask. Callers of this function - * should use the definitions found in the plat/dmtimer.h file under the - * comment "timer capabilities used in hwmod database". Returns pointer to - * timer handle on success and a NULL pointer on failure. - */ -struct omap_dm_timer *omap_dm_timer_request_by_cap(u32 cap) -{ - return _omap_dm_timer_request(REQUEST_BY_CAP, &cap); -} - -/** - * omap_dm_timer_request_by_node - Request a timer by device-tree node - * @np: Pointer to device-tree timer node - * - * Request a timer based upon a device node pointer. Returns pointer to - * timer handle on success and a NULL pointer on failure. - */ -struct omap_dm_timer *omap_dm_timer_request_by_node(struct device_node *np) -{ - if (!np) - return NULL; - - return _omap_dm_timer_request(REQUEST_BY_NODE, np); -} - -int omap_dm_timer_free(struct omap_dm_timer *timer) -{ - if (unlikely(!timer)) - return -EINVAL; - - clk_put(timer->fclk); - - WARN_ON(!timer->reserved); - timer->reserved = 0; - return 0; -} - -void omap_dm_timer_enable(struct omap_dm_timer *timer) -{ - int c; - - pm_runtime_get_sync(&timer->pdev->dev); - - if (!(timer->capability & OMAP_TIMER_ALWON)) { - if (timer->get_context_loss_count) { - c = timer->get_context_loss_count(&timer->pdev->dev); - if (c != timer->ctx_loss_count) { - omap_timer_restore_context(timer); - timer->ctx_loss_count = c; - } - } else { - omap_timer_restore_context(timer); - } - } -} - -void omap_dm_timer_disable(struct omap_dm_timer *timer) -{ - pm_runtime_put_sync(&timer->pdev->dev); -} - -int omap_dm_timer_get_irq(struct omap_dm_timer *timer) -{ - if (timer) - return timer->irq; - return -EINVAL; -} - -#if defined(CONFIG_ARCH_OMAP1) -#include -/** - * omap_dm_timer_modify_idlect_mask - Check if any running timers use ARMXOR - * @inputmask: current value of idlect mask - */ -__u32 omap_dm_timer_modify_idlect_mask(__u32 inputmask) -{ - int i = 0; - struct omap_dm_timer *timer = NULL; - unsigned long flags; - - /* If ARMXOR cannot be idled this function call is unnecessary */ - if (!(inputmask & (1 << 1))) - return inputmask; - - /* If any active timer is using ARMXOR return modified mask */ - spin_lock_irqsave(&dm_timer_lock, flags); - list_for_each_entry(timer, &omap_timer_list, node) { - u32 l; - - l = omap_dm_timer_read_reg(timer, OMAP_TIMER_CTRL_REG); - if (l & OMAP_TIMER_CTRL_ST) { - if (((omap_readl(MOD_CONF_CTRL_1) >> (i * 2)) & 0x03) == 0) - inputmask &= ~(1 << 1); - else - inputmask &= ~(1 << 2); - } - i++; - } - spin_unlock_irqrestore(&dm_timer_lock, flags); - - return inputmask; -} - -#else - -struct clk *omap_dm_timer_get_fclk(struct omap_dm_timer *timer) -{ - if (timer && !IS_ERR(timer->fclk)) - return timer->fclk; - return NULL; -} - -__u32 omap_dm_timer_modify_idlect_mask(__u32 inputmask) -{ - BUG(); - - return 0; -} - -#endif - -int omap_dm_timer_trigger(struct omap_dm_timer *timer) -{ - if (unlikely(!timer || pm_runtime_suspended(&timer->pdev->dev))) { - pr_err("%s: timer not available or enabled.\n", __func__); - return -EINVAL; - } - - omap_dm_timer_write_reg(timer, OMAP_TIMER_TRIGGER_REG, 0); - return 0; -} - -int omap_dm_timer_start(struct omap_dm_timer *timer) -{ - u32 l; - - if (unlikely(!timer)) - return -EINVAL; - - omap_dm_timer_enable(timer); - - l = omap_dm_timer_read_reg(timer, OMAP_TIMER_CTRL_REG); - if (!(l & OMAP_TIMER_CTRL_ST)) { - l |= OMAP_TIMER_CTRL_ST; - omap_dm_timer_write_reg(timer, OMAP_TIMER_CTRL_REG, l); - } - - /* Save the context */ - timer->context.tclr = l; - return 0; -} - -int omap_dm_timer_stop(struct omap_dm_timer *timer) -{ - unsigned long rate = 0; - - if (unlikely(!timer)) - return -EINVAL; - - if (!(timer->capability & OMAP_TIMER_NEEDS_RESET)) - rate = clk_get_rate(timer->fclk); - - __omap_dm_timer_stop(timer, timer->posted, rate); - - /* - * Since the register values are computed and written within - * __omap_dm_timer_stop, we need to use read to retrieve the - * context. - */ - timer->context.tclr = - omap_dm_timer_read_reg(timer, OMAP_TIMER_CTRL_REG); - omap_dm_timer_disable(timer); - return 0; -} - -int omap_dm_timer_set_source(struct omap_dm_timer *timer, int source) -{ - int ret; - char *parent_name = NULL; - struct clk *parent; - struct dmtimer_platform_data *pdata; - - if (unlikely(!timer)) - return -EINVAL; - - pdata = timer->pdev->dev.platform_data; - - if (source < 0 || source >= 3) - return -EINVAL; - - /* - * FIXME: Used for OMAP1 devices only because they do not currently - * use the clock framework to set the parent clock. To be removed - * once OMAP1 migrated to using clock framework for dmtimers - */ - if (pdata && pdata->set_timer_src) - return pdata->set_timer_src(timer->pdev, source); - - if (IS_ERR(timer->fclk)) - return -EINVAL; - -#if defined(CONFIG_COMMON_CLK) - /* Check if the clock has configurable parents */ - if (clk_hw_get_num_parents(__clk_get_hw(timer->fclk)) < 2) - return 0; -#endif - - switch (source) { - case OMAP_TIMER_SRC_SYS_CLK: - parent_name = "timer_sys_ck"; - break; - - case OMAP_TIMER_SRC_32_KHZ: - parent_name = "timer_32k_ck"; - break; - - case OMAP_TIMER_SRC_EXT_CLK: - parent_name = "timer_ext_ck"; - break; - } - - parent = clk_get(&timer->pdev->dev, parent_name); - if (IS_ERR(parent)) { - pr_err("%s: %s not found\n", __func__, parent_name); - return -EINVAL; - } - - ret = clk_set_parent(timer->fclk, parent); - if (ret < 0) - pr_err("%s: failed to set %s as parent\n", __func__, - parent_name); - - clk_put(parent); - - return ret; -} - -int omap_dm_timer_set_load(struct omap_dm_timer *timer, int autoreload, - unsigned int load) -{ - u32 l; - - if (unlikely(!timer)) - return -EINVAL; - - omap_dm_timer_enable(timer); - l = omap_dm_timer_read_reg(timer, OMAP_TIMER_CTRL_REG); - if (autoreload) - l |= OMAP_TIMER_CTRL_AR; - else - l &= ~OMAP_TIMER_CTRL_AR; - omap_dm_timer_write_reg(timer, OMAP_TIMER_CTRL_REG, l); - omap_dm_timer_write_reg(timer, OMAP_TIMER_LOAD_REG, load); - - omap_dm_timer_write_reg(timer, OMAP_TIMER_TRIGGER_REG, 0); - /* Save the context */ - timer->context.tclr = l; - timer->context.tldr = load; - omap_dm_timer_disable(timer); - return 0; -} - -/* Optimized set_load which removes costly spin wait in timer_start */ -int omap_dm_timer_set_load_start(struct omap_dm_timer *timer, int autoreload, - unsigned int load) -{ - u32 l; - - if (unlikely(!timer)) - return -EINVAL; - - omap_dm_timer_enable(timer); - - l = omap_dm_timer_read_reg(timer, OMAP_TIMER_CTRL_REG); - if (autoreload) { - l |= OMAP_TIMER_CTRL_AR; - omap_dm_timer_write_reg(timer, OMAP_TIMER_LOAD_REG, load); - } else { - l &= ~OMAP_TIMER_CTRL_AR; - } - l |= OMAP_TIMER_CTRL_ST; - - __omap_dm_timer_load_start(timer, l, load, timer->posted); - - /* Save the context */ - timer->context.tclr = l; - timer->context.tldr = load; - timer->context.tcrr = load; - return 0; -} - -int omap_dm_timer_set_match(struct omap_dm_timer *timer, int enable, - unsigned int match) -{ - u32 l; - - if (unlikely(!timer)) - return -EINVAL; - - omap_dm_timer_enable(timer); - l = omap_dm_timer_read_reg(timer, OMAP_TIMER_CTRL_REG); - if (enable) - l |= OMAP_TIMER_CTRL_CE; - else - l &= ~OMAP_TIMER_CTRL_CE; - omap_dm_timer_write_reg(timer, OMAP_TIMER_MATCH_REG, match); - omap_dm_timer_write_reg(timer, OMAP_TIMER_CTRL_REG, l); - - /* Save the context */ - timer->context.tclr = l; - timer->context.tmar = match; - omap_dm_timer_disable(timer); - return 0; -} - -int omap_dm_timer_set_pwm(struct omap_dm_timer *timer, int def_on, - int toggle, int trigger) -{ - u32 l; - - if (unlikely(!timer)) - return -EINVAL; - - omap_dm_timer_enable(timer); - l = omap_dm_timer_read_reg(timer, OMAP_TIMER_CTRL_REG); - l &= ~(OMAP_TIMER_CTRL_GPOCFG | OMAP_TIMER_CTRL_SCPWM | - OMAP_TIMER_CTRL_PT | (0x03 << 10)); - if (def_on) - l |= OMAP_TIMER_CTRL_SCPWM; - if (toggle) - l |= OMAP_TIMER_CTRL_PT; - l |= trigger << 10; - omap_dm_timer_write_reg(timer, OMAP_TIMER_CTRL_REG, l); - - /* Save the context */ - timer->context.tclr = l; - omap_dm_timer_disable(timer); - return 0; -} - -int omap_dm_timer_set_prescaler(struct omap_dm_timer *timer, int prescaler) -{ - u32 l; - - if (unlikely(!timer)) - return -EINVAL; - - omap_dm_timer_enable(timer); - l = omap_dm_timer_read_reg(timer, OMAP_TIMER_CTRL_REG); - l &= ~(OMAP_TIMER_CTRL_PRE | (0x07 << 2)); - if (prescaler >= 0x00 && prescaler <= 0x07) { - l |= OMAP_TIMER_CTRL_PRE; - l |= prescaler << 2; - } - omap_dm_timer_write_reg(timer, OMAP_TIMER_CTRL_REG, l); - - /* Save the context */ - timer->context.tclr = l; - omap_dm_timer_disable(timer); - return 0; -} - -int omap_dm_timer_set_int_enable(struct omap_dm_timer *timer, - unsigned int value) -{ - if (unlikely(!timer)) - return -EINVAL; - - omap_dm_timer_enable(timer); - __omap_dm_timer_int_enable(timer, value); - - /* Save the context */ - timer->context.tier = value; - timer->context.twer = value; - omap_dm_timer_disable(timer); - return 0; -} - -/** - * omap_dm_timer_set_int_disable - disable timer interrupts - * @timer: pointer to timer handle - * @mask: bit mask of interrupts to be disabled - * - * Disables the specified timer interrupts for a timer. - */ -int omap_dm_timer_set_int_disable(struct omap_dm_timer *timer, u32 mask) -{ - u32 l = mask; - - if (unlikely(!timer)) - return -EINVAL; - - omap_dm_timer_enable(timer); - - if (timer->revision == 1) - l = readl_relaxed(timer->irq_ena) & ~mask; - - writel_relaxed(l, timer->irq_dis); - l = omap_dm_timer_read_reg(timer, OMAP_TIMER_WAKEUP_EN_REG) & ~mask; - omap_dm_timer_write_reg(timer, OMAP_TIMER_WAKEUP_EN_REG, l); - - /* Save the context */ - timer->context.tier &= ~mask; - timer->context.twer &= ~mask; - omap_dm_timer_disable(timer); - return 0; -} - -unsigned int omap_dm_timer_read_status(struct omap_dm_timer *timer) -{ - unsigned int l; - - if (unlikely(!timer || pm_runtime_suspended(&timer->pdev->dev))) { - pr_err("%s: timer not available or enabled.\n", __func__); - return 0; - } - - l = readl_relaxed(timer->irq_stat); - - return l; -} - -int omap_dm_timer_write_status(struct omap_dm_timer *timer, unsigned int value) -{ - if (unlikely(!timer || pm_runtime_suspended(&timer->pdev->dev))) - return -EINVAL; - - __omap_dm_timer_write_status(timer, value); - - return 0; -} - -unsigned int omap_dm_timer_read_counter(struct omap_dm_timer *timer) -{ - if (unlikely(!timer || pm_runtime_suspended(&timer->pdev->dev))) { - pr_err("%s: timer not iavailable or enabled.\n", __func__); - return 0; - } - - return __omap_dm_timer_read_counter(timer, timer->posted); -} - -int omap_dm_timer_write_counter(struct omap_dm_timer *timer, unsigned int value) -{ - if (unlikely(!timer || pm_runtime_suspended(&timer->pdev->dev))) { - pr_err("%s: timer not available or enabled.\n", __func__); - return -EINVAL; - } - - omap_dm_timer_write_reg(timer, OMAP_TIMER_COUNTER_REG, value); - - /* Save the context */ - timer->context.tcrr = value; - return 0; -} - -int omap_dm_timers_active(void) -{ - struct omap_dm_timer *timer; - - list_for_each_entry(timer, &omap_timer_list, node) { - if (!timer->reserved) - continue; - - if (omap_dm_timer_read_reg(timer, OMAP_TIMER_CTRL_REG) & - OMAP_TIMER_CTRL_ST) { - return 1; - } - } - return 0; -} - -static const struct of_device_id omap_timer_match[]; - -/** - * omap_dm_timer_probe - probe function called for every registered device - * @pdev: pointer to current timer platform device - * - * Called by driver framework at the end of device registration for all - * timer devices. - */ -static int omap_dm_timer_probe(struct platform_device *pdev) -{ - unsigned long flags; - struct omap_dm_timer *timer; - struct resource *mem, *irq; - struct device *dev = &pdev->dev; - const struct of_device_id *match; - const struct dmtimer_platform_data *pdata; - int ret; - - match = of_match_device(of_match_ptr(omap_timer_match), dev); - pdata = match ? match->data : dev->platform_data; - - if (!pdata && !dev->of_node) { - dev_err(dev, "%s: no platform data.\n", __func__); - return -ENODEV; - } - - irq = platform_get_resource(pdev, IORESOURCE_IRQ, 0); - if (unlikely(!irq)) { - dev_err(dev, "%s: no IRQ resource.\n", __func__); - return -ENODEV; - } - - mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (unlikely(!mem)) { - dev_err(dev, "%s: no memory resource.\n", __func__); - return -ENODEV; - } - - timer = devm_kzalloc(dev, sizeof(*timer), GFP_KERNEL); - if (!timer) - return -ENOMEM; - - timer->fclk = ERR_PTR(-ENODEV); - timer->io_base = devm_ioremap_resource(dev, mem); - if (IS_ERR(timer->io_base)) - return PTR_ERR(timer->io_base); - - if (dev->of_node) { - if (of_find_property(dev->of_node, "ti,timer-alwon", NULL)) - timer->capability |= OMAP_TIMER_ALWON; - if (of_find_property(dev->of_node, "ti,timer-dsp", NULL)) - timer->capability |= OMAP_TIMER_HAS_DSP_IRQ; - if (of_find_property(dev->of_node, "ti,timer-pwm", NULL)) - timer->capability |= OMAP_TIMER_HAS_PWM; - if (of_find_property(dev->of_node, "ti,timer-secure", NULL)) - timer->capability |= OMAP_TIMER_SECURE; - } else { - timer->id = pdev->id; - timer->capability = pdata->timer_capability; - timer->reserved = omap_dm_timer_reserved_systimer(timer->id); - timer->get_context_loss_count = pdata->get_context_loss_count; - } - - if (pdata) - timer->errata = pdata->timer_errata; - - timer->irq = irq->start; - timer->pdev = pdev; - - pm_runtime_enable(dev); - pm_runtime_irq_safe(dev); - - if (!timer->reserved) { - ret = pm_runtime_get_sync(dev); - if (ret < 0) { - dev_err(dev, "%s: pm_runtime_get_sync failed!\n", - __func__); - goto err_get_sync; - } - __omap_dm_timer_init_regs(timer); - pm_runtime_put(dev); - } - - /* add the timer element to the list */ - spin_lock_irqsave(&dm_timer_lock, flags); - list_add_tail(&timer->node, &omap_timer_list); - spin_unlock_irqrestore(&dm_timer_lock, flags); - - dev_dbg(dev, "Device Probed.\n"); - - return 0; - -err_get_sync: - pm_runtime_put_noidle(dev); - pm_runtime_disable(dev); - return ret; -} - -/** - * omap_dm_timer_remove - cleanup a registered timer device - * @pdev: pointer to current timer platform device - * - * Called by driver framework whenever a timer device is unregistered. - * In addition to freeing platform resources it also deletes the timer - * entry from the local list. - */ -static int omap_dm_timer_remove(struct platform_device *pdev) -{ - struct omap_dm_timer *timer; - unsigned long flags; - int ret = -EINVAL; - - spin_lock_irqsave(&dm_timer_lock, flags); - list_for_each_entry(timer, &omap_timer_list, node) - if (!strcmp(dev_name(&timer->pdev->dev), - dev_name(&pdev->dev))) { - list_del(&timer->node); - ret = 0; - break; - } - spin_unlock_irqrestore(&dm_timer_lock, flags); - - pm_runtime_disable(&pdev->dev); - - return ret; -} - -static const struct dmtimer_platform_data omap3plus_pdata = { - .timer_errata = OMAP_TIMER_ERRATA_I103_I767, -}; - -static const struct of_device_id omap_timer_match[] = { - { - .compatible = "ti,omap2420-timer", - }, - { - .compatible = "ti,omap3430-timer", - .data = &omap3plus_pdata, - }, - { - .compatible = "ti,omap4430-timer", - .data = &omap3plus_pdata, - }, - { - .compatible = "ti,omap5430-timer", - .data = &omap3plus_pdata, - }, - { - .compatible = "ti,am335x-timer", - .data = &omap3plus_pdata, - }, - { - .compatible = "ti,am335x-timer-1ms", - .data = &omap3plus_pdata, - }, - { - .compatible = "ti,dm816-timer", - .data = &omap3plus_pdata, - }, - {}, -}; -MODULE_DEVICE_TABLE(of, omap_timer_match); - -static struct platform_driver omap_dm_timer_driver = { - .probe = omap_dm_timer_probe, - .remove = omap_dm_timer_remove, - .driver = { - .name = "omap_timer", - .of_match_table = of_match_ptr(omap_timer_match), - }, -}; - -early_platform_init("earlytimer", &omap_dm_timer_driver); -module_platform_driver(omap_dm_timer_driver); - -MODULE_DESCRIPTION("OMAP Dual-Mode Timer Driver"); -MODULE_LICENSE("GPL"); -MODULE_ALIAS("platform:" DRIVER_NAME); -MODULE_AUTHOR("Texas Instruments Inc"); diff --git a/drivers/clocksource/Kconfig b/drivers/clocksource/Kconfig index b3b4ed9b6874..5995086ec328 100644 --- a/drivers/clocksource/Kconfig +++ b/drivers/clocksource/Kconfig @@ -21,6 +21,9 @@ config CLKEVT_I8253 config I8253_LOCK bool +config OMAP_DM_TIMER + bool + config CLKBLD_I8253 def_bool y if CLKSRC_I8253 || CLKEVT_I8253 || I8253_LOCK diff --git a/drivers/clocksource/Makefile b/drivers/clocksource/Makefile index d6dec4489d66..c8c5109f2e50 100644 --- a/drivers/clocksource/Makefile +++ b/drivers/clocksource/Makefile @@ -16,6 +16,7 @@ obj-$(CONFIG_EM_TIMER_STI) += em_sti.o obj-$(CONFIG_CLKBLD_I8253) += i8253.o obj-$(CONFIG_CLKSRC_MMIO) += mmio.o obj-$(CONFIG_DIGICOLOR_TIMER) += timer-digicolor.o +obj-$(CONFIG_OMAP_DM_TIMER) += timer-ti-dm.o obj-$(CONFIG_DW_APB_TIMER) += dw_apb_timer.o obj-$(CONFIG_DW_APB_TIMER_OF) += dw_apb_timer_of.o obj-$(CONFIG_FTTMR010_TIMER) += timer-fttmr010.o diff --git a/drivers/clocksource/timer-ti-dm.c b/drivers/clocksource/timer-ti-dm.c new file mode 100644 index 000000000000..346898809276 --- /dev/null +++ b/drivers/clocksource/timer-ti-dm.c @@ -0,0 +1,973 @@ +/* + * linux/arch/arm/plat-omap/dmtimer.c + * + * OMAP Dual-Mode Timers + * + * Copyright (C) 2010 Texas Instruments Incorporated - http://www.ti.com/ + * Tarun Kanti DebBarma + * Thara Gopinath + * + * dmtimer adaptation to platform_driver. + * + * Copyright (C) 2005 Nokia Corporation + * OMAP2 support by Juha Yrjola + * API improvements and OMAP2 clock framework support by Timo Teras + * + * Copyright (C) 2009 Texas Instruments + * Added OMAP4 support - 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; either version 2 of the License, or (at your + * option) any later version. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN + * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * 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., + * 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +static u32 omap_reserved_systimers; +static LIST_HEAD(omap_timer_list); +static DEFINE_SPINLOCK(dm_timer_lock); + +enum { + REQUEST_ANY = 0, + REQUEST_BY_ID, + REQUEST_BY_CAP, + REQUEST_BY_NODE, +}; + +/** + * omap_dm_timer_read_reg - read timer registers in posted and non-posted mode + * @timer: timer pointer over which read operation to perform + * @reg: lowest byte holds the register offset + * + * The posted mode bit is encoded in reg. Note that in posted mode write + * pending bit must be checked. Otherwise a read of a non completed write + * will produce an error. + */ +static inline u32 omap_dm_timer_read_reg(struct omap_dm_timer *timer, u32 reg) +{ + WARN_ON((reg & 0xff) < _OMAP_TIMER_WAKEUP_EN_OFFSET); + return __omap_dm_timer_read(timer, reg, timer->posted); +} + +/** + * omap_dm_timer_write_reg - write timer registers in posted and non-posted mode + * @timer: timer pointer over which write operation is to perform + * @reg: lowest byte holds the register offset + * @value: data to write into the register + * + * The posted mode bit is encoded in reg. Note that in posted mode the write + * pending bit must be checked. Otherwise a write on a register which has a + * pending write will be lost. + */ +static void omap_dm_timer_write_reg(struct omap_dm_timer *timer, u32 reg, + u32 value) +{ + WARN_ON((reg & 0xff) < _OMAP_TIMER_WAKEUP_EN_OFFSET); + __omap_dm_timer_write(timer, reg, value, timer->posted); +} + +static void omap_timer_restore_context(struct omap_dm_timer *timer) +{ + omap_dm_timer_write_reg(timer, OMAP_TIMER_WAKEUP_EN_REG, + timer->context.twer); + omap_dm_timer_write_reg(timer, OMAP_TIMER_COUNTER_REG, + timer->context.tcrr); + omap_dm_timer_write_reg(timer, OMAP_TIMER_LOAD_REG, + timer->context.tldr); + omap_dm_timer_write_reg(timer, OMAP_TIMER_MATCH_REG, + timer->context.tmar); + omap_dm_timer_write_reg(timer, OMAP_TIMER_IF_CTRL_REG, + timer->context.tsicr); + writel_relaxed(timer->context.tier, timer->irq_ena); + omap_dm_timer_write_reg(timer, OMAP_TIMER_CTRL_REG, + timer->context.tclr); +} + +static int omap_dm_timer_reset(struct omap_dm_timer *timer) +{ + u32 l, timeout = 100000; + + if (timer->revision != 1) + return -EINVAL; + + omap_dm_timer_write_reg(timer, OMAP_TIMER_IF_CTRL_REG, 0x06); + + do { + l = __omap_dm_timer_read(timer, + OMAP_TIMER_V1_SYS_STAT_OFFSET, 0); + } while (!l && timeout--); + + if (!timeout) { + dev_err(&timer->pdev->dev, "Timer failed to reset\n"); + return -ETIMEDOUT; + } + + /* Configure timer for smart-idle mode */ + l = __omap_dm_timer_read(timer, OMAP_TIMER_OCP_CFG_OFFSET, 0); + l |= 0x2 << 0x3; + __omap_dm_timer_write(timer, OMAP_TIMER_OCP_CFG_OFFSET, l, 0); + + timer->posted = 0; + + return 0; +} + +static int omap_dm_timer_of_set_source(struct omap_dm_timer *timer) +{ + int ret; + struct clk *parent; + + /* + * FIXME: OMAP1 devices do not use the clock framework for dmtimers so + * do not call clk_get() for these devices. + */ + if (!timer->fclk) + return -ENODEV; + + parent = clk_get(&timer->pdev->dev, NULL); + if (IS_ERR(parent)) + return -ENODEV; + + ret = clk_set_parent(timer->fclk, parent); + if (ret < 0) + pr_err("%s: failed to set parent\n", __func__); + + clk_put(parent); + + return ret; +} + +static int omap_dm_timer_prepare(struct omap_dm_timer *timer) +{ + int rc; + + /* + * FIXME: OMAP1 devices do not use the clock framework for dmtimers so + * do not call clk_get() for these devices. + */ + if (!(timer->capability & OMAP_TIMER_NEEDS_RESET)) { + timer->fclk = clk_get(&timer->pdev->dev, "fck"); + if (WARN_ON_ONCE(IS_ERR(timer->fclk))) { + dev_err(&timer->pdev->dev, ": No fclk handle.\n"); + return -EINVAL; + } + } + + omap_dm_timer_enable(timer); + + if (timer->capability & OMAP_TIMER_NEEDS_RESET) { + rc = omap_dm_timer_reset(timer); + if (rc) { + omap_dm_timer_disable(timer); + return rc; + } + } + + __omap_dm_timer_enable_posted(timer); + omap_dm_timer_disable(timer); + + rc = omap_dm_timer_of_set_source(timer); + if (rc == -ENODEV) + return omap_dm_timer_set_source(timer, OMAP_TIMER_SRC_32_KHZ); + + return rc; +} + +static inline u32 omap_dm_timer_reserved_systimer(int id) +{ + return (omap_reserved_systimers & (1 << (id - 1))) ? 1 : 0; +} + +int omap_dm_timer_reserve_systimer(int id) +{ + if (omap_dm_timer_reserved_systimer(id)) + return -ENODEV; + + omap_reserved_systimers |= (1 << (id - 1)); + + return 0; +} + +static struct omap_dm_timer *_omap_dm_timer_request(int req_type, void *data) +{ + struct omap_dm_timer *timer = NULL, *t; + struct device_node *np = NULL; + unsigned long flags; + u32 cap = 0; + int id = 0; + + switch (req_type) { + case REQUEST_BY_ID: + id = *(int *)data; + break; + case REQUEST_BY_CAP: + cap = *(u32 *)data; + break; + case REQUEST_BY_NODE: + np = (struct device_node *)data; + break; + default: + /* REQUEST_ANY */ + break; + } + + spin_lock_irqsave(&dm_timer_lock, flags); + list_for_each_entry(t, &omap_timer_list, node) { + if (t->reserved) + continue; + + switch (req_type) { + case REQUEST_BY_ID: + if (id == t->pdev->id) { + timer = t; + timer->reserved = 1; + goto found; + } + break; + case REQUEST_BY_CAP: + if (cap == (t->capability & cap)) { + /* + * If timer is not NULL, we have already found + * one timer. But it was not an exact match + * because it had more capabilities than what + * was required. Therefore, unreserve the last + * timer found and see if this one is a better + * match. + */ + if (timer) + timer->reserved = 0; + timer = t; + timer->reserved = 1; + + /* Exit loop early if we find an exact match */ + if (t->capability == cap) + goto found; + } + break; + case REQUEST_BY_NODE: + if (np == t->pdev->dev.of_node) { + timer = t; + timer->reserved = 1; + goto found; + } + break; + default: + /* REQUEST_ANY */ + timer = t; + timer->reserved = 1; + goto found; + } + } +found: + spin_unlock_irqrestore(&dm_timer_lock, flags); + + if (timer && omap_dm_timer_prepare(timer)) { + timer->reserved = 0; + timer = NULL; + } + + if (!timer) + pr_debug("%s: timer request failed!\n", __func__); + + return timer; +} + +struct omap_dm_timer *omap_dm_timer_request(void) +{ + return _omap_dm_timer_request(REQUEST_ANY, NULL); +} + +struct omap_dm_timer *omap_dm_timer_request_specific(int id) +{ + /* Requesting timer by ID is not supported when device tree is used */ + if (of_have_populated_dt()) { + pr_warn("%s: Please use omap_dm_timer_request_by_cap/node()\n", + __func__); + return NULL; + } + + return _omap_dm_timer_request(REQUEST_BY_ID, &id); +} + +/** + * omap_dm_timer_request_by_cap - Request a timer by capability + * @cap: Bit mask of capabilities to match + * + * Find a timer based upon capabilities bit mask. Callers of this function + * should use the definitions found in the plat/dmtimer.h file under the + * comment "timer capabilities used in hwmod database". Returns pointer to + * timer handle on success and a NULL pointer on failure. + */ +struct omap_dm_timer *omap_dm_timer_request_by_cap(u32 cap) +{ + return _omap_dm_timer_request(REQUEST_BY_CAP, &cap); +} + +/** + * omap_dm_timer_request_by_node - Request a timer by device-tree node + * @np: Pointer to device-tree timer node + * + * Request a timer based upon a device node pointer. Returns pointer to + * timer handle on success and a NULL pointer on failure. + */ +struct omap_dm_timer *omap_dm_timer_request_by_node(struct device_node *np) +{ + if (!np) + return NULL; + + return _omap_dm_timer_request(REQUEST_BY_NODE, np); +} + +int omap_dm_timer_free(struct omap_dm_timer *timer) +{ + if (unlikely(!timer)) + return -EINVAL; + + clk_put(timer->fclk); + + WARN_ON(!timer->reserved); + timer->reserved = 0; + return 0; +} + +void omap_dm_timer_enable(struct omap_dm_timer *timer) +{ + int c; + + pm_runtime_get_sync(&timer->pdev->dev); + + if (!(timer->capability & OMAP_TIMER_ALWON)) { + if (timer->get_context_loss_count) { + c = timer->get_context_loss_count(&timer->pdev->dev); + if (c != timer->ctx_loss_count) { + omap_timer_restore_context(timer); + timer->ctx_loss_count = c; + } + } else { + omap_timer_restore_context(timer); + } + } +} + +void omap_dm_timer_disable(struct omap_dm_timer *timer) +{ + pm_runtime_put_sync(&timer->pdev->dev); +} + +int omap_dm_timer_get_irq(struct omap_dm_timer *timer) +{ + if (timer) + return timer->irq; + return -EINVAL; +} + +#if defined(CONFIG_ARCH_OMAP1) +#include +/** + * omap_dm_timer_modify_idlect_mask - Check if any running timers use ARMXOR + * @inputmask: current value of idlect mask + */ +__u32 omap_dm_timer_modify_idlect_mask(__u32 inputmask) +{ + int i = 0; + struct omap_dm_timer *timer = NULL; + unsigned long flags; + + /* If ARMXOR cannot be idled this function call is unnecessary */ + if (!(inputmask & (1 << 1))) + return inputmask; + + /* If any active timer is using ARMXOR return modified mask */ + spin_lock_irqsave(&dm_timer_lock, flags); + list_for_each_entry(timer, &omap_timer_list, node) { + u32 l; + + l = omap_dm_timer_read_reg(timer, OMAP_TIMER_CTRL_REG); + if (l & OMAP_TIMER_CTRL_ST) { + if (((omap_readl(MOD_CONF_CTRL_1) >> (i * 2)) & 0x03) == 0) + inputmask &= ~(1 << 1); + else + inputmask &= ~(1 << 2); + } + i++; + } + spin_unlock_irqrestore(&dm_timer_lock, flags); + + return inputmask; +} + +#else + +struct clk *omap_dm_timer_get_fclk(struct omap_dm_timer *timer) +{ + if (timer && !IS_ERR(timer->fclk)) + return timer->fclk; + return NULL; +} + +__u32 omap_dm_timer_modify_idlect_mask(__u32 inputmask) +{ + BUG(); + + return 0; +} + +#endif + +int omap_dm_timer_trigger(struct omap_dm_timer *timer) +{ + if (unlikely(!timer || pm_runtime_suspended(&timer->pdev->dev))) { + pr_err("%s: timer not available or enabled.\n", __func__); + return -EINVAL; + } + + omap_dm_timer_write_reg(timer, OMAP_TIMER_TRIGGER_REG, 0); + return 0; +} + +int omap_dm_timer_start(struct omap_dm_timer *timer) +{ + u32 l; + + if (unlikely(!timer)) + return -EINVAL; + + omap_dm_timer_enable(timer); + + l = omap_dm_timer_read_reg(timer, OMAP_TIMER_CTRL_REG); + if (!(l & OMAP_TIMER_CTRL_ST)) { + l |= OMAP_TIMER_CTRL_ST; + omap_dm_timer_write_reg(timer, OMAP_TIMER_CTRL_REG, l); + } + + /* Save the context */ + timer->context.tclr = l; + return 0; +} + +int omap_dm_timer_stop(struct omap_dm_timer *timer) +{ + unsigned long rate = 0; + + if (unlikely(!timer)) + return -EINVAL; + + if (!(timer->capability & OMAP_TIMER_NEEDS_RESET)) + rate = clk_get_rate(timer->fclk); + + __omap_dm_timer_stop(timer, timer->posted, rate); + + /* + * Since the register values are computed and written within + * __omap_dm_timer_stop, we need to use read to retrieve the + * context. + */ + timer->context.tclr = + omap_dm_timer_read_reg(timer, OMAP_TIMER_CTRL_REG); + omap_dm_timer_disable(timer); + return 0; +} + +int omap_dm_timer_set_source(struct omap_dm_timer *timer, int source) +{ + int ret; + char *parent_name = NULL; + struct clk *parent; + struct dmtimer_platform_data *pdata; + + if (unlikely(!timer)) + return -EINVAL; + + pdata = timer->pdev->dev.platform_data; + + if (source < 0 || source >= 3) + return -EINVAL; + + /* + * FIXME: Used for OMAP1 devices only because they do not currently + * use the clock framework to set the parent clock. To be removed + * once OMAP1 migrated to using clock framework for dmtimers + */ + if (pdata && pdata->set_timer_src) + return pdata->set_timer_src(timer->pdev, source); + + if (IS_ERR(timer->fclk)) + return -EINVAL; + +#if defined(CONFIG_COMMON_CLK) + /* Check if the clock has configurable parents */ + if (clk_hw_get_num_parents(__clk_get_hw(timer->fclk)) < 2) + return 0; +#endif + + switch (source) { + case OMAP_TIMER_SRC_SYS_CLK: + parent_name = "timer_sys_ck"; + break; + + case OMAP_TIMER_SRC_32_KHZ: + parent_name = "timer_32k_ck"; + break; + + case OMAP_TIMER_SRC_EXT_CLK: + parent_name = "timer_ext_ck"; + break; + } + + parent = clk_get(&timer->pdev->dev, parent_name); + if (IS_ERR(parent)) { + pr_err("%s: %s not found\n", __func__, parent_name); + return -EINVAL; + } + + ret = clk_set_parent(timer->fclk, parent); + if (ret < 0) + pr_err("%s: failed to set %s as parent\n", __func__, + parent_name); + + clk_put(parent); + + return ret; +} + +int omap_dm_timer_set_load(struct omap_dm_timer *timer, int autoreload, + unsigned int load) +{ + u32 l; + + if (unlikely(!timer)) + return -EINVAL; + + omap_dm_timer_enable(timer); + l = omap_dm_timer_read_reg(timer, OMAP_TIMER_CTRL_REG); + if (autoreload) + l |= OMAP_TIMER_CTRL_AR; + else + l &= ~OMAP_TIMER_CTRL_AR; + omap_dm_timer_write_reg(timer, OMAP_TIMER_CTRL_REG, l); + omap_dm_timer_write_reg(timer, OMAP_TIMER_LOAD_REG, load); + + omap_dm_timer_write_reg(timer, OMAP_TIMER_TRIGGER_REG, 0); + /* Save the context */ + timer->context.tclr = l; + timer->context.tldr = load; + omap_dm_timer_disable(timer); + return 0; +} + +/* Optimized set_load which removes costly spin wait in timer_start */ +int omap_dm_timer_set_load_start(struct omap_dm_timer *timer, int autoreload, + unsigned int load) +{ + u32 l; + + if (unlikely(!timer)) + return -EINVAL; + + omap_dm_timer_enable(timer); + + l = omap_dm_timer_read_reg(timer, OMAP_TIMER_CTRL_REG); + if (autoreload) { + l |= OMAP_TIMER_CTRL_AR; + omap_dm_timer_write_reg(timer, OMAP_TIMER_LOAD_REG, load); + } else { + l &= ~OMAP_TIMER_CTRL_AR; + } + l |= OMAP_TIMER_CTRL_ST; + + __omap_dm_timer_load_start(timer, l, load, timer->posted); + + /* Save the context */ + timer->context.tclr = l; + timer->context.tldr = load; + timer->context.tcrr = load; + return 0; +} + +int omap_dm_timer_set_match(struct omap_dm_timer *timer, int enable, + unsigned int match) +{ + u32 l; + + if (unlikely(!timer)) + return -EINVAL; + + omap_dm_timer_enable(timer); + l = omap_dm_timer_read_reg(timer, OMAP_TIMER_CTRL_REG); + if (enable) + l |= OMAP_TIMER_CTRL_CE; + else + l &= ~OMAP_TIMER_CTRL_CE; + omap_dm_timer_write_reg(timer, OMAP_TIMER_MATCH_REG, match); + omap_dm_timer_write_reg(timer, OMAP_TIMER_CTRL_REG, l); + + /* Save the context */ + timer->context.tclr = l; + timer->context.tmar = match; + omap_dm_timer_disable(timer); + return 0; +} + +int omap_dm_timer_set_pwm(struct omap_dm_timer *timer, int def_on, + int toggle, int trigger) +{ + u32 l; + + if (unlikely(!timer)) + return -EINVAL; + + omap_dm_timer_enable(timer); + l = omap_dm_timer_read_reg(timer, OMAP_TIMER_CTRL_REG); + l &= ~(OMAP_TIMER_CTRL_GPOCFG | OMAP_TIMER_CTRL_SCPWM | + OMAP_TIMER_CTRL_PT | (0x03 << 10)); + if (def_on) + l |= OMAP_TIMER_CTRL_SCPWM; + if (toggle) + l |= OMAP_TIMER_CTRL_PT; + l |= trigger << 10; + omap_dm_timer_write_reg(timer, OMAP_TIMER_CTRL_REG, l); + + /* Save the context */ + timer->context.tclr = l; + omap_dm_timer_disable(timer); + return 0; +} + +int omap_dm_timer_set_prescaler(struct omap_dm_timer *timer, int prescaler) +{ + u32 l; + + if (unlikely(!timer)) + return -EINVAL; + + omap_dm_timer_enable(timer); + l = omap_dm_timer_read_reg(timer, OMAP_TIMER_CTRL_REG); + l &= ~(OMAP_TIMER_CTRL_PRE | (0x07 << 2)); + if (prescaler >= 0x00 && prescaler <= 0x07) { + l |= OMAP_TIMER_CTRL_PRE; + l |= prescaler << 2; + } + omap_dm_timer_write_reg(timer, OMAP_TIMER_CTRL_REG, l); + + /* Save the context */ + timer->context.tclr = l; + omap_dm_timer_disable(timer); + return 0; +} + +int omap_dm_timer_set_int_enable(struct omap_dm_timer *timer, + unsigned int value) +{ + if (unlikely(!timer)) + return -EINVAL; + + omap_dm_timer_enable(timer); + __omap_dm_timer_int_enable(timer, value); + + /* Save the context */ + timer->context.tier = value; + timer->context.twer = value; + omap_dm_timer_disable(timer); + return 0; +} + +/** + * omap_dm_timer_set_int_disable - disable timer interrupts + * @timer: pointer to timer handle + * @mask: bit mask of interrupts to be disabled + * + * Disables the specified timer interrupts for a timer. + */ +int omap_dm_timer_set_int_disable(struct omap_dm_timer *timer, u32 mask) +{ + u32 l = mask; + + if (unlikely(!timer)) + return -EINVAL; + + omap_dm_timer_enable(timer); + + if (timer->revision == 1) + l = readl_relaxed(timer->irq_ena) & ~mask; + + writel_relaxed(l, timer->irq_dis); + l = omap_dm_timer_read_reg(timer, OMAP_TIMER_WAKEUP_EN_REG) & ~mask; + omap_dm_timer_write_reg(timer, OMAP_TIMER_WAKEUP_EN_REG, l); + + /* Save the context */ + timer->context.tier &= ~mask; + timer->context.twer &= ~mask; + omap_dm_timer_disable(timer); + return 0; +} + +unsigned int omap_dm_timer_read_status(struct omap_dm_timer *timer) +{ + unsigned int l; + + if (unlikely(!timer || pm_runtime_suspended(&timer->pdev->dev))) { + pr_err("%s: timer not available or enabled.\n", __func__); + return 0; + } + + l = readl_relaxed(timer->irq_stat); + + return l; +} + +int omap_dm_timer_write_status(struct omap_dm_timer *timer, unsigned int value) +{ + if (unlikely(!timer || pm_runtime_suspended(&timer->pdev->dev))) + return -EINVAL; + + __omap_dm_timer_write_status(timer, value); + + return 0; +} + +unsigned int omap_dm_timer_read_counter(struct omap_dm_timer *timer) +{ + if (unlikely(!timer || pm_runtime_suspended(&timer->pdev->dev))) { + pr_err("%s: timer not iavailable or enabled.\n", __func__); + return 0; + } + + return __omap_dm_timer_read_counter(timer, timer->posted); +} + +int omap_dm_timer_write_counter(struct omap_dm_timer *timer, unsigned int value) +{ + if (unlikely(!timer || pm_runtime_suspended(&timer->pdev->dev))) { + pr_err("%s: timer not available or enabled.\n", __func__); + return -EINVAL; + } + + omap_dm_timer_write_reg(timer, OMAP_TIMER_COUNTER_REG, value); + + /* Save the context */ + timer->context.tcrr = value; + return 0; +} + +int omap_dm_timers_active(void) +{ + struct omap_dm_timer *timer; + + list_for_each_entry(timer, &omap_timer_list, node) { + if (!timer->reserved) + continue; + + if (omap_dm_timer_read_reg(timer, OMAP_TIMER_CTRL_REG) & + OMAP_TIMER_CTRL_ST) { + return 1; + } + } + return 0; +} + +static const struct of_device_id omap_timer_match[]; + +/** + * omap_dm_timer_probe - probe function called for every registered device + * @pdev: pointer to current timer platform device + * + * Called by driver framework at the end of device registration for all + * timer devices. + */ +static int omap_dm_timer_probe(struct platform_device *pdev) +{ + unsigned long flags; + struct omap_dm_timer *timer; + struct resource *mem, *irq; + struct device *dev = &pdev->dev; + const struct of_device_id *match; + const struct dmtimer_platform_data *pdata; + int ret; + + match = of_match_device(of_match_ptr(omap_timer_match), dev); + pdata = match ? match->data : dev->platform_data; + + if (!pdata && !dev->of_node) { + dev_err(dev, "%s: no platform data.\n", __func__); + return -ENODEV; + } + + irq = platform_get_resource(pdev, IORESOURCE_IRQ, 0); + if (unlikely(!irq)) { + dev_err(dev, "%s: no IRQ resource.\n", __func__); + return -ENODEV; + } + + mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (unlikely(!mem)) { + dev_err(dev, "%s: no memory resource.\n", __func__); + return -ENODEV; + } + + timer = devm_kzalloc(dev, sizeof(*timer), GFP_KERNEL); + if (!timer) + return -ENOMEM; + + timer->fclk = ERR_PTR(-ENODEV); + timer->io_base = devm_ioremap_resource(dev, mem); + if (IS_ERR(timer->io_base)) + return PTR_ERR(timer->io_base); + + if (dev->of_node) { + if (of_find_property(dev->of_node, "ti,timer-alwon", NULL)) + timer->capability |= OMAP_TIMER_ALWON; + if (of_find_property(dev->of_node, "ti,timer-dsp", NULL)) + timer->capability |= OMAP_TIMER_HAS_DSP_IRQ; + if (of_find_property(dev->of_node, "ti,timer-pwm", NULL)) + timer->capability |= OMAP_TIMER_HAS_PWM; + if (of_find_property(dev->of_node, "ti,timer-secure", NULL)) + timer->capability |= OMAP_TIMER_SECURE; + } else { + timer->id = pdev->id; + timer->capability = pdata->timer_capability; + timer->reserved = omap_dm_timer_reserved_systimer(timer->id); + timer->get_context_loss_count = pdata->get_context_loss_count; + } + + if (pdata) + timer->errata = pdata->timer_errata; + + timer->irq = irq->start; + timer->pdev = pdev; + + pm_runtime_enable(dev); + pm_runtime_irq_safe(dev); + + if (!timer->reserved) { + ret = pm_runtime_get_sync(dev); + if (ret < 0) { + dev_err(dev, "%s: pm_runtime_get_sync failed!\n", + __func__); + goto err_get_sync; + } + __omap_dm_timer_init_regs(timer); + pm_runtime_put(dev); + } + + /* add the timer element to the list */ + spin_lock_irqsave(&dm_timer_lock, flags); + list_add_tail(&timer->node, &omap_timer_list); + spin_unlock_irqrestore(&dm_timer_lock, flags); + + dev_dbg(dev, "Device Probed.\n"); + + return 0; + +err_get_sync: + pm_runtime_put_noidle(dev); + pm_runtime_disable(dev); + return ret; +} + +/** + * omap_dm_timer_remove - cleanup a registered timer device + * @pdev: pointer to current timer platform device + * + * Called by driver framework whenever a timer device is unregistered. + * In addition to freeing platform resources it also deletes the timer + * entry from the local list. + */ +static int omap_dm_timer_remove(struct platform_device *pdev) +{ + struct omap_dm_timer *timer; + unsigned long flags; + int ret = -EINVAL; + + spin_lock_irqsave(&dm_timer_lock, flags); + list_for_each_entry(timer, &omap_timer_list, node) + if (!strcmp(dev_name(&timer->pdev->dev), + dev_name(&pdev->dev))) { + list_del(&timer->node); + ret = 0; + break; + } + spin_unlock_irqrestore(&dm_timer_lock, flags); + + pm_runtime_disable(&pdev->dev); + + return ret; +} + +static const struct dmtimer_platform_data omap3plus_pdata = { + .timer_errata = OMAP_TIMER_ERRATA_I103_I767, +}; + +static const struct of_device_id omap_timer_match[] = { + { + .compatible = "ti,omap2420-timer", + }, + { + .compatible = "ti,omap3430-timer", + .data = &omap3plus_pdata, + }, + { + .compatible = "ti,omap4430-timer", + .data = &omap3plus_pdata, + }, + { + .compatible = "ti,omap5430-timer", + .data = &omap3plus_pdata, + }, + { + .compatible = "ti,am335x-timer", + .data = &omap3plus_pdata, + }, + { + .compatible = "ti,am335x-timer-1ms", + .data = &omap3plus_pdata, + }, + { + .compatible = "ti,dm816-timer", + .data = &omap3plus_pdata, + }, + {}, +}; +MODULE_DEVICE_TABLE(of, omap_timer_match); + +static struct platform_driver omap_dm_timer_driver = { + .probe = omap_dm_timer_probe, + .remove = omap_dm_timer_remove, + .driver = { + .name = "omap_timer", + .of_match_table = of_match_ptr(omap_timer_match), + }, +}; + +early_platform_init("earlytimer", &omap_dm_timer_driver); +module_platform_driver(omap_dm_timer_driver); + +MODULE_DESCRIPTION("OMAP Dual-Mode Timer Driver"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:" DRIVER_NAME); +MODULE_AUTHOR("Texas Instruments Inc"); -- cgit v1.2.3 From 76234f7c074ca18c0bfe0b5a608973f9b43c7ebd Mon Sep 17 00:00:00 2001 From: Keerthy Date: Thu, 15 Feb 2018 11:31:48 +0530 Subject: clocksource: timer-ti-dm: Populate the timer ops to the pdata Add the timer ops to the platform data Signed-off-by: Keerthy Reviewed-by: Sebastian Reichel Tested-by: Ladislav Michl Signed-off-by: Tony Lindgren --- drivers/clocksource/timer-ti-dm.c | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) (limited to 'drivers') diff --git a/drivers/clocksource/timer-ti-dm.c b/drivers/clocksource/timer-ti-dm.c index 346898809276..4496172b2927 100644 --- a/drivers/clocksource/timer-ti-dm.c +++ b/drivers/clocksource/timer-ti-dm.c @@ -919,8 +919,33 @@ static int omap_dm_timer_remove(struct platform_device *pdev) return ret; } +const static struct omap_dm_timer_ops dmtimer_ops = { + .request_by_node = omap_dm_timer_request_by_node, + .request_specific = omap_dm_timer_request_specific, + .request = omap_dm_timer_request, + .set_source = omap_dm_timer_set_source, + .get_irq = omap_dm_timer_get_irq, + .set_int_enable = omap_dm_timer_set_int_enable, + .set_int_disable = omap_dm_timer_set_int_disable, + .free = omap_dm_timer_free, + .enable = omap_dm_timer_enable, + .disable = omap_dm_timer_disable, + .get_fclk = omap_dm_timer_get_fclk, + .start = omap_dm_timer_start, + .stop = omap_dm_timer_stop, + .set_load = omap_dm_timer_set_load, + .set_match = omap_dm_timer_set_match, + .set_pwm = omap_dm_timer_set_pwm, + .set_prescaler = omap_dm_timer_set_prescaler, + .read_counter = omap_dm_timer_read_counter, + .write_counter = omap_dm_timer_write_counter, + .read_status = omap_dm_timer_read_status, + .write_status = omap_dm_timer_write_status, +}; + static const struct dmtimer_platform_data omap3plus_pdata = { .timer_errata = OMAP_TIMER_ERRATA_I103_I767, + .timer_ops = &dmtimer_ops, }; static const struct of_device_id omap_timer_match[] = { -- cgit v1.2.3 From 1a3acad26613fff7c7590ab32ff21f8c2a10fba9 Mon Sep 17 00:00:00 2001 From: Ladislav Michl Date: Thu, 15 Feb 2018 11:31:49 +0530 Subject: clocksource: timer-ti-dm: Hook device platform data if not already assigned In the case of device tree boot the device platform data is usually NULL so hook the platform data obtained from the match. As part of un-constify the platform_data pointer. Signed-off-by: Ladislav Michl Signed-off-by: Keerthy Reviewed-by: Sebastian Reichel Signed-off-by: Tony Lindgren --- drivers/clocksource/timer-ti-dm.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) (limited to 'drivers') diff --git a/drivers/clocksource/timer-ti-dm.c b/drivers/clocksource/timer-ti-dm.c index 4496172b2927..70782a41c493 100644 --- a/drivers/clocksource/timer-ti-dm.c +++ b/drivers/clocksource/timer-ti-dm.c @@ -806,14 +806,16 @@ static int omap_dm_timer_probe(struct platform_device *pdev) struct omap_dm_timer *timer; struct resource *mem, *irq; struct device *dev = &pdev->dev; - const struct of_device_id *match; const struct dmtimer_platform_data *pdata; int ret; - match = of_match_device(of_match_ptr(omap_timer_match), dev); - pdata = match ? match->data : dev->platform_data; + pdata = of_device_get_match_data(dev); + if (!pdata) + pdata = dev_get_platdata(dev); + else + dev->platform_data = (void *)pdata; - if (!pdata && !dev->of_node) { + if (!pdata) { dev_err(dev, "%s: no platform data.\n", __func__); return -ENODEV; } -- cgit v1.2.3 From b7290cf6ff7869ec12070aa146c370728cab62c2 Mon Sep 17 00:00:00 2001 From: Keerthy Date: Thu, 15 Feb 2018 11:31:50 +0530 Subject: pwm: pwm-omap-dmtimer: Adapt driver to utilize dmtimer pdata ops Adapt driver to utilize dmtimer pdata ops instead of pdata-quirks. Signed-off-by: Keerthy Acked-by: Neil Armstrong Reviewed-by: Claudiu Beznea Signed-off-by: Tony Lindgren --- drivers/pwm/pwm-omap-dmtimer.c | 68 ++++++++++++++++++++++++++---------------- 1 file changed, 42 insertions(+), 26 deletions(-) (limited to 'drivers') diff --git a/drivers/pwm/pwm-omap-dmtimer.c b/drivers/pwm/pwm-omap-dmtimer.c index 5ad42f33e70c..665da3c8fbce 100644 --- a/drivers/pwm/pwm-omap-dmtimer.c +++ b/drivers/pwm/pwm-omap-dmtimer.c @@ -23,6 +23,7 @@ #include #include #include +#include #include #include #include @@ -37,7 +38,7 @@ struct pwm_omap_dmtimer_chip { struct pwm_chip chip; struct mutex mutex; pwm_omap_dmtimer *dm_timer; - struct pwm_omap_dmtimer_pdata *pdata; + const struct omap_dm_timer_ops *pdata; struct platform_device *dm_timer_pdev; }; @@ -242,19 +243,35 @@ static int pwm_omap_dmtimer_probe(struct platform_device *pdev) { struct device_node *np = pdev->dev.of_node; struct device_node *timer; + struct platform_device *timer_pdev; struct pwm_omap_dmtimer_chip *omap; - struct pwm_omap_dmtimer_pdata *pdata; + struct dmtimer_platform_data *timer_pdata; + const struct omap_dm_timer_ops *pdata; pwm_omap_dmtimer *dm_timer; u32 v; - int status; + int ret = 0; - pdata = dev_get_platdata(&pdev->dev); - if (!pdata) { - dev_err(&pdev->dev, "Missing dmtimer platform data\n"); - return -EINVAL; + timer = of_parse_phandle(np, "ti,timers", 0); + if (!timer) + return -ENODEV; + + timer_pdev = of_find_device_by_node(timer); + if (!timer_pdev) { + dev_err(&pdev->dev, "Unable to find Timer pdev\n"); + ret = -ENODEV; + goto put; } - if (!pdata->request_by_node || + timer_pdata = dev_get_platdata(&timer_pdev->dev); + if (!timer_pdata) { + dev_err(&pdev->dev, "dmtimer pdata structure NULL\n"); + ret = -EINVAL; + goto put; + } + + pdata = timer_pdata->timer_ops; + + if (!pdata || !pdata->request_by_node || !pdata->free || !pdata->enable || !pdata->disable || @@ -267,21 +284,26 @@ static int pwm_omap_dmtimer_probe(struct platform_device *pdev) !pdata->set_prescaler || !pdata->write_counter) { dev_err(&pdev->dev, "Incomplete dmtimer pdata structure\n"); - return -EINVAL; + ret = -EINVAL; + goto put; } - timer = of_parse_phandle(np, "ti,timers", 0); - if (!timer) - return -ENODEV; - if (!of_get_property(timer, "ti,timer-pwm", NULL)) { dev_err(&pdev->dev, "Missing ti,timer-pwm capability\n"); - return -ENODEV; + ret = -ENODEV; + goto put; } dm_timer = pdata->request_by_node(timer); - if (!dm_timer) - return -EPROBE_DEFER; + if (!dm_timer) { + ret = -EPROBE_DEFER; + goto put; + } + +put: + of_node_put(timer); + if (ret < 0) + return ret; omap = devm_kzalloc(&pdev->dev, sizeof(*omap), GFP_KERNEL); if (!omap) { @@ -291,13 +313,7 @@ static int pwm_omap_dmtimer_probe(struct platform_device *pdev) omap->pdata = pdata; omap->dm_timer = dm_timer; - - omap->dm_timer_pdev = of_find_device_by_node(timer); - if (!omap->dm_timer_pdev) { - dev_err(&pdev->dev, "Unable to find timer pdev\n"); - omap->pdata->free(dm_timer); - return -EINVAL; - } + omap->dm_timer_pdev = timer_pdev; /* * Ensure that the timer is stopped before we allow PWM core to call @@ -322,11 +338,11 @@ static int pwm_omap_dmtimer_probe(struct platform_device *pdev) mutex_init(&omap->mutex); - status = pwmchip_add(&omap->chip); - if (status < 0) { + ret = pwmchip_add(&omap->chip); + if (ret < 0) { dev_err(&pdev->dev, "failed to register PWM\n"); omap->pdata->free(omap->dm_timer); - return status; + return ret; } platform_set_drvdata(pdev, omap); -- cgit v1.2.3 From bf79cd635db50fab2319add2462cf803ff76d346 Mon Sep 17 00:00:00 2001 From: Jacopo Mondi Date: Tue, 20 Feb 2018 16:12:04 +0100 Subject: soc: renesas: rcar-rst: Add support for R-Car M3-N Signed-off-by: Jacopo Mondi Reviewed-by: Geert Uytterhoeven Signed-off-by: Simon Horman --- Documentation/devicetree/bindings/reset/renesas,rst.txt | 1 + drivers/soc/renesas/Kconfig | 4 ++-- drivers/soc/renesas/rcar-rst.c | 1 + 3 files changed, 4 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/Documentation/devicetree/bindings/reset/renesas,rst.txt b/Documentation/devicetree/bindings/reset/renesas,rst.txt index a55b88658446..294a0dae106a 100644 --- a/Documentation/devicetree/bindings/reset/renesas,rst.txt +++ b/Documentation/devicetree/bindings/reset/renesas,rst.txt @@ -26,6 +26,7 @@ Required properties: - "renesas,r8a7794-rst" (R-Car E2) - "renesas,r8a7795-rst" (R-Car H3) - "renesas,r8a7796-rst" (R-Car M3-W) + - "renesas,r8a77965-rst" (R-Car M3-N) - "renesas,r8a77970-rst" (R-Car V3M) - "renesas,r8a77980-rst" (R-Car V3H) - "renesas,r8a77995-rst" (R-Car D3) diff --git a/drivers/soc/renesas/Kconfig b/drivers/soc/renesas/Kconfig index 7106a6330210..3bbe6114a420 100644 --- a/drivers/soc/renesas/Kconfig +++ b/drivers/soc/renesas/Kconfig @@ -3,8 +3,8 @@ config SOC_RENESAS default y if ARCH_RENESAS select SOC_BUS select RST_RCAR if ARCH_RCAR_GEN1 || ARCH_RCAR_GEN2 || \ - ARCH_R8A7795 || ARCH_R8A7796 || ARCH_R8A77970 || \ - ARCH_R8A77980 || ARCH_R8A77995 + ARCH_R8A7795 || ARCH_R8A7796 || ARCH_R8A77965 || \ + ARCH_R8A77970 || ARCH_R8A77980 || ARCH_R8A77995 select SYSC_R8A7743 if ARCH_R8A7743 select SYSC_R8A7745 if ARCH_R8A7745 select SYSC_R8A7779 if ARCH_R8A7779 diff --git a/drivers/soc/renesas/rcar-rst.c b/drivers/soc/renesas/rcar-rst.c index 34136664ece4..8e9cb7996ab0 100644 --- a/drivers/soc/renesas/rcar-rst.c +++ b/drivers/soc/renesas/rcar-rst.c @@ -56,6 +56,7 @@ static const struct of_device_id rcar_rst_matches[] __initconst = { /* R-Car Gen3 */ { .compatible = "renesas,r8a7795-rst", .data = &rcar_rst_gen3 }, { .compatible = "renesas,r8a7796-rst", .data = &rcar_rst_gen3 }, + { .compatible = "renesas,r8a77965-rst", .data = &rcar_rst_gen3 }, { .compatible = "renesas,r8a77970-rst", .data = &rcar_rst_gen3 }, { .compatible = "renesas,r8a77980-rst", .data = &rcar_rst_gen3 }, { .compatible = "renesas,r8a77995-rst", .data = &rcar_rst_gen3 }, -- cgit v1.2.3 From cc396436c24de8ae28b3c0bee795a19bffea815b Mon Sep 17 00:00:00 2001 From: Miquel Raynal Date: Mon, 19 Feb 2018 23:35:53 +0100 Subject: mtd: nand: remove deprecated pxa3xx_nand driver All board files and defconfig files have been moved to use the new marvell_nand driver instead of pxa3xx_nand, so we can safely remove this file now. People should use the new driver which is supposed to behave exactly like the old one. Signed-off-by: Miquel Raynal Signed-off-by: Boris Brezillon --- drivers/mtd/nand/Kconfig | 11 - drivers/mtd/nand/Makefile | 1 - drivers/mtd/nand/pxa3xx_nand.c | 2105 ---------------------------------------- 3 files changed, 2117 deletions(-) delete mode 100644 drivers/mtd/nand/pxa3xx_nand.c (limited to 'drivers') diff --git a/drivers/mtd/nand/Kconfig b/drivers/mtd/nand/Kconfig index e6b8c59f2c0d..2c6ecb7ae753 100644 --- a/drivers/mtd/nand/Kconfig +++ b/drivers/mtd/nand/Kconfig @@ -313,17 +313,6 @@ config MTD_NAND_ATMEL Enables support for NAND Flash / Smart Media Card interface on Atmel AT91 processors. -config MTD_NAND_PXA3xx - tristate "NAND support on PXA3xx and Armada 370/XP" - depends on !MTD_NAND_MARVELL - depends on PXA3xx || ARCH_MMP || PLAT_ORION || ARCH_MVEBU - help - - This enables the driver for the NAND flash device found on - PXA3xx processors (NFCv1) and also on 32-bit Armada - platforms (XP, 370, 375, 38x, 39x) and 64-bit Armada - platforms (7K, 8K) (NFCv2). - config MTD_NAND_MARVELL tristate "NAND controller support on Marvell boards" depends on PXA3xx || ARCH_MMP || PLAT_ORION || ARCH_MVEBU || \ diff --git a/drivers/mtd/nand/Makefile b/drivers/mtd/nand/Makefile index 921634ba400c..c882d5ef192a 100644 --- a/drivers/mtd/nand/Makefile +++ b/drivers/mtd/nand/Makefile @@ -31,7 +31,6 @@ omap2_nand-objs := omap2.o obj-$(CONFIG_MTD_NAND_OMAP2) += omap2_nand.o obj-$(CONFIG_MTD_NAND_OMAP_BCH_BUILD) += omap_elm.o obj-$(CONFIG_MTD_NAND_CM_X270) += cmx270_nand.o -obj-$(CONFIG_MTD_NAND_PXA3xx) += pxa3xx_nand.o obj-$(CONFIG_MTD_NAND_MARVELL) += marvell_nand.o obj-$(CONFIG_MTD_NAND_TMIO) += tmio_nand.o obj-$(CONFIG_MTD_NAND_PLATFORM) += plat_nand.o diff --git a/drivers/mtd/nand/pxa3xx_nand.c b/drivers/mtd/nand/pxa3xx_nand.c deleted file mode 100644 index d1979c7dbe7e..000000000000 --- a/drivers/mtd/nand/pxa3xx_nand.c +++ /dev/null @@ -1,2105 +0,0 @@ -/* - * drivers/mtd/nand/pxa3xx_nand.c - * - * Copyright © 2005 Intel Corporation - * Copyright © 2006 Marvell International Ltd. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * See Documentation/mtd/nand/pxa3xx-nand.txt for more details. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#define CHIP_DELAY_TIMEOUT msecs_to_jiffies(200) -#define NAND_STOP_DELAY msecs_to_jiffies(40) -#define PAGE_CHUNK_SIZE (2048) - -/* - * Define a buffer size for the initial command that detects the flash device: - * STATUS, READID and PARAM. - * ONFI param page is 256 bytes, and there are three redundant copies - * to be read. JEDEC param page is 512 bytes, and there are also three - * redundant copies to be read. - * Hence this buffer should be at least 512 x 3. Let's pick 2048. - */ -#define INIT_BUFFER_SIZE 2048 - -/* System control register and bit to enable NAND on some SoCs */ -#define GENCONF_SOC_DEVICE_MUX 0x208 -#define GENCONF_SOC_DEVICE_MUX_NFC_EN BIT(0) - -/* registers and bit definitions */ -#define NDCR (0x00) /* Control register */ -#define NDTR0CS0 (0x04) /* Timing Parameter 0 for CS0 */ -#define NDTR1CS0 (0x0C) /* Timing Parameter 1 for CS0 */ -#define NDSR (0x14) /* Status Register */ -#define NDPCR (0x18) /* Page Count Register */ -#define NDBDR0 (0x1C) /* Bad Block Register 0 */ -#define NDBDR1 (0x20) /* Bad Block Register 1 */ -#define NDECCCTRL (0x28) /* ECC control */ -#define NDDB (0x40) /* Data Buffer */ -#define NDCB0 (0x48) /* Command Buffer0 */ -#define NDCB1 (0x4C) /* Command Buffer1 */ -#define NDCB2 (0x50) /* Command Buffer2 */ - -#define NDCR_SPARE_EN (0x1 << 31) -#define NDCR_ECC_EN (0x1 << 30) -#define NDCR_DMA_EN (0x1 << 29) -#define NDCR_ND_RUN (0x1 << 28) -#define NDCR_DWIDTH_C (0x1 << 27) -#define NDCR_DWIDTH_M (0x1 << 26) -#define NDCR_PAGE_SZ (0x1 << 24) -#define NDCR_NCSX (0x1 << 23) -#define NDCR_ND_MODE (0x3 << 21) -#define NDCR_NAND_MODE (0x0) -#define NDCR_CLR_PG_CNT (0x1 << 20) -#define NFCV1_NDCR_ARB_CNTL (0x1 << 19) -#define NFCV2_NDCR_STOP_ON_UNCOR (0x1 << 19) -#define NDCR_RD_ID_CNT_MASK (0x7 << 16) -#define NDCR_RD_ID_CNT(x) (((x) << 16) & NDCR_RD_ID_CNT_MASK) - -#define NDCR_RA_START (0x1 << 15) -#define NDCR_PG_PER_BLK (0x1 << 14) -#define NDCR_ND_ARB_EN (0x1 << 12) -#define NDCR_INT_MASK (0xFFF) - -#define NDSR_MASK (0xfff) -#define NDSR_ERR_CNT_OFF (16) -#define NDSR_ERR_CNT_MASK (0x1f) -#define NDSR_ERR_CNT(sr) ((sr >> NDSR_ERR_CNT_OFF) & NDSR_ERR_CNT_MASK) -#define NDSR_RDY (0x1 << 12) -#define NDSR_FLASH_RDY (0x1 << 11) -#define NDSR_CS0_PAGED (0x1 << 10) -#define NDSR_CS1_PAGED (0x1 << 9) -#define NDSR_CS0_CMDD (0x1 << 8) -#define NDSR_CS1_CMDD (0x1 << 7) -#define NDSR_CS0_BBD (0x1 << 6) -#define NDSR_CS1_BBD (0x1 << 5) -#define NDSR_UNCORERR (0x1 << 4) -#define NDSR_CORERR (0x1 << 3) -#define NDSR_WRDREQ (0x1 << 2) -#define NDSR_RDDREQ (0x1 << 1) -#define NDSR_WRCMDREQ (0x1) - -#define NDCB0_LEN_OVRD (0x1 << 28) -#define NDCB0_ST_ROW_EN (0x1 << 26) -#define NDCB0_AUTO_RS (0x1 << 25) -#define NDCB0_CSEL (0x1 << 24) -#define NDCB0_EXT_CMD_TYPE_MASK (0x7 << 29) -#define NDCB0_EXT_CMD_TYPE(x) (((x) << 29) & NDCB0_EXT_CMD_TYPE_MASK) -#define NDCB0_CMD_TYPE_MASK (0x7 << 21) -#define NDCB0_CMD_TYPE(x) (((x) << 21) & NDCB0_CMD_TYPE_MASK) -#define NDCB0_NC (0x1 << 20) -#define NDCB0_DBC (0x1 << 19) -#define NDCB0_ADDR_CYC_MASK (0x7 << 16) -#define NDCB0_ADDR_CYC(x) (((x) << 16) & NDCB0_ADDR_CYC_MASK) -#define NDCB0_CMD2_MASK (0xff << 8) -#define NDCB0_CMD1_MASK (0xff) -#define NDCB0_ADDR_CYC_SHIFT (16) - -#define EXT_CMD_TYPE_DISPATCH 6 /* Command dispatch */ -#define EXT_CMD_TYPE_NAKED_RW 5 /* Naked read or Naked write */ -#define EXT_CMD_TYPE_READ 4 /* Read */ -#define EXT_CMD_TYPE_DISP_WR 4 /* Command dispatch with write */ -#define EXT_CMD_TYPE_FINAL 3 /* Final command */ -#define EXT_CMD_TYPE_LAST_RW 1 /* Last naked read/write */ -#define EXT_CMD_TYPE_MONO 0 /* Monolithic read/write */ - -/* - * This should be large enough to read 'ONFI' and 'JEDEC'. - * Let's use 7 bytes, which is the maximum ID count supported - * by the controller (see NDCR_RD_ID_CNT_MASK). - */ -#define READ_ID_BYTES 7 - -/* macros for registers read/write */ -#define nand_writel(info, off, val) \ - do { \ - dev_vdbg(&info->pdev->dev, \ - "%s():%d nand_writel(0x%x, 0x%04x)\n", \ - __func__, __LINE__, (val), (off)); \ - writel_relaxed((val), (info)->mmio_base + (off)); \ - } while (0) - -#define nand_readl(info, off) \ - ({ \ - unsigned int _v; \ - _v = readl_relaxed((info)->mmio_base + (off)); \ - dev_vdbg(&info->pdev->dev, \ - "%s():%d nand_readl(0x%04x) = 0x%x\n", \ - __func__, __LINE__, (off), _v); \ - _v; \ - }) - -/* error code and state */ -enum { - ERR_NONE = 0, - ERR_DMABUSERR = -1, - ERR_SENDCMD = -2, - ERR_UNCORERR = -3, - ERR_BBERR = -4, - ERR_CORERR = -5, -}; - -enum { - STATE_IDLE = 0, - STATE_PREPARED, - STATE_CMD_HANDLE, - STATE_DMA_READING, - STATE_DMA_WRITING, - STATE_DMA_DONE, - STATE_PIO_READING, - STATE_PIO_WRITING, - STATE_CMD_DONE, - STATE_READY, -}; - -enum pxa3xx_nand_variant { - PXA3XX_NAND_VARIANT_PXA, - PXA3XX_NAND_VARIANT_ARMADA370, - PXA3XX_NAND_VARIANT_ARMADA_8K, -}; - -struct pxa3xx_nand_host { - struct nand_chip chip; - void *info_data; - - /* page size of attached chip */ - int use_ecc; - int cs; - - /* calculated from pxa3xx_nand_flash data */ - unsigned int col_addr_cycles; - unsigned int row_addr_cycles; -}; - -struct pxa3xx_nand_info { - struct nand_hw_control controller; - struct platform_device *pdev; - - struct clk *clk; - void __iomem *mmio_base; - unsigned long mmio_phys; - struct completion cmd_complete, dev_ready; - - unsigned int buf_start; - unsigned int buf_count; - unsigned int buf_size; - unsigned int data_buff_pos; - unsigned int oob_buff_pos; - - /* DMA information */ - struct scatterlist sg; - enum dma_data_direction dma_dir; - struct dma_chan *dma_chan; - dma_cookie_t dma_cookie; - int drcmr_dat; - - unsigned char *data_buff; - unsigned char *oob_buff; - dma_addr_t data_buff_phys; - int data_dma_ch; - - struct pxa3xx_nand_host *host[NUM_CHIP_SELECT]; - unsigned int state; - - /* - * This driver supports NFCv1 (as found in PXA SoC) - * and NFCv2 (as found in Armada 370/XP SoC). - */ - enum pxa3xx_nand_variant variant; - - int cs; - int use_ecc; /* use HW ECC ? */ - int ecc_bch; /* using BCH ECC? */ - int use_dma; /* use DMA ? */ - int use_spare; /* use spare ? */ - int need_wait; - - /* Amount of real data per full chunk */ - unsigned int chunk_size; - - /* Amount of spare data per full chunk */ - unsigned int spare_size; - - /* Number of full chunks (i.e chunk_size + spare_size) */ - unsigned int nfullchunks; - - /* - * Total number of chunks. If equal to nfullchunks, then there - * are only full chunks. Otherwise, there is one last chunk of - * size (last_chunk_size + last_spare_size) - */ - unsigned int ntotalchunks; - - /* Amount of real data in the last chunk */ - unsigned int last_chunk_size; - - /* Amount of spare data in the last chunk */ - unsigned int last_spare_size; - - unsigned int ecc_size; - unsigned int ecc_err_cnt; - unsigned int max_bitflips; - int retcode; - - /* - * Variables only valid during command - * execution. step_chunk_size and step_spare_size is the - * amount of real data and spare data in the current - * chunk. cur_chunk is the current chunk being - * read/programmed. - */ - unsigned int step_chunk_size; - unsigned int step_spare_size; - unsigned int cur_chunk; - - /* cached register value */ - uint32_t reg_ndcr; - uint32_t ndtr0cs0; - uint32_t ndtr1cs0; - - /* generated NDCBx register values */ - uint32_t ndcb0; - uint32_t ndcb1; - uint32_t ndcb2; - uint32_t ndcb3; -}; - -static bool use_dma = 1; -module_param(use_dma, bool, 0444); -MODULE_PARM_DESC(use_dma, "enable DMA for data transferring to/from NAND HW"); - -struct pxa3xx_nand_timing { - unsigned int tCH; /* Enable signal hold time */ - unsigned int tCS; /* Enable signal setup time */ - unsigned int tWH; /* ND_nWE high duration */ - unsigned int tWP; /* ND_nWE pulse time */ - unsigned int tRH; /* ND_nRE high duration */ - unsigned int tRP; /* ND_nRE pulse width */ - unsigned int tR; /* ND_nWE high to ND_nRE low for read */ - unsigned int tWHR; /* ND_nWE high to ND_nRE low for status read */ - unsigned int tAR; /* ND_ALE low to ND_nRE low delay */ -}; - -struct pxa3xx_nand_flash { - uint32_t chip_id; - unsigned int flash_width; /* Width of Flash memory (DWIDTH_M) */ - unsigned int dfc_width; /* Width of flash controller(DWIDTH_C) */ - struct pxa3xx_nand_timing *timing; /* NAND Flash timing */ -}; - -static struct pxa3xx_nand_timing timing[] = { - { 40, 80, 60, 100, 80, 100, 90000, 400, 40, }, - { 10, 0, 20, 40, 30, 40, 11123, 110, 10, }, - { 10, 25, 15, 25, 15, 30, 25000, 60, 10, }, - { 10, 35, 15, 25, 15, 25, 25000, 60, 10, }, -}; - -static struct pxa3xx_nand_flash builtin_flash_types[] = { - { 0x46ec, 16, 16, &timing[1] }, - { 0xdaec, 8, 8, &timing[1] }, - { 0xd7ec, 8, 8, &timing[1] }, - { 0xa12c, 8, 8, &timing[2] }, - { 0xb12c, 16, 16, &timing[2] }, - { 0xdc2c, 8, 8, &timing[2] }, - { 0xcc2c, 16, 16, &timing[2] }, - { 0xba20, 16, 16, &timing[3] }, -}; - -static int pxa3xx_ooblayout_ecc(struct mtd_info *mtd, int section, - struct mtd_oob_region *oobregion) -{ - struct nand_chip *chip = mtd_to_nand(mtd); - struct pxa3xx_nand_host *host = nand_get_controller_data(chip); - struct pxa3xx_nand_info *info = host->info_data; - int nchunks = mtd->writesize / info->chunk_size; - - if (section >= nchunks) - return -ERANGE; - - oobregion->offset = ((info->ecc_size + info->spare_size) * section) + - info->spare_size; - oobregion->length = info->ecc_size; - - return 0; -} - -static int pxa3xx_ooblayout_free(struct mtd_info *mtd, int section, - struct mtd_oob_region *oobregion) -{ - struct nand_chip *chip = mtd_to_nand(mtd); - struct pxa3xx_nand_host *host = nand_get_controller_data(chip); - struct pxa3xx_nand_info *info = host->info_data; - int nchunks = mtd->writesize / info->chunk_size; - - if (section >= nchunks) - return -ERANGE; - - if (!info->spare_size) - return 0; - - oobregion->offset = section * (info->ecc_size + info->spare_size); - oobregion->length = info->spare_size; - if (!section) { - /* - * Bootrom looks in bytes 0 & 5 for bad blocks for the - * 4KB page / 4bit BCH combination. - */ - if (mtd->writesize == 4096 && info->chunk_size == 2048) { - oobregion->offset += 6; - oobregion->length -= 6; - } else { - oobregion->offset += 2; - oobregion->length -= 2; - } - } - - return 0; -} - -static const struct mtd_ooblayout_ops pxa3xx_ooblayout_ops = { - .ecc = pxa3xx_ooblayout_ecc, - .free = pxa3xx_ooblayout_free, -}; - -static u8 bbt_pattern[] = {'M', 'V', 'B', 'b', 't', '0' }; -static u8 bbt_mirror_pattern[] = {'1', 't', 'b', 'B', 'V', 'M' }; - -static struct nand_bbt_descr bbt_main_descr = { - .options = NAND_BBT_LASTBLOCK | NAND_BBT_CREATE | NAND_BBT_WRITE - | NAND_BBT_2BIT | NAND_BBT_VERSION, - .offs = 8, - .len = 6, - .veroffs = 14, - .maxblocks = 8, /* Last 8 blocks in each chip */ - .pattern = bbt_pattern -}; - -static struct nand_bbt_descr bbt_mirror_descr = { - .options = NAND_BBT_LASTBLOCK | NAND_BBT_CREATE | NAND_BBT_WRITE - | NAND_BBT_2BIT | NAND_BBT_VERSION, - .offs = 8, - .len = 6, - .veroffs = 14, - .maxblocks = 8, /* Last 8 blocks in each chip */ - .pattern = bbt_mirror_pattern -}; - -#define NDTR0_tCH(c) (min((c), 7) << 19) -#define NDTR0_tCS(c) (min((c), 7) << 16) -#define NDTR0_tWH(c) (min((c), 7) << 11) -#define NDTR0_tWP(c) (min((c), 7) << 8) -#define NDTR0_tRH(c) (min((c), 7) << 3) -#define NDTR0_tRP(c) (min((c), 7) << 0) - -#define NDTR1_tR(c) (min((c), 65535) << 16) -#define NDTR1_tWHR(c) (min((c), 15) << 4) -#define NDTR1_tAR(c) (min((c), 15) << 0) - -/* convert nano-seconds to nand flash controller clock cycles */ -#define ns2cycle(ns, clk) (int)((ns) * (clk / 1000000) / 1000) - -static const struct of_device_id pxa3xx_nand_dt_ids[] = { - { - .compatible = "marvell,pxa3xx-nand", - .data = (void *)PXA3XX_NAND_VARIANT_PXA, - }, - { - .compatible = "marvell,armada370-nand", - .data = (void *)PXA3XX_NAND_VARIANT_ARMADA370, - }, - { - .compatible = "marvell,armada-8k-nand", - .data = (void *)PXA3XX_NAND_VARIANT_ARMADA_8K, - }, - {} -}; -MODULE_DEVICE_TABLE(of, pxa3xx_nand_dt_ids); - -static enum pxa3xx_nand_variant -pxa3xx_nand_get_variant(struct platform_device *pdev) -{ - const struct of_device_id *of_id = - of_match_device(pxa3xx_nand_dt_ids, &pdev->dev); - if (!of_id) - return PXA3XX_NAND_VARIANT_PXA; - return (enum pxa3xx_nand_variant)of_id->data; -} - -static void pxa3xx_nand_set_timing(struct pxa3xx_nand_host *host, - const struct pxa3xx_nand_timing *t) -{ - struct pxa3xx_nand_info *info = host->info_data; - unsigned long nand_clk = clk_get_rate(info->clk); - uint32_t ndtr0, ndtr1; - - ndtr0 = NDTR0_tCH(ns2cycle(t->tCH, nand_clk)) | - NDTR0_tCS(ns2cycle(t->tCS, nand_clk)) | - NDTR0_tWH(ns2cycle(t->tWH, nand_clk)) | - NDTR0_tWP(ns2cycle(t->tWP, nand_clk)) | - NDTR0_tRH(ns2cycle(t->tRH, nand_clk)) | - NDTR0_tRP(ns2cycle(t->tRP, nand_clk)); - - ndtr1 = NDTR1_tR(ns2cycle(t->tR, nand_clk)) | - NDTR1_tWHR(ns2cycle(t->tWHR, nand_clk)) | - NDTR1_tAR(ns2cycle(t->tAR, nand_clk)); - - info->ndtr0cs0 = ndtr0; - info->ndtr1cs0 = ndtr1; - nand_writel(info, NDTR0CS0, ndtr0); - nand_writel(info, NDTR1CS0, ndtr1); -} - -static void pxa3xx_nand_set_sdr_timing(struct pxa3xx_nand_host *host, - const struct nand_sdr_timings *t) -{ - struct pxa3xx_nand_info *info = host->info_data; - struct nand_chip *chip = &host->chip; - unsigned long nand_clk = clk_get_rate(info->clk); - uint32_t ndtr0, ndtr1; - - u32 tCH_min = DIV_ROUND_UP(t->tCH_min, 1000); - u32 tCS_min = DIV_ROUND_UP(t->tCS_min, 1000); - u32 tWH_min = DIV_ROUND_UP(t->tWH_min, 1000); - u32 tWP_min = DIV_ROUND_UP(t->tWC_min - t->tWH_min, 1000); - u32 tREH_min = DIV_ROUND_UP(t->tREH_min, 1000); - u32 tRP_min = DIV_ROUND_UP(t->tRC_min - t->tREH_min, 1000); - u32 tR = chip->chip_delay * 1000; - u32 tWHR_min = DIV_ROUND_UP(t->tWHR_min, 1000); - u32 tAR_min = DIV_ROUND_UP(t->tAR_min, 1000); - - /* fallback to a default value if tR = 0 */ - if (!tR) - tR = 20000; - - ndtr0 = NDTR0_tCH(ns2cycle(tCH_min, nand_clk)) | - NDTR0_tCS(ns2cycle(tCS_min, nand_clk)) | - NDTR0_tWH(ns2cycle(tWH_min, nand_clk)) | - NDTR0_tWP(ns2cycle(tWP_min, nand_clk)) | - NDTR0_tRH(ns2cycle(tREH_min, nand_clk)) | - NDTR0_tRP(ns2cycle(tRP_min, nand_clk)); - - ndtr1 = NDTR1_tR(ns2cycle(tR, nand_clk)) | - NDTR1_tWHR(ns2cycle(tWHR_min, nand_clk)) | - NDTR1_tAR(ns2cycle(tAR_min, nand_clk)); - - info->ndtr0cs0 = ndtr0; - info->ndtr1cs0 = ndtr1; - nand_writel(info, NDTR0CS0, ndtr0); - nand_writel(info, NDTR1CS0, ndtr1); -} - -static int pxa3xx_nand_init_timings_compat(struct pxa3xx_nand_host *host, - unsigned int *flash_width, - unsigned int *dfc_width) -{ - struct nand_chip *chip = &host->chip; - struct pxa3xx_nand_info *info = host->info_data; - const struct pxa3xx_nand_flash *f = NULL; - int i, id, ntypes; - u8 idbuf[2]; - - ntypes = ARRAY_SIZE(builtin_flash_types); - - nand_readid_op(chip, 0, idbuf, sizeof(idbuf)); - id = idbuf[0] | (idbuf[1] << 8); - - for (i = 0; i < ntypes; i++) { - f = &builtin_flash_types[i]; - - if (f->chip_id == id) - break; - } - - if (i == ntypes) { - dev_err(&info->pdev->dev, "Error: timings not found\n"); - return -EINVAL; - } - - pxa3xx_nand_set_timing(host, f->timing); - - *flash_width = f->flash_width; - *dfc_width = f->dfc_width; - - return 0; -} - -static int pxa3xx_nand_init_timings_onfi(struct pxa3xx_nand_host *host, - int mode) -{ - const struct nand_sdr_timings *timings; - - mode = fls(mode) - 1; - if (mode < 0) - mode = 0; - - timings = onfi_async_timing_mode_to_sdr_timings(mode); - if (IS_ERR(timings)) - return PTR_ERR(timings); - - pxa3xx_nand_set_sdr_timing(host, timings); - - return 0; -} - -static int pxa3xx_nand_init(struct pxa3xx_nand_host *host) -{ - struct nand_chip *chip = &host->chip; - struct pxa3xx_nand_info *info = host->info_data; - unsigned int flash_width = 0, dfc_width = 0; - int mode, err; - - mode = onfi_get_async_timing_mode(chip); - if (mode == ONFI_TIMING_MODE_UNKNOWN) { - err = pxa3xx_nand_init_timings_compat(host, &flash_width, - &dfc_width); - if (err) - return err; - - if (flash_width == 16) { - info->reg_ndcr |= NDCR_DWIDTH_M; - chip->options |= NAND_BUSWIDTH_16; - } - - info->reg_ndcr |= (dfc_width == 16) ? NDCR_DWIDTH_C : 0; - } else { - err = pxa3xx_nand_init_timings_onfi(host, mode); - if (err) - return err; - } - - return 0; -} - -/** - * NOTE: it is a must to set ND_RUN firstly, then write - * command buffer, otherwise, it does not work. - * We enable all the interrupt at the same time, and - * let pxa3xx_nand_irq to handle all logic. - */ -static void pxa3xx_nand_start(struct pxa3xx_nand_info *info) -{ - uint32_t ndcr; - - ndcr = info->reg_ndcr; - - if (info->use_ecc) { - ndcr |= NDCR_ECC_EN; - if (info->ecc_bch) - nand_writel(info, NDECCCTRL, 0x1); - } else { - ndcr &= ~NDCR_ECC_EN; - if (info->ecc_bch) - nand_writel(info, NDECCCTRL, 0x0); - } - - if (info->use_dma) - ndcr |= NDCR_DMA_EN; - else - ndcr &= ~NDCR_DMA_EN; - - if (info->use_spare) - ndcr |= NDCR_SPARE_EN; - else - ndcr &= ~NDCR_SPARE_EN; - - ndcr |= NDCR_ND_RUN; - - /* clear status bits and run */ - nand_writel(info, NDSR, NDSR_MASK); - nand_writel(info, NDCR, 0); - nand_writel(info, NDCR, ndcr); -} - -static void pxa3xx_nand_stop(struct pxa3xx_nand_info *info) -{ - uint32_t ndcr; - int timeout = NAND_STOP_DELAY; - - /* wait RUN bit in NDCR become 0 */ - ndcr = nand_readl(info, NDCR); - while ((ndcr & NDCR_ND_RUN) && (timeout-- > 0)) { - ndcr = nand_readl(info, NDCR); - udelay(1); - } - - if (timeout <= 0) { - ndcr &= ~NDCR_ND_RUN; - nand_writel(info, NDCR, ndcr); - } - if (info->dma_chan) - dmaengine_terminate_all(info->dma_chan); - - /* clear status bits */ - nand_writel(info, NDSR, NDSR_MASK); -} - -static void __maybe_unused -enable_int(struct pxa3xx_nand_info *info, uint32_t int_mask) -{ - uint32_t ndcr; - - ndcr = nand_readl(info, NDCR); - nand_writel(info, NDCR, ndcr & ~int_mask); -} - -static void disable_int(struct pxa3xx_nand_info *info, uint32_t int_mask) -{ - uint32_t ndcr; - - ndcr = nand_readl(info, NDCR); - nand_writel(info, NDCR, ndcr | int_mask); -} - -static void drain_fifo(struct pxa3xx_nand_info *info, void *data, int len) -{ - if (info->ecc_bch) { - u32 val; - int ret; - - /* - * According to the datasheet, when reading from NDDB - * with BCH enabled, after each 32 bytes reads, we - * have to make sure that the NDSR.RDDREQ bit is set. - * - * Drain the FIFO 8 32 bits reads at a time, and skip - * the polling on the last read. - */ - while (len > 8) { - ioread32_rep(info->mmio_base + NDDB, data, 8); - - ret = readl_relaxed_poll_timeout(info->mmio_base + NDSR, val, - val & NDSR_RDDREQ, 1000, 5000); - if (ret) { - dev_err(&info->pdev->dev, - "Timeout on RDDREQ while draining the FIFO\n"); - return; - } - - data += 32; - len -= 8; - } - } - - ioread32_rep(info->mmio_base + NDDB, data, len); -} - -static void handle_data_pio(struct pxa3xx_nand_info *info) -{ - switch (info->state) { - case STATE_PIO_WRITING: - if (info->step_chunk_size) - writesl(info->mmio_base + NDDB, - info->data_buff + info->data_buff_pos, - DIV_ROUND_UP(info->step_chunk_size, 4)); - - if (info->step_spare_size) - writesl(info->mmio_base + NDDB, - info->oob_buff + info->oob_buff_pos, - DIV_ROUND_UP(info->step_spare_size, 4)); - break; - case STATE_PIO_READING: - if (info->step_chunk_size) - drain_fifo(info, - info->data_buff + info->data_buff_pos, - DIV_ROUND_UP(info->step_chunk_size, 4)); - - if (info->step_spare_size) - drain_fifo(info, - info->oob_buff + info->oob_buff_pos, - DIV_ROUND_UP(info->step_spare_size, 4)); - break; - default: - dev_err(&info->pdev->dev, "%s: invalid state %d\n", __func__, - info->state); - BUG(); - } - - /* Update buffer pointers for multi-page read/write */ - info->data_buff_pos += info->step_chunk_size; - info->oob_buff_pos += info->step_spare_size; -} - -static void pxa3xx_nand_data_dma_irq(void *data) -{ - struct pxa3xx_nand_info *info = data; - struct dma_tx_state state; - enum dma_status status; - - status = dmaengine_tx_status(info->dma_chan, info->dma_cookie, &state); - if (likely(status == DMA_COMPLETE)) { - info->state = STATE_DMA_DONE; - } else { - dev_err(&info->pdev->dev, "DMA error on data channel\n"); - info->retcode = ERR_DMABUSERR; - } - dma_unmap_sg(info->dma_chan->device->dev, &info->sg, 1, info->dma_dir); - - nand_writel(info, NDSR, NDSR_WRDREQ | NDSR_RDDREQ); - enable_int(info, NDCR_INT_MASK); -} - -static void start_data_dma(struct pxa3xx_nand_info *info) -{ - enum dma_transfer_direction direction; - struct dma_async_tx_descriptor *tx; - - switch (info->state) { - case STATE_DMA_WRITING: - info->dma_dir = DMA_TO_DEVICE; - direction = DMA_MEM_TO_DEV; - break; - case STATE_DMA_READING: - info->dma_dir = DMA_FROM_DEVICE; - direction = DMA_DEV_TO_MEM; - break; - default: - dev_err(&info->pdev->dev, "%s: invalid state %d\n", __func__, - info->state); - BUG(); - } - info->sg.length = info->chunk_size; - if (info->use_spare) - info->sg.length += info->spare_size + info->ecc_size; - dma_map_sg(info->dma_chan->device->dev, &info->sg, 1, info->dma_dir); - - tx = dmaengine_prep_slave_sg(info->dma_chan, &info->sg, 1, direction, - DMA_PREP_INTERRUPT); - if (!tx) { - dev_err(&info->pdev->dev, "prep_slave_sg() failed\n"); - return; - } - tx->callback = pxa3xx_nand_data_dma_irq; - tx->callback_param = info; - info->dma_cookie = dmaengine_submit(tx); - dma_async_issue_pending(info->dma_chan); - dev_dbg(&info->pdev->dev, "%s(dir=%d cookie=%x size=%u)\n", - __func__, direction, info->dma_cookie, info->sg.length); -} - -static irqreturn_t pxa3xx_nand_irq_thread(int irq, void *data) -{ - struct pxa3xx_nand_info *info = data; - - handle_data_pio(info); - - info->state = STATE_CMD_DONE; - nand_writel(info, NDSR, NDSR_WRDREQ | NDSR_RDDREQ); - - return IRQ_HANDLED; -} - -static irqreturn_t pxa3xx_nand_irq(int irq, void *devid) -{ - struct pxa3xx_nand_info *info = devid; - unsigned int status, is_completed = 0, is_ready = 0; - unsigned int ready, cmd_done; - irqreturn_t ret = IRQ_HANDLED; - - if (info->cs == 0) { - ready = NDSR_FLASH_RDY; - cmd_done = NDSR_CS0_CMDD; - } else { - ready = NDSR_RDY; - cmd_done = NDSR_CS1_CMDD; - } - - status = nand_readl(info, NDSR); - - if (status & NDSR_UNCORERR) - info->retcode = ERR_UNCORERR; - if (status & NDSR_CORERR) { - info->retcode = ERR_CORERR; - if ((info->variant == PXA3XX_NAND_VARIANT_ARMADA370 || - info->variant == PXA3XX_NAND_VARIANT_ARMADA_8K) && - info->ecc_bch) - info->ecc_err_cnt = NDSR_ERR_CNT(status); - else - info->ecc_err_cnt = 1; - - /* - * Each chunk composing a page is corrected independently, - * and we need to store maximum number of corrected bitflips - * to return it to the MTD layer in ecc.read_page(). - */ - info->max_bitflips = max_t(unsigned int, - info->max_bitflips, - info->ecc_err_cnt); - } - if (status & (NDSR_RDDREQ | NDSR_WRDREQ)) { - /* whether use dma to transfer data */ - if (info->use_dma) { - disable_int(info, NDCR_INT_MASK); - info->state = (status & NDSR_RDDREQ) ? - STATE_DMA_READING : STATE_DMA_WRITING; - start_data_dma(info); - goto NORMAL_IRQ_EXIT; - } else { - info->state = (status & NDSR_RDDREQ) ? - STATE_PIO_READING : STATE_PIO_WRITING; - ret = IRQ_WAKE_THREAD; - goto NORMAL_IRQ_EXIT; - } - } - if (status & cmd_done) { - info->state = STATE_CMD_DONE; - is_completed = 1; - } - if (status & ready) { - info->state = STATE_READY; - is_ready = 1; - } - - /* - * Clear all status bit before issuing the next command, which - * can and will alter the status bits and will deserve a new - * interrupt on its own. This lets the controller exit the IRQ - */ - nand_writel(info, NDSR, status); - - if (status & NDSR_WRCMDREQ) { - status &= ~NDSR_WRCMDREQ; - info->state = STATE_CMD_HANDLE; - - /* - * Command buffer registers NDCB{0-2} (and optionally NDCB3) - * must be loaded by writing directly either 12 or 16 - * bytes directly to NDCB0, four bytes at a time. - * - * Direct write access to NDCB1, NDCB2 and NDCB3 is ignored - * but each NDCBx register can be read. - */ - nand_writel(info, NDCB0, info->ndcb0); - nand_writel(info, NDCB0, info->ndcb1); - nand_writel(info, NDCB0, info->ndcb2); - - /* NDCB3 register is available in NFCv2 (Armada 370/XP SoC) */ - if (info->variant == PXA3XX_NAND_VARIANT_ARMADA370 || - info->variant == PXA3XX_NAND_VARIANT_ARMADA_8K) - nand_writel(info, NDCB0, info->ndcb3); - } - - if (is_completed) - complete(&info->cmd_complete); - if (is_ready) - complete(&info->dev_ready); -NORMAL_IRQ_EXIT: - return ret; -} - -static inline int is_buf_blank(uint8_t *buf, size_t len) -{ - for (; len > 0; len--) - if (*buf++ != 0xff) - return 0; - return 1; -} - -static void set_command_address(struct pxa3xx_nand_info *info, - unsigned int page_size, uint16_t column, int page_addr) -{ - /* small page addr setting */ - if (page_size < PAGE_CHUNK_SIZE) { - info->ndcb1 = ((page_addr & 0xFFFFFF) << 8) - | (column & 0xFF); - - info->ndcb2 = 0; - } else { - info->ndcb1 = ((page_addr & 0xFFFF) << 16) - | (column & 0xFFFF); - - if (page_addr & 0xFF0000) - info->ndcb2 = (page_addr & 0xFF0000) >> 16; - else - info->ndcb2 = 0; - } -} - -static void prepare_start_command(struct pxa3xx_nand_info *info, int command) -{ - struct pxa3xx_nand_host *host = info->host[info->cs]; - struct mtd_info *mtd = nand_to_mtd(&host->chip); - - /* reset data and oob column point to handle data */ - info->buf_start = 0; - info->buf_count = 0; - info->data_buff_pos = 0; - info->oob_buff_pos = 0; - info->step_chunk_size = 0; - info->step_spare_size = 0; - info->cur_chunk = 0; - info->use_ecc = 0; - info->use_spare = 1; - info->retcode = ERR_NONE; - info->ecc_err_cnt = 0; - info->ndcb3 = 0; - info->need_wait = 0; - - switch (command) { - case NAND_CMD_READ0: - case NAND_CMD_READOOB: - case NAND_CMD_PAGEPROG: - info->use_ecc = 1; - break; - case NAND_CMD_PARAM: - info->use_spare = 0; - break; - default: - info->ndcb1 = 0; - info->ndcb2 = 0; - break; - } - - /* - * If we are about to issue a read command, or about to set - * the write address, then clean the data buffer. - */ - if (command == NAND_CMD_READ0 || - command == NAND_CMD_READOOB || - command == NAND_CMD_SEQIN) { - - info->buf_count = mtd->writesize + mtd->oobsize; - memset(info->data_buff, 0xFF, info->buf_count); - } - -} - -static int prepare_set_command(struct pxa3xx_nand_info *info, int command, - int ext_cmd_type, uint16_t column, int page_addr) -{ - int addr_cycle, exec_cmd; - struct pxa3xx_nand_host *host; - struct mtd_info *mtd; - - host = info->host[info->cs]; - mtd = nand_to_mtd(&host->chip); - addr_cycle = 0; - exec_cmd = 1; - - if (info->cs != 0) - info->ndcb0 = NDCB0_CSEL; - else - info->ndcb0 = 0; - - if (command == NAND_CMD_SEQIN) - exec_cmd = 0; - - addr_cycle = NDCB0_ADDR_CYC(host->row_addr_cycles - + host->col_addr_cycles); - - switch (command) { - case NAND_CMD_READOOB: - case NAND_CMD_READ0: - info->buf_start = column; - info->ndcb0 |= NDCB0_CMD_TYPE(0) - | addr_cycle - | NAND_CMD_READ0; - - if (command == NAND_CMD_READOOB) - info->buf_start += mtd->writesize; - - if (info->cur_chunk < info->nfullchunks) { - info->step_chunk_size = info->chunk_size; - info->step_spare_size = info->spare_size; - } else { - info->step_chunk_size = info->last_chunk_size; - info->step_spare_size = info->last_spare_size; - } - - /* - * Multiple page read needs an 'extended command type' field, - * which is either naked-read or last-read according to the - * state. - */ - if (mtd->writesize == PAGE_CHUNK_SIZE) { - info->ndcb0 |= NDCB0_DBC | (NAND_CMD_READSTART << 8); - } else if (mtd->writesize > PAGE_CHUNK_SIZE) { - info->ndcb0 |= NDCB0_DBC | (NAND_CMD_READSTART << 8) - | NDCB0_LEN_OVRD - | NDCB0_EXT_CMD_TYPE(ext_cmd_type); - info->ndcb3 = info->step_chunk_size + - info->step_spare_size; - } - - set_command_address(info, mtd->writesize, column, page_addr); - break; - - case NAND_CMD_SEQIN: - - info->buf_start = column; - set_command_address(info, mtd->writesize, 0, page_addr); - - /* - * Multiple page programming needs to execute the initial - * SEQIN command that sets the page address. - */ - if (mtd->writesize > PAGE_CHUNK_SIZE) { - info->ndcb0 |= NDCB0_CMD_TYPE(0x1) - | NDCB0_EXT_CMD_TYPE(ext_cmd_type) - | addr_cycle - | command; - exec_cmd = 1; - } - break; - - case NAND_CMD_PAGEPROG: - if (is_buf_blank(info->data_buff, - (mtd->writesize + mtd->oobsize))) { - exec_cmd = 0; - break; - } - - if (info->cur_chunk < info->nfullchunks) { - info->step_chunk_size = info->chunk_size; - info->step_spare_size = info->spare_size; - } else { - info->step_chunk_size = info->last_chunk_size; - info->step_spare_size = info->last_spare_size; - } - - /* Second command setting for large pages */ - if (mtd->writesize > PAGE_CHUNK_SIZE) { - /* - * Multiple page write uses the 'extended command' - * field. This can be used to issue a command dispatch - * or a naked-write depending on the current stage. - */ - info->ndcb0 |= NDCB0_CMD_TYPE(0x1) - | NDCB0_LEN_OVRD - | NDCB0_EXT_CMD_TYPE(ext_cmd_type); - info->ndcb3 = info->step_chunk_size + - info->step_spare_size; - - /* - * This is the command dispatch that completes a chunked - * page program operation. - */ - if (info->cur_chunk == info->ntotalchunks) { - info->ndcb0 = NDCB0_CMD_TYPE(0x1) - | NDCB0_EXT_CMD_TYPE(ext_cmd_type) - | command; - info->ndcb1 = 0; - info->ndcb2 = 0; - info->ndcb3 = 0; - } - } else { - info->ndcb0 |= NDCB0_CMD_TYPE(0x1) - | NDCB0_AUTO_RS - | NDCB0_ST_ROW_EN - | NDCB0_DBC - | (NAND_CMD_PAGEPROG << 8) - | NAND_CMD_SEQIN - | addr_cycle; - } - break; - - case NAND_CMD_PARAM: - info->buf_count = INIT_BUFFER_SIZE; - info->ndcb0 |= NDCB0_CMD_TYPE(0) - | NDCB0_ADDR_CYC(1) - | NDCB0_LEN_OVRD - | command; - info->ndcb1 = (column & 0xFF); - info->ndcb3 = INIT_BUFFER_SIZE; - info->step_chunk_size = INIT_BUFFER_SIZE; - break; - - case NAND_CMD_READID: - info->buf_count = READ_ID_BYTES; - info->ndcb0 |= NDCB0_CMD_TYPE(3) - | NDCB0_ADDR_CYC(1) - | command; - info->ndcb1 = (column & 0xFF); - - info->step_chunk_size = 8; - break; - case NAND_CMD_STATUS: - info->buf_count = 1; - info->ndcb0 |= NDCB0_CMD_TYPE(4) - | NDCB0_ADDR_CYC(1) - | command; - - info->step_chunk_size = 8; - break; - - case NAND_CMD_ERASE1: - info->ndcb0 |= NDCB0_CMD_TYPE(2) - | NDCB0_AUTO_RS - | NDCB0_ADDR_CYC(3) - | NDCB0_DBC - | (NAND_CMD_ERASE2 << 8) - | NAND_CMD_ERASE1; - info->ndcb1 = page_addr; - info->ndcb2 = 0; - - break; - case NAND_CMD_RESET: - info->ndcb0 |= NDCB0_CMD_TYPE(5) - | command; - - break; - - case NAND_CMD_ERASE2: - exec_cmd = 0; - break; - - default: - exec_cmd = 0; - dev_err(&info->pdev->dev, "non-supported command %x\n", - command); - break; - } - - return exec_cmd; -} - -static void nand_cmdfunc(struct mtd_info *mtd, unsigned command, - int column, int page_addr) -{ - struct nand_chip *chip = mtd_to_nand(mtd); - struct pxa3xx_nand_host *host = nand_get_controller_data(chip); - struct pxa3xx_nand_info *info = host->info_data; - int exec_cmd; - - /* - * if this is a x16 device ,then convert the input - * "byte" address into a "word" address appropriate - * for indexing a word-oriented device - */ - if (info->reg_ndcr & NDCR_DWIDTH_M) - column /= 2; - - /* - * There may be different NAND chip hooked to - * different chip select, so check whether - * chip select has been changed, if yes, reset the timing - */ - if (info->cs != host->cs) { - info->cs = host->cs; - nand_writel(info, NDTR0CS0, info->ndtr0cs0); - nand_writel(info, NDTR1CS0, info->ndtr1cs0); - } - - prepare_start_command(info, command); - - info->state = STATE_PREPARED; - exec_cmd = prepare_set_command(info, command, 0, column, page_addr); - - if (exec_cmd) { - init_completion(&info->cmd_complete); - init_completion(&info->dev_ready); - info->need_wait = 1; - pxa3xx_nand_start(info); - - if (!wait_for_completion_timeout(&info->cmd_complete, - CHIP_DELAY_TIMEOUT)) { - dev_err(&info->pdev->dev, "Wait time out!!!\n"); - /* Stop State Machine for next command cycle */ - pxa3xx_nand_stop(info); - } - } - info->state = STATE_IDLE; -} - -static void nand_cmdfunc_extended(struct mtd_info *mtd, - const unsigned command, - int column, int page_addr) -{ - struct nand_chip *chip = mtd_to_nand(mtd); - struct pxa3xx_nand_host *host = nand_get_controller_data(chip); - struct pxa3xx_nand_info *info = host->info_data; - int exec_cmd, ext_cmd_type; - - /* - * if this is a x16 device then convert the input - * "byte" address into a "word" address appropriate - * for indexing a word-oriented device - */ - if (info->reg_ndcr & NDCR_DWIDTH_M) - column /= 2; - - /* - * There may be different NAND chip hooked to - * different chip select, so check whether - * chip select has been changed, if yes, reset the timing - */ - if (info->cs != host->cs) { - info->cs = host->cs; - nand_writel(info, NDTR0CS0, info->ndtr0cs0); - nand_writel(info, NDTR1CS0, info->ndtr1cs0); - } - - /* Select the extended command for the first command */ - switch (command) { - case NAND_CMD_READ0: - case NAND_CMD_READOOB: - ext_cmd_type = EXT_CMD_TYPE_MONO; - break; - case NAND_CMD_SEQIN: - ext_cmd_type = EXT_CMD_TYPE_DISPATCH; - break; - case NAND_CMD_PAGEPROG: - ext_cmd_type = EXT_CMD_TYPE_NAKED_RW; - break; - default: - ext_cmd_type = 0; - break; - } - - prepare_start_command(info, command); - - /* - * Prepare the "is ready" completion before starting a command - * transaction sequence. If the command is not executed the - * completion will be completed, see below. - * - * We can do that inside the loop because the command variable - * is invariant and thus so is the exec_cmd. - */ - info->need_wait = 1; - init_completion(&info->dev_ready); - do { - info->state = STATE_PREPARED; - - exec_cmd = prepare_set_command(info, command, ext_cmd_type, - column, page_addr); - if (!exec_cmd) { - info->need_wait = 0; - complete(&info->dev_ready); - break; - } - - init_completion(&info->cmd_complete); - pxa3xx_nand_start(info); - - if (!wait_for_completion_timeout(&info->cmd_complete, - CHIP_DELAY_TIMEOUT)) { - dev_err(&info->pdev->dev, "Wait time out!!!\n"); - /* Stop State Machine for next command cycle */ - pxa3xx_nand_stop(info); - break; - } - - /* Only a few commands need several steps */ - if (command != NAND_CMD_PAGEPROG && - command != NAND_CMD_READ0 && - command != NAND_CMD_READOOB) - break; - - info->cur_chunk++; - - /* Check if the sequence is complete */ - if (info->cur_chunk == info->ntotalchunks && command != NAND_CMD_PAGEPROG) - break; - - /* - * After a splitted program command sequence has issued - * the command dispatch, the command sequence is complete. - */ - if (info->cur_chunk == (info->ntotalchunks + 1) && - command == NAND_CMD_PAGEPROG && - ext_cmd_type == EXT_CMD_TYPE_DISPATCH) - break; - - if (command == NAND_CMD_READ0 || command == NAND_CMD_READOOB) { - /* Last read: issue a 'last naked read' */ - if (info->cur_chunk == info->ntotalchunks - 1) - ext_cmd_type = EXT_CMD_TYPE_LAST_RW; - else - ext_cmd_type = EXT_CMD_TYPE_NAKED_RW; - - /* - * If a splitted program command has no more data to transfer, - * the command dispatch must be issued to complete. - */ - } else if (command == NAND_CMD_PAGEPROG && - info->cur_chunk == info->ntotalchunks) { - ext_cmd_type = EXT_CMD_TYPE_DISPATCH; - } - } while (1); - - info->state = STATE_IDLE; -} - -static int pxa3xx_nand_write_page_hwecc(struct mtd_info *mtd, - struct nand_chip *chip, const uint8_t *buf, int oob_required, - int page) -{ - nand_prog_page_begin_op(chip, page, 0, buf, mtd->writesize); - chip->write_buf(mtd, chip->oob_poi, mtd->oobsize); - - return nand_prog_page_end_op(chip); -} - -static int pxa3xx_nand_read_page_hwecc(struct mtd_info *mtd, - struct nand_chip *chip, uint8_t *buf, int oob_required, - int page) -{ - struct pxa3xx_nand_host *host = nand_get_controller_data(chip); - struct pxa3xx_nand_info *info = host->info_data; - - nand_read_page_op(chip, page, 0, buf, mtd->writesize); - chip->read_buf(mtd, chip->oob_poi, mtd->oobsize); - - if (info->retcode == ERR_CORERR && info->use_ecc) { - mtd->ecc_stats.corrected += info->ecc_err_cnt; - - } else if (info->retcode == ERR_UNCORERR) { - /* - * for blank page (all 0xff), HW will calculate its ECC as - * 0, which is different from the ECC information within - * OOB, ignore such uncorrectable errors - */ - if (is_buf_blank(buf, mtd->writesize)) - info->retcode = ERR_NONE; - else - mtd->ecc_stats.failed++; - } - - return info->max_bitflips; -} - -static uint8_t pxa3xx_nand_read_byte(struct mtd_info *mtd) -{ - struct nand_chip *chip = mtd_to_nand(mtd); - struct pxa3xx_nand_host *host = nand_get_controller_data(chip); - struct pxa3xx_nand_info *info = host->info_data; - char retval = 0xFF; - - if (info->buf_start < info->buf_count) - /* Has just send a new command? */ - retval = info->data_buff[info->buf_start++]; - - return retval; -} - -static u16 pxa3xx_nand_read_word(struct mtd_info *mtd) -{ - struct nand_chip *chip = mtd_to_nand(mtd); - struct pxa3xx_nand_host *host = nand_get_controller_data(chip); - struct pxa3xx_nand_info *info = host->info_data; - u16 retval = 0xFFFF; - - if (!(info->buf_start & 0x01) && info->buf_start < info->buf_count) { - retval = *((u16 *)(info->data_buff+info->buf_start)); - info->buf_start += 2; - } - return retval; -} - -static void pxa3xx_nand_read_buf(struct mtd_info *mtd, uint8_t *buf, int len) -{ - struct nand_chip *chip = mtd_to_nand(mtd); - struct pxa3xx_nand_host *host = nand_get_controller_data(chip); - struct pxa3xx_nand_info *info = host->info_data; - int real_len = min_t(size_t, len, info->buf_count - info->buf_start); - - memcpy(buf, info->data_buff + info->buf_start, real_len); - info->buf_start += real_len; -} - -static void pxa3xx_nand_write_buf(struct mtd_info *mtd, - const uint8_t *buf, int len) -{ - struct nand_chip *chip = mtd_to_nand(mtd); - struct pxa3xx_nand_host *host = nand_get_controller_data(chip); - struct pxa3xx_nand_info *info = host->info_data; - int real_len = min_t(size_t, len, info->buf_count - info->buf_start); - - memcpy(info->data_buff + info->buf_start, buf, real_len); - info->buf_start += real_len; -} - -static void pxa3xx_nand_select_chip(struct mtd_info *mtd, int chip) -{ - return; -} - -static int pxa3xx_nand_waitfunc(struct mtd_info *mtd, struct nand_chip *this) -{ - struct nand_chip *chip = mtd_to_nand(mtd); - struct pxa3xx_nand_host *host = nand_get_controller_data(chip); - struct pxa3xx_nand_info *info = host->info_data; - - if (info->need_wait) { - info->need_wait = 0; - if (!wait_for_completion_timeout(&info->dev_ready, - CHIP_DELAY_TIMEOUT)) { - dev_err(&info->pdev->dev, "Ready time out!!!\n"); - return NAND_STATUS_FAIL; - } - } - - /* pxa3xx_nand_send_command has waited for command complete */ - if (this->state == FL_WRITING || this->state == FL_ERASING) { - if (info->retcode == ERR_NONE) - return 0; - else - return NAND_STATUS_FAIL; - } - - return NAND_STATUS_READY; -} - -static int pxa3xx_nand_config_ident(struct pxa3xx_nand_info *info) -{ - struct pxa3xx_nand_host *host = info->host[info->cs]; - struct platform_device *pdev = info->pdev; - struct pxa3xx_nand_platform_data *pdata = dev_get_platdata(&pdev->dev); - const struct nand_sdr_timings *timings; - - /* Configure default flash values */ - info->chunk_size = PAGE_CHUNK_SIZE; - info->reg_ndcr = 0x0; /* enable all interrupts */ - info->reg_ndcr |= (pdata->enable_arbiter) ? NDCR_ND_ARB_EN : 0; - info->reg_ndcr |= NDCR_RD_ID_CNT(READ_ID_BYTES); - info->reg_ndcr |= NDCR_SPARE_EN; - - /* use the common timing to make a try */ - timings = onfi_async_timing_mode_to_sdr_timings(0); - if (IS_ERR(timings)) - return PTR_ERR(timings); - - pxa3xx_nand_set_sdr_timing(host, timings); - return 0; -} - -static void pxa3xx_nand_config_tail(struct pxa3xx_nand_info *info) -{ - struct pxa3xx_nand_host *host = info->host[info->cs]; - struct nand_chip *chip = &host->chip; - struct mtd_info *mtd = nand_to_mtd(chip); - - info->reg_ndcr |= (host->col_addr_cycles == 2) ? NDCR_RA_START : 0; - info->reg_ndcr |= (chip->page_shift == 6) ? NDCR_PG_PER_BLK : 0; - info->reg_ndcr |= (mtd->writesize == 2048) ? NDCR_PAGE_SZ : 0; -} - -static void pxa3xx_nand_detect_config(struct pxa3xx_nand_info *info) -{ - struct platform_device *pdev = info->pdev; - struct pxa3xx_nand_platform_data *pdata = dev_get_platdata(&pdev->dev); - uint32_t ndcr = nand_readl(info, NDCR); - - /* Set an initial chunk size */ - info->chunk_size = ndcr & NDCR_PAGE_SZ ? 2048 : 512; - info->reg_ndcr = ndcr & - ~(NDCR_INT_MASK | NDCR_ND_ARB_EN | NFCV1_NDCR_ARB_CNTL); - info->reg_ndcr |= (pdata->enable_arbiter) ? NDCR_ND_ARB_EN : 0; - info->ndtr0cs0 = nand_readl(info, NDTR0CS0); - info->ndtr1cs0 = nand_readl(info, NDTR1CS0); -} - -static int pxa3xx_nand_init_buff(struct pxa3xx_nand_info *info) -{ - struct platform_device *pdev = info->pdev; - struct dma_slave_config config; - dma_cap_mask_t mask; - struct pxad_param param; - int ret; - - info->data_buff = kmalloc(info->buf_size, GFP_KERNEL); - if (info->data_buff == NULL) - return -ENOMEM; - if (use_dma == 0) - return 0; - - ret = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(32)); - if (ret) - return ret; - - sg_init_one(&info->sg, info->data_buff, info->buf_size); - dma_cap_zero(mask); - dma_cap_set(DMA_SLAVE, mask); - param.prio = PXAD_PRIO_LOWEST; - param.drcmr = info->drcmr_dat; - info->dma_chan = dma_request_slave_channel_compat(mask, pxad_filter_fn, - ¶m, &pdev->dev, - "data"); - if (!info->dma_chan) { - dev_err(&pdev->dev, "unable to request data dma channel\n"); - return -ENODEV; - } - - memset(&config, 0, sizeof(config)); - config.src_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; - config.dst_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; - config.src_addr = info->mmio_phys + NDDB; - config.dst_addr = info->mmio_phys + NDDB; - config.src_maxburst = 32; - config.dst_maxburst = 32; - ret = dmaengine_slave_config(info->dma_chan, &config); - if (ret < 0) { - dev_err(&info->pdev->dev, - "dma channel configuration failed: %d\n", - ret); - return ret; - } - - /* - * Now that DMA buffers are allocated we turn on - * DMA proper for I/O operations. - */ - info->use_dma = 1; - return 0; -} - -static void pxa3xx_nand_free_buff(struct pxa3xx_nand_info *info) -{ - if (info->use_dma) { - dmaengine_terminate_all(info->dma_chan); - dma_release_channel(info->dma_chan); - } - kfree(info->data_buff); -} - -static int pxa_ecc_init(struct pxa3xx_nand_info *info, - struct mtd_info *mtd, - int strength, int ecc_stepsize, int page_size) -{ - struct nand_chip *chip = mtd_to_nand(mtd); - struct nand_ecc_ctrl *ecc = &chip->ecc; - - if (strength == 1 && ecc_stepsize == 512 && page_size == 2048) { - info->nfullchunks = 1; - info->ntotalchunks = 1; - info->chunk_size = 2048; - info->spare_size = 40; - info->ecc_size = 24; - ecc->mode = NAND_ECC_HW; - ecc->size = 512; - ecc->strength = 1; - - } else if (strength == 1 && ecc_stepsize == 512 && page_size == 512) { - info->nfullchunks = 1; - info->ntotalchunks = 1; - info->chunk_size = 512; - info->spare_size = 8; - info->ecc_size = 8; - ecc->mode = NAND_ECC_HW; - ecc->size = 512; - ecc->strength = 1; - - /* - * Required ECC: 4-bit correction per 512 bytes - * Select: 16-bit correction per 2048 bytes - */ - } else if (strength == 4 && ecc_stepsize == 512 && page_size == 2048) { - info->ecc_bch = 1; - info->nfullchunks = 1; - info->ntotalchunks = 1; - info->chunk_size = 2048; - info->spare_size = 32; - info->ecc_size = 32; - ecc->mode = NAND_ECC_HW; - ecc->size = info->chunk_size; - mtd_set_ooblayout(mtd, &pxa3xx_ooblayout_ops); - ecc->strength = 16; - - } else if (strength == 4 && ecc_stepsize == 512 && page_size == 4096) { - info->ecc_bch = 1; - info->nfullchunks = 2; - info->ntotalchunks = 2; - info->chunk_size = 2048; - info->spare_size = 32; - info->ecc_size = 32; - ecc->mode = NAND_ECC_HW; - ecc->size = info->chunk_size; - mtd_set_ooblayout(mtd, &pxa3xx_ooblayout_ops); - ecc->strength = 16; - - /* - * Required ECC: 8-bit correction per 512 bytes - * Select: 16-bit correction per 1024 bytes - */ - } else if (strength == 8 && ecc_stepsize == 512 && page_size == 4096) { - info->ecc_bch = 1; - info->nfullchunks = 4; - info->ntotalchunks = 5; - info->chunk_size = 1024; - info->spare_size = 0; - info->last_chunk_size = 0; - info->last_spare_size = 64; - info->ecc_size = 32; - ecc->mode = NAND_ECC_HW; - ecc->size = info->chunk_size; - mtd_set_ooblayout(mtd, &pxa3xx_ooblayout_ops); - ecc->strength = 16; - } else { - dev_err(&info->pdev->dev, - "ECC strength %d at page size %d is not supported\n", - strength, page_size); - return -ENODEV; - } - - dev_info(&info->pdev->dev, "ECC strength %d, ECC step size %d\n", - ecc->strength, ecc->size); - return 0; -} - -static int pxa3xx_nand_scan(struct mtd_info *mtd) -{ - struct nand_chip *chip = mtd_to_nand(mtd); - struct pxa3xx_nand_host *host = nand_get_controller_data(chip); - struct pxa3xx_nand_info *info = host->info_data; - struct platform_device *pdev = info->pdev; - struct pxa3xx_nand_platform_data *pdata = dev_get_platdata(&pdev->dev); - int ret; - uint16_t ecc_strength, ecc_step; - - if (pdata->keep_config) { - pxa3xx_nand_detect_config(info); - } else { - ret = pxa3xx_nand_config_ident(info); - if (ret) - return ret; - } - - if (info->reg_ndcr & NDCR_DWIDTH_M) - chip->options |= NAND_BUSWIDTH_16; - - /* Device detection must be done with ECC disabled */ - if (info->variant == PXA3XX_NAND_VARIANT_ARMADA370 || - info->variant == PXA3XX_NAND_VARIANT_ARMADA_8K) - nand_writel(info, NDECCCTRL, 0x0); - - if (pdata->flash_bbt) - chip->bbt_options |= NAND_BBT_USE_FLASH; - - chip->ecc.strength = pdata->ecc_strength; - chip->ecc.size = pdata->ecc_step_size; - - ret = nand_scan_ident(mtd, 1, NULL); - if (ret) - return ret; - - if (!pdata->keep_config) { - ret = pxa3xx_nand_init(host); - if (ret) { - dev_err(&info->pdev->dev, "Failed to init nand: %d\n", - ret); - return ret; - } - } - - if (chip->bbt_options & NAND_BBT_USE_FLASH) { - /* - * We'll use a bad block table stored in-flash and don't - * allow writing the bad block marker to the flash. - */ - chip->bbt_options |= NAND_BBT_NO_OOB_BBM; - chip->bbt_td = &bbt_main_descr; - chip->bbt_md = &bbt_mirror_descr; - } - - /* - * If the page size is bigger than the FIFO size, let's check - * we are given the right variant and then switch to the extended - * (aka splitted) command handling, - */ - if (mtd->writesize > PAGE_CHUNK_SIZE) { - if (info->variant == PXA3XX_NAND_VARIANT_ARMADA370 || - info->variant == PXA3XX_NAND_VARIANT_ARMADA_8K) { - chip->cmdfunc = nand_cmdfunc_extended; - } else { - dev_err(&info->pdev->dev, - "unsupported page size on this variant\n"); - return -ENODEV; - } - } - - ecc_strength = chip->ecc.strength; - ecc_step = chip->ecc.size; - if (!ecc_strength || !ecc_step) { - ecc_strength = chip->ecc_strength_ds; - ecc_step = chip->ecc_step_ds; - } - - /* Set default ECC strength requirements on non-ONFI devices */ - if (ecc_strength < 1 && ecc_step < 1) { - ecc_strength = 1; - ecc_step = 512; - } - - ret = pxa_ecc_init(info, mtd, ecc_strength, - ecc_step, mtd->writesize); - if (ret) - return ret; - - /* calculate addressing information */ - if (mtd->writesize >= 2048) - host->col_addr_cycles = 2; - else - host->col_addr_cycles = 1; - - /* release the initial buffer */ - kfree(info->data_buff); - - /* allocate the real data + oob buffer */ - info->buf_size = mtd->writesize + mtd->oobsize; - ret = pxa3xx_nand_init_buff(info); - if (ret) - return ret; - info->oob_buff = info->data_buff + mtd->writesize; - - if ((mtd->size >> chip->page_shift) > 65536) - host->row_addr_cycles = 3; - else - host->row_addr_cycles = 2; - - if (!pdata->keep_config) - pxa3xx_nand_config_tail(info); - - return nand_scan_tail(mtd); -} - -static int alloc_nand_resource(struct platform_device *pdev) -{ - struct device_node *np = pdev->dev.of_node; - struct pxa3xx_nand_platform_data *pdata; - struct pxa3xx_nand_info *info; - struct pxa3xx_nand_host *host; - struct nand_chip *chip = NULL; - struct mtd_info *mtd; - struct resource *r; - int ret, irq, cs; - - pdata = dev_get_platdata(&pdev->dev); - if (pdata->num_cs <= 0) { - dev_err(&pdev->dev, "invalid number of chip selects\n"); - return -ENODEV; - } - - info = devm_kzalloc(&pdev->dev, - sizeof(*info) + sizeof(*host) * pdata->num_cs, - GFP_KERNEL); - if (!info) - return -ENOMEM; - - info->pdev = pdev; - info->variant = pxa3xx_nand_get_variant(pdev); - for (cs = 0; cs < pdata->num_cs; cs++) { - host = (void *)&info[1] + sizeof(*host) * cs; - chip = &host->chip; - nand_set_controller_data(chip, host); - mtd = nand_to_mtd(chip); - info->host[cs] = host; - host->cs = cs; - host->info_data = info; - mtd->dev.parent = &pdev->dev; - /* FIXME: all chips use the same device tree partitions */ - nand_set_flash_node(chip, np); - - nand_set_controller_data(chip, host); - chip->ecc.read_page = pxa3xx_nand_read_page_hwecc; - chip->ecc.write_page = pxa3xx_nand_write_page_hwecc; - chip->controller = &info->controller; - chip->waitfunc = pxa3xx_nand_waitfunc; - chip->select_chip = pxa3xx_nand_select_chip; - chip->read_word = pxa3xx_nand_read_word; - chip->read_byte = pxa3xx_nand_read_byte; - chip->read_buf = pxa3xx_nand_read_buf; - chip->write_buf = pxa3xx_nand_write_buf; - chip->options |= NAND_NO_SUBPAGE_WRITE; - chip->cmdfunc = nand_cmdfunc; - chip->onfi_set_features = nand_onfi_get_set_features_notsupp; - chip->onfi_get_features = nand_onfi_get_set_features_notsupp; - } - - nand_hw_control_init(chip->controller); - info->clk = devm_clk_get(&pdev->dev, NULL); - if (IS_ERR(info->clk)) { - ret = PTR_ERR(info->clk); - dev_err(&pdev->dev, "failed to get nand clock: %d\n", ret); - return ret; - } - ret = clk_prepare_enable(info->clk); - if (ret < 0) - return ret; - - if (!np && use_dma) { - r = platform_get_resource(pdev, IORESOURCE_DMA, 0); - if (r == NULL) { - dev_err(&pdev->dev, - "no resource defined for data DMA\n"); - ret = -ENXIO; - goto fail_disable_clk; - } - info->drcmr_dat = r->start; - } - - irq = platform_get_irq(pdev, 0); - if (irq < 0) { - dev_err(&pdev->dev, "no IRQ resource defined\n"); - ret = -ENXIO; - goto fail_disable_clk; - } - - r = platform_get_resource(pdev, IORESOURCE_MEM, 0); - info->mmio_base = devm_ioremap_resource(&pdev->dev, r); - if (IS_ERR(info->mmio_base)) { - ret = PTR_ERR(info->mmio_base); - dev_err(&pdev->dev, "failed to map register space: %d\n", ret); - goto fail_disable_clk; - } - info->mmio_phys = r->start; - - /* Allocate a buffer to allow flash detection */ - info->buf_size = INIT_BUFFER_SIZE; - info->data_buff = kmalloc(info->buf_size, GFP_KERNEL); - if (info->data_buff == NULL) { - ret = -ENOMEM; - goto fail_disable_clk; - } - - /* initialize all interrupts to be disabled */ - disable_int(info, NDSR_MASK); - - ret = request_threaded_irq(irq, pxa3xx_nand_irq, - pxa3xx_nand_irq_thread, IRQF_ONESHOT, - pdev->name, info); - if (ret < 0) { - dev_err(&pdev->dev, "failed to request IRQ: %d\n", ret); - goto fail_free_buf; - } - - platform_set_drvdata(pdev, info); - - return 0; - -fail_free_buf: - free_irq(irq, info); - kfree(info->data_buff); -fail_disable_clk: - clk_disable_unprepare(info->clk); - return ret; -} - -static int pxa3xx_nand_remove(struct platform_device *pdev) -{ - struct pxa3xx_nand_info *info = platform_get_drvdata(pdev); - struct pxa3xx_nand_platform_data *pdata; - int irq, cs; - - if (!info) - return 0; - - pdata = dev_get_platdata(&pdev->dev); - - irq = platform_get_irq(pdev, 0); - if (irq >= 0) - free_irq(irq, info); - pxa3xx_nand_free_buff(info); - - /* - * In the pxa3xx case, the DFI bus is shared between the SMC and NFC. - * In order to prevent a lockup of the system bus, the DFI bus - * arbitration is granted to SMC upon driver removal. This is done by - * setting the x_ARB_CNTL bit, which also prevents the NAND to have - * access to the bus anymore. - */ - nand_writel(info, NDCR, - (nand_readl(info, NDCR) & ~NDCR_ND_ARB_EN) | - NFCV1_NDCR_ARB_CNTL); - clk_disable_unprepare(info->clk); - - for (cs = 0; cs < pdata->num_cs; cs++) - nand_release(nand_to_mtd(&info->host[cs]->chip)); - return 0; -} - -static int pxa3xx_nand_probe_dt(struct platform_device *pdev) -{ - struct pxa3xx_nand_platform_data *pdata; - struct device_node *np = pdev->dev.of_node; - const struct of_device_id *of_id = - of_match_device(pxa3xx_nand_dt_ids, &pdev->dev); - - if (!of_id) - return 0; - - /* - * Some SoCs like A7k/A8k need to enable manually the NAND - * controller to avoid being bootloader dependent. This is done - * through the use of a single bit in the System Functions registers. - */ - if (pxa3xx_nand_get_variant(pdev) == PXA3XX_NAND_VARIANT_ARMADA_8K) { - struct regmap *sysctrl_base = syscon_regmap_lookup_by_phandle( - pdev->dev.of_node, "marvell,system-controller"); - u32 reg; - - if (IS_ERR(sysctrl_base)) - return PTR_ERR(sysctrl_base); - - regmap_read(sysctrl_base, GENCONF_SOC_DEVICE_MUX, ®); - reg |= GENCONF_SOC_DEVICE_MUX_NFC_EN; - regmap_write(sysctrl_base, GENCONF_SOC_DEVICE_MUX, reg); - } - - pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL); - if (!pdata) - return -ENOMEM; - - if (of_get_property(np, "marvell,nand-enable-arbiter", NULL)) - pdata->enable_arbiter = 1; - if (of_get_property(np, "marvell,nand-keep-config", NULL)) - pdata->keep_config = 1; - of_property_read_u32(np, "num-cs", &pdata->num_cs); - - pdev->dev.platform_data = pdata; - - return 0; -} - -static int pxa3xx_nand_probe(struct platform_device *pdev) -{ - struct pxa3xx_nand_platform_data *pdata; - struct pxa3xx_nand_info *info; - int ret, cs, probe_success, dma_available; - - dma_available = IS_ENABLED(CONFIG_ARM) && - (IS_ENABLED(CONFIG_ARCH_PXA) || IS_ENABLED(CONFIG_ARCH_MMP)); - if (use_dma && !dma_available) { - use_dma = 0; - dev_warn(&pdev->dev, - "This platform can't do DMA on this device\n"); - } - - ret = pxa3xx_nand_probe_dt(pdev); - if (ret) - return ret; - - pdata = dev_get_platdata(&pdev->dev); - if (!pdata) { - dev_err(&pdev->dev, "no platform data defined\n"); - return -ENODEV; - } - - ret = alloc_nand_resource(pdev); - if (ret) - return ret; - - info = platform_get_drvdata(pdev); - probe_success = 0; - for (cs = 0; cs < pdata->num_cs; cs++) { - struct mtd_info *mtd = nand_to_mtd(&info->host[cs]->chip); - - /* - * The mtd name matches the one used in 'mtdparts' kernel - * parameter. This name cannot be changed or otherwise - * user's mtd partitions configuration would get broken. - */ - mtd->name = "pxa3xx_nand-0"; - info->cs = cs; - ret = pxa3xx_nand_scan(mtd); - if (ret) { - dev_warn(&pdev->dev, "failed to scan nand at cs %d\n", - cs); - continue; - } - - ret = mtd_device_register(mtd, pdata->parts[cs], - pdata->nr_parts[cs]); - if (!ret) - probe_success = 1; - } - - if (!probe_success) { - pxa3xx_nand_remove(pdev); - return -ENODEV; - } - - return 0; -} - -#ifdef CONFIG_PM -static int pxa3xx_nand_suspend(struct device *dev) -{ - struct pxa3xx_nand_info *info = dev_get_drvdata(dev); - - if (info->state) { - dev_err(dev, "driver busy, state = %d\n", info->state); - return -EAGAIN; - } - - clk_disable(info->clk); - return 0; -} - -static int pxa3xx_nand_resume(struct device *dev) -{ - struct pxa3xx_nand_info *info = dev_get_drvdata(dev); - int ret; - - ret = clk_enable(info->clk); - if (ret < 0) - return ret; - - /* We don't want to handle interrupt without calling mtd routine */ - disable_int(info, NDCR_INT_MASK); - - /* - * Directly set the chip select to a invalid value, - * then the driver would reset the timing according - * to current chip select at the beginning of cmdfunc - */ - info->cs = 0xff; - - /* - * As the spec says, the NDSR would be updated to 0x1800 when - * doing the nand_clk disable/enable. - * To prevent it damaging state machine of the driver, clear - * all status before resume - */ - nand_writel(info, NDSR, NDSR_MASK); - - return 0; -} -#else -#define pxa3xx_nand_suspend NULL -#define pxa3xx_nand_resume NULL -#endif - -static const struct dev_pm_ops pxa3xx_nand_pm_ops = { - .suspend = pxa3xx_nand_suspend, - .resume = pxa3xx_nand_resume, -}; - -static struct platform_driver pxa3xx_nand_driver = { - .driver = { - .name = "pxa3xx-nand", - .of_match_table = pxa3xx_nand_dt_ids, - .pm = &pxa3xx_nand_pm_ops, - }, - .probe = pxa3xx_nand_probe, - .remove = pxa3xx_nand_remove, -}; - -module_platform_driver(pxa3xx_nand_driver); - -MODULE_LICENSE("GPL"); -MODULE_DESCRIPTION("PXA3xx NAND controller driver"); -- cgit v1.2.3 From 2c355ff6b6ca6e0153cc6845aa004b9f5212d81e Mon Sep 17 00:00:00 2001 From: Tony Lindgren Date: Thu, 22 Feb 2018 13:58:03 -0800 Subject: bus: ti-sysc: Add fck clock alias for children with notifier_block The functional clock is used by several child device drivers to query the rate for the child device internal configuration. The functional clock is really for the whole interconnect target module, and not just for the child device, and can also be shared across multiple children. At least the timers, i2c and mmc driver query the fck for rate. So let's just create a clock alias for the child fck if it does not yet exits. We can do this with the BUS_NOTIFY_ADD_DEVICE before the child is probed. Note that we need to now also remove the legacy mode check for getting the dts clocks in ti-sysc driver. Signed-off-by: Tony Lindgren --- drivers/bus/ti-sysc.c | 91 ++++++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 87 insertions(+), 4 deletions(-) (limited to 'drivers') diff --git a/drivers/bus/ti-sysc.c b/drivers/bus/ti-sysc.c index 4d46003c46cf..3ffe5446ff34 100644 --- a/drivers/bus/ti-sysc.c +++ b/drivers/bus/ti-sysc.c @@ -13,11 +13,14 @@ #include #include +#include #include #include #include #include #include +#include + #include #include @@ -136,9 +139,6 @@ static int sysc_get_clocks(struct sysc *ddata) { int i, error; - if (ddata->legacy_mode) - return 0; - for (i = 0; i < SYSC_MAX_CLOCKS; i++) { error = sysc_get_one_clock(ddata, i); if (error && error != -ENOENT) @@ -605,6 +605,74 @@ static int sysc_init_syss_mask(struct sysc *ddata) return 0; } +/* + * Many child device drivers need to have fck available to get the clock + * rate for device internal configuration. + */ +static int sysc_child_add_fck(struct sysc *ddata, + struct device *child) +{ + struct clk *fck; + struct clk_lookup *l; + const char *name = clock_names[SYSC_FCK]; + + if (IS_ERR_OR_NULL(ddata->clocks[SYSC_FCK])) + return 0; + + fck = clk_get(child, name); + if (!IS_ERR(fck)) { + clk_put(fck); + + return -EEXIST; + } + + l = clkdev_create(ddata->clocks[SYSC_FCK], name, dev_name(child)); + + return l ? 0 : -ENODEV; +} + +static struct device_type sysc_device_type = { +}; + +static struct sysc *sysc_child_to_parent(struct device *dev) +{ + struct device *parent = dev->parent; + + if (!parent || parent->type != &sysc_device_type) + return NULL; + + return dev_get_drvdata(parent); +} + +static int sysc_notifier_call(struct notifier_block *nb, + unsigned long event, void *device) +{ + struct device *dev = device; + struct sysc *ddata; + int error; + + ddata = sysc_child_to_parent(dev); + if (!ddata) + return NOTIFY_DONE; + + switch (event) { + case BUS_NOTIFY_ADD_DEVICE: + error = sysc_child_add_fck(ddata, dev); + if (error && error != -EEXIST) + dev_warn(ddata->dev, "could not add %s fck: %i\n", + dev_name(dev), error); + break; + default: + break; + } + + return NOTIFY_DONE; +} + +static struct notifier_block sysc_nb = { + .notifier_call = sysc_notifier_call, +}; + /* Device tree configured quirks */ struct sysc_dts_quirk { const char *name; @@ -937,6 +1005,7 @@ static int sysc_probe(struct platform_device *pdev) sysc_show_registers(ddata); + ddata->dev->type = &sysc_device_type; error = of_platform_populate(ddata->dev->of_node, NULL, NULL, ddata->dev); if (error) @@ -1008,7 +1077,21 @@ static struct platform_driver sysc_driver = { .pm = &sysc_pm_ops, }, }; -module_platform_driver(sysc_driver); + +static int __init sysc_init(void) +{ + bus_register_notifier(&platform_bus_type, &sysc_nb); + + return platform_driver_register(&sysc_driver); +} +module_init(sysc_init); + +static void __exit sysc_exit(void) +{ + bus_unregister_notifier(&platform_bus_type, &sysc_nb); + platform_driver_unregister(&sysc_driver); +} +module_exit(sysc_exit); MODULE_DESCRIPTION("TI sysc interconnect target driver"); MODULE_LICENSE("GPL v2"); -- cgit v1.2.3 From 62020f231232215df73ca54669e38fe3f1d1b29a Mon Sep 17 00:00:00 2001 From: Tony Lindgren Date: Thu, 22 Feb 2018 13:59:44 -0800 Subject: bus: ti-sysc: Add suspend and resume handling This allows us to idle the module on suspend after the children are suspended. Signed-off-by: Tony Lindgren --- drivers/bus/ti-sysc.c | 34 ++++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) (limited to 'drivers') diff --git a/drivers/bus/ti-sysc.c b/drivers/bus/ti-sysc.c index 3ffe5446ff34..2bb0d0061624 100644 --- a/drivers/bus/ti-sysc.c +++ b/drivers/bus/ti-sysc.c @@ -58,6 +58,7 @@ static const char * const clock_names[] = { "fck", "ick", }; * @cfg: interconnect target module configuration * @name: name if available * @revision: interconnect target module revision + * @needs_resume: runtime resume needed on resume from suspend */ struct sysc { struct device *dev; @@ -71,6 +72,8 @@ struct sysc { struct sysc_config cfg; const char *name; u32 revision; + bool enabled; + bool needs_resume; }; static u32 sysc_read(struct sysc *ddata, int offset) @@ -497,7 +500,38 @@ static int __maybe_unused sysc_runtime_resume(struct device *dev) return 0; } +#ifdef CONFIG_PM_SLEEP +static int sysc_suspend(struct device *dev) +{ + struct sysc *ddata; + + ddata = dev_get_drvdata(dev); + + if (!ddata->enabled) + return 0; + + ddata->needs_resume = true; + + return sysc_runtime_suspend(dev); +} + +static int sysc_resume(struct device *dev) +{ + struct sysc *ddata; + + ddata = dev_get_drvdata(dev); + if (ddata->needs_resume) { + ddata->needs_resume = false; + + return sysc_runtime_resume(dev); + } + + return 0; +} +#endif + static const struct dev_pm_ops sysc_pm_ops = { + SET_SYSTEM_SLEEP_PM_OPS(sysc_suspend, sysc_resume) SET_RUNTIME_PM_OPS(sysc_runtime_suspend, sysc_runtime_resume, NULL) -- cgit v1.2.3 From 3bb37c8e6e6a6191233c97f294cecba10bb5fc50 Mon Sep 17 00:00:00 2001 From: Tony Lindgren Date: Thu, 22 Feb 2018 14:05:14 -0800 Subject: bus: ti-sysc: Handle stdout-path for debug console If we have stdout-path specified for earlycon, we must prevent the debug console from idling until runtime PM kicks in. Signed-off-by: Tony Lindgren --- drivers/bus/ti-sysc.c | 46 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 46 insertions(+) (limited to 'drivers') diff --git a/drivers/bus/ti-sysc.c b/drivers/bus/ti-sysc.c index 2bb0d0061624..7d82d5add7c1 100644 --- a/drivers/bus/ti-sysc.c +++ b/drivers/bus/ti-sysc.c @@ -206,6 +206,50 @@ static int sysc_parse_and_check_child_range(struct sysc *ddata) return 0; } +static struct device_node *stdout_path; + +static void sysc_init_stdout_path(struct sysc *ddata) +{ + struct device_node *np = NULL; + const char *uart; + + if (IS_ERR(stdout_path)) + return; + + if (stdout_path) + return; + + np = of_find_node_by_path("/chosen"); + if (!np) + goto err; + + uart = of_get_property(np, "stdout-path", NULL); + if (!uart) + goto err; + + np = of_find_node_by_path(uart); + if (!np) + goto err; + + stdout_path = np; + + return; + +err: + stdout_path = ERR_PTR(-ENODEV); +} + +static void sysc_check_quirk_stdout(struct sysc *ddata, + struct device_node *np) +{ + sysc_init_stdout_path(ddata); + if (np != stdout_path) + return; + + ddata->cfg.quirks |= SYSC_QUIRK_NO_IDLE_ON_INIT | + SYSC_QUIRK_NO_RESET_ON_INIT; +} + /** * sysc_check_one_child - check child configuration * @ddata: device driver data @@ -224,6 +268,8 @@ static int sysc_check_one_child(struct sysc *ddata, if (name) dev_warn(ddata->dev, "really a child ti,hwmods property?"); + sysc_check_quirk_stdout(ddata, np); + return 0; } -- cgit v1.2.3 From 76f0f772e4691dd59ef93a6c2c583a0435357ca0 Mon Sep 17 00:00:00 2001 From: Tony Lindgren Date: Fri, 23 Feb 2018 08:28:45 -0800 Subject: bus: ti-sysc: Improve handling for no-reset-on-init and no-idle-on-init At least earlycon needs a delayed idle before the 8250 driver probes to avoid glitches in the console output. Let's handle the delayed idle for devices tagged with ti,no-reset-on-init and ti,no-idle-on-init with delayed_work. Others don't need it, and there should be no need to use runtime PM autosuspend for the interconnect target driver as it's the child device drivers that should configure it. Signed-off-by: Tony Lindgren --- drivers/bus/ti-sysc.c | 28 ++++++++++++++++++++++------ 1 file changed, 22 insertions(+), 6 deletions(-) (limited to 'drivers') diff --git a/drivers/bus/ti-sysc.c b/drivers/bus/ti-sysc.c index 7d82d5add7c1..2e37272d990f 100644 --- a/drivers/bus/ti-sysc.c +++ b/drivers/bus/ti-sysc.c @@ -74,6 +74,7 @@ struct sysc { u32 revision; bool enabled; bool needs_resume; + struct delayed_work idle_work; }; static u32 sysc_read(struct sysc *ddata, int offset) @@ -1028,6 +1029,16 @@ static int sysc_init_match(struct sysc *ddata) return 0; } +static void ti_sysc_idle(struct work_struct *work) +{ + struct sysc *ddata; + + ddata = container_of(work, struct sysc, idle_work.work); + + if (pm_runtime_active(ddata->dev)) + pm_runtime_put_sync(ddata->dev); +} + static int sysc_probe(struct platform_device *pdev) { struct sysc *ddata; @@ -1081,8 +1092,6 @@ static int sysc_probe(struct platform_device *pdev) goto unprepare; } - pm_runtime_use_autosuspend(ddata->dev); - sysc_show_registers(ddata); ddata->dev->type = &sysc_device_type; @@ -1091,13 +1100,19 @@ static int sysc_probe(struct platform_device *pdev) if (error) goto err; - pm_runtime_mark_last_busy(ddata->dev); - pm_runtime_put_autosuspend(ddata->dev); + INIT_DELAYED_WORK(&ddata->idle_work, ti_sysc_idle); + + /* At least earlycon won't survive without deferred idle */ + if (ddata->cfg.quirks & (SYSC_QUIRK_NO_IDLE_ON_INIT | + SYSC_QUIRK_NO_RESET_ON_INIT)) { + schedule_delayed_work(&ddata->idle_work, 3000); + } else { + pm_runtime_put(&pdev->dev); + } return 0; err: - pm_runtime_dont_use_autosuspend(&pdev->dev); pm_runtime_put_sync(&pdev->dev); pm_runtime_disable(&pdev->dev); unprepare: @@ -1111,6 +1126,8 @@ static int sysc_remove(struct platform_device *pdev) struct sysc *ddata = platform_get_drvdata(pdev); int error; + cancel_delayed_work_sync(&ddata->idle_work); + error = pm_runtime_get_sync(ddata->dev); if (error < 0) { pm_runtime_put_noidle(ddata->dev); @@ -1120,7 +1137,6 @@ static int sysc_remove(struct platform_device *pdev) of_platform_depopulate(&pdev->dev); - pm_runtime_dont_use_autosuspend(&pdev->dev); pm_runtime_put_sync(&pdev->dev); pm_runtime_disable(&pdev->dev); -- cgit v1.2.3 From dd57ac1e5c1c6dac90564c69e57d5ba4a132969e Mon Sep 17 00:00:00 2001 From: Tony Lindgren Date: Thu, 22 Feb 2018 14:09:57 -0800 Subject: bus: ti-sysc: Remove unnecessary debugging statements We already show the status for the interconnect target module when debugging is enabled, there's no need to be more verbose about that. So let's just cut down the noise and remove the extra debug statements. Signed-off-by: Tony Lindgren --- drivers/bus/ti-sysc.c | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) (limited to 'drivers') diff --git a/drivers/bus/ti-sysc.c b/drivers/bus/ti-sysc.c index 2e37272d990f..fc9aac3d4d02 100644 --- a/drivers/bus/ti-sysc.c +++ b/drivers/bus/ti-sysc.c @@ -201,9 +201,6 @@ static int sysc_parse_and_check_child_range(struct sysc *ddata) ddata->module_pa = of_translate_address(np, ranges++); ddata->module_size = be32_to_cpup(ranges); - dev_dbg(ddata->dev, "interconnect target 0x%llx size 0x%x for %pOF\n", - ddata->module_pa, ddata->module_size, np); - return 0; } @@ -296,11 +293,8 @@ static int sysc_check_children(struct sysc *ddata) */ static void sysc_check_quirk_16bit(struct sysc *ddata, struct resource *res) { - if (resource_size(res) == 8) { - dev_dbg(ddata->dev, - "enabling 16-bit and clockactivity quirks\n"); + if (resource_size(res) == 8) ddata->cfg.quirks |= SYSC_QUIRK_16BIT | SYSC_QUIRK_USE_CLOCKACT; - } } /** @@ -326,7 +320,6 @@ static int sysc_parse_one(struct sysc *ddata, enum sysc_registers reg) res = platform_get_resource_byname(to_platform_device(ddata->dev), IORESOURCE_MEM, name); if (!res) { - dev_dbg(ddata->dev, "has no %s register\n", name); ddata->offsets[reg] = -ENODEV; return 0; -- cgit v1.2.3 From ef70b0bdeaf893dd6d9c3a8d05d9b65d395506c0 Mon Sep 17 00:00:00 2001 From: Tony Lindgren Date: Thu, 22 Feb 2018 14:00:25 -0800 Subject: bus: ti-sysc: Add support for platform data callbacks We want to pass the device tree configuration for interconnect target modules from ti-sysc driver to the existing platform hwmod code. This allows us to first validate the dts data against the existing platform data before we start dropping the platform data in favor of device tree data. To do this, let's add platform data callbacks for PM runtime functions to call for the interconnect target modules if platform data is available. Note that as ti-sysc driver can rebind, omap_auxdata_lookup and related functions can no longer be __init. Signed-off-by: Tony Lindgren --- arch/arm/mach-omap2/board-n8x0.c | 4 +- arch/arm/mach-omap2/pdata-quirks.c | 40 ++++++++++++++- drivers/bus/ti-sysc.c | 96 ++++++++++++++++++++++++++++++----- include/linux/platform_data/ti-sysc.h | 49 ++++++++++++++++++ 4 files changed, 172 insertions(+), 17 deletions(-) (limited to 'drivers') diff --git a/arch/arm/mach-omap2/board-n8x0.c b/arch/arm/mach-omap2/board-n8x0.c index 20f25539d572..75bc18646df6 100644 --- a/arch/arm/mach-omap2/board-n8x0.c +++ b/arch/arm/mach-omap2/board-n8x0.c @@ -566,11 +566,11 @@ static int n8x0_menelaus_late_init(struct device *dev) } #endif -struct menelaus_platform_data n8x0_menelaus_platform_data __initdata = { +struct menelaus_platform_data n8x0_menelaus_platform_data = { .late_init = n8x0_menelaus_late_init, }; -struct aic3x_pdata n810_aic33_data __initdata = { +struct aic3x_pdata n810_aic33_data = { .gpio_reset = 118, }; diff --git a/arch/arm/mach-omap2/pdata-quirks.c b/arch/arm/mach-omap2/pdata-quirks.c index 1cca66ad68b5..eea82c31ee77 100644 --- a/arch/arm/mach-omap2/pdata-quirks.c +++ b/arch/arm/mach-omap2/pdata-quirks.c @@ -24,6 +24,7 @@ #include #include #include +#include #include #include #include @@ -455,6 +456,42 @@ static void __init dra7x_evm_mmc_quirk(void) } #endif +static int ti_sysc_enable_module(struct device *dev, + const struct ti_sysc_cookie *cookie) +{ + if (!cookie->data) + return -EINVAL; + + return omap_hwmod_enable(cookie->data); +} + +static int ti_sysc_idle_module(struct device *dev, + const struct ti_sysc_cookie *cookie) +{ + if (!cookie->data) + return -EINVAL; + + return omap_hwmod_idle(cookie->data); +} + +static int ti_sysc_shutdown_module(struct device *dev, + const struct ti_sysc_cookie *cookie) +{ + if (!cookie->data) + return -EINVAL; + + return omap_hwmod_shutdown(cookie->data); +} + +static struct of_dev_auxdata omap_auxdata_lookup[]; + +static struct ti_sysc_platform_data ti_sysc_pdata = { + .auxdata = omap_auxdata_lookup, + .enable_module = ti_sysc_enable_module, + .idle_module = ti_sysc_idle_module, + .shutdown_module = ti_sysc_shutdown_module, +}; + static struct pcs_pdata pcs_pdata; void omap_pcs_legacy_init(int irq, void (*rearm)(void)) @@ -545,7 +582,7 @@ static struct pdata_init auxdata_quirks[] __initdata = { struct omap_sr_data __maybe_unused omap_sr_pdata[OMAP_SR_NR]; -static struct of_dev_auxdata omap_auxdata_lookup[] __initdata = { +static struct of_dev_auxdata omap_auxdata_lookup[] = { #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), @@ -603,6 +640,7 @@ static struct of_dev_auxdata omap_auxdata_lookup[] __initdata = { &dra7_hsmmc_data_mmc3), #endif /* Common auxdata */ + OF_DEV_AUXDATA("ti,sysc", 0, NULL, &ti_sysc_pdata), OF_DEV_AUXDATA("pinctrl-single", 0, NULL, &pcs_pdata), { /* sentinel */ }, }; diff --git a/drivers/bus/ti-sysc.c b/drivers/bus/ti-sysc.c index fc9aac3d4d02..50fcb04e8179 100644 --- a/drivers/bus/ti-sysc.c +++ b/drivers/bus/ti-sysc.c @@ -25,13 +25,6 @@ #include -enum sysc_registers { - SYSC_REVISION, - SYSC_SYSCONFIG, - SYSC_SYSSTATUS, - SYSC_MAX_REGS, -}; - static const char * const reg_names[] = { "rev", "sysc", "syss", }; enum sysc_clocks { @@ -70,6 +63,7 @@ struct sysc { const char *legacy_mode; const struct sysc_capabilities *cap; struct sysc_config cfg; + struct ti_sysc_cookie cookie; const char *name; u32 revision; bool enabled; @@ -494,6 +488,7 @@ static void sysc_show_registers(struct sysc *ddata) bufp += sysc_show_reg(ddata, bufp, i); bufp += sysc_show_rev(bufp, ddata); + bufp += sysc_show_rev(bufp, ddata); dev_dbg(ddata->dev, "%llx:%x%s\n", ddata->module_pa, ddata->module_size, @@ -502,33 +497,70 @@ static void sysc_show_registers(struct sysc *ddata) static int __maybe_unused sysc_runtime_suspend(struct device *dev) { + struct ti_sysc_platform_data *pdata; struct sysc *ddata; - int i; + int error = 0, i; ddata = dev_get_drvdata(dev); - if (ddata->legacy_mode) + if (!ddata->enabled) return 0; + if (ddata->legacy_mode) { + pdata = dev_get_platdata(ddata->dev); + if (!pdata) + return 0; + + if (!pdata->idle_module) + return -ENODEV; + + error = pdata->idle_module(dev, &ddata->cookie); + if (error) + dev_err(dev, "%s: could not idle: %i\n", + __func__, error); + + goto idled; + } + for (i = 0; i < SYSC_MAX_CLOCKS; i++) { if (IS_ERR_OR_NULL(ddata->clocks[i])) continue; clk_disable(ddata->clocks[i]); } - return 0; +idled: + ddata->enabled = false; + + return error; } static int __maybe_unused sysc_runtime_resume(struct device *dev) { + struct ti_sysc_platform_data *pdata; struct sysc *ddata; - int i, error; + int error = 0, i; ddata = dev_get_drvdata(dev); - if (ddata->legacy_mode) + if (ddata->enabled) return 0; + if (ddata->legacy_mode) { + pdata = dev_get_platdata(ddata->dev); + if (!pdata) + return 0; + + if (!pdata->enable_module) + return -ENODEV; + + error = pdata->enable_module(dev, &ddata->cookie); + if (error) + dev_err(dev, "%s: could not enable: %i\n", + __func__, error); + + goto awake; + } + for (i = 0; i < SYSC_MAX_CLOCKS; i++) { if (IS_ERR_OR_NULL(ddata->clocks[i])) continue; @@ -537,7 +569,10 @@ static int __maybe_unused sysc_runtime_resume(struct device *dev) return error; } - return 0; +awake: + ddata->enabled = true; + + return error; } #ifdef CONFIG_PM_SLEEP @@ -1007,6 +1042,33 @@ static const struct sysc_capabilities sysc_omap4_usb_host_fs = { .regbits = &sysc_regbits_omap4_usb_host_fs, }; +static int sysc_init_pdata(struct sysc *ddata) +{ + struct ti_sysc_platform_data *pdata = dev_get_platdata(ddata->dev); + struct ti_sysc_module_data mdata; + int error = 0; + + if (!pdata || !ddata->legacy_mode) + return 0; + + mdata.name = ddata->legacy_mode; + mdata.module_pa = ddata->module_pa; + mdata.module_size = ddata->module_size; + mdata.offsets = ddata->offsets; + mdata.nr_offsets = SYSC_MAX_REGS; + mdata.cap = ddata->cap; + mdata.cfg = &ddata->cfg; + + if (!pdata->init_module) + return -ENODEV; + + error = pdata->init_module(ddata->dev, &mdata, &ddata->cookie); + if (error == -EEXIST) + error = 0; + + return error; +} + static int sysc_init_match(struct sysc *ddata) { const struct sysc_capabilities *cap; @@ -1034,6 +1096,7 @@ static void ti_sysc_idle(struct work_struct *work) static int sysc_probe(struct platform_device *pdev) { + struct ti_sysc_platform_data *pdata = dev_get_platdata(&pdev->dev); struct sysc *ddata; int error; @@ -1072,6 +1135,10 @@ static int sysc_probe(struct platform_device *pdev) if (error) goto unprepare; + error = sysc_init_pdata(ddata); + if (error) + goto unprepare; + pm_runtime_enable(ddata->dev); error = sysc_init_module(ddata); @@ -1089,7 +1156,8 @@ static int sysc_probe(struct platform_device *pdev) ddata->dev->type = &sysc_device_type; error = of_platform_populate(ddata->dev->of_node, - NULL, NULL, ddata->dev); + NULL, pdata ? pdata->auxdata : NULL, + ddata->dev); if (error) goto err; diff --git a/include/linux/platform_data/ti-sysc.h b/include/linux/platform_data/ti-sysc.h index 1be356330b96..4176cb90e195 100644 --- a/include/linux/platform_data/ti-sysc.h +++ b/include/linux/platform_data/ti-sysc.h @@ -16,6 +16,10 @@ enum ti_sysc_module_type { TI_SYSC_OMAP4_USB_HOST_FS, }; +struct ti_sysc_cookie { + void *data; +}; + /** * struct sysc_regbits - TI OCP_SYSCONFIG register field offsets * @midle_shift: Offset of the midle bit @@ -83,4 +87,49 @@ struct sysc_config { u32 quirks; }; +enum sysc_registers { + SYSC_REVISION, + SYSC_SYSCONFIG, + SYSC_SYSSTATUS, + SYSC_MAX_REGS, +}; + +/** + * struct ti_sysc_module_data - ti-sysc to hwmod translation data for a module + * @name: legacy "ti,hwmods" module name + * @module_pa: physical address of the interconnect target module + * @module_size: size of the interconnect target module + * @offsets: array of register offsets as listed in enum sysc_registers + * @nr_offsets: number of registers + * @cap: interconnect target module capabilities + * @cfg: interconnect target module configuration + * + * This data is enough to allocate a new struct omap_hwmod_class_sysconfig + * based on device tree data parsed by ti-sysc driver. + */ +struct ti_sysc_module_data { + const char *name; + u64 module_pa; + u32 module_size; + int *offsets; + int nr_offsets; + const struct sysc_capabilities *cap; + struct sysc_config *cfg; +}; + +struct device; + +struct ti_sysc_platform_data { + struct of_dev_auxdata *auxdata; + int (*init_module)(struct device *dev, + const struct ti_sysc_module_data *data, + struct ti_sysc_cookie *cookie); + int (*enable_module)(struct device *dev, + const struct ti_sysc_cookie *cookie); + int (*idle_module)(struct device *dev, + const struct ti_sysc_cookie *cookie); + int (*shutdown_module)(struct device *dev, + const struct ti_sysc_cookie *cookie); +}; + #endif /* __TI_SYSC_DATA_H__ */ -- cgit v1.2.3 From a885f0fe209f262efa2c1cac9278a5774e5f7a80 Mon Sep 17 00:00:00 2001 From: Tony Lindgren Date: Thu, 22 Feb 2018 14:03:48 -0800 Subject: bus: ti-sysc: Handle some devices in omap_device compatible way Now that ti-sysc can manage child devices, we must also be backwards compatible with the current omap_device code. With omap_device, we assume that the child device manages the interconnect target module directly. The drivers needing special handling are the ones that still set pm_runtime_irq_safe(). In the long run we want to update those drivers as otherwise they will cause problems with genpd as a permanent PM runtime usage count is set on the parent device. We can handle omap_device these devices by improving the ti-sysc quirk handling to detect the devices needing special handling based on register map and revision register if usable. We also need to implement dev_pm_domain for these child devices just like omap_device does. Signed-off-by: Tony Lindgren --- drivers/bus/ti-sysc.c | 226 +++++++++++++++++++++++++++++++++- include/linux/platform_data/ti-sysc.h | 1 + 2 files changed, 224 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/bus/ti-sysc.c b/drivers/bus/ti-sysc.c index 50fcb04e8179..5aeab4533b5f 100644 --- a/drivers/bus/ti-sysc.c +++ b/drivers/bus/ti-sysc.c @@ -14,8 +14,10 @@ #include #include #include +#include #include #include +#include #include #include #include @@ -68,6 +70,7 @@ struct sysc { u32 revision; bool enabled; bool needs_resume; + bool child_needs_resume; struct delayed_work idle_work; }; @@ -474,6 +477,14 @@ static int sysc_show_reg(struct sysc *ddata, return sprintf(bufp, ":%x", ddata->offsets[reg]); } +static int sysc_show_name(char *bufp, struct sysc *ddata) +{ + if (!ddata->name) + return 0; + + return sprintf(bufp, ":%s", ddata->name); +} + /** * sysc_show_registers - show information about interconnect target module * @ddata: device driver data @@ -488,7 +499,7 @@ static void sysc_show_registers(struct sysc *ddata) bufp += sysc_show_reg(ddata, bufp, i); bufp += sysc_show_rev(bufp, ddata); - bufp += sysc_show_rev(bufp, ddata); + bufp += sysc_show_name(bufp, ddata); dev_dbg(ddata->dev, "%llx:%x%s\n", ddata->module_pa, ddata->module_size, @@ -612,11 +623,93 @@ static const struct dev_pm_ops sysc_pm_ops = { NULL) }; +/* Module revision register based quirks */ +struct sysc_revision_quirk { + const char *name; + u32 base; + int rev_offset; + int sysc_offset; + int syss_offset; + u32 revision; + u32 revision_mask; + u32 quirks; +}; + +#define SYSC_QUIRK(optname, optbase, optrev, optsysc, optsyss, \ + optrev_val, optrevmask, optquirkmask) \ + { \ + .name = (optname), \ + .base = (optbase), \ + .rev_offset = (optrev), \ + .sysc_offset = (optsysc), \ + .syss_offset = (optsyss), \ + .revision = (optrev_val), \ + .revision_mask = (optrevmask), \ + .quirks = (optquirkmask), \ + } + +static const struct sysc_revision_quirk sysc_revision_quirks[] = { + /* These drivers need to be fixed to not use pm_runtime_irq_safe() */ + SYSC_QUIRK("gpio", 0, 0, 0x10, 0x114, 0x50600801, 0xffffffff, + SYSC_QUIRK_LEGACY_IDLE), + SYSC_QUIRK("mmu", 0, 0, 0x10, 0x14, 0x00000020, 0xffffffff, + SYSC_QUIRK_LEGACY_IDLE), + SYSC_QUIRK("mmu", 0, 0, 0x10, 0x14, 0x00000030, 0xffffffff, + SYSC_QUIRK_LEGACY_IDLE), + SYSC_QUIRK("sham", 0, 0x100, 0x110, 0x114, 0x40000c03, 0xffffffff, + SYSC_QUIRK_LEGACY_IDLE), + SYSC_QUIRK("smartreflex", 0, -1, 0x24, -1, 0x00000000, 0xffffffff, + SYSC_QUIRK_LEGACY_IDLE), + SYSC_QUIRK("smartreflex", 0, -1, 0x38, -1, 0x00000000, 0xffffffff, + SYSC_QUIRK_LEGACY_IDLE), + SYSC_QUIRK("timer", 0, 0, 0x10, 0x14, 0x00000015, 0xffffffff, + SYSC_QUIRK_LEGACY_IDLE), + SYSC_QUIRK("uart", 0, 0x50, 0x54, 0x58, 0x00000052, 0xffffffff, + SYSC_QUIRK_LEGACY_IDLE), +}; + +static void sysc_init_revision_quirks(struct sysc *ddata) +{ + const struct sysc_revision_quirk *q; + int i; + + for (i = 0; i < ARRAY_SIZE(sysc_revision_quirks); i++) { + q = &sysc_revision_quirks[i]; + + if (q->base && q->base != ddata->module_pa) + continue; + + if (q->rev_offset >= 0 && + q->rev_offset != ddata->offsets[SYSC_REVISION]) + continue; + + if (q->sysc_offset >= 0 && + q->sysc_offset != ddata->offsets[SYSC_SYSCONFIG]) + continue; + + if (q->syss_offset >= 0 && + q->syss_offset != ddata->offsets[SYSC_SYSSTATUS]) + continue; + + if (q->revision == ddata->revision || + (q->revision & q->revision_mask) == + (ddata->revision & q->revision_mask)) { + ddata->name = q->name; + ddata->cfg.quirks |= q->quirks; + } + } +} + /* At this point the module is configured enough to read the revision */ static int sysc_init_module(struct sysc *ddata) { int error; + if (ddata->cfg.quirks & SYSC_QUIRK_NO_IDLE_ON_INIT) { + ddata->revision = sysc_read_revision(ddata); + goto rev_quirks; + } + error = pm_runtime_get_sync(ddata->dev); if (error < 0) { pm_runtime_put_noidle(ddata->dev); @@ -626,6 +719,9 @@ static int sysc_init_module(struct sysc *ddata) ddata->revision = sysc_read_revision(ddata); pm_runtime_put_sync(ddata->dev); +rev_quirks: + sysc_init_revision_quirks(ddata); + return 0; } @@ -753,6 +849,127 @@ static struct sysc *sysc_child_to_parent(struct device *dev) return dev_get_drvdata(parent); } +static int __maybe_unused sysc_child_runtime_suspend(struct device *dev) +{ + struct sysc *ddata; + int error; + + ddata = sysc_child_to_parent(dev); + + error = pm_generic_runtime_suspend(dev); + if (error) + return error; + + if (!ddata->enabled) + return 0; + + return sysc_runtime_suspend(ddata->dev); +} + +static int __maybe_unused sysc_child_runtime_resume(struct device *dev) +{ + struct sysc *ddata; + int error; + + ddata = sysc_child_to_parent(dev); + + if (!ddata->enabled) { + error = sysc_runtime_resume(ddata->dev); + if (error < 0) + dev_err(ddata->dev, + "%s error: %i\n", __func__, error); + } + + return pm_generic_runtime_resume(dev); +} + +#ifdef CONFIG_PM_SLEEP +static int sysc_child_suspend_noirq(struct device *dev) +{ + struct sysc *ddata; + int error; + + ddata = sysc_child_to_parent(dev); + + error = pm_generic_suspend_noirq(dev); + if (error) + return error; + + if (!pm_runtime_status_suspended(dev)) { + error = pm_generic_runtime_suspend(dev); + if (error) + return error; + + error = sysc_runtime_suspend(ddata->dev); + if (error) + return error; + + ddata->child_needs_resume = true; + } + + return 0; +} + +static int sysc_child_resume_noirq(struct device *dev) +{ + struct sysc *ddata; + int error; + + ddata = sysc_child_to_parent(dev); + + if (ddata->child_needs_resume) { + ddata->child_needs_resume = false; + + error = sysc_runtime_resume(ddata->dev); + if (error) + dev_err(ddata->dev, + "%s runtime resume error: %i\n", + __func__, error); + + error = pm_generic_runtime_resume(dev); + if (error) + dev_err(ddata->dev, + "%s generic runtime resume: %i\n", + __func__, error); + } + + return pm_generic_resume_noirq(dev); +} +#endif + +struct dev_pm_domain sysc_child_pm_domain = { + .ops = { + SET_RUNTIME_PM_OPS(sysc_child_runtime_suspend, + sysc_child_runtime_resume, + NULL) + USE_PLATFORM_PM_SLEEP_OPS + SET_NOIRQ_SYSTEM_SLEEP_PM_OPS(sysc_child_suspend_noirq, + sysc_child_resume_noirq) + } +}; + +/** + * sysc_legacy_idle_quirk - handle children in omap_device compatible way + * @ddata: device driver data + * @child: child device driver + * + * Allow idle for child devices as done with _od_runtime_suspend(). + * Otherwise many child devices will not idle because of the permanent + * parent usecount set in pm_runtime_irq_safe(). + * + * Note that the long term solution is to just modify the child device + * drivers to not set pm_runtime_irq_safe() and then this can be just + * dropped. + */ +static void sysc_legacy_idle_quirk(struct sysc *ddata, struct device *child) +{ + if (!ddata->legacy_mode) + return; + + if (ddata->cfg.quirks & SYSC_QUIRK_LEGACY_IDLE) + dev_pm_domain_set(child, &sysc_child_pm_domain); +} + static int sysc_notifier_call(struct notifier_block *nb, unsigned long event, void *device) { @@ -770,6 +987,7 @@ static int sysc_notifier_call(struct notifier_block *nb, if (error && error != -EEXIST) dev_warn(ddata->dev, "could not add %s fck: %i\n", dev_name(dev), error); + sysc_legacy_idle_quirk(ddata, dev); break; default: break; @@ -974,7 +1192,8 @@ static const struct sysc_capabilities sysc_34xx_sr = { .type = TI_SYSC_OMAP34XX_SR, .sysc_mask = SYSC_OMAP2_CLOCKACTIVITY, .regbits = &sysc_regbits_omap34xx_sr, - .mod_quirks = SYSC_QUIRK_USE_CLOCKACT | SYSC_QUIRK_UNCACHED, + .mod_quirks = SYSC_QUIRK_USE_CLOCKACT | SYSC_QUIRK_UNCACHED | + SYSC_QUIRK_LEGACY_IDLE, }; /* @@ -995,12 +1214,13 @@ static const struct sysc_capabilities sysc_36xx_sr = { .type = TI_SYSC_OMAP36XX_SR, .sysc_mask = SYSC_OMAP3_SR_ENAWAKEUP, .regbits = &sysc_regbits_omap36xx_sr, - .mod_quirks = SYSC_QUIRK_UNCACHED, + .mod_quirks = SYSC_QUIRK_UNCACHED | SYSC_QUIRK_LEGACY_IDLE, }; static const struct sysc_capabilities sysc_omap4_sr = { .type = TI_SYSC_OMAP4_SR, .regbits = &sysc_regbits_omap36xx_sr, + .mod_quirks = SYSC_QUIRK_LEGACY_IDLE, }; /* diff --git a/include/linux/platform_data/ti-sysc.h b/include/linux/platform_data/ti-sysc.h index 4176cb90e195..80ce28d40832 100644 --- a/include/linux/platform_data/ti-sysc.h +++ b/include/linux/platform_data/ti-sysc.h @@ -45,6 +45,7 @@ struct sysc_regbits { s8 emufree_shift; }; +#define SYSC_QUIRK_LEGACY_IDLE BIT(8) #define SYSC_QUIRK_RESET_STATUS BIT(7) #define SYSC_QUIRK_NO_IDLE_ON_INIT BIT(6) #define SYSC_QUIRK_NO_RESET_ON_INIT BIT(5) -- cgit v1.2.3 From afe761f8d3e99155b76833421e76553a3ac69577 Mon Sep 17 00:00:00 2001 From: Dave Gerlach Date: Fri, 23 Feb 2018 09:43:57 -0600 Subject: soc: ti: Add pm33xx driver for basic suspend support AM335x and AM437x support various low power modes as documented in section 8.1.4.3 of the AM335x Technical Reference Manual and section 6.4.3 of the AM437x Technical Reference Manual. DeepSleep0 mode offers the lowest power mode with limited wakeup sources without a system reboot and is mapped as the suspend state in the kernel. In this state, MPU and PER domains are turned off with the internal RAM held in retention to facilitate the resume process. As part of the boot process, the assembly code is copied over to OCMCRAM so it can be executed to turn of the EMIF and put DDR into self refresh. Both platforms have a Cortex-M3 (WKUP_M3) which assists the MPU in DeepSleep0 entry and exit. WKUP_M3 takes care of the clockdomain and powerdomain transitions based on the intended low power state. MPU needs to load the appropriate WKUP_M3 binary onto the WKUP_M3 memory space before it can leverage any of the PM features like DeepSleep. This loading is handled by the remoteproc driver wkup_m3_rproc. Communication with the WKUP_M3 is handled by a wkup_m3_ipc driver that exposes the specific PM functionality to be used the PM code. In the current implementation when the suspend process is initiated, MPU interrupts the WKUP_M3 to let it know about the intent of entering DeepSleep0 and waits for an ACK. When the ACK is received MPU continues with its suspend process to suspend all the drivers and then jumps to assembly in OCMC RAM. The assembly code puts the external RAM in self-refresh mode, gates the MPU clock, and then finally executes the WFI instruction. Execution of the WFI instruction with MPU clock gated triggers another interrupt to the WKUP_M3 which then continues with the power down sequence wherein the clockdomain and powerdomain transition takes place. As part of the sleep sequence, WKUP_M3 unmasks the interrupt lines for the wakeup sources. WFI execution on WKUP_M3 causes the hardware to disable the main oscillator of the SoC and from here system remains in sleep state until a wake source brings the system into resume path. When a wakeup event occurs, WKUP_M3 starts the power-up sequence by switching on the power domains and finally enabling the clock to MPU. Since the MPU gets powered down as part of the sleep sequence in the resume path ROM code starts executing. The ROM code detects a wakeup from sleep and then jumps to the resume location in OCMC which was populated in one of the IPC registers as part of the suspend sequence. Code is based on work by Vaibhav Bedia. Signed-off-by: Dave Gerlach Acked-by: Santosh Shilimkar Signed-off-by: Tony Lindgren --- Documentation/devicetree/bindings/arm/omap/mpu.txt | 16 + drivers/soc/ti/Kconfig | 9 + drivers/soc/ti/Makefile | 1 + drivers/soc/ti/pm33xx.c | 349 +++++++++++++++++++++ 4 files changed, 375 insertions(+) create mode 100644 drivers/soc/ti/pm33xx.c (limited to 'drivers') diff --git a/Documentation/devicetree/bindings/arm/omap/mpu.txt b/Documentation/devicetree/bindings/arm/omap/mpu.txt index 763695db2bd9..f301e636fd52 100644 --- a/Documentation/devicetree/bindings/arm/omap/mpu.txt +++ b/Documentation/devicetree/bindings/arm/omap/mpu.txt @@ -13,6 +13,13 @@ Required properties: Optional properties: - sram: Phandle to the ocmcram node +am335x and am437x only: +- pm-sram: Phandles to ocmcram nodes to be used for power management. + First should be type 'protect-exec' for the driver to use to copy + and run PM functions, second should be regular pool to be used for + data region for code. See Documentation/devicetree/bindings/sram/sram.txt + for more details. + Examples: - For an OMAP5 SMP system: @@ -36,3 +43,12 @@ mpu { compatible = "ti,omap3-mpu"; ti,hwmods = "mpu"; }; + +- For an AM335x system: + +mpu { + compatible = "ti,omap3-mpu"; + ti,hwmods = "mpu"; + pm-sram = <&pm_sram_code + &pm_sram_data>; +}; diff --git a/drivers/soc/ti/Kconfig b/drivers/soc/ti/Kconfig index 39e152abe6b9..92770d84a288 100644 --- a/drivers/soc/ti/Kconfig +++ b/drivers/soc/ti/Kconfig @@ -28,6 +28,15 @@ config KEYSTONE_NAVIGATOR_DMA If unsure, say N. +config AMX3_PM + tristate "AMx3 Power Management" + depends on SOC_AM33XX || SOC_AM43XX + depends on WKUP_M3_IPC && TI_EMIF_SRAM && SRAM + help + Enable power management on AM335x and AM437x. Required for suspend to mem + and standby states on both AM335x and AM437x platforms and for deeper cpuidle + c-states on AM335x. + config WKUP_M3_IPC tristate "TI AMx3 Wkup-M3 IPC Driver" depends on WKUP_M3_RPROC diff --git a/drivers/soc/ti/Makefile b/drivers/soc/ti/Makefile index 8e205287f120..a22edc0b258a 100644 --- a/drivers/soc/ti/Makefile +++ b/drivers/soc/ti/Makefile @@ -5,5 +5,6 @@ obj-$(CONFIG_KEYSTONE_NAVIGATOR_QMSS) += knav_qmss.o knav_qmss-y := knav_qmss_queue.o knav_qmss_acc.o obj-$(CONFIG_KEYSTONE_NAVIGATOR_DMA) += knav_dma.o +obj-$(CONFIG_AMX3_PM) += pm33xx.o obj-$(CONFIG_WKUP_M3_IPC) += wkup_m3_ipc.o obj-$(CONFIG_TI_SCI_PM_DOMAINS) += ti_sci_pm_domains.o diff --git a/drivers/soc/ti/pm33xx.c b/drivers/soc/ti/pm33xx.c new file mode 100644 index 000000000000..652739c7f718 --- /dev/null +++ b/drivers/soc/ti/pm33xx.c @@ -0,0 +1,349 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * AM33XX Power Management Routines + * + * Copyright (C) 2012-2018 Texas Instruments Incorporated - http://www.ti.com/ + * Vaibhav Bedia, Dave Gerlach + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#define AMX3_PM_SRAM_SYMBOL_OFFSET(sym) ((unsigned long)(sym) - \ + (unsigned long)pm_sram->do_wfi) + +static int (*am33xx_do_wfi_sram)(unsigned long unused); +static phys_addr_t am33xx_do_wfi_sram_phys; + +static struct gen_pool *sram_pool, *sram_pool_data; +static unsigned long ocmcram_location, ocmcram_location_data; + +static struct am33xx_pm_platform_data *pm_ops; +static struct am33xx_pm_sram_addr *pm_sram; + +static struct device *pm33xx_dev; +static struct wkup_m3_ipc *m3_ipc; + +static u32 sram_suspend_address(unsigned long addr) +{ + return ((unsigned long)am33xx_do_wfi_sram + + AMX3_PM_SRAM_SYMBOL_OFFSET(addr)); +} + +#ifdef CONFIG_SUSPEND +static int am33xx_pm_suspend(suspend_state_t suspend_state) +{ + int i, ret = 0; + + ret = pm_ops->soc_suspend((unsigned long)suspend_state, + am33xx_do_wfi_sram); + + if (ret) { + dev_err(pm33xx_dev, "PM: Kernel suspend failure\n"); + } else { + i = m3_ipc->ops->request_pm_status(m3_ipc); + + switch (i) { + case 0: + dev_info(pm33xx_dev, + "PM: Successfully put all powerdomains to target state\n"); + break; + case 1: + dev_err(pm33xx_dev, + "PM: Could not transition all powerdomains to target state\n"); + ret = -1; + break; + default: + dev_err(pm33xx_dev, + "PM: CM3 returned unknown result = %d\n", i); + ret = -1; + } + } + + return ret; +} + +static int am33xx_pm_enter(suspend_state_t suspend_state) +{ + int ret = 0; + + switch (suspend_state) { + case PM_SUSPEND_MEM: + case PM_SUSPEND_STANDBY: + ret = am33xx_pm_suspend(suspend_state); + break; + default: + ret = -EINVAL; + } + + return ret; +} + +static int am33xx_pm_begin(suspend_state_t state) +{ + int ret = -EINVAL; + + switch (state) { + case PM_SUSPEND_MEM: + ret = m3_ipc->ops->prepare_low_power(m3_ipc, WKUP_M3_DEEPSLEEP); + break; + case PM_SUSPEND_STANDBY: + ret = m3_ipc->ops->prepare_low_power(m3_ipc, WKUP_M3_STANDBY); + break; + } + + return ret; +} + +static void am33xx_pm_end(void) +{ + m3_ipc->ops->finish_low_power(m3_ipc); +} + +static int am33xx_pm_valid(suspend_state_t state) +{ + switch (state) { + case PM_SUSPEND_STANDBY: + case PM_SUSPEND_MEM: + return 1; + default: + return 0; + } +} + +static const struct platform_suspend_ops am33xx_pm_ops = { + .begin = am33xx_pm_begin, + .end = am33xx_pm_end, + .enter = am33xx_pm_enter, + .valid = am33xx_pm_valid, +}; +#endif /* CONFIG_SUSPEND */ + +static void am33xx_pm_set_ipc_ops(void) +{ + u32 resume_address; + int temp; + + temp = ti_emif_get_mem_type(); + if (temp < 0) { + dev_err(pm33xx_dev, "PM: Cannot determine memory type, no PM available\n"); + return; + } + m3_ipc->ops->set_mem_type(m3_ipc, temp); + + /* Physical resume address to be used by ROM code */ + resume_address = am33xx_do_wfi_sram_phys + + *pm_sram->resume_offset + 0x4; + + m3_ipc->ops->set_resume_address(m3_ipc, (void *)resume_address); +} + +static void am33xx_pm_free_sram(void) +{ + gen_pool_free(sram_pool, ocmcram_location, *pm_sram->do_wfi_sz); + gen_pool_free(sram_pool_data, ocmcram_location_data, + sizeof(struct am33xx_pm_ro_sram_data)); +} + +/* + * Push the minimal suspend-resume code to SRAM + */ +static int am33xx_pm_alloc_sram(void) +{ + struct device_node *np; + int ret = 0; + + np = of_find_compatible_node(NULL, NULL, "ti,omap3-mpu"); + if (!np) { + np = of_find_compatible_node(NULL, NULL, "ti,omap4-mpu"); + if (!np) { + dev_err(pm33xx_dev, "PM: %s: Unable to find device node for mpu\n", + __func__); + return -ENODEV; + } + } + + sram_pool = of_gen_pool_get(np, "pm-sram", 0); + if (!sram_pool) { + dev_err(pm33xx_dev, "PM: %s: Unable to get sram pool for ocmcram\n", + __func__); + ret = -ENODEV; + goto mpu_put_node; + } + + sram_pool_data = of_gen_pool_get(np, "pm-sram", 1); + if (!sram_pool_data) { + dev_err(pm33xx_dev, "PM: %s: Unable to get sram data pool for ocmcram\n", + __func__); + ret = -ENODEV; + goto mpu_put_node; + } + + ocmcram_location = gen_pool_alloc(sram_pool, *pm_sram->do_wfi_sz); + if (!ocmcram_location) { + dev_err(pm33xx_dev, "PM: %s: Unable to allocate memory from ocmcram\n", + __func__); + ret = -ENOMEM; + goto mpu_put_node; + } + + ocmcram_location_data = gen_pool_alloc(sram_pool_data, + sizeof(struct emif_regs_amx3)); + if (!ocmcram_location_data) { + dev_err(pm33xx_dev, "PM: Unable to allocate memory from ocmcram\n"); + gen_pool_free(sram_pool, ocmcram_location, *pm_sram->do_wfi_sz); + ret = -ENOMEM; + } + +mpu_put_node: + of_node_put(np); + return ret; +} + +static int am33xx_push_sram_idle(void) +{ + struct am33xx_pm_ro_sram_data ro_sram_data; + int ret; + u32 table_addr, ro_data_addr; + void *copy_addr; + + ro_sram_data.amx3_pm_sram_data_virt = ocmcram_location_data; + ro_sram_data.amx3_pm_sram_data_phys = + gen_pool_virt_to_phys(sram_pool_data, ocmcram_location_data); + + /* Save physical address to calculate resume offset during pm init */ + am33xx_do_wfi_sram_phys = gen_pool_virt_to_phys(sram_pool, + ocmcram_location); + + am33xx_do_wfi_sram = sram_exec_copy(sram_pool, (void *)ocmcram_location, + pm_sram->do_wfi, + *pm_sram->do_wfi_sz); + if (!am33xx_do_wfi_sram) { + dev_err(pm33xx_dev, + "PM: %s: am33xx_do_wfi copy to sram failed\n", + __func__); + return -ENODEV; + } + + table_addr = + sram_suspend_address((unsigned long)pm_sram->emif_sram_table); + ret = ti_emif_copy_pm_function_table(sram_pool, (void *)table_addr); + if (ret) { + dev_dbg(pm33xx_dev, + "PM: %s: EMIF function copy failed\n", __func__); + return -EPROBE_DEFER; + } + + ro_data_addr = + sram_suspend_address((unsigned long)pm_sram->ro_sram_data); + copy_addr = sram_exec_copy(sram_pool, (void *)ro_data_addr, + &ro_sram_data, + sizeof(ro_sram_data)); + if (!copy_addr) { + dev_err(pm33xx_dev, + "PM: %s: ro_sram_data copy to sram failed\n", + __func__); + return -ENODEV; + } + + return 0; +} + +static int am33xx_pm_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + int ret; + + if (!of_machine_is_compatible("ti,am33xx") && + !of_machine_is_compatible("ti,am43")) + return -ENODEV; + + pm_ops = dev->platform_data; + if (!pm_ops) { + dev_err(dev, "PM: Cannot get core PM ops!\n"); + return -ENODEV; + } + + pm_sram = pm_ops->get_sram_addrs(); + if (!pm_sram) { + dev_err(dev, "PM: Cannot get PM asm function addresses!!\n"); + return -ENODEV; + } + + pm33xx_dev = dev; + + ret = am33xx_pm_alloc_sram(); + if (ret) + return ret; + + ret = am33xx_push_sram_idle(); + if (ret) + goto err_free_sram; + + m3_ipc = wkup_m3_ipc_get(); + if (!m3_ipc) { + dev_dbg(dev, "PM: Cannot get wkup_m3_ipc handle\n"); + ret = -EPROBE_DEFER; + goto err_free_sram; + } + + am33xx_pm_set_ipc_ops(); + +#ifdef CONFIG_SUSPEND + suspend_set_ops(&am33xx_pm_ops); +#endif /* CONFIG_SUSPEND */ + + ret = pm_ops->init(); + if (ret) { + dev_err(dev, "Unable to call core pm init!\n"); + ret = -ENODEV; + goto err_put_wkup_m3_ipc; + } + + return 0; + +err_put_wkup_m3_ipc: + wkup_m3_ipc_put(m3_ipc); +err_free_sram: + am33xx_pm_free_sram(); + pm33xx_dev = NULL; + return ret; +} + +static int am33xx_pm_remove(struct platform_device *pdev) +{ + suspend_set_ops(NULL); + wkup_m3_ipc_put(m3_ipc); + am33xx_pm_free_sram(); + return 0; +} + +static struct platform_driver am33xx_pm_driver = { + .driver = { + .name = "pm33xx", + }, + .probe = am33xx_pm_probe, + .remove = am33xx_pm_remove, +}; +module_platform_driver(am33xx_pm_driver); + +MODULE_ALIAS("platform:pm33xx"); +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("am33xx power management driver"); -- cgit v1.2.3 From 592ea6bd1fad6068fb7d813d36cfd832313f4421 Mon Sep 17 00:00:00 2001 From: Ladislav Michl Date: Wed, 28 Feb 2018 08:25:19 +0100 Subject: clocksource: timer-ti-dm: Make unexported functions static As dmtimer no longer exports functions, make those previously exported static (this requires few functions to be moved around as their prototypes were deleted). Signed-off-by: Ladislav Michl Signed-off-by: Tony Lindgren --- drivers/clocksource/timer-ti-dm.c | 224 +++++++++++++++++++------------------- include/clocksource/timer-ti-dm.h | 33 ------ 2 files changed, 115 insertions(+), 142 deletions(-) (limited to 'drivers') diff --git a/drivers/clocksource/timer-ti-dm.c b/drivers/clocksource/timer-ti-dm.c index 70782a41c493..935350176c01 100644 --- a/drivers/clocksource/timer-ti-dm.c +++ b/drivers/clocksource/timer-ti-dm.c @@ -163,6 +163,92 @@ static int omap_dm_timer_of_set_source(struct omap_dm_timer *timer) return ret; } +static int omap_dm_timer_set_source(struct omap_dm_timer *timer, int source) +{ + int ret; + char *parent_name = NULL; + struct clk *parent; + struct dmtimer_platform_data *pdata; + + if (unlikely(!timer)) + return -EINVAL; + + pdata = timer->pdev->dev.platform_data; + + if (source < 0 || source >= 3) + return -EINVAL; + + /* + * FIXME: Used for OMAP1 devices only because they do not currently + * use the clock framework to set the parent clock. To be removed + * once OMAP1 migrated to using clock framework for dmtimers + */ + if (pdata && pdata->set_timer_src) + return pdata->set_timer_src(timer->pdev, source); + + if (IS_ERR(timer->fclk)) + return -EINVAL; + +#if defined(CONFIG_COMMON_CLK) + /* Check if the clock has configurable parents */ + if (clk_hw_get_num_parents(__clk_get_hw(timer->fclk)) < 2) + return 0; +#endif + + switch (source) { + case OMAP_TIMER_SRC_SYS_CLK: + parent_name = "timer_sys_ck"; + break; + + case OMAP_TIMER_SRC_32_KHZ: + parent_name = "timer_32k_ck"; + break; + + case OMAP_TIMER_SRC_EXT_CLK: + parent_name = "timer_ext_ck"; + break; + } + + parent = clk_get(&timer->pdev->dev, parent_name); + if (IS_ERR(parent)) { + pr_err("%s: %s not found\n", __func__, parent_name); + return -EINVAL; + } + + ret = clk_set_parent(timer->fclk, parent); + if (ret < 0) + pr_err("%s: failed to set %s as parent\n", __func__, + parent_name); + + clk_put(parent); + + return ret; +} + +static void omap_dm_timer_enable(struct omap_dm_timer *timer) +{ + int c; + + pm_runtime_get_sync(&timer->pdev->dev); + + if (!(timer->capability & OMAP_TIMER_ALWON)) { + if (timer->get_context_loss_count) { + c = timer->get_context_loss_count(&timer->pdev->dev); + if (c != timer->ctx_loss_count) { + omap_timer_restore_context(timer); + timer->ctx_loss_count = c; + } + } else { + omap_timer_restore_context(timer); + } + } +} + +static void omap_dm_timer_disable(struct omap_dm_timer *timer) +{ + pm_runtime_put_sync(&timer->pdev->dev); +} + static int omap_dm_timer_prepare(struct omap_dm_timer *timer) { int rc; @@ -298,16 +384,16 @@ found: return timer; } -struct omap_dm_timer *omap_dm_timer_request(void) +static struct omap_dm_timer *omap_dm_timer_request(void) { return _omap_dm_timer_request(REQUEST_ANY, NULL); } -struct omap_dm_timer *omap_dm_timer_request_specific(int id) +static struct omap_dm_timer *omap_dm_timer_request_specific(int id) { /* Requesting timer by ID is not supported when device tree is used */ if (of_have_populated_dt()) { - pr_warn("%s: Please use omap_dm_timer_request_by_cap/node()\n", + pr_warn("%s: Please use omap_dm_timer_request_by_node()\n", __func__); return NULL; } @@ -336,7 +422,7 @@ struct omap_dm_timer *omap_dm_timer_request_by_cap(u32 cap) * Request a timer based upon a device node pointer. Returns pointer to * timer handle on success and a NULL pointer on failure. */ -struct omap_dm_timer *omap_dm_timer_request_by_node(struct device_node *np) +static struct omap_dm_timer *omap_dm_timer_request_by_node(struct device_node *np) { if (!np) return NULL; @@ -344,7 +430,7 @@ struct omap_dm_timer *omap_dm_timer_request_by_node(struct device_node *np) return _omap_dm_timer_request(REQUEST_BY_NODE, np); } -int omap_dm_timer_free(struct omap_dm_timer *timer) +static int omap_dm_timer_free(struct omap_dm_timer *timer) { if (unlikely(!timer)) return -EINVAL; @@ -356,30 +442,6 @@ int omap_dm_timer_free(struct omap_dm_timer *timer) return 0; } -void omap_dm_timer_enable(struct omap_dm_timer *timer) -{ - int c; - - pm_runtime_get_sync(&timer->pdev->dev); - - if (!(timer->capability & OMAP_TIMER_ALWON)) { - if (timer->get_context_loss_count) { - c = timer->get_context_loss_count(&timer->pdev->dev); - if (c != timer->ctx_loss_count) { - omap_timer_restore_context(timer); - timer->ctx_loss_count = c; - } - } else { - omap_timer_restore_context(timer); - } - } -} - -void omap_dm_timer_disable(struct omap_dm_timer *timer) -{ - pm_runtime_put_sync(&timer->pdev->dev); -} - int omap_dm_timer_get_irq(struct omap_dm_timer *timer) { if (timer) @@ -389,6 +451,12 @@ int omap_dm_timer_get_irq(struct omap_dm_timer *timer) #if defined(CONFIG_ARCH_OMAP1) #include + +static struct clk *omap_dm_timer_get_fclk(struct omap_dm_timer *timer) +{ + return NULL; +} + /** * omap_dm_timer_modify_idlect_mask - Check if any running timers use ARMXOR * @inputmask: current value of idlect mask @@ -424,7 +492,7 @@ __u32 omap_dm_timer_modify_idlect_mask(__u32 inputmask) #else -struct clk *omap_dm_timer_get_fclk(struct omap_dm_timer *timer) +static struct clk *omap_dm_timer_get_fclk(struct omap_dm_timer *timer) { if (timer && !IS_ERR(timer->fclk)) return timer->fclk; @@ -451,7 +519,7 @@ int omap_dm_timer_trigger(struct omap_dm_timer *timer) return 0; } -int omap_dm_timer_start(struct omap_dm_timer *timer) +static int omap_dm_timer_start(struct omap_dm_timer *timer) { u32 l; @@ -471,7 +539,7 @@ int omap_dm_timer_start(struct omap_dm_timer *timer) return 0; } -int omap_dm_timer_stop(struct omap_dm_timer *timer) +static int omap_dm_timer_stop(struct omap_dm_timer *timer) { unsigned long rate = 0; @@ -494,70 +562,8 @@ int omap_dm_timer_stop(struct omap_dm_timer *timer) return 0; } -int omap_dm_timer_set_source(struct omap_dm_timer *timer, int source) -{ - int ret; - char *parent_name = NULL; - struct clk *parent; - struct dmtimer_platform_data *pdata; - - if (unlikely(!timer)) - return -EINVAL; - - pdata = timer->pdev->dev.platform_data; - - if (source < 0 || source >= 3) - return -EINVAL; - - /* - * FIXME: Used for OMAP1 devices only because they do not currently - * use the clock framework to set the parent clock. To be removed - * once OMAP1 migrated to using clock framework for dmtimers - */ - if (pdata && pdata->set_timer_src) - return pdata->set_timer_src(timer->pdev, source); - - if (IS_ERR(timer->fclk)) - return -EINVAL; - -#if defined(CONFIG_COMMON_CLK) - /* Check if the clock has configurable parents */ - if (clk_hw_get_num_parents(__clk_get_hw(timer->fclk)) < 2) - return 0; -#endif - - switch (source) { - case OMAP_TIMER_SRC_SYS_CLK: - parent_name = "timer_sys_ck"; - break; - - case OMAP_TIMER_SRC_32_KHZ: - parent_name = "timer_32k_ck"; - break; - - case OMAP_TIMER_SRC_EXT_CLK: - parent_name = "timer_ext_ck"; - break; - } - - parent = clk_get(&timer->pdev->dev, parent_name); - if (IS_ERR(parent)) { - pr_err("%s: %s not found\n", __func__, parent_name); - return -EINVAL; - } - - ret = clk_set_parent(timer->fclk, parent); - if (ret < 0) - pr_err("%s: failed to set %s as parent\n", __func__, - parent_name); - - clk_put(parent); - - return ret; -} - -int omap_dm_timer_set_load(struct omap_dm_timer *timer, int autoreload, - unsigned int load) +static int omap_dm_timer_set_load(struct omap_dm_timer *timer, int autoreload, + unsigned int load) { u32 l; @@ -609,9 +615,8 @@ int omap_dm_timer_set_load_start(struct omap_dm_timer *timer, int autoreload, timer->context.tcrr = load; return 0; } - -int omap_dm_timer_set_match(struct omap_dm_timer *timer, int enable, - unsigned int match) +static int omap_dm_timer_set_match(struct omap_dm_timer *timer, int enable, + unsigned int match) { u32 l; @@ -634,8 +639,8 @@ int omap_dm_timer_set_match(struct omap_dm_timer *timer, int enable, return 0; } -int omap_dm_timer_set_pwm(struct omap_dm_timer *timer, int def_on, - int toggle, int trigger) +static int omap_dm_timer_set_pwm(struct omap_dm_timer *timer, int def_on, + int toggle, int trigger) { u32 l; @@ -659,7 +664,8 @@ int omap_dm_timer_set_pwm(struct omap_dm_timer *timer, int def_on, return 0; } -int omap_dm_timer_set_prescaler(struct omap_dm_timer *timer, int prescaler) +static int omap_dm_timer_set_prescaler(struct omap_dm_timer *timer, + int prescaler) { u32 l; @@ -681,8 +687,8 @@ int omap_dm_timer_set_prescaler(struct omap_dm_timer *timer, int prescaler) return 0; } -int omap_dm_timer_set_int_enable(struct omap_dm_timer *timer, - unsigned int value) +static int omap_dm_timer_set_int_enable(struct omap_dm_timer *timer, + unsigned int value) { if (unlikely(!timer)) return -EINVAL; @@ -704,7 +710,7 @@ int omap_dm_timer_set_int_enable(struct omap_dm_timer *timer, * * Disables the specified timer interrupts for a timer. */ -int omap_dm_timer_set_int_disable(struct omap_dm_timer *timer, u32 mask) +static int omap_dm_timer_set_int_disable(struct omap_dm_timer *timer, u32 mask) { u32 l = mask; @@ -727,7 +733,7 @@ int omap_dm_timer_set_int_disable(struct omap_dm_timer *timer, u32 mask) return 0; } -unsigned int omap_dm_timer_read_status(struct omap_dm_timer *timer) +static unsigned int omap_dm_timer_read_status(struct omap_dm_timer *timer) { unsigned int l; @@ -741,7 +747,7 @@ unsigned int omap_dm_timer_read_status(struct omap_dm_timer *timer) return l; } -int omap_dm_timer_write_status(struct omap_dm_timer *timer, unsigned int value) +static int omap_dm_timer_write_status(struct omap_dm_timer *timer, unsigned int value) { if (unlikely(!timer || pm_runtime_suspended(&timer->pdev->dev))) return -EINVAL; @@ -751,7 +757,7 @@ int omap_dm_timer_write_status(struct omap_dm_timer *timer, unsigned int value) return 0; } -unsigned int omap_dm_timer_read_counter(struct omap_dm_timer *timer) +static unsigned int omap_dm_timer_read_counter(struct omap_dm_timer *timer) { if (unlikely(!timer || pm_runtime_suspended(&timer->pdev->dev))) { pr_err("%s: timer not iavailable or enabled.\n", __func__); @@ -761,7 +767,7 @@ unsigned int omap_dm_timer_read_counter(struct omap_dm_timer *timer) return __omap_dm_timer_read_counter(timer, timer->posted); } -int omap_dm_timer_write_counter(struct omap_dm_timer *timer, unsigned int value) +static int omap_dm_timer_write_counter(struct omap_dm_timer *timer, unsigned int value) { if (unlikely(!timer || pm_runtime_suspended(&timer->pdev->dev))) { pr_err("%s: timer not available or enabled.\n", __func__); diff --git a/include/clocksource/timer-ti-dm.h b/include/clocksource/timer-ti-dm.h index 4f310957c21b..7d9598dc578d 100644 --- a/include/clocksource/timer-ti-dm.h +++ b/include/clocksource/timer-ti-dm.h @@ -119,46 +119,13 @@ struct omap_dm_timer { }; int omap_dm_timer_reserve_systimer(int id); -struct omap_dm_timer *omap_dm_timer_request(void); -struct omap_dm_timer *omap_dm_timer_request_specific(int timer_id); struct omap_dm_timer *omap_dm_timer_request_by_cap(u32 cap); -struct omap_dm_timer *omap_dm_timer_request_by_node(struct device_node *np); -int omap_dm_timer_free(struct omap_dm_timer *timer); -void omap_dm_timer_enable(struct omap_dm_timer *timer); -void omap_dm_timer_disable(struct omap_dm_timer *timer); int omap_dm_timer_get_irq(struct omap_dm_timer *timer); u32 omap_dm_timer_modify_idlect_mask(u32 inputmask); -#ifndef CONFIG_ARCH_OMAP1 -struct clk *omap_dm_timer_get_fclk(struct omap_dm_timer *timer); -#else -static inline -struct clk *omap_dm_timer_get_fclk(struct omap_dm_timer *timer) -{ - return NULL; -} -#endif - int omap_dm_timer_trigger(struct omap_dm_timer *timer); -int omap_dm_timer_start(struct omap_dm_timer *timer); -int omap_dm_timer_stop(struct omap_dm_timer *timer); - -int omap_dm_timer_set_source(struct omap_dm_timer *timer, int source); -int omap_dm_timer_set_load(struct omap_dm_timer *timer, int autoreload, unsigned int value); -int omap_dm_timer_set_load_start(struct omap_dm_timer *timer, int autoreload, unsigned int value); -int omap_dm_timer_set_match(struct omap_dm_timer *timer, int enable, unsigned int match); -int omap_dm_timer_set_pwm(struct omap_dm_timer *timer, int def_on, int toggle, int trigger); -int omap_dm_timer_set_prescaler(struct omap_dm_timer *timer, int prescaler); - -int omap_dm_timer_set_int_enable(struct omap_dm_timer *timer, unsigned int value); -int omap_dm_timer_set_int_disable(struct omap_dm_timer *timer, u32 mask); - -unsigned int omap_dm_timer_read_status(struct omap_dm_timer *timer); -int omap_dm_timer_write_status(struct omap_dm_timer *timer, unsigned int value); -unsigned int omap_dm_timer_read_counter(struct omap_dm_timer *timer); -int omap_dm_timer_write_counter(struct omap_dm_timer *timer, unsigned int value); int omap_dm_timers_active(void); -- cgit v1.2.3 From ad6e4b6fdc99ebbd520d2faf3c6d48198c74a99b Mon Sep 17 00:00:00 2001 From: Ladislav Michl Date: Fri, 23 Feb 2018 11:14:22 +0100 Subject: clocksource: timer-ti-dm: Consolidate set source Reorder omap_dm_timer_set_source internals to get source verification more straightforward. Signed-off-by: Ladislav Michl Signed-off-by: Tony Lindgren --- drivers/clocksource/timer-ti-dm.c | 38 ++++++++++++++++---------------------- 1 file changed, 16 insertions(+), 22 deletions(-) (limited to 'drivers') diff --git a/drivers/clocksource/timer-ti-dm.c b/drivers/clocksource/timer-ti-dm.c index 935350176c01..6abe5ab553ce 100644 --- a/drivers/clocksource/timer-ti-dm.c +++ b/drivers/clocksource/timer-ti-dm.c @@ -166,17 +166,28 @@ static int omap_dm_timer_of_set_source(struct omap_dm_timer *timer) static int omap_dm_timer_set_source(struct omap_dm_timer *timer, int source) { int ret; - char *parent_name = NULL; + const char *parent_name; struct clk *parent; struct dmtimer_platform_data *pdata; - if (unlikely(!timer)) + if (unlikely(!timer) || IS_ERR(timer->fclk)) return -EINVAL; - pdata = timer->pdev->dev.platform_data; - - if (source < 0 || source >= 3) + switch (source) { + case OMAP_TIMER_SRC_SYS_CLK: + parent_name = "timer_sys_ck"; + break; + case OMAP_TIMER_SRC_32_KHZ: + parent_name = "timer_32k_ck"; + break; + case OMAP_TIMER_SRC_EXT_CLK: + parent_name = "timer_ext_ck"; + break; + default: return -EINVAL; + } + + pdata = timer->pdev->dev.platform_data; /* * FIXME: Used for OMAP1 devices only because they do not currently @@ -186,29 +197,12 @@ static int omap_dm_timer_set_source(struct omap_dm_timer *timer, int source) if (pdata && pdata->set_timer_src) return pdata->set_timer_src(timer->pdev, source); - if (IS_ERR(timer->fclk)) - return -EINVAL; - #if defined(CONFIG_COMMON_CLK) /* Check if the clock has configurable parents */ if (clk_hw_get_num_parents(__clk_get_hw(timer->fclk)) < 2) return 0; #endif - switch (source) { - case OMAP_TIMER_SRC_SYS_CLK: - parent_name = "timer_sys_ck"; - break; - - case OMAP_TIMER_SRC_32_KHZ: - parent_name = "timer_32k_ck"; - break; - - case OMAP_TIMER_SRC_EXT_CLK: - parent_name = "timer_ext_ck"; - break; - } - parent = clk_get(&timer->pdev->dev, parent_name); if (IS_ERR(parent)) { pr_err("%s: %s not found\n", __func__, parent_name); -- cgit v1.2.3 From 58a54f03e73d67432216931854363e4ba15839b9 Mon Sep 17 00:00:00 2001 From: Ladislav Michl Date: Fri, 23 Feb 2018 11:15:01 +0100 Subject: clocksource: timer-ti-dm: Check prescaler value Invalid value silently disables use of the prescaler. Use -1 explicitely for that purpose and error out on invalid value. Signed-off-by: Ladislav Michl Signed-off-by: Tony Lindgren --- drivers/clocksource/timer-ti-dm.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/clocksource/timer-ti-dm.c b/drivers/clocksource/timer-ti-dm.c index 6abe5ab553ce..4cce6b224b87 100644 --- a/drivers/clocksource/timer-ti-dm.c +++ b/drivers/clocksource/timer-ti-dm.c @@ -663,13 +663,13 @@ static int omap_dm_timer_set_prescaler(struct omap_dm_timer *timer, { u32 l; - if (unlikely(!timer)) + if (unlikely(!timer) || prescaler < -1 || prescaler > 7) return -EINVAL; omap_dm_timer_enable(timer); l = omap_dm_timer_read_reg(timer, OMAP_TIMER_CTRL_REG); l &= ~(OMAP_TIMER_CTRL_PRE | (0x07 << 2)); - if (prescaler >= 0x00 && prescaler <= 0x07) { + if (prescaler >= 0) { l |= OMAP_TIMER_CTRL_PRE; l |= prescaler << 2; } -- cgit v1.2.3 From 2ad1ec0d2fddcb9cf17632dacf4d70c81a0c49e7 Mon Sep 17 00:00:00 2001 From: Tony Lindgren Date: Fri, 23 Feb 2018 10:03:28 -0800 Subject: PM / AVS: SmartReflex: Prepare to use device tree based probing We are currently probing smartreflex with omap_device while we are already probing smartreflex related interconnect target module with ti-sysc driver and dts data. Before we can flip things on for ti-sysc, we need to prepare the smartreflex driver a bit: 1. The smartreflex clock is really for the whole interconnect target module. So it may be configured at the parent device level with ti-sysc 2. With ti-sysc, we have the child device manage interconnect target module directly if pm_runtime_irq_safe() is set and there is only one child. In that case nobody else is going to call pm_runtime_get and put, so we need to add these calls to idle smartreflex properly after probe if not fully configured 3. With ti-sysc, the parent driver may rebind. So we want to use platform_driver_register() and don't want probe to be __init Note that this patch depends on the related changes to ti-sysc driver and omap_device probing to prevent both ti-sysc and omap_device to try to probe smartreflex. Cc: linux-pm@vger.kernel.org Cc: Rafael J. Wysocki Acked-by: Kevin Hilman Signed-off-by: Tony Lindgren --- drivers/power/avs/smartreflex.c | 41 ++++++++++++++++++++++++++++++++++------- 1 file changed, 34 insertions(+), 7 deletions(-) (limited to 'drivers') diff --git a/drivers/power/avs/smartreflex.c b/drivers/power/avs/smartreflex.c index 89bf4d6cb486..cb0237143dbe 100644 --- a/drivers/power/avs/smartreflex.c +++ b/drivers/power/avs/smartreflex.c @@ -132,12 +132,16 @@ static void sr_set_clk_length(struct omap_sr *sr) struct clk *fck; u32 fclk_speed; - fck = clk_get(&sr->pdev->dev, "fck"); - + /* Try interconnect target module fck first if it already exists */ + fck = clk_get(sr->pdev->dev.parent, "fck"); if (IS_ERR(fck)) { - dev_err(&sr->pdev->dev, "%s: unable to get fck for device %s\n", - __func__, dev_name(&sr->pdev->dev)); - return; + fck = clk_get(&sr->pdev->dev, "fck"); + if (IS_ERR(fck)) { + dev_err(&sr->pdev->dev, + "%s: unable to get fck for device %s\n", + __func__, dev_name(&sr->pdev->dev)); + return; + } } fclk_speed = clk_get_rate(fck); @@ -838,7 +842,7 @@ static int omap_sr_autocomp_store(void *data, u64 val) DEFINE_SIMPLE_ATTRIBUTE(pm_sr_fops, omap_sr_autocomp_show, omap_sr_autocomp_store, "%llu\n"); -static int __init omap_sr_probe(struct platform_device *pdev) +static int omap_sr_probe(struct platform_device *pdev) { struct omap_sr *sr_info; struct omap_sr_data *pdata = pdev->dev.platform_data; @@ -898,6 +902,12 @@ static int __init omap_sr_probe(struct platform_device *pdev) list_add(&sr_info->node, &sr_list); + ret = pm_runtime_get_sync(&pdev->dev); + if (ret < 0) { + pm_runtime_put_noidle(&pdev->dev); + goto err_list_del; + } + /* * Call into late init to do initializations that require * both sr driver and sr class driver to be initiallized. @@ -966,12 +976,17 @@ static int __init omap_sr_probe(struct platform_device *pdev) } + pm_runtime_put_sync(&pdev->dev); + return ret; err_debugfs: debugfs_remove_recursive(sr_info->dbg_dir); err_list_del: list_del(&sr_info->node); + + pm_runtime_put_sync(&pdev->dev); + return ret; } @@ -1025,11 +1040,23 @@ static void omap_sr_shutdown(struct platform_device *pdev) return; } +static const struct of_device_id omap_sr_match[] = { + { .compatible = "ti,omap3-smartreflex-core", }, + { .compatible = "ti,omap3-smartreflex-mpu-iva", }, + { .compatible = "ti,omap4-smartreflex-core", }, + { .compatible = "ti,omap4-smartreflex-mpu", }, + { .compatible = "ti,omap4-smartreflex-iva", }, + { }, +}; +MODULE_DEVICE_TABLE(of, omap_sr_match); + static struct platform_driver smartreflex_driver = { + .probe = omap_sr_probe, .remove = omap_sr_remove, .shutdown = omap_sr_shutdown, .driver = { .name = DRIVER_NAME, + .of_match_table = omap_sr_match, }, }; @@ -1048,7 +1075,7 @@ static int __init sr_init(void) else pr_warn("%s: No PMIC hook to init smartreflex\n", __func__); - ret = platform_driver_probe(&smartreflex_driver, omap_sr_probe); + ret = platform_driver_register(&smartreflex_driver); if (ret) { pr_err("%s: platform driver register failed for SR\n", __func__); -- cgit v1.2.3 From 7576594c8e69f5a9e08c5b952d5139bb43574bbc Mon Sep 17 00:00:00 2001 From: Miquel Raynal Date: Mon, 19 Feb 2018 23:35:54 +0100 Subject: mtd: nand: remove useless fields from pxa3xx NAND platform data The "enable arbiter" bit is available only for pxa3xx based platforms but it was experimentally shown that even if this bit is reserved, some Marvell platforms (64-bit) actually need it to be set. The driver always set this bit regardless of this property, which is harmless. Then this property is not needed. The "num_cs" field is always 1 and for a good reason, the old driver (pxa3xx_nand.c) could only handle one. The new driver that replaces it (marvell_nand.c) can handle more, but better use device tree for such description. As there is only one available chip select, there is no need for an array of partitions neither an array of partition numbers. Signed-off-by: Miquel Raynal Acked-by: Robert Jarzmik Signed-off-by: Boris Brezillon --- arch/arm/mach-mmp/aspenite.c | 6 ++-- arch/arm/mach-mmp/ttc_dkb.c | 5 +--- arch/arm/mach-pxa/cm-x300.c | 6 ++-- arch/arm/mach-pxa/colibri-pxa3xx.c | 6 ++-- arch/arm/mach-pxa/littleton.c | 6 ++-- arch/arm/mach-pxa/mxm8x10.c | 6 ++-- arch/arm/mach-pxa/raumfeld.c | 6 ++-- arch/arm/mach-pxa/zylonite.c | 6 ++-- drivers/mtd/nand/marvell_nand.c | 3 +- include/linux/platform_data/mtd-nand-pxa3xx.h | 43 ++++++++------------------- 10 files changed, 28 insertions(+), 65 deletions(-) (limited to 'drivers') diff --git a/arch/arm/mach-mmp/aspenite.c b/arch/arm/mach-mmp/aspenite.c index d2283009a5ff..6c2ebf01893a 100644 --- a/arch/arm/mach-mmp/aspenite.c +++ b/arch/arm/mach-mmp/aspenite.c @@ -172,10 +172,8 @@ static struct mtd_partition aspenite_nand_partitions[] = { }; static struct pxa3xx_nand_platform_data aspenite_nand_info = { - .enable_arbiter = 1, - .num_cs = 1, - .parts[0] = aspenite_nand_partitions, - .nr_parts[0] = ARRAY_SIZE(aspenite_nand_partitions), + .parts = aspenite_nand_partitions, + .nr_parts = ARRAY_SIZE(aspenite_nand_partitions), }; static struct i2c_board_info aspenite_i2c_info[] __initdata = { diff --git a/arch/arm/mach-mmp/ttc_dkb.c b/arch/arm/mach-mmp/ttc_dkb.c index e0b6073c61a7..c7897fb2b6da 100644 --- a/arch/arm/mach-mmp/ttc_dkb.c +++ b/arch/arm/mach-mmp/ttc_dkb.c @@ -179,10 +179,7 @@ static struct mv_usb_platform_data ttc_usb_pdata = { #endif #if IS_ENABLED(CONFIG_MTD_NAND_MARVELL) -static struct pxa3xx_nand_platform_data dkb_nand_info = { - .enable_arbiter = 1, - .num_cs = 1, -}; +static struct pxa3xx_nand_platform_data dkb_nand_info = {}; #endif #if IS_ENABLED(CONFIG_MMP_DISP) diff --git a/arch/arm/mach-pxa/cm-x300.c b/arch/arm/mach-pxa/cm-x300.c index de1f8c995076..0e71799cab25 100644 --- a/arch/arm/mach-pxa/cm-x300.c +++ b/arch/arm/mach-pxa/cm-x300.c @@ -429,11 +429,9 @@ static struct mtd_partition cm_x300_nand_partitions[] = { }; static struct pxa3xx_nand_platform_data cm_x300_nand_info = { - .enable_arbiter = 1, .keep_config = 1, - .num_cs = 1, - .parts[0] = cm_x300_nand_partitions, - .nr_parts[0] = ARRAY_SIZE(cm_x300_nand_partitions), + .parts = cm_x300_nand_partitions, + .nr_parts = ARRAY_SIZE(cm_x300_nand_partitions), }; static void __init cm_x300_init_nand(void) diff --git a/arch/arm/mach-pxa/colibri-pxa3xx.c b/arch/arm/mach-pxa/colibri-pxa3xx.c index 3018eafd723e..e31a591e949f 100644 --- a/arch/arm/mach-pxa/colibri-pxa3xx.c +++ b/arch/arm/mach-pxa/colibri-pxa3xx.c @@ -138,11 +138,9 @@ static struct mtd_partition colibri_nand_partitions[] = { }; static struct pxa3xx_nand_platform_data colibri_nand_info = { - .enable_arbiter = 1, .keep_config = 1, - .num_cs = 1, - .parts[0] = colibri_nand_partitions, - .nr_parts[0] = ARRAY_SIZE(colibri_nand_partitions), + .parts = colibri_nand_partitions, + .nr_parts = ARRAY_SIZE(colibri_nand_partitions), }; void __init colibri_pxa3xx_init_nand(void) diff --git a/arch/arm/mach-pxa/littleton.c b/arch/arm/mach-pxa/littleton.c index 193dccca1086..9e132b3e48c6 100644 --- a/arch/arm/mach-pxa/littleton.c +++ b/arch/arm/mach-pxa/littleton.c @@ -329,10 +329,8 @@ static struct mtd_partition littleton_nand_partitions[] = { }; static struct pxa3xx_nand_platform_data littleton_nand_info = { - .enable_arbiter = 1, - .num_cs = 1, - .parts[0] = littleton_nand_partitions, - .nr_parts[0] = ARRAY_SIZE(littleton_nand_partitions), + .parts = littleton_nand_partitions, + .nr_parts = ARRAY_SIZE(littleton_nand_partitions), }; static void __init littleton_init_nand(void) diff --git a/arch/arm/mach-pxa/mxm8x10.c b/arch/arm/mach-pxa/mxm8x10.c index 5cc379c0626c..616b22397d73 100644 --- a/arch/arm/mach-pxa/mxm8x10.c +++ b/arch/arm/mach-pxa/mxm8x10.c @@ -389,11 +389,9 @@ static struct mtd_partition mxm_8x10_nand_partitions[] = { }; static struct pxa3xx_nand_platform_data mxm_8x10_nand_info = { - .enable_arbiter = 1, .keep_config = 1, - .num_cs = 1, - .parts[0] = mxm_8x10_nand_partitions, - .nr_parts[0] = ARRAY_SIZE(mxm_8x10_nand_partitions) + .parts = mxm_8x10_nand_partitions, + .nr_parts = ARRAY_SIZE(mxm_8x10_nand_partitions) }; static void __init mxm_8x10_nand_init(void) diff --git a/arch/arm/mach-pxa/raumfeld.c b/arch/arm/mach-pxa/raumfeld.c index 4d5d05cf87d6..8c95ae58312a 100644 --- a/arch/arm/mach-pxa/raumfeld.c +++ b/arch/arm/mach-pxa/raumfeld.c @@ -346,11 +346,9 @@ static struct mtd_partition raumfeld_nand_partitions[] = { }; static struct pxa3xx_nand_platform_data raumfeld_nand_info = { - .enable_arbiter = 1, .keep_config = 1, - .num_cs = 1, - .parts[0] = raumfeld_nand_partitions, - .nr_parts[0] = ARRAY_SIZE(raumfeld_nand_partitions), + .parts = raumfeld_nand_partitions, + .nr_parts = ARRAY_SIZE(raumfeld_nand_partitions), }; /** diff --git a/arch/arm/mach-pxa/zylonite.c b/arch/arm/mach-pxa/zylonite.c index 3a99fc054e96..d69de312d8d9 100644 --- a/arch/arm/mach-pxa/zylonite.c +++ b/arch/arm/mach-pxa/zylonite.c @@ -376,10 +376,8 @@ static struct mtd_partition zylonite_nand_partitions[] = { }; static struct pxa3xx_nand_platform_data zylonite_nand_info = { - .enable_arbiter = 1, - .num_cs = 1, - .parts[0] = zylonite_nand_partitions, - .nr_parts[0] = ARRAY_SIZE(zylonite_nand_partitions), + .parts = zylonite_nand_partitions, + .nr_parts = ARRAY_SIZE(zylonite_nand_partitions), }; static void __init zylonite_init_nand(void) diff --git a/drivers/mtd/nand/marvell_nand.c b/drivers/mtd/nand/marvell_nand.c index 2196f2a233d6..03805f9669da 100644 --- a/drivers/mtd/nand/marvell_nand.c +++ b/drivers/mtd/nand/marvell_nand.c @@ -2520,8 +2520,7 @@ static int marvell_nand_chip_init(struct device *dev, struct marvell_nfc *nfc, if (pdata) /* Legacy bindings support only one chip */ - ret = mtd_device_register(mtd, pdata->parts[0], - pdata->nr_parts[0]); + ret = mtd_device_register(mtd, pdata->parts, pdata->nr_parts); else ret = mtd_device_register(mtd, NULL, 0); if (ret) { diff --git a/include/linux/platform_data/mtd-nand-pxa3xx.h b/include/linux/platform_data/mtd-nand-pxa3xx.h index b42ad83cbc20..4fd0f592a2d2 100644 --- a/include/linux/platform_data/mtd-nand-pxa3xx.h +++ b/include/linux/platform_data/mtd-nand-pxa3xx.h @@ -6,41 +6,22 @@ #include /* - * Current pxa3xx_nand controller has two chip select which - * both be workable. - * - * Notice should be taken that: - * When you want to use this feature, you should not enable the - * keep configuration feature, for two chip select could be - * attached with different nand chip. The different page size - * and timing requirement make the keep configuration impossible. + * Current pxa3xx_nand controller has two chip select which both be workable but + * historically all platforms remaining on platform data used only one. Switch + * to device tree if you need more. */ - -/* The max num of chip select current support */ -#define NUM_CHIP_SELECT (2) struct pxa3xx_nand_platform_data { - - /* the data flash bus is shared between the Static Memory - * Controller and the Data Flash Controller, the arbiter - * controls the ownership of the bus - */ - int enable_arbiter; - - /* allow platform code to keep OBM/bootloader defined NFC config */ - int keep_config; - - /* indicate how many chip selects will be used */ - int num_cs; - - /* use an flash-based bad block table */ - bool flash_bbt; - - /* requested ECC strength and ECC step size */ + /* Keep OBM/bootloader NFC timing configuration */ + bool keep_config; + /* Use a flash-based bad block table */ + bool flash_bbt; + /* Requested ECC strength and ECC step size */ int ecc_strength, ecc_step_size; - - const struct mtd_partition *parts[NUM_CHIP_SELECT]; - unsigned int nr_parts[NUM_CHIP_SELECT]; + /* Partitions */ + const struct mtd_partition *parts; + unsigned int nr_parts; }; extern void pxa3xx_set_nand_info(struct pxa3xx_nand_platform_data *info); + #endif /* __ASM_ARCH_PXA3XX_NAND_H */ -- cgit v1.2.3