From 64b878ba46af224fa2c1d0bf7c784927e1890f3a Mon Sep 17 00:00:00 2001 From: Romuald JEANNE Date: Mon, 10 Dec 2018 15:53:08 +0100 Subject: [PATCH 43/52] ARM: stm32mp1-r0-rc3: MISC --- .../devicetree/bindings/soc/stm32/stm32_hdp.txt | 39 ++++ arch/arm/Kconfig.debug | 30 ++- arch/arm/boot/dts/Makefile | 2 + arch/arm/include/debug/stm32.S | 19 +- drivers/hwspinlock/hwspinlock_core.c | 15 +- drivers/hwspinlock/stm32_hwspinlock.c | 7 + drivers/hwtracing/coresight/coresight-stm.c | 58 ++++- drivers/soc/st/Kconfig | 8 + drivers/soc/st/Makefile | 1 + drivers/soc/st/stm32_hdp.c | 242 +++++++++++++++++++++ drivers/spi/spi-stm32-qspi.c | 192 +++++++++++++++- include/dt-bindings/soc/stm32-hdp.h | 108 +++++++++ 12 files changed, 694 insertions(+), 27 deletions(-) create mode 100644 Documentation/devicetree/bindings/soc/stm32/stm32_hdp.txt create mode 100644 drivers/soc/st/stm32_hdp.c create mode 100644 include/dt-bindings/soc/stm32-hdp.h diff --git a/Documentation/devicetree/bindings/soc/stm32/stm32_hdp.txt b/Documentation/devicetree/bindings/soc/stm32/stm32_hdp.txt new file mode 100644 index 0000000..e2bd82f --- /dev/null +++ b/Documentation/devicetree/bindings/soc/stm32/stm32_hdp.txt @@ -0,0 +1,39 @@ +STM32 - STM32MP1- HDP Pin configuration for STM32MP1 +======================================================= + +The Hardware Debug Port (HDP) allows the observation of internal signals. By using multiplexers, +up to 16 signals for each of 8-bit output can be observed. + +Required Properties: + + - compatible: Must be "st,stm32mp1-hdp" + - muxing-hdp: Indicates for each HDP pins selected which HDP output among the 16 available signals you want + +For each HDP pins you can select one of 16 signals which will be described in file : include/dt-bindings/soc/stm32-hdp.h + +Example +------- + +In common dtsi file: + +hdp: hdp@5002a000 { + compatible = "st,stm32mp1-hdp"; + reg = <0x5002a000 0x400>; + clocks = <&rcc HDP>; + clock-names = "hdp"; +}; + +In board-specific file: + +In this example I've selected HDP0, HDP6 and HDP7, and for HDP0 the output signal is HDP0_GPOVAL_0, +for HDP6 is HDP6_GPOVAL_6, and for HDP7 is HDP7_GPOVAL_7. + +&hdp { + pinctrl-names = "default", "sleep"; + pinctrl-0 = <&hdp0_pins_a &hdp6_pins_a &hdp7_pins_a>; + pinctrl-1 = <&hdp0_pins_sleep_a &hdp6_pins_sleep_a &hdp7_pins_sleep_a>; + + muxing-hdp = <(STM32_HDP(0, HDP0_GPOVAL_0) | + STM32_HDP(6, HDP6_GPOVAL_6) | + STM32_HDP(7, HDP7_GPOVAL_7))>; +}; diff --git a/arch/arm/Kconfig.debug b/arch/arm/Kconfig.debug index d5ec6c3..88849c1 100644 --- a/arch/arm/Kconfig.debug +++ b/arch/arm/Kconfig.debug @@ -1186,23 +1186,37 @@ choice config STM32F4_DEBUG_UART bool "Use STM32F4 UART for low-level debug" - depends on ARCH_STM32 + depends on ARCH_STM32 && ARM_SINGLE_ARMV7M select DEBUG_STM32_UART help Say Y here if you want kernel low-level debugging support on STM32F4 based platforms, which default UART is wired on - USART1. + USART1, but another UART instance can be selected by modifying + CONFIG_DEBUG_UART_PHYS. If unsure, say N. config STM32F7_DEBUG_UART bool "Use STM32F7 UART for low-level debug" - depends on ARCH_STM32 + depends on ARCH_STM32 && ARM_SINGLE_ARMV7M select DEBUG_STM32_UART help Say Y here if you want kernel low-level debugging support on STM32F7 based platforms, which default UART is wired on - USART1. + USART1, but another UART instance can be selected by modifying + CONFIG_DEBUG_UART_PHYS. + + If unsure, say N. + + config STM32MP1_DEBUG_UART + bool "Use STM32MP1 UART for low-level debug" + depends on ARCH_STM32 && ARCH_MULTI_V7 + select DEBUG_STM32_UART + help + Say Y here if you want kernel low-level debugging support + on STM32MP1 based platforms, wich default UART is wired on + UART4, but another UART instance can be selected by modifying + CONFIG_DEBUG_UART_PHYS and CONFIG_DEBUG_UART_VIRT. If unsure, say N. @@ -1607,6 +1621,8 @@ config DEBUG_UART_PHYS default 0x3e000000 if DEBUG_BCM_KONA_UART default 0x3f201000 if DEBUG_BCM2836 default 0x4000e400 if DEBUG_LL_UART_EFM32 + default 0x40010000 if STM32MP1_DEBUG_UART + default 0x40011000 if STM32F4_DEBUG_UART || STM32F7_DEBUG_UART default 0x40028000 if DEBUG_AT91_SAMV7_USART1 default 0x40081000 if DEBUG_LPC18XX_UART0 default 0x40090000 if DEBUG_LPC32XX @@ -1700,10 +1716,12 @@ config DEBUG_UART_PHYS DEBUG_S3C64XX_UART || \ DEBUG_BCM63XX_UART || DEBUG_ASM9260_UART || \ DEBUG_SIRFSOC_UART || DEBUG_DIGICOLOR_UA0 || \ - DEBUG_AT91_UART + DEBUG_AT91_UART || STM32F4_DEBUG_UART || \ + STM32F7_DEBUG_UART || STM32MP1_DEBUG_UART config DEBUG_UART_VIRT hex "Virtual base address of debug UART" + default 0xfe010000 if STM32MP1_DEBUG_UART default 0xc881f000 if DEBUG_RV1108_UART2 default 0xc8821000 if DEBUG_RV1108_UART1 default 0xc8912000 if DEBUG_RV1108_UART0 @@ -1815,7 +1833,7 @@ config DEBUG_UART_VIRT DEBUG_S3C64XX_UART || \ DEBUG_BCM63XX_UART || DEBUG_ASM9260_UART || \ DEBUG_SIRFSOC_UART || DEBUG_DIGICOLOR_UA0 || \ - DEBUG_AT91_UART + DEBUG_AT91_UART || STM32MP1_DEBUG_UART config DEBUG_UART_8250_SHIFT int "Register offset shift for the 8250 debug UART" diff --git a/arch/arm/boot/dts/Makefile b/arch/arm/boot/dts/Makefile index fdf53f5..5665290 100644 --- a/arch/arm/boot/dts/Makefile +++ b/arch/arm/boot/dts/Makefile @@ -924,9 +924,11 @@ dtb-$(CONFIG_ARCH_STM32) += \ stm32h743i-disco.dtb \ stm32mp157a-dk1.dtb \ stm32mp157c-dk2.dtb \ + stm32mp157c-dk2-a7-examples.dtb \ stm32mp157c-dk2-m4-examples.dtb \ stm32mp157c-ed1.dtb \ stm32mp157c-ev1.dtb \ + stm32mp157c-ev1-a7-examples.dtb \ stm32mp157c-ev1-m4-examples.dtb dtb-$(CONFIG_MACH_SUN4I) += \ sun4i-a10-a1000.dtb \ diff --git a/arch/arm/include/debug/stm32.S b/arch/arm/include/debug/stm32.S index 427561e..3353a81 100644 --- a/arch/arm/include/debug/stm32.S +++ b/arch/arm/include/debug/stm32.S @@ -4,21 +4,12 @@ * Author: Gerald Baeza for STMicroelectronics. */ -#ifdef CONFIG_ARM_SINGLE_ARMV7M -#define STM32_UART_BASE_PHYS 0x40011000 /* USART1 */ -#define STM32_UART_BASE_VIRT STM32_UART_BASE_PHYS /* no MMU */ -#else -#define STM32_UART_BASE_PHYS 0x40010000 /* UART4 */ -#define STM32_UART_BASE_VIRT 0xfe010000 /* UART4 */ -#endif - - #ifdef CONFIG_STM32F4_DEBUG_UART #define STM32_USART_SR_OFF 0x00 #define STM32_USART_TDR_OFF 0x04 #endif -#ifdef CONFIG_STM32F7_DEBUG_UART +#if defined(CONFIG_STM32F7_DEBUG_UART) || defined(CONFIG_STM32MP1_DEBUG_UART) #define STM32_USART_SR_OFF 0x1C #define STM32_USART_TDR_OFF 0x28 #endif @@ -27,8 +18,12 @@ #define STM32_USART_TXE (1 << 7) /* Tx data reg empty */ .macro addruart, rp, rv, tmp - ldr \rp, =STM32_UART_BASE_PHYS @ physical base - ldr \rv, =STM32_UART_BASE_VIRT @ virt base + ldr \rp, =CONFIG_DEBUG_UART_PHYS @ physical base +#if defined(CONFIG_MMU) + ldr \rv, =CONFIG_DEBUG_UART_VIRT @ virt base +#else + ldr \rv, =CONFIG_DEBUG_UART_PHYS @ same as physical base +#endif .endm .macro senduart,rd,rx diff --git a/drivers/hwspinlock/hwspinlock_core.c b/drivers/hwspinlock/hwspinlock_core.c index 2bad40d..287e1b3 100644 --- a/drivers/hwspinlock/hwspinlock_core.c +++ b/drivers/hwspinlock/hwspinlock_core.c @@ -333,6 +333,9 @@ int of_hwspin_lock_get_id(struct device_node *np, int index) if (ret) return ret; + if (!of_device_is_available(args.np)) + return -ENOENT; + /* Find the hwspinlock device: we need its base_id */ ret = -EPROBE_DEFER; rcu_read_lock(); @@ -742,13 +745,11 @@ struct hwspinlock *hwspin_lock_request_specific(unsigned int id) /* sanity check (this shouldn't happen) */ WARN_ON(hwlock_to_id(hwlock) != id); - /* make sure this hwspinlock is unused */ - ret = radix_tree_tag_get(&hwspinlock_tree, id, HWSPINLOCK_UNUSED); - if (ret == 0) { - pr_warn("hwspinlock %u is already in use\n", id); - hwlock = NULL; - goto out; - } + /* + * We intentionally do not check for the HWSPINLOCK_UNUSED tag as + * we want to share HWSPINLOCK between several devices. This is safe + * since the lock/unlock requests are called under &hwlock->lock control + */ /* mark as used and power up */ ret = __hwspin_lock_request(hwlock); diff --git a/drivers/hwspinlock/stm32_hwspinlock.c b/drivers/hwspinlock/stm32_hwspinlock.c index 3242b72..b9b9b99 100644 --- a/drivers/hwspinlock/stm32_hwspinlock.c +++ b/drivers/hwspinlock/stm32_hwspinlock.c @@ -5,6 +5,7 @@ */ #include +#include #include #include #include @@ -42,9 +43,15 @@ static void stm32_hwspinlock_unlock(struct hwspinlock *lock) writel(STM32_MUTEX_COREID, lock_addr); } +static void stm32_hwspinlock_relax(struct hwspinlock *lock) +{ + ndelay(50); +} + static const struct hwspinlock_ops stm32_hwspinlock_ops = { .trylock = stm32_hwspinlock_trylock, .unlock = stm32_hwspinlock_unlock, + .relax = stm32_hwspinlock_relax, }; static int stm32_hwspinlock_probe(struct platform_device *pdev) diff --git a/drivers/hwtracing/coresight/coresight-stm.c b/drivers/hwtracing/coresight/coresight-stm.c index c46c70a..65687c0 100644 --- a/drivers/hwtracing/coresight/coresight-stm.c +++ b/drivers/hwtracing/coresight/coresight-stm.c @@ -40,6 +40,7 @@ #define STMHETER 0xd20 #define STMHEBSR 0xd60 #define STMHEMCR 0xd64 +#define STMHEEXTMUXR 0xd68 #define STMHEMASTR 0xdf4 #define STMHEFEAT1R 0xdf8 #define STMHEIDR 0xdfc @@ -125,9 +126,11 @@ struct channel_space { * @stmheer: settings for register STMHEER. * @stmheter: settings for register STMHETER. * @stmhebsr: settings for register STMHEBSR. + * @stmheextmuxr: settings for register STMHEEXTMUXR. */ struct stm_drvdata { void __iomem *base; + void __iomem *base_cti; struct device *dev; struct clk *atclk; struct coresight_device *csdev; @@ -143,6 +146,7 @@ struct stm_drvdata { u32 stmheer; u32 stmheter; u32 stmhebsr; + u32 stmheextmuxr; }; static void stm_hwevent_enable_hw(struct stm_drvdata *drvdata) @@ -152,6 +156,7 @@ static void stm_hwevent_enable_hw(struct stm_drvdata *drvdata) writel_relaxed(drvdata->stmhebsr, drvdata->base + STMHEBSR); writel_relaxed(drvdata->stmheter, drvdata->base + STMHETER); writel_relaxed(drvdata->stmheer, drvdata->base + STMHEER); + writel_relaxed(drvdata->stmheextmuxr, drvdata->base + STMHEEXTMUXR); writel_relaxed(0x01 | /* Enable HW event tracing */ 0x04, /* Error detection on event tracing */ drvdata->base + STMHEMCR); @@ -222,6 +227,7 @@ static void stm_hwevent_disable_hw(struct stm_drvdata *drvdata) writel_relaxed(0x0, drvdata->base + STMHEMCR); writel_relaxed(0x0, drvdata->base + STMHEER); writel_relaxed(0x0, drvdata->base + STMHETER); + writel_relaxed(0x0, drvdata->base + STMHEEXTMUXR); CS_LOCK(drvdata->base); } @@ -455,6 +461,34 @@ static ssize_t notrace stm_generic_packet(struct stm_data *stm_data, return size; } +static ssize_t hwevent_extmux_select_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct stm_drvdata *drvdata = dev_get_drvdata(dev->parent); + unsigned long val = drvdata->stmheextmuxr; + + return scnprintf(buf, PAGE_SIZE, "%#lx\n", val); +} + +static ssize_t hwevent_extmux_select_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t size) +{ + struct stm_drvdata *drvdata = dev_get_drvdata(dev->parent); + unsigned long val; + int ret = 0; + + ret = kstrtoul(buf, 16, &val); + if (ret) + return -EINVAL; + + drvdata->stmheextmuxr = val; + + return size; +} +static DEVICE_ATTR_RW(hwevent_extmux_select); + static ssize_t hwevent_enable_show(struct device *dev, struct device_attribute *attr, char *buf) { @@ -644,10 +678,16 @@ coresight_stm_reg(spfeat1r, STMSPFEAT1R); coresight_stm_reg(spfeat2r, STMSPFEAT2R); coresight_stm_reg(spfeat3r, STMSPFEAT3R); coresight_stm_reg(devid, CORESIGHT_DEVID); +coresight_stm_reg(stmheer, STMHEER); +coresight_stm_reg(stmheter, STMHETER); +coresight_stm_reg(stmhebsr, STMHEBSR); +coresight_stm_reg(stmheextmux, STMHEEXTMUXR); +coresight_stm_reg(stmhemcr, STMHEMCR); static struct attribute *coresight_stm_attrs[] = { &dev_attr_hwevent_enable.attr, &dev_attr_hwevent_select.attr, + &dev_attr_hwevent_extmux_select.attr, &dev_attr_port_enable.attr, &dev_attr_port_select.attr, &dev_attr_traceid.attr, @@ -667,6 +707,11 @@ static struct attribute *coresight_stm_mgmt_attrs[] = { &dev_attr_spfeat2r.attr, &dev_attr_spfeat3r.attr, &dev_attr_devid.attr, + &dev_attr_stmheer.attr, + &dev_attr_stmheter.attr, + &dev_attr_stmhebsr.attr, + &dev_attr_stmheextmux.attr, + &dev_attr_stmhemcr.attr, NULL, }; @@ -792,7 +837,7 @@ static int stm_probe(struct amba_device *adev, const struct amba_id *id) struct coresight_platform_data *pdata = NULL; struct stm_drvdata *drvdata; struct resource *res = &adev->res; - struct resource ch_res; + struct resource ch_res, cti_res; size_t res_size, bitmap_size; struct coresight_desc desc = { 0 }; struct device_node *np = adev->dev.of_node; @@ -821,6 +866,17 @@ static int stm_probe(struct amba_device *adev, const struct amba_id *id) return PTR_ERR(base); drvdata->base = base; + ret = stm_get_resource_byname(np, "cti-base", &cti_res); + if (ret) + return ret; + + base = devm_ioremap_resource(dev, &cti_res); + + if (IS_ERR(base)) + return PTR_ERR(base); + + drvdata->base_cti = base; + ret = stm_get_resource_byname(np, "stm-stimulus-base", &ch_res); if (ret) return ret; diff --git a/drivers/soc/st/Kconfig b/drivers/soc/st/Kconfig index 82ee423..8ab6049 100644 --- a/drivers/soc/st/Kconfig +++ b/drivers/soc/st/Kconfig @@ -6,4 +6,12 @@ config STM32_PM_DOMAINS select PM_GENERIC_DOMAINS default y if MACH_STM32MP157 +config STM32_HDP + bool "STMicroelectronics STM32MP157 Hardware Debug Port (HDP) pin control" + depends on MACH_STM32MP157 + default n if MACH_STM32MP157 + help + The Hardware Debug Port allows the observation of internal signals. By using multiplexers, + up to 16 signals for each of 8-bit output can be observed. + endif # ARCH_STM32 diff --git a/drivers/soc/st/Makefile b/drivers/soc/st/Makefile index 8d7f291..85905b7 100644 --- a/drivers/soc/st/Makefile +++ b/drivers/soc/st/Makefile @@ -1 +1,2 @@ obj-$(CONFIG_STM32_PM_DOMAINS) += stm32_pm_domain.o +obj-$(CONFIG_STM32_HDP) += stm32_hdp.o diff --git a/drivers/soc/st/stm32_hdp.c b/drivers/soc/st/stm32_hdp.c new file mode 100644 index 0000000..6408ac6 --- /dev/null +++ b/drivers/soc/st/stm32_hdp.c @@ -0,0 +1,242 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) STMicroelectronics 2018 - All Rights Reserved + * Author: Christophe Roullier + * for STMicroelectronics. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define HDP_CTRL_ENABLE 1 +#define HDP_CTRL_DISABLE 0 + +enum { + HDP_CTRL = 0, + HDP_MUX = 0x4, + HDP_VAL = 0x10, + HDP_GPOSET = 0x14, + HDP_GPOCLR = 0x18, + HDP_GPOVAL = 0x1c, + HDP_VERR = 0x3f4, + HDP_IPIDR = 0x3f8, + HDP_SIDR = 0x3fc +} HDP_register_offsets; + +struct data_priv { + struct clk *clk; + int clk_is_enabled; + struct dentry *pwr_dentry; + unsigned char __iomem *hdp_membase; + unsigned int hdp_ctrl; + unsigned int hdp_mux; +}; + +/* enable/disable */ +static int stm32_hdp_enable_set(void *data, int val) +{ + struct data_priv *e = (struct data_priv *)data; + + if (!e->clk) + return -EPERM; + + if (val == 1) { + if (clk_prepare_enable(e->clk) < 0) { + pr_err("Failed to enable HDP clock\n"); + return -EPERM; + } + e->clk_is_enabled = 1; + } else { + clk_disable_unprepare(e->clk); + e->clk_is_enabled = 0; + } + return 0; +} + +static int stm32_hdp_fops_set(void *data, u64 val) +{ + unsigned char __iomem *addr = (unsigned char __iomem *)data; + + writel_relaxed(val, addr); + + return 0; +} + +static int stm32_hdp_fops_get(void *data, u64 *val) +{ + unsigned char __iomem *addr = (unsigned char __iomem *)data; + + *val = readl_relaxed(addr); + + return 0; +} + +DEFINE_SIMPLE_ATTRIBUTE(stm32_hdp_fops, stm32_hdp_fops_get, + stm32_hdp_fops_set, "0x%llx\n"); + +int stm32_hdp_probe(struct platform_device *pdev) +{ + struct device_node *np = pdev->dev.of_node; + struct device *dev = &pdev->dev; + struct resource *res; + + struct data_priv *data; + struct dentry *r; + + int ret; + const __be32 *getmuxing; + u32 muxing, version; + + if (!np) + return -ENODEV; + + data = devm_kzalloc(&pdev->dev, sizeof(struct data_priv), GFP_KERNEL); + if (!data) + return -ENOMEM; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + data->hdp_membase = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(data->hdp_membase)) + return PTR_ERR(data->hdp_membase); + + /* Get HDP clocks */ + data->clk = devm_clk_get(dev, "hdp"); + if (IS_ERR(data->clk)) { + dev_err(dev, "No HDP CK clock provided...\n"); + return PTR_ERR(data->clk); + } + + /* Enable clock */ + ret = stm32_hdp_enable_set(data, 1); + if (ret != 0) + return ret; + + getmuxing = of_get_property(np, "muxing-hdp", NULL); + if (!getmuxing) { + dev_err(dev, + "no muxing-hdp property in node\n"); + /* Disable clock */ + ret = stm32_hdp_enable_set(data, 0); + if (ret != 0) + return ret; + + return -EINVAL; + } + + /* add hdp directory */ + r = debugfs_create_dir("hdp", NULL); + if (!r) { + dev_err(dev, "Unable to create HDP debugFS\n"); + /* Disable clock */ + ret = stm32_hdp_enable_set(data, 0); + if (ret != 0) + return ret; + + return -ENODEV; + } + + debugfs_create_file("ctrl", 0644, r, + data->hdp_membase + HDP_CTRL, &stm32_hdp_fops); + debugfs_create_file("mux", 0644, r, + data->hdp_membase + HDP_MUX, &stm32_hdp_fops); + debugfs_create_file("val", 0644, r, + data->hdp_membase + HDP_VAL, &stm32_hdp_fops); + debugfs_create_file("gposet", 0644, r, + data->hdp_membase + HDP_GPOSET, &stm32_hdp_fops); + debugfs_create_file("gpoclr", 0644, r, + data->hdp_membase + HDP_GPOCLR, &stm32_hdp_fops); + debugfs_create_file("gpoval", 0644, r, + data->hdp_membase + HDP_GPOVAL, &stm32_hdp_fops); + + /* Enable HDP */ + writel(HDP_CTRL_ENABLE, data->hdp_membase + HDP_CTRL); + + /* HDP Multiplexing */ + muxing = of_read_number(getmuxing, + of_n_addr_cells(np)); + + writel(muxing, data->hdp_membase + HDP_MUX); + + platform_set_drvdata(pdev, data); + + /* Get Majeur, Minor version */ + version = readl(data->hdp_membase + HDP_VERR); + + dev_info(dev, "STM32 HDP version %d.%d initialized\n", + version >> 4, version & 0x0F); + + return 0; +} + +static int stm32_hdp_remove(struct platform_device *pdev) +{ + struct data_priv *data = platform_get_drvdata(pdev); + + /* Disable HDP */ + writel(HDP_CTRL_DISABLE, data->hdp_membase + HDP_CTRL); + + if (data->clk) { + if (data->clk_is_enabled) + clk_disable_unprepare(data->clk); + } + + pr_info("driver STM32 HDP removed\n"); + return 0; +} + +#ifdef CONFIG_PM_SLEEP +static int stm32_hdp_suspend(struct device *dev) +{ + struct data_priv *data = dev_get_drvdata(dev); + + data->hdp_ctrl = readl_relaxed(data->hdp_membase + HDP_CTRL); + data->hdp_mux = readl_relaxed(data->hdp_membase + HDP_MUX); + + pinctrl_pm_select_sleep_state(dev); + + return 0; +} + +static int stm32_hdp_resume(struct device *dev) +{ + struct data_priv *data = dev_get_drvdata(dev); + + writel_relaxed(data->hdp_ctrl, data->hdp_membase + HDP_CTRL); + writel_relaxed(data->hdp_mux, data->hdp_membase + HDP_MUX); + + pinctrl_pm_select_default_state(dev); + + return 0; +} +#endif /* CONFIG_PM_SLEEP */ + +static SIMPLE_DEV_PM_OPS(stm32_hdp_pm_ops, + stm32_hdp_suspend, + stm32_hdp_resume); + +static const struct of_device_id hdp_match[] = { + { .compatible = "st,stm32mp1-hdp",}, + { } +}; +MODULE_DEVICE_TABLE(of, hdp_match); + +static struct platform_driver hdp_driver = { + .probe = stm32_hdp_probe, + .remove = stm32_hdp_remove, + .driver = { + .name = "hdp", + .of_match_table = hdp_match, + .pm = &stm32_hdp_pm_ops, + }, +}; + +module_platform_driver(hdp_driver); diff --git a/drivers/spi/spi-stm32-qspi.c b/drivers/spi/spi-stm32-qspi.c index 3e8ca10..9c67718 100644 --- a/drivers/spi/spi-stm32-qspi.c +++ b/drivers/spi/spi-stm32-qspi.c @@ -5,7 +5,10 @@ */ #include #include +#include +#include #include +#include #include #include #include @@ -84,6 +87,7 @@ #define STM32_FIFO_TIMEOUT_US 30000 #define STM32_BUSY_TIMEOUT_US 100000 #define STM32_ABT_TIMEOUT_US 100000 +#define STM32_COMP_TIMEOUT_MS 1000 struct stm32_qspi_flash { struct stm32_qspi *qspi; @@ -93,6 +97,7 @@ struct stm32_qspi_flash { struct stm32_qspi { struct device *dev; + phys_addr_t phys_base; void __iomem *io_base; void __iomem *mm_base; resource_size_t mm_size; @@ -102,6 +107,11 @@ struct stm32_qspi { struct completion data_completion; u32 fmode; + struct dma_chan *dma_chtx; + struct dma_chan *dma_chrx; + struct completion dma_completion; + struct sg_table dma_sgt; + u32 cr_reg; u32 dcr_reg; @@ -180,6 +190,131 @@ static int stm32_qspi_tx_mm(struct stm32_qspi *qspi, return 0; } +static void stm32_qspi_dma_callback(void *arg) +{ + struct completion *dma_completion = arg; + + complete(dma_completion); +} + +static int stm32_qspi_tx_dma(struct stm32_qspi *qspi, + const struct spi_mem_op *op) +{ + struct dma_async_tx_descriptor *desc; + enum dma_transfer_direction dma_dir; + enum dma_data_direction map_dir; + bool addr_valid, vmalloced_buf; + unsigned int dma_max_seg_size; + struct scatterlist *sg; + struct dma_chan *dma_ch; + struct page *vm_page; + dma_cookie_t cookie; + u32 cr, len, t_out; + int i, err, nents, desc_len; + u8 *buf; + + cr = readl_relaxed(qspi->io_base + QSPI_CR); + + if (op->data.dir == SPI_MEM_DATA_IN) { + map_dir = DMA_FROM_DEVICE; + dma_dir = DMA_DEV_TO_MEM; + dma_ch = qspi->dma_chrx; + buf = op->data.buf.in; + } else { + map_dir = DMA_TO_DEVICE; + dma_dir = DMA_MEM_TO_DEV; + dma_ch = qspi->dma_chtx; + buf = (u8 *)op->data.buf.out; + } + + /* the stm32 dma could tx MAX_DMA_BLOCK_LEN */ + dma_max_seg_size = dma_get_max_seg_size(dma_ch->device->dev); + len = op->data.nbytes; + + vmalloced_buf = is_vmalloc_addr(buf); + addr_valid = virt_addr_valid(buf); + if (addr_valid) { + desc_len = dma_max_seg_size; + nents = DIV_ROUND_UP(len, desc_len); + } else { + desc_len = min_t(int, dma_max_seg_size, PAGE_SIZE); + nents = DIV_ROUND_UP(len + offset_in_page(buf), desc_len); + } + + if (nents != qspi->dma_sgt.nents) { + sg_free_table(&qspi->dma_sgt); + + err = sg_alloc_table(&qspi->dma_sgt, nents, GFP_KERNEL); + if (err) + return err; + } + + for_each_sg(qspi->dma_sgt.sgl, sg, nents, i) { + size_t bytes; + + if (addr_valid) { + bytes = min_t(size_t, len, desc_len); + sg_set_buf(sg, buf, bytes); + } else { + bytes = min_t(size_t, len, + desc_len - offset_in_page(buf)); + if (vmalloced_buf) + vm_page = vmalloc_to_page(buf); + else + vm_page = kmap_to_page(buf); + if (!vm_page) { + sg_free_table(&qspi->dma_sgt); + return -ENOMEM; + } + sg_set_page(sg, vm_page, bytes, + offset_in_page(buf)); + } + + buf += bytes; + len -= bytes; + } + + if (dma_map_sg(qspi->dev, qspi->dma_sgt.sgl, nents, map_dir) != nents) + return -ENOMEM; + + desc = dmaengine_prep_slave_sg(dma_ch, qspi->dma_sgt.sgl, nents, + dma_dir, DMA_PREP_INTERRUPT); + if (!desc) { + err = -ENOMEM; + goto out_unmap; + } + + reinit_completion(&qspi->dma_completion); + desc->callback = stm32_qspi_dma_callback; + desc->callback_param = &qspi->dma_completion; + cookie = dmaengine_submit(desc); + err = dma_submit_error(cookie); + if (err) + goto out_unmap; + + dma_async_issue_pending(dma_ch); + + writel_relaxed(cr | CR_DMAEN, qspi->io_base + QSPI_CR); + + t_out = nents * STM32_COMP_TIMEOUT_MS; + if (!wait_for_completion_interruptible_timeout(&qspi->dma_completion, + msecs_to_jiffies(t_out))) + err = -ETIMEDOUT; + + if (dma_async_is_tx_complete(dma_ch, cookie, + NULL, NULL) != DMA_COMPLETE) + err = -ETIMEDOUT; + + if (err) + dmaengine_terminate_all(dma_ch); + +out_unmap: + writel_relaxed(cr & ~CR_DMAEN, qspi->io_base + QSPI_CR); + dma_unmap_sg(qspi->dev, qspi->dma_sgt.sgl, nents, map_dir); + + return err; +} + static int stm32_qspi_tx(struct stm32_qspi *qspi, const struct spi_mem_op *op) { if (!op->data.nbytes) @@ -187,6 +322,10 @@ static int stm32_qspi_tx(struct stm32_qspi *qspi, const struct spi_mem_op *op) if (qspi->fmode == CCR_FMODE_MM) return stm32_qspi_tx_mm(qspi, op); + else if (op->data.dir == SPI_MEM_DATA_IN && qspi->dma_chrx) + return stm32_qspi_tx_dma(qspi, op); + else if (op->data.dir == SPI_MEM_DATA_OUT && qspi->dma_chtx) + return stm32_qspi_tx_dma(qspi, op); return stm32_qspi_tx_poll(qspi, op); } @@ -217,7 +356,7 @@ static int stm32_qspi_wait_cmd(struct stm32_qspi *qspi, writel_relaxed(cr | CR_TCIE | CR_TEIE, qspi->io_base + QSPI_CR); if (!wait_for_completion_interruptible_timeout(&qspi->data_completion, - msecs_to_jiffies(1000))) { + msecs_to_jiffies(STM32_COMP_TIMEOUT_MS))) { err = -ETIMEDOUT; } else { sr = readl_relaxed(qspi->io_base + QSPI_SR); @@ -386,6 +525,52 @@ static int stm32_qspi_setup(struct spi_device *spi) return 0; } +static void stm32_qspi_dma_setup(struct stm32_qspi *qspi) +{ + struct dma_slave_config dma_cfg; + struct device *dev = qspi->dev; + int ret; + + memset(&dma_cfg, 0, sizeof(dma_cfg)); + + dma_cfg.src_addr_width = DMA_SLAVE_BUSWIDTH_1_BYTE; + dma_cfg.dst_addr_width = DMA_SLAVE_BUSWIDTH_1_BYTE; + dma_cfg.src_addr = qspi->phys_base + QSPI_DR; + dma_cfg.dst_addr = qspi->phys_base + QSPI_DR; + dma_cfg.src_maxburst = 4; + dma_cfg.dst_maxburst = 4; + + qspi->dma_chrx = dma_request_slave_channel(dev, "rx"); + if (qspi->dma_chrx) { + ret = dmaengine_slave_config(qspi->dma_chrx, &dma_cfg); + if (ret) { + dev_err(dev, "dma rx config failed\n"); + dma_release_channel(qspi->dma_chrx); + qspi->dma_chrx = NULL; + } + } + + qspi->dma_chtx = dma_request_slave_channel(dev, "tx"); + if (qspi->dma_chtx) { + ret = dmaengine_slave_config(qspi->dma_chtx, &dma_cfg); + if (ret) { + dev_err(dev, "dma tx config failed\n"); + dma_release_channel(qspi->dma_chtx); + qspi->dma_chtx = NULL; + } + } + + init_completion(&qspi->dma_completion); +} + +static void stm32_qspi_dma_free(struct stm32_qspi *qspi) +{ + if (qspi->dma_chtx) + dma_release_channel(qspi->dma_chtx); + if (qspi->dma_chrx) + dma_release_channel(qspi->dma_chrx); +} + /* * no special host constraint, so use default spi_mem_default_supports_op * to check supported mode. @@ -398,6 +583,8 @@ static void stm32_qspi_release(struct stm32_qspi *qspi) { /* disable qspi */ writel_relaxed(0, qspi->io_base + QSPI_CR); + stm32_qspi_dma_free(qspi); + sg_free_table(&qspi->dma_sgt); mutex_destroy(&qspi->lock); clk_disable_unprepare(qspi->clk); } @@ -422,6 +609,8 @@ static int stm32_qspi_probe(struct platform_device *pdev) if (IS_ERR(qspi->io_base)) return PTR_ERR(qspi->io_base); + qspi->phys_base = res->start; + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "qspi_mm"); qspi->mm_base = devm_ioremap_resource(dev, res); if (IS_ERR(qspi->mm_base)) @@ -464,6 +653,7 @@ static int stm32_qspi_probe(struct platform_device *pdev) qspi->dev = dev; platform_set_drvdata(pdev, qspi); + stm32_qspi_dma_setup(qspi); mutex_init(&qspi->lock); ctrl->mode_bits = SPI_RX_DUAL | SPI_RX_QUAD diff --git a/include/dt-bindings/soc/stm32-hdp.h b/include/dt-bindings/soc/stm32-hdp.h new file mode 100644 index 0000000..d986653 --- /dev/null +++ b/include/dt-bindings/soc/stm32-hdp.h @@ -0,0 +1,108 @@ +/* SPDX-License-Identifier: (GPL-2.0+ OR BSD-3-Clause) */ +/* + * Copyright (C) STMicroelectronics 2018 - All Rights Reserved + * Author: Roullier Christophe + * for STMicroelectronics. + */ + +#ifndef _DT_BINDINGS_STM32_HDP_H +#define _DT_BINDINGS_STM32_HDP_H + +#define STM32_HDP(port, value) ((value) << ((port) * 4)) + +/* define HDP Pins number*/ +#define HDP0_PWR_PWRWAKE_SYS 0 +#define HDP0_CM4_SLEEPDEEP 1 +#define HDP0_PWR_STDBY_WKUP 2 +#define HDP0_PWR_ENCOMP_VDDCORE 3 +#define HDP0_BSEC_OUT_SEC_NIDEN 4 +#define HDP0_RCC_CM4_SLEEPDEEP 6 +#define HDP0_GPU_DBG7 7 +#define HDP0_DDRCTRL_LP_REQ 8 +#define HDP0_PWR_DDR_RET_ENABLE_N 9 +#define HDP0_GPOVAL_0 15 + +#define HDP1_PWR_PWRWAKE_MCU 0 +#define HDP1_CM4_HALTED 1 +#define HDP1_CA7_NAXIERRIRQ 2 +#define HDP1_PWR_OKIN_MR 3 +#define HDP1_BSEC_OUT_SEC_DBGEN 4 +#define HDP1_EXTI_SYS_WAKEUP 5 +#define HDP1_RCC_PWRDS_MPU 6 +#define HDP1_GPU_DBG6 7 +#define HDP1_DDRCTRL_DFI_CTRLUPD_REQ 8 +#define HDP1_DDRCTRL_CACTIVE_DDRC_ASR 9 +#define HDP1_GPOVAL_1 15 + +#define HDP2_PWR_PWRWAKE_MPU 0 +#define HDP2_CM4_RXEV 1 +#define HDP2_CA7_NPMUIRQ1 2 +#define HDP2_CA7_NFIQOUT1 3 +#define HDP2_BSEC_IN_RSTCORE_N 4 +#define HDP2_EXTI_C2_WAKEUP 5 +#define HDP2_RCC_PWRDS_MCU 6 +#define HDP2_GPU_DBG5 7 +#define HDP2_DDRCTRL_DFI_INIT_COMPLETE 8 +#define HDP2_DDRCTRL_PERF_OP_IS_REFRESH 9 +#define HDP2_DDRCTRL_GSKP_DFI_LP_REQ 10 +#define HDP2_GPOVAL_2 15 + +#define HDP3_PWR_SEL_VTH_VDD_CORE 0 +#define HDP3_CM4_TXEV 1 +#define HDP3_CA7_NPMUIRQ0 2 +#define HDP3_CA7_NFIQOUT0 3 +#define HDP3_BSEC_OUT_SEC_DFTLOCK 4 +#define HDP3_EXTI_C1_WAKEUP 5 +#define HDP3_RCC_PWRDS_SYS 6 +#define HDP3_GPU_DBG4 7 +#define HDP3_DDRCTRL_STAT_DDRC_REG_SELREF_TYPE0 8 +#define HDP3_DDRCTRL_CACTIVE_1 9 +#define HDP3_GPOVAL_3 15 + +#define HDP4_PWR_PDDS 0 +#define HDP4_CM4_SLEEPING 1 +#define HDP4_CA7_NRESET1 2 +#define HDP4_CA7_NIRQOUT1 3 +#define HDP4_BSEC_OUT_SEC_DFTEN 4 +#define HDP4_BSEC_OUT_SEC_DBGSWENABLE 5 +#define HDP4_ETH_OUT_PMT_INTR_O 6 +#define HDP4_GPU_DBG3 7 +#define HDP4_DDRCTRL_STAT_DDRC_REG_SELREF_TYPE1 8 +#define HDP4_DDRCTRL_CACTIVE_0 9 +#define HDP4_GPOVAL_4 15 + +#define HDP5_CA7_STANDBYWFIL2 0 +#define HDP5_PWR_VTH_VDDCORE_ACK 1 +#define HDP5_CA7_NRESET0 2 +#define HDP5_CA7_NIRQOUT0 3 +#define HDP5_BSEC_IN_PWROK 4 +#define HDP5_BSEC_OUT_SEC_DEVICEEN 5 +#define HDP5_ETH_OUT_LPI_INTR_O 6 +#define HDP5_GPU_DBG2 7 +#define HDP5_DDRCTRL_CACTIVE_DDRC 8 +#define HDP5_DDRCTRL_WR_CREDIT_CNT 9 +#define HDP5_GPOVAL_5 15 + +#define HDP6_CA7_STANDBYWFI1 0 +#define HDP6_CA7_STANDBYWFE1 1 +#define HDP6_CA7_EVENT0 2 +#define HDP6_CA7_DBGACK1 3 +#define HDP6_BSEC_OUT_SEC_SPNIDEN 5 +#define HDP6_ETH_OUT_MAC_SPEED_O1 6 +#define HDP6_GPU_DBG1 7 +#define HDP6_DDRCTRL_CSYSACK_DDRC 8 +#define HDP6_DDRCTRL_LPR_CREDIT_CNT 9 +#define HDP6_GPOVAL_6 15 + +#define HDP7_CA7_STANDBYWFI0 0 +#define HDP7_CA7_STANDBYWFE0 1 +#define HDP7_CA7_DBGACK0 3 +#define HDP7_BSEC_OUT_FUSE_OK 4 +#define HDP7_BSEC_OUT_SEC_SPIDEN 5 +#define HDP7_ETH_OUT_MAC_SPEED_O0 6 +#define HDP7_GPU_DBG0 7 +#define HDP7_DDRCTRL_CSYSREQ_DDRC 8 +#define HDP7_DDRCTRL_HPR_CREDIT_CNT 9 +#define HDP7_GPOVAL_7 15 + +#endif /* _DT_BINDINGS_STM32_HDP_H */ -- 2.7.4