meta-st-stm32mp/recipes-kernel/linux/linux-stm32mp/4.19/4.19.9/0043-ARM-stm32mp1-r0-rc3-MI...

1068 lines
31 KiB
Diff

From 64b878ba46af224fa2c1d0bf7c784927e1890f3a Mon Sep 17 00:00:00 2001
From: Romuald JEANNE <romuald.jeanne@st.com>
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 <gerald.baeza@st.com> 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 <linux/clk.h>
+#include <linux/delay.h>
#include <linux/hwspinlock.h>
#include <linux/io.h>
#include <linux/kernel.h>
@@ -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 <christophe.roullier@st.com>
+ * for STMicroelectronics.
+ */
+
+#include <linux/clk.h>
+#include <linux/debugfs.h>
+#include <linux/init.h>
+#include <linux/of.h>
+#include <linux/module.h>
+#include <linux/pinctrl/pinconf.h>
+#include <linux/pinctrl/pinconf-generic.h>
+#include <linux/pinctrl/pinctrl.h>
+#include <linux/platform_device.h>
+#include <linux/io.h>
+#include <linux/suspend.h>
+
+#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 <linux/bitfield.h>
#include <linux/clk.h>
+#include <linux/dmaengine.h>
+#include <linux/dma-mapping.h>
#include <linux/errno.h>
+#include <linux/highmem.h>
#include <linux/io.h>
#include <linux/iopoll.h>
#include <linux/interrupt.h>
@@ -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 <christophe.roullier@st.com>
+ * 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