Merge pull request #4762 from dhewg/crust

Allwinner: add support for crust SCP firmware
This commit is contained in:
CvH 2020-12-30 23:05:57 +01:00 committed by GitHub
commit 753893dde1
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
35 changed files with 2541 additions and 21 deletions

View File

@ -0,0 +1,36 @@
# SPDX-License-Identifier: GPL-2.0
# Copyright (C) 2020-present Team LibreELEC (https://libreelec.tv)
PKG_NAME="gcc-or1k-linux"
PKG_VERSION="2020.08-1"
PKG_SHA256="697ef122917022f400003931bc6da75fe07bb5234ef8186cbe027e560f04a168"
PKG_LICENSE="GPL"
PKG_SITE="https://toolchains.bootlin.com/releases_openrisc.html"
PKG_URL="https://toolchains.bootlin.com/downloads/releases/toolchains/openrisc/tarballs/openrisc--musl--stable-${PKG_VERSION}.tar.bz2"
PKG_DEPENDS_HOST="ccache:host"
PKG_LONGDESC="OpenRISC 1000 GNU Linux Binary Toolchain"
PKG_TOOLCHAIN="manual"
makeinstall_host() {
mkdir -p $TOOLCHAIN/lib/gcc-or1k-linux/
cp -a * $TOOLCHAIN/lib/gcc-or1k-linux
# wrap gcc and g++ with ccache like in gcc package.mk
PKG_GCC_PREFIX="$TOOLCHAIN/lib/gcc-or1k-linux/bin/or1k-linux-"
rm -f "${PKG_GCC_PREFIX}gcc"
cat > "${PKG_GCC_PREFIX}gcc" << EOF
#!/bin/sh
$TOOLCHAIN/bin/ccache $TOOLCHAIN/lib/gcc-or1k-linux/bin/or1k-buildroot-linux-musl-gcc "\$@"
EOF
chmod +x "${PKG_GCC_PREFIX}gcc"
rm -f "${PKG_GCC_PREFIX}g++"
cat > "${PKG_GCC_PREFIX}g++" << EOF
#!/bin/sh
$TOOLCHAIN/bin/ccache $TOOLCHAIN/lib/gcc-or1k-linux/bin/or1k-buildroot-linux-musl-g++ "\$@"
EOF
chmod +x "${PKG_GCC_PREFIX}g++"
}

View File

@ -31,6 +31,10 @@ esac
PKG_KERNEL_CFG_FILE=$(kernel_config_path) || die
if [ -n "$($ROOT/$SCRIPTS/uboot_helper $PROJECT $DEVICE $UBOOT_SYSTEM crust_config)" ]; then
PKG_PATCH_DIRS="$PKG_PATCH_DIRS crust"
fi
if [ -n "$KERNEL_TOOLCHAIN" ]; then
PKG_DEPENDS_HOST="$PKG_DEPENDS_HOST gcc-arm-$KERNEL_TOOLCHAIN:host"
PKG_DEPENDS_TARGET="$PKG_DEPENDS_TARGET gcc-arm-$KERNEL_TOOLCHAIN:host"

View File

@ -2,8 +2,8 @@
# Copyright (C) 2018-present Team LibreELEC
PKG_NAME="atf"
PKG_VERSION="2.2"
PKG_SHA256="07e3c058ae2d95c7d516a46fc93565b797e912c3271ddbf29df523b1ab1ee911"
PKG_VERSION="2.4"
PKG_SHA256="4bfda9fdbe5022f2e88ad3344165f7d38a8ae4a0e2d91d44d9a1603425cc642d"
PKG_ARCH="arm aarch64"
PKG_LICENSE="BSD-3c"
PKG_SITE="https://github.com/ARM-software/arm-trusted-firmware"

View File

@ -0,0 +1,43 @@
# SPDX-License-Identifier: GPL-2.0
# Copyright (C) 2020-present Team LibreELEC (https://libreelec.tv)
PKG_NAME="crust"
PKG_VERSION="18f74f493690844a4392dc28d879abd4ba995513"
PKG_SHA256="7b8f004933054b6f394db532bf16fd9c3b42bd606f3e603f162f358436c4c3d8"
PKG_ARCH="arm aarch64"
PKG_LICENSE="BSD-3c"
PKG_SITE="https://github.com/crust-firmware/crust"
PKG_URL="https://github.com/crust-firmware/crust/archive/${PKG_VERSION}.tar.gz"
PKG_DEPENDS_TARGET="gcc-or1k-linux:host"
PKG_LONGDESC="Crust: Libre SCP firmware for Allwinner sunxi SoCs"
PKG_TOOLCHAIN="manual"
pre_configure_target() {
export CROSS_COMPILE="$TOOLCHAIN/lib/gcc-or1k-linux/bin/or1k-linux-"
}
make_target() {
CRUST_CONFIG=$($ROOT/$SCRIPTS/uboot_helper $PROJECT $DEVICE $UBOOT_SYSTEM crust_config)
if [ -z "$CRUST_CONFIG" ]; then
echo "crust_config must be set to build an image"
echo "see './scripts/uboot_helper' for more information"
exit 1
fi
make distclean
if [ "${BUILD_WITH_DEBUG}" = "yes" ]; then
echo "CONFIG_DEBUG_LOG=y" >> configs/$CRUST_CONFIG
else
echo "CONFIG_SERIAL=n" >> configs/$CRUST_CONFIG
fi
# Boards with a PMIC need to disable CONFIG_PMIC_SHUTDOWN to get CIR wakeup from suspend
echo "CONFIG_PMIC_SHUTDOWN=n" >> configs/$CRUST_CONFIG
make $CRUST_CONFIG
make scp
}
makeinstall_target() {
mkdir -p $INSTALL/usr/share/bootloader
cp -a build/scp/scp.bin $INSTALL/usr/share/bootloader
}

View File

@ -18,6 +18,9 @@ if [ -n "$UBOOT_FIRMWARE" ]; then
PKG_DEPENDS_UNPACK+=" $UBOOT_FIRMWARE"
fi
CRUST_CONFIG=$($ROOT/$SCRIPTS/uboot_helper $PROJECT $DEVICE $UBOOT_SYSTEM crust_config)
[ -n "$CRUST_CONFIG" ] && PKG_DEPENDS_TARGET+=" crust"
PKG_NEED_UNPACK="$PROJECT_DIR/$PROJECT/bootloader"
[ -n "$DEVICE" ] && PKG_NEED_UNPACK+=" $PROJECT_DIR/$PROJECT/devices/$DEVICE/bootloader"
@ -29,8 +32,8 @@ case "$PROJECT" in
PKG_PATCH_DIRS="rockchip"
;;
*)
PKG_VERSION="v2020.10"
PKG_SHA256="0c022ca6796aa8c0689faae8b515eb62ac84519c31de3153257a9ee0f446618f"
PKG_VERSION="v2021.01-rc4"
PKG_SHA256="9e05a96f0cac4f8b6fdea5cdf10f0d814e6a0d7141243a6eb9862385c7ddeb14"
PKG_URL="https://github.com/u-boot/u-boot/archive/$PKG_VERSION.tar.gz"
;;
esac
@ -50,7 +53,8 @@ make_target() {
echo "see './scripts/uboot_helper' for more information"
else
[ "${BUILD_WITH_DEBUG}" = "yes" ] && PKG_DEBUG=1 || PKG_DEBUG=0
[ -n "$UBOOT_FIRMWARE" ] && find_file_path bootloader/firmware && . ${FOUND_PATH}
[ -n "$ATF_PLATFORM" ] && cp -av $(get_install_dir atf)/usr/share/bootloader/bl31.bin .
[ -n "$CRUST_CONFIG" ] && cp -av $(get_install_dir crust)/usr/share/bootloader/scp.bin .
DEBUG=${PKG_DEBUG} CROSS_COMPILE="$TARGET_KERNEL_PREFIX" LDFLAGS="" ARCH=arm make mrproper
DEBUG=${PKG_DEBUG} CROSS_COMPILE="$TARGET_KERNEL_PREFIX" LDFLAGS="" ARCH=arm make $($ROOT/$SCRIPTS/uboot_helper $PROJECT $DEVICE $UBOOT_SYSTEM config)
DEBUG=${PKG_DEBUG} CROSS_COMPILE="$TARGET_KERNEL_PREFIX" LDFLAGS="" ARCH=arm _python_sysroot="$TOOLCHAIN" _python_prefix=/ _python_exec_prefix=/ make $UBOOT_TARGET HOSTCC="$HOST_CC" HOSTLDFLAGS="-L$TOOLCHAIN/lib" HOSTSTRIP="true" CONFIG_MKIMAGE_DTC_PATH="scripts/dtc/dtc"

View File

@ -1,4 +0,0 @@
# SPDX-License-Identifier: GPL-2.0
# Copyright (C) 2019-present Team LibreELEC (https://libreelec.tv)
[ -n "$ATF_PLATFORM" ] && cp -av $(get_install_dir atf)/usr/share/bootloader/bl31.bin .

View File

@ -0,0 +1,137 @@
From 49d3e172a2e06e7670c508e1d960282d40e7237e Mon Sep 17 00:00:00 2001
From: Samuel Holland <samuel@sholland.org>
Date: Wed, 12 Feb 2020 22:58:30 -0600
Subject: [PATCH 01/31] i2c: mv64xxx: Add runtime PM support
To save power, gate the clock when the bus is inactive, and during
system suspend. Also reset the controller during system suspend, since
it may be used by platform firmware, and we don't want to make any
assumptions about the hardware state at resume.
On some platforms, specifically Allwinner A13/A20, gating the clock
implicitly resets the module as well. Since the module already needs to
be reset after some suspend/resume cycles, it is simple enough to reset
it during every runtime suspend/resume.
Because the bus may be used by wakeup source IRQ threads, it needs to be
functional as soon as IRQs are enabled. Thus, its system PM hooks need
to run in the noirq phase.
Signed-off-by: Samuel Holland <samuel@sholland.org>
---
drivers/i2c/busses/i2c-mv64xxx.c | 52 ++++++++++++++++++++++++++------
1 file changed, 42 insertions(+), 10 deletions(-)
--- a/drivers/i2c/busses/i2c-mv64xxx.c
+++ b/drivers/i2c/busses/i2c-mv64xxx.c
@@ -17,6 +17,7 @@
#include <linux/interrupt.h>
#include <linux/mv643xx_i2c.h>
#include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
#include <linux/reset.h>
#include <linux/io.h>
#include <linux/of.h>
@@ -713,6 +714,10 @@ mv64xxx_i2c_xfer(struct i2c_adapter *ada
struct mv64xxx_i2c_data *drv_data = i2c_get_adapdata(adap);
int rc, ret = num;
+ rc = pm_runtime_get_sync(&adap->dev);
+ if (rc < 0)
+ return rc;
+
BUG_ON(drv_data->msgs != NULL);
drv_data->msgs = msgs;
drv_data->num_msgs = num;
@@ -728,6 +733,9 @@ mv64xxx_i2c_xfer(struct i2c_adapter *ada
drv_data->num_msgs = 0;
drv_data->msgs = NULL;
+ pm_runtime_mark_last_busy(&adap->dev);
+ pm_runtime_put_autosuspend(&adap->dev);
+
return ret;
}
@@ -950,6 +958,11 @@ mv64xxx_i2c_probe(struct platform_device
goto exit_free_irq;
}
+ pm_runtime_set_active(&pd->dev);
+ pm_runtime_set_autosuspend_delay(&pd->dev, 1000);
+ pm_runtime_use_autosuspend(&pd->dev);
+ pm_runtime_enable(&pd->dev);
+
return 0;
exit_free_irq:
@@ -968,40 +981,59 @@ mv64xxx_i2c_remove(struct platform_devic
{
struct mv64xxx_i2c_data *drv_data = platform_get_drvdata(dev);
+ pm_runtime_get_sync(&dev->dev);
+
i2c_del_adapter(&drv_data->adapter);
free_irq(drv_data->irq, drv_data);
reset_control_assert(drv_data->rstc);
clk_disable_unprepare(drv_data->reg_clk);
clk_disable_unprepare(drv_data->clk);
+ pm_runtime_put_noidle(&dev->dev);
+ pm_runtime_disable(&dev->dev);
+
+ return 0;
+}
+
+static int __maybe_unused mv64xxx_i2c_runtime_suspend(struct device *dev)
+{
+ struct mv64xxx_i2c_data *drv_data = dev_get_drvdata(dev);
+
+ reset_control_assert(drv_data->rstc);
+ clk_disable_unprepare(drv_data->reg_clk);
+ clk_disable_unprepare(drv_data->clk);
+
return 0;
}
-#ifdef CONFIG_PM
-static int mv64xxx_i2c_resume(struct device *dev)
+static int __maybe_unused mv64xxx_i2c_runtime_resume(struct device *dev)
{
struct mv64xxx_i2c_data *drv_data = dev_get_drvdata(dev);
+ if (!IS_ERR(drv_data->clk))
+ clk_prepare_enable(drv_data->clk);
+ if (!IS_ERR(drv_data->reg_clk))
+ clk_prepare_enable(drv_data->reg_clk);
+ reset_control_reset(drv_data->rstc);
+
mv64xxx_i2c_hw_init(drv_data);
return 0;
}
-static const struct dev_pm_ops mv64xxx_i2c_pm = {
- .resume = mv64xxx_i2c_resume,
+static const struct dev_pm_ops mv64xxx_i2c_pm_ops = {
+ SET_RUNTIME_PM_OPS(mv64xxx_i2c_runtime_suspend,
+ mv64xxx_i2c_runtime_resume, NULL)
+ SET_NOIRQ_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend,
+ pm_runtime_force_resume)
};
-#define mv64xxx_i2c_pm_ops (&mv64xxx_i2c_pm)
-#else
-#define mv64xxx_i2c_pm_ops NULL
-#endif
-
static struct platform_driver mv64xxx_i2c_driver = {
.probe = mv64xxx_i2c_probe,
.remove = mv64xxx_i2c_remove,
.driver = {
.name = MV64XXX_I2C_CTLR_NAME,
- .pm = mv64xxx_i2c_pm_ops,
+ .pm = &mv64xxx_i2c_pm_ops,
.of_match_table = mv64xxx_i2c_of_match_table,
},
};

View File

@ -0,0 +1,198 @@
From 87f8a2dea1cc62492a6eab63f50323127e508c3a Mon Sep 17 00:00:00 2001
From: Samuel Holland <samuel@sholland.org>
Date: Sun, 29 Dec 2019 20:23:28 -0600
Subject: [PATCH 02/31] clk: Implement protected-clocks for all OF clock
providers
This is a generic implementation of the "protected-clocks" property from
the common clock binding. It allows firmware to inform the OS about
clocks that must not be disabled while the OS is running.
This implementation comes with some caveats:
1) Clocks that have CLK_IS_CRITICAL in their init data are prepared/
enabled before they are attached to the clock tree. protected-clocks are
only protected once the clock provider is added, which is generally
after all of the clocks it provides have been registered. This leaves a
window of opportunity where something could disable or modify the clock,
such as a driver running on another CPU, or the clock core itself. There
is a comment to this effect in __clk_core_init():
/*
* Enable CLK_IS_CRITICAL clocks so newly added critical clocks
* don't get accidentally disabled when walking the orphan tree and
* reparenting clocks
*/
Similarly, these clocks will be enabled after they are first reparented,
unlike other CLK_IS_CRITICAL clocks. See the comment in
clk_core_reparent_orphans_nolock():
/*
* We need to use __clk_set_parent_before() and _after() to
* to properly migrate any prepare/enable count of the orphan
* clock. This is important for CLK_IS_CRITICAL clocks, which
* are enabled during init but might not have a parent yet.
*/
Ideally we could detect protected clocks before they are reparented, but
there are two problems with that:
a) From the clock core's perspective, hw->init is const.
b) The clock core doesn't see the device_node until __clk_register is
called on the first clock.
So the only "race-free" way to detect protected-clocks is to do it in
the middle of __clk_register, between when core->flags is initialized
and calling __clk_core_init(). That requires scanning the device tree
again for each clock, which is part of why I didn't do it that way.
2) __clk_protect needs to be idempotent, for two reasons:
a) Clocks with CLK_IS_CRITICAL in their init data are already
prepared/enabled, and we don't want to prepare/enable them again.
b) of_clk_set_defaults() is called twice for (at least some) clock
controllers registered with CLK_OF_DECLARE. It is called first in
of_clk_add_provider()/of_clk_add_hw_provider() inside clk_init_cb,
and again afterward in of_clk_init(). The second call in
of_clk_init() may be unnecessary, but verifying that would require
auditing all users of CLK_OF_DECLARE to ensure they called one of
the of_clk_add{,_hw}_provider functions.
Signed-off-by: Samuel Holland <samuel@sholland.org>
---
drivers/clk/clk-conf.c | 54 ++++++++++++++++++++++++++++++++++++++++++
drivers/clk/clk.c | 31 ++++++++++++++++++++++++
drivers/clk/clk.h | 2 ++
3 files changed, 87 insertions(+)
--- a/drivers/clk/clk-conf.c
+++ b/drivers/clk/clk-conf.c
@@ -11,6 +11,54 @@
#include <linux/of.h>
#include <linux/printk.h>
+#include "clk.h"
+
+static int __set_clk_flags(struct device_node *node)
+{
+ struct of_phandle_args clkspec;
+ struct property *prop;
+ int i, index = 0, rc;
+ const __be32 *cur;
+ struct clk *clk;
+ u32 nr_cells;
+
+ rc = of_property_read_u32(node, "#clock-cells", &nr_cells);
+ if (rc < 0) {
+ pr_err("clk: missing #clock-cells property on %pOF\n", node);
+ return rc;
+ }
+
+ clkspec.np = node;
+ clkspec.args_count = nr_cells;
+
+ of_property_for_each_u32(node, "protected-clocks", prop, cur, clkspec.args[0]) {
+ /* read the remainder of the clock specifier */
+ for (i = 1; i < nr_cells; ++i) {
+ cur = of_prop_next_u32(prop, cur, &clkspec.args[i]);
+ if (!cur) {
+ pr_err("clk: invalid value of protected-clocks"
+ " property at %pOF\n", node);
+ return -EINVAL;
+ }
+ }
+ clk = of_clk_get_from_provider(&clkspec);
+ if (IS_ERR(clk)) {
+ if (PTR_ERR(clk) != -EPROBE_DEFER)
+ pr_err("clk: couldn't get protected clock"
+ " %u for %pOF\n", index, node);
+ return PTR_ERR(clk);
+ }
+
+ rc = __clk_protect(clk);
+ if (rc < 0)
+ pr_warn("clk: failed to protect %s: %d\n",
+ __clk_get_name(clk), rc);
+ clk_put(clk);
+ index++;
+ }
+ return 0;
+}
+
static int __set_clk_parents(struct device_node *node, bool clk_supplier)
{
struct of_phandle_args clkspec;
@@ -135,6 +183,12 @@ int of_clk_set_defaults(struct device_no
if (!node)
return 0;
+ if (clk_supplier) {
+ rc = __set_clk_flags(node);
+ if (rc < 0)
+ return rc;
+ }
+
rc = __set_clk_parents(node, clk_supplier);
if (rc < 0)
return rc;
--- a/drivers/clk/clk.c
+++ b/drivers/clk/clk.c
@@ -4188,6 +4188,37 @@ void devm_clk_hw_unregister(struct devic
EXPORT_SYMBOL_GPL(devm_clk_hw_unregister);
/*
+ * clk-conf helpers
+ */
+
+int __clk_protect(struct clk *clk)
+{
+ struct clk_core *core = clk->core;
+ int ret = 0;
+
+ clk_prepare_lock();
+
+ /*
+ * If CLK_IS_CRITICAL was set in the clock's init data, then
+ * the clock was already prepared/enabled when it was added.
+ */
+ if (core->flags & CLK_IS_CRITICAL)
+ goto out;
+
+ core->flags |= CLK_IS_CRITICAL;
+ ret = clk_core_prepare(core);
+ if (ret)
+ goto out;
+
+ ret = clk_core_enable_lock(core);
+
+out:
+ clk_prepare_unlock();
+
+ return ret;
+}
+
+/*
* clkdev helpers
*/
--- a/drivers/clk/clk.h
+++ b/drivers/clk/clk.h
@@ -24,6 +24,7 @@ struct clk_hw *clk_find_hw(const char *d
#ifdef CONFIG_COMMON_CLK
struct clk *clk_hw_create_clk(struct device *dev, struct clk_hw *hw,
const char *dev_id, const char *con_id);
+int __clk_protect(struct clk *clk);
void __clk_put(struct clk *clk);
#else
/* All these casts to avoid ifdefs in clkdev... */
@@ -33,6 +34,7 @@ clk_hw_create_clk(struct device *dev, st
{
return (struct clk *)hw;
}
+static inline int __clk_protect(struct clk *clk) { return 0; }
static inline void __clk_put(struct clk *clk) { }
#endif

View File

@ -0,0 +1,49 @@
From fedd64540faff5a08239c062e03c64fb70d0aa4d Mon Sep 17 00:00:00 2001
From: Samuel Holland <samuel@sholland.org>
Date: Mon, 30 Dec 2019 12:39:31 -0600
Subject: [PATCH 03/31] Revert "clk: qcom: Support 'protected-clocks' property"
Now that protected-clocks is handled in the clk core, this
driver-specific implementation is redundant.
This reverts commit b181b3b801da8893c8eb706e448dd5111b02de60.
Signed-off-by: Samuel Holland <samuel@sholland.org>
---
drivers/clk/qcom/common.c | 18 ------------------
1 file changed, 18 deletions(-)
--- a/drivers/clk/qcom/common.c
+++ b/drivers/clk/qcom/common.c
@@ -194,22 +194,6 @@ int qcom_cc_register_sleep_clk(struct de
}
EXPORT_SYMBOL_GPL(qcom_cc_register_sleep_clk);
-/* Drop 'protected-clocks' from the list of clocks to register */
-static void qcom_cc_drop_protected(struct device *dev, struct qcom_cc *cc)
-{
- struct device_node *np = dev->of_node;
- struct property *prop;
- const __be32 *p;
- u32 i;
-
- of_property_for_each_u32(np, "protected-clocks", prop, p, i) {
- if (i >= cc->num_rclks)
- continue;
-
- cc->rclks[i] = NULL;
- }
-}
-
static struct clk_hw *qcom_cc_clk_hw_get(struct of_phandle_args *clkspec,
void *data)
{
@@ -272,8 +256,6 @@ int qcom_cc_really_probe(struct platform
cc->rclks = rclks;
cc->num_rclks = num_clks;
- qcom_cc_drop_protected(dev, cc);
-
for (i = 0; i < num_clk_hws; i++) {
ret = devm_clk_hw_register(dev, clk_hws[i]);
if (ret)

View File

@ -0,0 +1,29 @@
From 0cd71d2659136cd9bead16348637d317d4bbfa66 Mon Sep 17 00:00:00 2001
From: Samuel Holland <samuel@sholland.org>
Date: Wed, 1 Jan 2020 16:03:46 -0600
Subject: [PATCH 05/31] [DO NOT MERGE] ARM: dts: sunxi: h3/h5: Protect SCP
clocks
Signed-off-by: Samuel Holland <samuel@sholland.org>
---
arch/arm/boot/dts/sunxi-h3-h5.dtsi | 2 ++
1 file changed, 2 insertions(+)
--- a/arch/arm/boot/dts/sunxi-h3-h5.dtsi
+++ b/arch/arm/boot/dts/sunxi-h3-h5.dtsi
@@ -388,6 +388,7 @@
reg = <0x01c20000 0x400>;
clocks = <&osc24M>, <&rtc 0>;
clock-names = "hosc", "losc";
+ protected-clocks = <CLK_BUS_MSGBOX>;
#clock-cells = <1>;
#reset-cells = <1>;
};
@@ -852,6 +853,7 @@
clocks = <&osc24M>, <&rtc 0>, <&rtc 2>,
<&ccu CLK_PLL_PERIPH0>;
clock-names = "hosc", "losc", "iosc", "pll-periph";
+ protected-clocks = <CLK_APB0_TWD>;
#clock-cells = <1>;
#reset-cells = <1>;
};

View File

@ -0,0 +1,29 @@
From bd91939780869b8c6b48e6eae07733660dceb56b Mon Sep 17 00:00:00 2001
From: Samuel Holland <samuel@sholland.org>
Date: Mon, 30 Dec 2019 15:10:32 -0600
Subject: [PATCH 06/31] [DO NOT MERGE] arm64: dts: allwinner: a64: Protect SCP
clocks
Signed-off-by: Samuel Holland <samuel@sholland.org>
---
arch/arm64/boot/dts/allwinner/sun50i-a64.dtsi | 2 ++
1 file changed, 2 insertions(+)
--- a/arch/arm64/boot/dts/allwinner/sun50i-a64.dtsi
+++ b/arch/arm64/boot/dts/allwinner/sun50i-a64.dtsi
@@ -637,6 +637,7 @@
reg = <0x01c20000 0x400>;
clocks = <&osc24M>, <&rtc 0>;
clock-names = "hosc", "losc";
+ protected-clocks = <CLK_BUS_MSGBOX>;
#clock-cells = <1>;
#reset-cells = <1>;
};
@@ -1212,6 +1213,7 @@
clocks = <&osc24M>, <&rtc 0>, <&rtc 2>,
<&ccu CLK_PLL_PERIPH0>;
clock-names = "hosc", "losc", "iosc", "pll-periph";
+ protected-clocks = <CLK_APB0_TWD>;
#clock-cells = <1>;
#reset-cells = <1>;
};

View File

@ -0,0 +1,29 @@
From 082b5969ad4a2705169c1c1612413ba7c98dc27f Mon Sep 17 00:00:00 2001
From: Samuel Holland <samuel@sholland.org>
Date: Wed, 1 Jan 2020 16:04:01 -0600
Subject: [PATCH 07/31] [DO NOT MERGE] arm64: dts: allwinner: h6: Protect SCP
clocks
Signed-off-by: Samuel Holland <samuel@sholland.org>
---
arch/arm64/boot/dts/allwinner/sun50i-h6.dtsi | 2 ++
1 file changed, 2 insertions(+)
--- a/arch/arm64/boot/dts/allwinner/sun50i-h6.dtsi
+++ b/arch/arm64/boot/dts/allwinner/sun50i-h6.dtsi
@@ -230,6 +230,7 @@
reg = <0x03001000 0x1000>;
clocks = <&osc24M>, <&rtc 0>, <&rtc 2>;
clock-names = "hosc", "losc", "iosc";
+ protected-clocks = <CLK_BUS_MSGBOX>;
#clock-cells = <1>;
#reset-cells = <1>;
};
@@ -894,6 +895,7 @@
clocks = <&osc24M>, <&rtc 0>, <&rtc 2>,
<&ccu CLK_PLL_PERIPH0>;
clock-names = "hosc", "losc", "iosc", "pll-periph";
+ protected-clocks = <CLK_R_APB1_TWD>;
#clock-cells = <1>;
#reset-cells = <1>;
};

View File

@ -0,0 +1,27 @@
From fe3a4fd11558fac2ac10928d217b21ff2f67483f Mon Sep 17 00:00:00 2001
From: Samuel Holland <samuel@sholland.org>
Date: Mon, 30 Dec 2019 21:56:59 -0600
Subject: [PATCH 08/31] bus: sunxi-rsb: Always check register address validity
The register address was already validated for read operations before
being truncated to a u8. Write operations have the same set of possible
addresses, and the address is being truncated from u32 to u8 here as
well, so the same check is needed.
Signed-off-by: Samuel Holland <samuel@sholland.org>
---
drivers/bus/sunxi-rsb.c | 3 +++
1 file changed, 3 insertions(+)
--- a/drivers/bus/sunxi-rsb.c
+++ b/drivers/bus/sunxi-rsb.c
@@ -414,6 +414,9 @@ static int regmap_sunxi_rsb_reg_write(vo
struct sunxi_rsb_ctx *ctx = context;
struct sunxi_rsb_device *rdev = ctx->rdev;
+ if (reg > 0xff)
+ return -EINVAL;
+
return sunxi_rsb_write(rdev->rsb, rdev->rtaddr, reg, &val, ctx->size);
}

View File

@ -0,0 +1,33 @@
From febbd571f841fcf680f22148b0b5b24301fad5c1 Mon Sep 17 00:00:00 2001
From: Samuel Holland <samuel@sholland.org>
Date: Mon, 30 Dec 2019 22:09:08 -0600
Subject: [PATCH 09/31] bus: sunxi-rsb: Use devm_platform_ioremap_resource
This simplifies the code and removes the need for a "struct resource".
Signed-off-by: Samuel Holland <samuel@sholland.org>
---
drivers/bus/sunxi-rsb.c | 5 ++---
1 file changed, 2 insertions(+), 3 deletions(-)
--- a/drivers/bus/sunxi-rsb.c
+++ b/drivers/bus/sunxi-rsb.c
@@ -627,7 +627,6 @@ static int sunxi_rsb_probe(struct platfo
{
struct device *dev = &pdev->dev;
struct device_node *np = dev->of_node;
- struct resource *r;
struct sunxi_rsb *rsb;
unsigned long p_clk_freq;
u32 clk_delay, clk_freq = 3000000;
@@ -648,8 +647,8 @@ static int sunxi_rsb_probe(struct platfo
rsb->dev = dev;
platform_set_drvdata(pdev, rsb);
- r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- rsb->regs = devm_ioremap_resource(dev, r);
+
+ rsb->regs = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(rsb->regs))
return PTR_ERR(rsb->regs);

View File

@ -0,0 +1,208 @@
From 0a9087b56ec06abe2ff7f4c2c2732677cec8793e Mon Sep 17 00:00:00 2001
From: Samuel Holland <samuel@sholland.org>
Date: Mon, 30 Dec 2019 21:49:29 -0600
Subject: [PATCH 10/31] bus: sunxi-rsb: Precompute read/write commands and mask
Since we know the size of the transfer at context creation time, go
ahead and determine the commands used for reading and writing then,
instead of running through a switch statement for every read/write.
We can do the same thing for the read mask.
The context pointer could be passed directly to sunxi_rsb_read/write
to avoid adding more parameters (which would be spilled for mutex_lock).
That would make the regmap read/write wrappers even more trivial, so I
inlined sunxi_rsb_read/write into them.
I changed the error message to print the name of the device requesting
the regmap, not the RSB controller; I expect that to be more helpful
when tracking down the source of the error.
Signed-off-by: Samuel Holland <samuel@sholland.org>
---
drivers/bus/sunxi-rsb.c | 120 +++++++++++++++-------------------------
1 file changed, 44 insertions(+), 76 deletions(-)
--- a/drivers/bus/sunxi-rsb.c
+++ b/drivers/bus/sunxi-rsb.c
@@ -311,41 +311,38 @@ static int _sunxi_rsb_run_xfer(struct su
return 0;
}
-static int sunxi_rsb_read(struct sunxi_rsb *rsb, u8 rtaddr, u8 addr,
- u32 *buf, size_t len)
+/* RSB regmap functions */
+struct sunxi_rsb_ctx {
+ struct sunxi_rsb_device *rdev;
+ u32 mask;
+ u8 rd_cmd;
+ u8 wr_cmd;
+};
+
+static int regmap_sunxi_rsb_reg_read(void *context, unsigned int reg,
+ unsigned int *val)
{
- u32 cmd;
+ struct sunxi_rsb_ctx *ctx = context;
+ struct sunxi_rsb_device *rdev = ctx->rdev;
+ struct sunxi_rsb *rsb = rdev->rsb;
int ret;
- if (!buf)
+ if (!val)
return -EINVAL;
-
- switch (len) {
- case 1:
- cmd = RSB_CMD_RD8;
- break;
- case 2:
- cmd = RSB_CMD_RD16;
- break;
- case 4:
- cmd = RSB_CMD_RD32;
- break;
- default:
- dev_err(rsb->dev, "Invalid access width: %zd\n", len);
+ if (reg > 0xff)
return -EINVAL;
- }
mutex_lock(&rsb->lock);
- writel(addr, rsb->regs + RSB_ADDR);
- writel(RSB_DAR_RTA(rtaddr), rsb->regs + RSB_DAR);
- writel(cmd, rsb->regs + RSB_CMD);
+ writel(reg, rsb->regs + RSB_ADDR);
+ writel(RSB_DAR_RTA(rdev->rtaddr), rsb->regs + RSB_DAR);
+ writel(ctx->rd_cmd, rsb->regs + RSB_CMD);
ret = _sunxi_rsb_run_xfer(rsb);
if (ret)
goto unlock;
- *buf = readl(rsb->regs + RSB_DATA) & GENMASK(len * 8 - 1, 0);
+ *val = readl(rsb->regs + RSB_DATA) & ctx->mask;
unlock:
mutex_unlock(&rsb->lock);
@@ -353,36 +350,24 @@ unlock:
return ret;
}
-static int sunxi_rsb_write(struct sunxi_rsb *rsb, u8 rtaddr, u8 addr,
- const u32 *buf, size_t len)
+static int regmap_sunxi_rsb_reg_write(void *context, unsigned int reg,
+ unsigned int val)
{
- u32 cmd;
+ struct sunxi_rsb_ctx *ctx = context;
+ struct sunxi_rsb_device *rdev = ctx->rdev;
+ struct sunxi_rsb *rsb = rdev->rsb;
int ret;
- if (!buf)
- return -EINVAL;
-
- switch (len) {
- case 1:
- cmd = RSB_CMD_WR8;
- break;
- case 2:
- cmd = RSB_CMD_WR16;
- break;
- case 4:
- cmd = RSB_CMD_WR32;
- break;
- default:
- dev_err(rsb->dev, "Invalid access width: %zd\n", len);
+ if (reg > 0xff)
return -EINVAL;
- }
mutex_lock(&rsb->lock);
- writel(addr, rsb->regs + RSB_ADDR);
- writel(RSB_DAR_RTA(rtaddr), rsb->regs + RSB_DAR);
- writel(*buf, rsb->regs + RSB_DATA);
- writel(cmd, rsb->regs + RSB_CMD);
+ writel(reg, rsb->regs + RSB_ADDR);
+ writel(RSB_DAR_RTA(rdev->rtaddr), rsb->regs + RSB_DAR);
+ writel(val, rsb->regs + RSB_DATA);
+ writel(ctx->wr_cmd, rsb->regs + RSB_CMD);
+
ret = _sunxi_rsb_run_xfer(rsb);
mutex_unlock(&rsb->lock);
@@ -390,36 +375,6 @@ static int sunxi_rsb_write(struct sunxi_
return ret;
}
-/* RSB regmap functions */
-struct sunxi_rsb_ctx {
- struct sunxi_rsb_device *rdev;
- int size;
-};
-
-static int regmap_sunxi_rsb_reg_read(void *context, unsigned int reg,
- unsigned int *val)
-{
- struct sunxi_rsb_ctx *ctx = context;
- struct sunxi_rsb_device *rdev = ctx->rdev;
-
- if (reg > 0xff)
- return -EINVAL;
-
- return sunxi_rsb_read(rdev->rsb, rdev->rtaddr, reg, val, ctx->size);
-}
-
-static int regmap_sunxi_rsb_reg_write(void *context, unsigned int reg,
- unsigned int val)
-{
- struct sunxi_rsb_ctx *ctx = context;
- struct sunxi_rsb_device *rdev = ctx->rdev;
-
- if (reg > 0xff)
- return -EINVAL;
-
- return sunxi_rsb_write(rdev->rsb, rdev->rtaddr, reg, &val, ctx->size);
-}
-
static void regmap_sunxi_rsb_free_ctx(void *context)
{
struct sunxi_rsb_ctx *ctx = context;
@@ -439,13 +394,24 @@ static struct sunxi_rsb_ctx *regmap_sunx
const struct regmap_config *config)
{
struct sunxi_rsb_ctx *ctx;
+ u8 rd_cmd, wr_cmd;
switch (config->val_bits) {
case 8:
+ rd_cmd = RSB_CMD_RD8;
+ wr_cmd = RSB_CMD_WR8;
+ break;
case 16:
+ rd_cmd = RSB_CMD_RD16;
+ wr_cmd = RSB_CMD_WR16;
+ break;
case 32:
+ rd_cmd = RSB_CMD_RD32;
+ wr_cmd = RSB_CMD_WR32;
break;
default:
+ dev_err(&rdev->dev, "Invalid RSB access width: %d\n",
+ config->val_bits);
return ERR_PTR(-EINVAL);
}
@@ -454,7 +420,9 @@ static struct sunxi_rsb_ctx *regmap_sunx
return ERR_PTR(-ENOMEM);
ctx->rdev = rdev;
- ctx->size = config->val_bits / 8;
+ ctx->mask = GENMASK(config->val_bits - 1, 0);
+ ctx->rd_cmd = rd_cmd;
+ ctx->wr_cmd = wr_cmd;
return ctx;
}

View File

@ -0,0 +1,43 @@
From d58ad83eaf72e072d0e0205847ab4332dba24cd0 Mon Sep 17 00:00:00 2001
From: Samuel Holland <samuel@sholland.org>
Date: Mon, 30 Dec 2019 17:12:42 -0600
Subject: [PATCH 11/31] bus: sunxi-rsb: Move OF match table
For some reason, this driver's OF match table was placed above the
probe/remove functions, far away from the platform_driver definition.
Adding device PM ops would move the table even farther away. Let's move
it to the usual place, right before the platform_driver.
Signed-off-by: Samuel Holland <samuel@sholland.org>
---
drivers/bus/sunxi-rsb.c | 12 ++++++------
1 file changed, 6 insertions(+), 6 deletions(-)
--- a/drivers/bus/sunxi-rsb.c
+++ b/drivers/bus/sunxi-rsb.c
@@ -585,12 +585,6 @@ static int of_rsb_register_devices(struc
return 0;
}
-static const struct of_device_id sunxi_rsb_of_match_table[] = {
- { .compatible = "allwinner,sun8i-a23-rsb" },
- {}
-};
-MODULE_DEVICE_TABLE(of, sunxi_rsb_of_match_table);
-
static int sunxi_rsb_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
@@ -717,6 +711,12 @@ static int sunxi_rsb_remove(struct platf
return 0;
}
+static const struct of_device_id sunxi_rsb_of_match_table[] = {
+ { .compatible = "allwinner,sun8i-a23-rsb" },
+ {}
+};
+MODULE_DEVICE_TABLE(of, sunxi_rsb_of_match_table);
+
static struct platform_driver sunxi_rsb_driver = {
.probe = sunxi_rsb_probe,
.remove = sunxi_rsb_remove,

View File

@ -0,0 +1,216 @@
From f8f487fcec9fc8ee7861b99dcf833bd82249ab21 Mon Sep 17 00:00:00 2001
From: Samuel Holland <samuel@sholland.org>
Date: Mon, 30 Dec 2019 22:36:04 -0600
Subject: [PATCH 12/31] bus: sunxi-rsb: Split out controller init/exit
functions
This separates the resource acquisition from the hardware initialization
phase, so the hardware initialization can be repeated after system
suspend/resume. The same is done for the exit/remove function, except
that there is no resource deallocation phase due to the use of devres.
The call to reset_control_deassert() is replaced with a call to
reset_control_reset() to ensure the hardware is fully reinitialized,
regardless of the state firmware left it in.
The requested RSB clock frequency is stored in `struct sunxi_rsb` so it
will be available when reinitializing the hardware.
Signed-off-by: Samuel Holland <samuel@sholland.org>
---
drivers/bus/sunxi-rsb.c | 129 +++++++++++++++++++++++-----------------
1 file changed, 73 insertions(+), 56 deletions(-)
--- a/drivers/bus/sunxi-rsb.c
+++ b/drivers/bus/sunxi-rsb.c
@@ -126,6 +126,7 @@ struct sunxi_rsb {
struct completion complete;
struct mutex lock;
unsigned int status;
+ u32 clk_freq;
};
/* bus / slave device related functions */
@@ -585,15 +586,75 @@ static int of_rsb_register_devices(struc
return 0;
}
+static int sunxi_rsb_init_controller(struct sunxi_rsb *rsb)
+{
+ struct device *dev = rsb->dev;
+ unsigned long p_clk_freq;
+ u32 clk_delay, reg;
+ int clk_div, ret;
+
+ ret = clk_prepare_enable(rsb->clk);
+ if (ret) {
+ dev_err(dev, "failed to enable clk: %d\n", ret);
+ return ret;
+ }
+
+ ret = reset_control_reset(rsb->rstc);
+ if (ret) {
+ dev_err(dev, "failed to deassert reset line: %d\n", ret);
+ goto err_clk_disable;
+ }
+
+ /* reset the controller */
+ writel(RSB_CTRL_SOFT_RST, rsb->regs + RSB_CTRL);
+ readl_poll_timeout(rsb->regs + RSB_CTRL, reg,
+ !(reg & RSB_CTRL_SOFT_RST), 1000, 100000);
+
+ /*
+ * Clock frequency and delay calculation code is from
+ * Allwinner U-boot sources.
+ *
+ * From A83 user manual:
+ * bus clock frequency = parent clock frequency / (2 * (divider + 1))
+ */
+ p_clk_freq = clk_get_rate(rsb->clk);
+ clk_div = p_clk_freq / rsb->clk_freq / 2;
+ if (!clk_div)
+ clk_div = 1;
+ else if (clk_div > RSB_CCR_MAX_CLK_DIV + 1)
+ clk_div = RSB_CCR_MAX_CLK_DIV + 1;
+
+ clk_delay = clk_div >> 1;
+ if (!clk_delay)
+ clk_delay = 1;
+
+ dev_info(dev, "RSB running at %lu Hz\n", p_clk_freq / clk_div / 2);
+ writel(RSB_CCR_SDA_OUT_DELAY(clk_delay) | RSB_CCR_CLK_DIV(clk_div - 1),
+ rsb->regs + RSB_CCR);
+
+ return 0;
+
+err_clk_disable:
+ clk_disable_unprepare(rsb->clk);
+
+ return ret;
+}
+
+static int sunxi_rsb_exit_controller(struct sunxi_rsb *rsb)
+{
+ reset_control_assert(rsb->rstc);
+ clk_disable_unprepare(rsb->clk);
+
+ return 0;
+}
+
static int sunxi_rsb_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct device_node *np = dev->of_node;
struct sunxi_rsb *rsb;
- unsigned long p_clk_freq;
- u32 clk_delay, clk_freq = 3000000;
- int clk_div, irq, ret;
- u32 reg;
+ u32 clk_freq = 3000000;
+ int irq, ret;
of_property_read_u32(np, "clock-frequency", &clk_freq);
if (clk_freq > RSB_MAX_FREQ) {
@@ -608,6 +669,7 @@ static int sunxi_rsb_probe(struct platfo
return -ENOMEM;
rsb->dev = dev;
+ rsb->clk_freq = clk_freq;
platform_set_drvdata(pdev, rsb);
rsb->regs = devm_platform_ioremap_resource(pdev, 0);
@@ -625,63 +687,27 @@ static int sunxi_rsb_probe(struct platfo
return ret;
}
- ret = clk_prepare_enable(rsb->clk);
- if (ret) {
- dev_err(dev, "failed to enable clk: %d\n", ret);
- return ret;
- }
-
- p_clk_freq = clk_get_rate(rsb->clk);
-
rsb->rstc = devm_reset_control_get(dev, NULL);
if (IS_ERR(rsb->rstc)) {
ret = PTR_ERR(rsb->rstc);
dev_err(dev, "failed to retrieve reset controller: %d\n", ret);
- goto err_clk_disable;
- }
-
- ret = reset_control_deassert(rsb->rstc);
- if (ret) {
- dev_err(dev, "failed to deassert reset line: %d\n", ret);
- goto err_clk_disable;
+ return ret;
}
init_completion(&rsb->complete);
mutex_init(&rsb->lock);
- /* reset the controller */
- writel(RSB_CTRL_SOFT_RST, rsb->regs + RSB_CTRL);
- readl_poll_timeout(rsb->regs + RSB_CTRL, reg,
- !(reg & RSB_CTRL_SOFT_RST), 1000, 100000);
-
- /*
- * Clock frequency and delay calculation code is from
- * Allwinner U-boot sources.
- *
- * From A83 user manual:
- * bus clock frequency = parent clock frequency / (2 * (divider + 1))
- */
- clk_div = p_clk_freq / clk_freq / 2;
- if (!clk_div)
- clk_div = 1;
- else if (clk_div > RSB_CCR_MAX_CLK_DIV + 1)
- clk_div = RSB_CCR_MAX_CLK_DIV + 1;
-
- clk_delay = clk_div >> 1;
- if (!clk_delay)
- clk_delay = 1;
-
- dev_info(dev, "RSB running at %lu Hz\n", p_clk_freq / clk_div / 2);
- writel(RSB_CCR_SDA_OUT_DELAY(clk_delay) | RSB_CCR_CLK_DIV(clk_div - 1),
- rsb->regs + RSB_CCR);
-
ret = devm_request_irq(dev, irq, sunxi_rsb_irq, 0, RSB_CTRL_NAME, rsb);
if (ret) {
dev_err(dev, "can't register interrupt handler irq %d: %d\n",
irq, ret);
- goto err_reset_assert;
+ return ret;
}
+ ret = sunxi_rsb_init_controller(rsb);
+ if (ret)
+ return ret;
+
/* initialize all devices on the bus into RSB mode */
ret = sunxi_rsb_init_device_mode(rsb);
if (ret)
@@ -690,14 +716,6 @@ static int sunxi_rsb_probe(struct platfo
of_rsb_register_devices(rsb);
return 0;
-
-err_reset_assert:
- reset_control_assert(rsb->rstc);
-
-err_clk_disable:
- clk_disable_unprepare(rsb->clk);
-
- return ret;
}
static int sunxi_rsb_remove(struct platform_device *pdev)
@@ -705,8 +723,7 @@ static int sunxi_rsb_remove(struct platf
struct sunxi_rsb *rsb = platform_get_drvdata(pdev);
device_for_each_child(rsb->dev, NULL, sunxi_rsb_remove_devices);
- reset_control_assert(rsb->rstc);
- clk_disable_unprepare(rsb->clk);
+ sunxi_rsb_exit_controller(rsb);
return 0;
}

View File

@ -0,0 +1,65 @@
From 96302816684155abd59e0dbba7a999c0d269e736 Mon Sep 17 00:00:00 2001
From: Samuel Holland <samuel@sholland.org>
Date: Mon, 30 Dec 2019 22:50:02 -0600
Subject: [PATCH 13/31] bus: sunxi-rsb: Implement global suspend/resume
callbacks
Since system firmware is likely to use the RSB bus to communicate with a
PMIC while the system is suspended, we cannot make any assumptions about
the controller state after resuming. Thus it is important to completely
reinitialize the controller.
The RSB bus needs to be ready as soon as IRQs are enabled, to handle
wakeup event IRQs coming from the PMIC. Thus it uses NOIRQ callbacks.
Signed-off-by: Samuel Holland <samuel@sholland.org>
---
drivers/bus/sunxi-rsb.c | 22 +++++++++++++++++++++-
1 file changed, 21 insertions(+), 1 deletion(-)
--- a/drivers/bus/sunxi-rsb.c
+++ b/drivers/bus/sunxi-rsb.c
@@ -45,6 +45,7 @@
#include <linux/of_irq.h>
#include <linux/of_platform.h>
#include <linux/platform_device.h>
+#include <linux/pm.h>
#include <linux/regmap.h>
#include <linux/reset.h>
#include <linux/slab.h>
@@ -648,6 +649,24 @@ static int sunxi_rsb_exit_controller(str
return 0;
}
+static int __maybe_unused sunxi_rsb_suspend(struct device *dev)
+{
+ struct sunxi_rsb *rsb = dev_get_drvdata(dev);
+
+ return sunxi_rsb_exit_controller(rsb);
+}
+
+static int __maybe_unused sunxi_rsb_resume(struct device *dev)
+{
+ struct sunxi_rsb *rsb = dev_get_drvdata(dev);
+
+ return sunxi_rsb_init_controller(rsb);
+}
+
+static const struct dev_pm_ops sunxi_rsb_dev_pm_ops = {
+ SET_NOIRQ_SYSTEM_SLEEP_PM_OPS(sunxi_rsb_suspend, sunxi_rsb_resume)
+};
+
static int sunxi_rsb_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
@@ -738,8 +757,9 @@ static struct platform_driver sunxi_rsb_
.probe = sunxi_rsb_probe,
.remove = sunxi_rsb_remove,
.driver = {
- .name = RSB_CTRL_NAME,
+ .name = RSB_CTRL_NAME,
.of_match_table = sunxi_rsb_of_match_table,
+ .pm = &sunxi_rsb_dev_pm_ops,
},
};

View File

@ -0,0 +1,122 @@
From f226c7c38ef1b9ae026680c9c68fedd4fbd4ecd7 Mon Sep 17 00:00:00 2001
From: Samuel Holland <samuel@sholland.org>
Date: Wed, 1 Jan 2020 00:10:40 -0600
Subject: [PATCH 14/31] bus: sunxi-rsb: Implement runtime power management
Signed-off-by: Samuel Holland <samuel@sholland.org>
---
drivers/bus/sunxi-rsb.c | 40 ++++++++++++++++++++++++++++++++++++++++
1 file changed, 40 insertions(+)
--- a/drivers/bus/sunxi-rsb.c
+++ b/drivers/bus/sunxi-rsb.c
@@ -46,6 +46,7 @@
#include <linux/of_platform.h>
#include <linux/platform_device.h>
#include <linux/pm.h>
+#include <linux/pm_runtime.h>
#include <linux/regmap.h>
#include <linux/reset.h>
#include <linux/slab.h>
@@ -334,6 +335,7 @@ static int regmap_sunxi_rsb_reg_read(voi
if (reg > 0xff)
return -EINVAL;
+ pm_runtime_get_sync(rsb->dev);
mutex_lock(&rsb->lock);
writel(reg, rsb->regs + RSB_ADDR);
@@ -348,6 +350,8 @@ static int regmap_sunxi_rsb_reg_read(voi
unlock:
mutex_unlock(&rsb->lock);
+ pm_runtime_mark_last_busy(rsb->dev);
+ pm_runtime_put_autosuspend(rsb->dev);
return ret;
}
@@ -363,6 +367,7 @@ static int regmap_sunxi_rsb_reg_write(vo
if (reg > 0xff)
return -EINVAL;
+ pm_runtime_get_sync(rsb->dev);
mutex_lock(&rsb->lock);
writel(reg, rsb->regs + RSB_ADDR);
@@ -373,6 +378,8 @@ static int regmap_sunxi_rsb_reg_write(vo
ret = _sunxi_rsb_run_xfer(rsb);
mutex_unlock(&rsb->lock);
+ pm_runtime_mark_last_busy(rsb->dev);
+ pm_runtime_put_autosuspend(rsb->dev);
return ret;
}
@@ -649,10 +656,30 @@ static int sunxi_rsb_exit_controller(str
return 0;
}
+static int __maybe_unused sunxi_rsb_runtime_suspend(struct device *dev)
+{
+ struct sunxi_rsb *rsb = dev_get_drvdata(dev);
+
+ clk_disable_unprepare(rsb->clk);
+
+ return 0;
+}
+
+static int __maybe_unused sunxi_rsb_runtime_resume(struct device *dev)
+{
+ struct sunxi_rsb *rsb = dev_get_drvdata(dev);
+
+ return clk_prepare_enable(rsb->clk);
+}
+
static int __maybe_unused sunxi_rsb_suspend(struct device *dev)
{
struct sunxi_rsb *rsb = dev_get_drvdata(dev);
+ /* Ensure the clock is running before asserting reset. */
+ if (pm_runtime_status_suspended(dev))
+ pm_runtime_resume(dev);
+
return sunxi_rsb_exit_controller(rsb);
}
@@ -664,6 +691,8 @@ static int __maybe_unused sunxi_rsb_resu
}
static const struct dev_pm_ops sunxi_rsb_dev_pm_ops = {
+ SET_RUNTIME_PM_OPS(sunxi_rsb_runtime_suspend,
+ sunxi_rsb_runtime_resume, NULL)
SET_NOIRQ_SYSTEM_SLEEP_PM_OPS(sunxi_rsb_suspend, sunxi_rsb_resume)
};
@@ -734,6 +763,12 @@ static int sunxi_rsb_probe(struct platfo
of_rsb_register_devices(rsb);
+ pm_suspend_ignore_children(dev, true);
+ pm_runtime_set_autosuspend_delay(dev, 1000);
+ pm_runtime_use_autosuspend(dev);
+ pm_runtime_set_active(dev);
+ pm_runtime_enable(dev);
+
return 0;
}
@@ -741,9 +776,14 @@ static int sunxi_rsb_remove(struct platf
{
struct sunxi_rsb *rsb = platform_get_drvdata(pdev);
+ pm_runtime_get_sync(&pdev->dev);
+
device_for_each_child(rsb->dev, NULL, sunxi_rsb_remove_devices);
sunxi_rsb_exit_controller(rsb);
+ pm_runtime_disable(&pdev->dev);
+ pm_runtime_put_noidle(&pdev->dev);
+
return 0;
}

View File

@ -0,0 +1,69 @@
From 3c8535aee4f58ffeae01dd4d6e32479e9fd68a7e Mon Sep 17 00:00:00 2001
From: Samuel Holland <samuel@sholland.org>
Date: Mon, 30 Dec 2019 22:58:21 -0600
Subject: [PATCH 15/31] bus: sunxi-rsb: Make interrupt handling more robust
The RSB controller has two registers for controlling interrupt inputs:
RSB_INTE, which has bits for each possible interrupt, and the global
interrupt enable bit in RSB_CTRL.
Currently, we enable the bits in RSB_INTE before each transfer, but this
is unnecessary because we never disable them. Move the initialization of
RSB_INTE so it is done only once.
We also set the global interrupt enable bit before each transfer. Unlike
other bits in RSB_CTRL, this bit is cleared by writing a zero. Thus, we
clear the bit in the post-timeout cleanup code, but that is not
documented, so note that in the comment. However, in the success/error
path (when an IRQ is received), we do not disable further interrupts.
Add a register write to do just that.
Signed-off-by: Samuel Holland <samuel@sholland.org>
---
drivers/bus/sunxi-rsb.c | 14 ++++++++++----
1 file changed, 10 insertions(+), 4 deletions(-)
--- a/drivers/bus/sunxi-rsb.c
+++ b/drivers/bus/sunxi-rsb.c
@@ -276,8 +276,6 @@ static int _sunxi_rsb_run_xfer(struct su
reinit_completion(&rsb->complete);
- writel(RSB_INTS_LOAD_BSY | RSB_INTS_TRANS_ERR | RSB_INTS_TRANS_OVER,
- rsb->regs + RSB_INTE);
writel(RSB_CTRL_START_TRANS | RSB_CTRL_GLOBAL_INT_ENB,
rsb->regs + RSB_CTRL);
@@ -285,7 +283,7 @@ static int _sunxi_rsb_run_xfer(struct su
msecs_to_jiffies(100))) {
dev_dbg(rsb->dev, "RSB timeout\n");
- /* abort the transfer */
+ /* abort the transfer and disable interrupts */
writel(RSB_CTRL_ABORT_TRANS, rsb->regs + RSB_CTRL);
/* clear any interrupt flags */
@@ -460,7 +458,8 @@ static irqreturn_t sunxi_rsb_irq(int irq
status = readl(rsb->regs + RSB_INTS);
rsb->status = status;
- /* Clear interrupts */
+ /* Disable and clear interrupts */
+ writel(0, rsb->regs + RSB_CTRL);
status &= (RSB_INTS_LOAD_BSY | RSB_INTS_TRANS_ERR |
RSB_INTS_TRANS_OVER);
writel(status, rsb->regs + RSB_INTS);
@@ -640,6 +639,13 @@ static int sunxi_rsb_init_controller(str
writel(RSB_CCR_SDA_OUT_DELAY(clk_delay) | RSB_CCR_CLK_DIV(clk_div - 1),
rsb->regs + RSB_CCR);
+ /*
+ * Select the interrupts we care about. They will not actually fire
+ * until the RSB_CTRL_GLOBAL_INT_ENB bit is set.
+ */
+ writel(RSB_INTS_LOAD_BSY | RSB_INTS_TRANS_ERR | RSB_INTS_TRANS_OVER,
+ rsb->regs + RSB_INTE);
+
return 0;
err_clk_disable:

View File

@ -0,0 +1,370 @@
From ca108f8037f884821d5b70ea0f4cfd73a9ae4188 Mon Sep 17 00:00:00 2001
From: Samuel Holland <samuel@sholland.org>
Date: Sun, 25 Aug 2019 05:35:08 -0500
Subject: [PATCH 16/31] irqchip/sun6i-r: Use a stacked irqchip driver
The R_INTC in the A31 and newer sun8i/sun50i SoCs is more similar to the
original sun4i interrupt controller than the sun7i/sun9i NMI controller.
It is used for two distinct purposes:
1) To control the trigger, latch, and mask for the NMI input pin
2) To provide the interrupt input for the ARISC coprocessor
As this interrupt controller is not documented, information about it
comes from vendor-provided ARISC firmware and from experimentation.
Like the original sun4i interrupt controller, it has:
- A VECTOR_REG at 0x00 (configurable via the BASE_ADDR_REG at 0x04)
- A NMI_CTRL_REG, PENDING_REG, and ENABLE_REG as used by both the
sun4i and sunxi-nmi drivers
- A MASK_REG at 0x50
- A RESP_REG at 0x60
Differences from the sun4i interrupt controller appear to be:
- It is only known to have one register of each kind (max 32 inputs)
- There is no FIQ-related logic
- There is no interrupt priority logic
In order to fulfill its two purposes, this hardware block combines two
types of IRQs. First, the NMI pin is routed to the "IRQ 0" input on this
chip, with a trigger type controlled by the NMI_CTRL_REG. The "IRQ 0
pending" output from this chip, if enabled, is then routed to a SPI IRQ
input on the GIC, as IRQ_TYPE_LEVEL_HIGH. In other words, bit 0 of
ENABLE_REG *does* affect the NMI IRQ seen at the GIC.
The NMI is then followed by a contiguous block of (at least) 15 IRQ
inputs that are connected in parallel to both R_INTC and the GIC. Or
in other words, the other bits of ENABLE_REG *do not* affect the IRQs
seen at the GIC.
Finally, the global "IRQ pending" output from R_INTC, after being masked
by MASK_REG and RESP_REG, is connected to the "external interrupt" input
of the ARISC CPU (an OR1200). This path is not relevant to Linux.
Because of the 1:1 correspondence between R_INTC and GIC inputs, this is
a perfect scenario for using a stacked irqchip driver. We want to hook
into enabling/disabling IRQs to add more features to the GIC
(specifically to allow masking the NMI and setting its trigger type),
but we don't need to actually handle the IRQ in this driver.
And since R_INTC is in the always-on power domain, and its output is
connected directly in to the power management coprocessor, a stacked
irqchip driver provides a simple way to add wakeup support to this set
of IRQs. That is a future patch; for now, just the NMI is moved over.
This driver keeps the same DT binding as the existing driver. The
"interrupt" property of the R_INTC node is used to determine 1) the
offset between GIC and R_INTC hwirq numbers and 2) the type of trigger
between the R_INTC "IRQ 0 pending" output and the GIC NMI input.
This commit mostly reverts commit 173bda53b340 ("irqchip/sunxi-nmi:
Support sun6i-a31-r-intc compatible").
Signed-off-by: Samuel Holland <samuel@sholland.org>
---
arch/arm/mach-sunxi/Kconfig | 2 +
arch/arm64/Kconfig.platforms | 2 +
drivers/irqchip/Makefile | 1 +
drivers/irqchip/irq-sun6i-r.c | 212 ++++++++++++++++++++++++++++++++
drivers/irqchip/irq-sunxi-nmi.c | 26 +---
5 files changed, 220 insertions(+), 23 deletions(-)
create mode 100644 drivers/irqchip/irq-sun6i-r.c
--- a/arch/arm/mach-sunxi/Kconfig
+++ b/arch/arm/mach-sunxi/Kconfig
@@ -6,6 +6,8 @@ menuconfig ARCH_SUNXI
select CLKSRC_MMIO
select GENERIC_IRQ_CHIP
select GPIOLIB
+ select IRQ_DOMAIN_HIERARCHY
+ select IRQ_FASTEOI_HIERARCHY_HANDLERS
select PINCTRL
select PM_OPP
select SUN4I_TIMER
--- a/arch/arm64/Kconfig.platforms
+++ b/arch/arm64/Kconfig.platforms
@@ -17,6 +17,8 @@ config ARCH_SUNXI
bool "Allwinner sunxi 64-bit SoC Family"
select ARCH_HAS_RESET_CONTROLLER
select GENERIC_IRQ_CHIP
+ select IRQ_DOMAIN_HIERARCHY
+ select IRQ_FASTEOI_HIERARCHY_HANDLERS
select PINCTRL
select RESET_CONTROLLER
help
--- a/drivers/irqchip/Makefile
+++ b/drivers/irqchip/Makefile
@@ -24,6 +24,7 @@ obj-$(CONFIG_OR1K_PIC) += irq-or1k-pic
obj-$(CONFIG_ORION_IRQCHIP) += irq-orion.o
obj-$(CONFIG_OMAP_IRQCHIP) += irq-omap-intc.o
obj-$(CONFIG_ARCH_SUNXI) += irq-sun4i.o
+obj-$(CONFIG_ARCH_SUNXI) += irq-sun6i-r.o
obj-$(CONFIG_ARCH_SUNXI) += irq-sunxi-nmi.o
obj-$(CONFIG_ARCH_SPEAR3XX) += spear-shirq.o
obj-$(CONFIG_ARM_GIC) += irq-gic.o irq-gic-common.o
--- /dev/null
+++ b/drivers/irqchip/irq-sun6i-r.c
@@ -0,0 +1,212 @@
+// SPDX-License-Identifier: GPL-2.0-only
+//
+// Allwinner A31 and newer SoCs R_INTC driver
+//
+
+#include <linux/irq.h>
+#include <linux/irqchip.h>
+#include <linux/irqdomain.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/of_irq.h>
+
+#include <dt-bindings/interrupt-controller/arm-gic.h>
+
+#define NMI_HWIRQ 0
+#define NMI_HWIRQ_BIT BIT(NMI_HWIRQ)
+
+#define SUN6I_R_INTC_NR_IRQS 16
+
+#define SUN6I_R_INTC_NMI_CTRL 0x0c
+#define SUN6I_R_INTC_PENDING 0x10
+#define SUN6I_R_INTC_ENABLE 0x40
+
+static void __iomem *base;
+static irq_hw_number_t parent_offset;
+static u32 parent_type;
+
+static struct irq_chip sun6i_r_intc_edge_chip;
+static struct irq_chip sun6i_r_intc_level_chip;
+
+static void sun6i_r_intc_nmi_ack(void)
+{
+ /*
+ * The NMI IRQ channel has a latch, separate from its trigger.
+ * This latch must be cleared to clear the output to the GIC.
+ */
+ writel_relaxed(NMI_HWIRQ_BIT, base + SUN6I_R_INTC_PENDING);
+}
+
+static void sun6i_r_intc_irq_mask(struct irq_data *data)
+{
+ if (data->hwirq == NMI_HWIRQ)
+ sun6i_r_intc_nmi_ack();
+
+ irq_chip_mask_parent(data);
+}
+
+static void sun6i_r_intc_irq_unmask(struct irq_data *data)
+{
+ if (data->hwirq == NMI_HWIRQ)
+ sun6i_r_intc_nmi_ack();
+
+ irq_chip_unmask_parent(data);
+}
+
+static int sun6i_r_intc_irq_set_type(struct irq_data *data, unsigned int type)
+{
+ /*
+ * Only the NMI IRQ is routed through this interrupt controller on its
+ * way to the GIC. Other IRQs are routed to the GIC in parallel and
+ * must have a trigger type appropriate for the GIC.
+ *
+ * The "External NMI" input to the GIC actually comes from bit 0 of
+ * this device's PENDING register. So the IRQ type of the NMI, as seen
+ * by the GIC, does not depend on the IRQ type of the NMI pin itself.
+ */
+ if (data->hwirq == NMI_HWIRQ) {
+ struct irq_chip *chip;
+ u32 nmi_src_type;
+
+ switch (type) {
+ case IRQ_TYPE_LEVEL_LOW:
+ chip = &sun6i_r_intc_level_chip;
+ nmi_src_type = 0;
+ break;
+ case IRQ_TYPE_EDGE_FALLING:
+ chip = &sun6i_r_intc_edge_chip;
+ nmi_src_type = 1;
+ break;
+ case IRQ_TYPE_LEVEL_HIGH:
+ chip = &sun6i_r_intc_level_chip;
+ nmi_src_type = 2;
+ break;
+ case IRQ_TYPE_EDGE_RISING:
+ chip = &sun6i_r_intc_edge_chip;
+ nmi_src_type = 3;
+ break;
+ default:
+ pr_err("%pOF: invalid trigger type %d for IRQ %d\n",
+ irq_domain_get_of_node(data->domain), type,
+ data->irq);
+ return -EBADR;
+ }
+
+ irq_set_chip_handler_name_locked(data, chip,
+ handle_fasteoi_irq, NULL);
+
+ writel_relaxed(nmi_src_type, base + SUN6I_R_INTC_NMI_CTRL);
+
+ /* Send the R_INTC -> GIC trigger type to the GIC driver. */
+ type = parent_type;
+ }
+
+ return irq_chip_set_type_parent(data, type);
+}
+
+static struct irq_chip sun6i_r_intc_edge_chip = {
+ .name = "sun6i-r-intc",
+ .irq_mask = sun6i_r_intc_irq_mask,
+ .irq_unmask = irq_chip_unmask_parent,
+ .irq_eoi = irq_chip_eoi_parent,
+ .irq_set_affinity = irq_chip_set_affinity_parent,
+ .irq_set_type = sun6i_r_intc_irq_set_type,
+ .irq_get_irqchip_state = irq_chip_get_parent_state,
+ .irq_set_irqchip_state = irq_chip_set_parent_state,
+ .irq_set_vcpu_affinity = irq_chip_set_vcpu_affinity_parent,
+ .flags = IRQCHIP_SET_TYPE_MASKED,
+};
+
+static struct irq_chip sun6i_r_intc_level_chip = {
+ .name = "sun6i-r-intc",
+ .irq_mask = irq_chip_mask_parent,
+ .irq_unmask = sun6i_r_intc_irq_unmask,
+ .irq_eoi = irq_chip_eoi_parent,
+ .irq_set_affinity = irq_chip_set_affinity_parent,
+ .irq_set_type = sun6i_r_intc_irq_set_type,
+ .irq_get_irqchip_state = irq_chip_get_parent_state,
+ .irq_set_irqchip_state = irq_chip_set_parent_state,
+ .irq_set_vcpu_affinity = irq_chip_set_vcpu_affinity_parent,
+ .flags = IRQCHIP_SET_TYPE_MASKED,
+};
+
+static int sun6i_r_intc_domain_alloc(struct irq_domain *domain,
+ unsigned int virq,
+ unsigned int nr_irqs, void *arg)
+{
+ struct irq_fwspec *fwspec = arg;
+ struct irq_fwspec gic_fwspec;
+ irq_hw_number_t hwirq;
+ unsigned int type;
+ int i, ret;
+
+ ret = irq_domain_translate_twocell(domain, fwspec, &hwirq, &type);
+ if (ret)
+ return ret;
+ if (hwirq + nr_irqs > SUN6I_R_INTC_NR_IRQS)
+ return -EINVAL;
+
+ /* Construct a GIC-compatible fwspec from this fwspec. */
+ gic_fwspec = (struct irq_fwspec) {
+ .fwnode = domain->parent->fwnode,
+ .param_count = 3,
+ .param = { GIC_SPI, parent_offset + hwirq, type },
+ };
+
+ for (i = 0; i < nr_irqs; ++i)
+ irq_domain_set_hwirq_and_chip(domain, virq + i, hwirq + i,
+ &sun6i_r_intc_level_chip, NULL);
+
+ return irq_domain_alloc_irqs_parent(domain, virq, nr_irqs, &gic_fwspec);
+}
+
+static const struct irq_domain_ops sun6i_r_intc_domain_ops = {
+ .translate = irq_domain_translate_twocell,
+ .alloc = sun6i_r_intc_domain_alloc,
+ .free = irq_domain_free_irqs_common,
+};
+
+static int __init sun6i_r_intc_init(struct device_node *node,
+ struct device_node *parent)
+{
+ struct irq_domain *domain, *parent_domain;
+ struct of_phandle_args parent_irq;
+ int ret;
+
+ /* Extract the R_INTC -> GIC mapping from the OF node. */
+ ret = of_irq_parse_one(node, 0, &parent_irq);
+ if (ret)
+ return ret;
+ if (parent_irq.args_count != 3 || parent_irq.args[0] != GIC_SPI)
+ return -EINVAL;
+ parent_offset = parent_irq.args[1];
+ parent_type = parent_irq.args[2];
+
+ parent_domain = irq_find_host(parent);
+ if (!parent_domain) {
+ pr_err("%pOF: Failed to obtain parent domain\n", node);
+ return -ENXIO;
+ }
+
+ base = of_io_request_and_map(node, 0, NULL);
+ if (IS_ERR(base)) {
+ pr_err("%pOF: Failed to map MMIO region\n", node);
+ return PTR_ERR(base);
+ }
+
+ domain = irq_domain_add_hierarchy(parent_domain, 0,
+ SUN6I_R_INTC_NR_IRQS, node,
+ &sun6i_r_intc_domain_ops, NULL);
+ if (!domain) {
+ pr_err("%pOF: Failed to allocate domain\n", node);
+ iounmap(base);
+ return -ENOMEM;
+ }
+
+ /* Clear and enable the NMI. */
+ writel_relaxed(NMI_HWIRQ_BIT, base + SUN6I_R_INTC_PENDING);
+ writel_relaxed(NMI_HWIRQ_BIT, base + SUN6I_R_INTC_ENABLE);
+
+ return 0;
+}
+IRQCHIP_DECLARE(sun6i_r_intc, "allwinner,sun6i-a31-r-intc", sun6i_r_intc_init);
--- a/drivers/irqchip/irq-sunxi-nmi.c
+++ b/drivers/irqchip/irq-sunxi-nmi.c
@@ -27,18 +27,12 @@
#define SUNXI_NMI_IRQ_BIT BIT(0)
-#define SUN6I_R_INTC_CTRL 0x0c
-#define SUN6I_R_INTC_PENDING 0x10
-#define SUN6I_R_INTC_ENABLE 0x40
-
/*
* For deprecated sun6i-a31-sc-nmi compatible.
- * Registers are offset by 0x0c.
*/
-#define SUN6I_R_INTC_NMI_OFFSET 0x0c
-#define SUN6I_NMI_CTRL (SUN6I_R_INTC_CTRL - SUN6I_R_INTC_NMI_OFFSET)
-#define SUN6I_NMI_PENDING (SUN6I_R_INTC_PENDING - SUN6I_R_INTC_NMI_OFFSET)
-#define SUN6I_NMI_ENABLE (SUN6I_R_INTC_ENABLE - SUN6I_R_INTC_NMI_OFFSET)
+#define SUN6I_NMI_CTRL 0x00
+#define SUN6I_NMI_PENDING 0x04
+#define SUN6I_NMI_ENABLE 0x34
#define SUN7I_NMI_CTRL 0x00
#define SUN7I_NMI_PENDING 0x04
@@ -61,12 +55,6 @@ struct sunxi_sc_nmi_reg_offs {
u32 enable;
};
-static const struct sunxi_sc_nmi_reg_offs sun6i_r_intc_reg_offs __initconst = {
- .ctrl = SUN6I_R_INTC_CTRL,
- .pend = SUN6I_R_INTC_PENDING,
- .enable = SUN6I_R_INTC_ENABLE,
-};
-
static const struct sunxi_sc_nmi_reg_offs sun6i_reg_offs __initconst = {
.ctrl = SUN6I_NMI_CTRL,
.pend = SUN6I_NMI_PENDING,
@@ -232,14 +220,6 @@ fail_irqd_remove:
return ret;
}
-static int __init sun6i_r_intc_irq_init(struct device_node *node,
- struct device_node *parent)
-{
- return sunxi_sc_nmi_irq_init(node, &sun6i_r_intc_reg_offs);
-}
-IRQCHIP_DECLARE(sun6i_r_intc, "allwinner,sun6i-a31-r-intc",
- sun6i_r_intc_irq_init);
-
static int __init sun6i_sc_nmi_irq_init(struct device_node *node,
struct device_node *parent)
{

View File

@ -0,0 +1,129 @@
From c1f31a42b5d9be0000b1748e0c4bdaf22060abfc Mon Sep 17 00:00:00 2001
From: Samuel Holland <samuel@sholland.org>
Date: Sun, 12 Jan 2020 20:00:36 -0600
Subject: [PATCH 17/31] irqchip/sun6i-r: Add wakeup support
Maintain a mask of wake-enabled IRQs, and enable them in hardware
during the syscore phase of suspend. The restore the original mask
of enabled IRQs (just the NMI) during resume.
This serves two purposes. First, it lets power management firmware
running on the ARISC coprocessor know which wakeup sources Linux wants
to have enabled. That way, it can avoid turning them off when it shuts
down the remainder of the clock tree. Second, it preconfigures the
coprocessor's interrupt controller, so the firmware's wakeup logic
is as simple as waiting for an interrupt to arrive.
Signed-off-by: Samuel Holland <samuel@sholland.org>
---
drivers/irqchip/irq-sun6i-r.c | 51 +++++++++++++++++++++++++++++++++++
1 file changed, 51 insertions(+)
--- a/drivers/irqchip/irq-sun6i-r.c
+++ b/drivers/irqchip/irq-sun6i-r.c
@@ -3,12 +3,14 @@
// Allwinner A31 and newer SoCs R_INTC driver
//
+#include <linux/atomic.h>
#include <linux/irq.h>
#include <linux/irqchip.h>
#include <linux/irqdomain.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/of_irq.h>
+#include <linux/syscore_ops.h>
#include <dt-bindings/interrupt-controller/arm-gic.h>
@@ -24,6 +26,9 @@
static void __iomem *base;
static irq_hw_number_t parent_offset;
static u32 parent_type;
+#ifdef CONFIG_PM_SLEEP
+static atomic_t wake_mask;
+#endif
static struct irq_chip sun6i_r_intc_edge_chip;
static struct irq_chip sun6i_r_intc_level_chip;
@@ -104,6 +109,20 @@ static int sun6i_r_intc_irq_set_type(str
return irq_chip_set_type_parent(data, type);
}
+#ifdef CONFIG_PM_SLEEP
+static int sun6i_r_intc_irq_set_wake(struct irq_data *data, unsigned int on)
+{
+ if (on)
+ atomic_or(BIT(data->hwirq), &wake_mask);
+ else
+ atomic_andnot(BIT(data->hwirq), &wake_mask);
+
+ return 0;
+}
+#else
+#define sun6i_r_intc_irq_set_wake NULL
+#endif
+
static struct irq_chip sun6i_r_intc_edge_chip = {
.name = "sun6i-r-intc",
.irq_mask = sun6i_r_intc_irq_mask,
@@ -113,6 +132,7 @@ static struct irq_chip sun6i_r_intc_edge
.irq_set_type = sun6i_r_intc_irq_set_type,
.irq_get_irqchip_state = irq_chip_get_parent_state,
.irq_set_irqchip_state = irq_chip_set_parent_state,
+ .irq_set_wake = sun6i_r_intc_irq_set_wake,
.irq_set_vcpu_affinity = irq_chip_set_vcpu_affinity_parent,
.flags = IRQCHIP_SET_TYPE_MASKED,
};
@@ -126,6 +146,7 @@ static struct irq_chip sun6i_r_intc_leve
.irq_set_type = sun6i_r_intc_irq_set_type,
.irq_get_irqchip_state = irq_chip_get_parent_state,
.irq_set_irqchip_state = irq_chip_set_parent_state,
+ .irq_set_wake = sun6i_r_intc_irq_set_wake,
.irq_set_vcpu_affinity = irq_chip_set_vcpu_affinity_parent,
.flags = IRQCHIP_SET_TYPE_MASKED,
};
@@ -166,6 +187,34 @@ static const struct irq_domain_ops sun6i
.free = irq_domain_free_irqs_common,
};
+#ifdef CONFIG_PM_SLEEP
+static int sun6i_r_intc_suspend(void)
+{
+ /* All wake IRQs are enabled during system sleep. */
+ writel_relaxed(atomic_read(&wake_mask), base + SUN6I_R_INTC_ENABLE);
+
+ return 0;
+}
+
+static void sun6i_r_intc_resume(void)
+{
+ /* Only the NMI is relevant during normal operation. */
+ writel_relaxed(NMI_HWIRQ_BIT, base + SUN6I_R_INTC_ENABLE);
+}
+
+static struct syscore_ops sun6i_r_intc_syscore_ops = {
+ .suspend = sun6i_r_intc_suspend,
+ .resume = sun6i_r_intc_resume,
+};
+
+static void sun6i_r_intc_syscore_init(void)
+{
+ register_syscore_ops(&sun6i_r_intc_syscore_ops);
+}
+#else
+static inline void sun6i_r_intc_syscore_init(void) {}
+#endif
+
static int __init sun6i_r_intc_init(struct device_node *node,
struct device_node *parent)
{
@@ -207,6 +256,8 @@ static int __init sun6i_r_intc_init(stru
writel_relaxed(NMI_HWIRQ_BIT, base + SUN6I_R_INTC_PENDING);
writel_relaxed(NMI_HWIRQ_BIT, base + SUN6I_R_INTC_ENABLE);
+ sun6i_r_intc_syscore_init();
+
return 0;
}
IRQCHIP_DECLARE(sun6i_r_intc, "allwinner,sun6i-a31-r-intc", sun6i_r_intc_init);

View File

@ -0,0 +1,48 @@
From 3d11a3e09d7ff58981243371c52c905b2bb24cde Mon Sep 17 00:00:00 2001
From: Samuel Holland <samuel@sholland.org>
Date: Sun, 12 Jan 2020 20:21:45 -0600
Subject: [PATCH 18/31] dt-bindings: irq: Add a compatible for the H3 R_INTC
The Allwinner H3 SoC contains an R_INTC that is, as far as we know,
compatible with the R_INTC present in other sun8i/sun50i SoCs starting
with the A31. Since the R_INTC hardware is undocumented, introduce a new
compatible for the R_INTC variant in this SoC, in case there turns out
to be some difference.
Signed-off-by: Samuel Holland <samuel@sholland.org>
---
.../allwinner,sun7i-a20-sc-nmi.yaml | 16 +++++++---------
1 file changed, 7 insertions(+), 9 deletions(-)
--- a/Documentation/devicetree/bindings/interrupt-controller/allwinner,sun7i-a20-sc-nmi.yaml
+++ b/Documentation/devicetree/bindings/interrupt-controller/allwinner,sun7i-a20-sc-nmi.yaml
@@ -23,22 +23,20 @@ properties:
compatible:
oneOf:
- const: allwinner,sun6i-a31-r-intc
+ - items:
+ - enum:
+ - allwinner,sun8i-a83t-r-intc
+ - allwinner,sun8i-h3-r-intc
+ - allwinner,sun50i-a64-r-intc
+ - allwinner,sun50i-h6-r-intc
+ - const: allwinner,sun6i-a31-r-intc
- const: allwinner,sun6i-a31-sc-nmi
deprecated: true
- const: allwinner,sun7i-a20-sc-nmi
- - items:
- - const: allwinner,sun8i-a83t-r-intc
- - const: allwinner,sun6i-a31-r-intc
- const: allwinner,sun9i-a80-nmi
- items:
- - const: allwinner,sun50i-a64-r-intc
- - const: allwinner,sun6i-a31-r-intc
- - items:
- const: allwinner,sun50i-a100-nmi
- const: allwinner,sun9i-a80-nmi
- - items:
- - const: allwinner,sun50i-h6-r-intc
- - const: allwinner,sun6i-a31-r-intc
reg:
maxItems: 1

View File

@ -0,0 +1,33 @@
From acb925d41cbefdf633dd7b1a75179aef7dd2c131 Mon Sep 17 00:00:00 2001
From: Samuel Holland <samuel@sholland.org>
Date: Sun, 12 Jan 2020 20:27:08 -0600
Subject: [PATCH 19/31] ARM: dts: sunxi: h3/h5: Add r_intc node
The H3 and H5 SoCs have an additional interrupt controller in the RTC
power domain that can be used to enable wakeup for certain IRQs.
Add a node for it.
Signed-off-by: Samuel Holland <samuel@sholland.org>
---
arch/arm/boot/dts/sunxi-h3-h5.dtsi | 9 +++++++++
1 file changed, 9 insertions(+)
--- a/arch/arm/boot/dts/sunxi-h3-h5.dtsi
+++ b/arch/arm/boot/dts/sunxi-h3-h5.dtsi
@@ -847,6 +847,15 @@
#clock-cells = <1>;
};
+ r_intc: interrupt-controller@1f00c00 {
+ compatible = "allwinner,sun8i-h3-r-intc",
+ "allwinner,sun6i-a31-r-intc";
+ interrupt-controller;
+ #interrupt-cells = <2>;
+ reg = <0x01f00c00 0x400>;
+ interrupts = <GIC_SPI 32 IRQ_TYPE_LEVEL_HIGH>;
+ };
+
r_ccu: clock@1f01400 {
compatible = "allwinner,sun8i-h3-r-ccu";
reg = <0x01f01400 0x100>;

View File

@ -0,0 +1,51 @@
From 8882b2c4cca1e25bd6b6fb29c9ea072d8ef7f68f Mon Sep 17 00:00:00 2001
From: Samuel Holland <samuel@sholland.org>
Date: Sun, 25 Aug 2019 19:41:02 -0500
Subject: [PATCH 20/31] ARM: dts: sunxi: h3/h5: Move wakeup-capable IRQs to
r_intc
All IRQs that can be used to wake up the system must be routed through
r_intc, so they are visible to firmware while the system is suspended.
For the H3/H5, r_intc IRQ numbers are offset by 32 from the GIC IRQ
numbers.
Signed-off-by: Samuel Holland <samuel@sholland.org>
---
arch/arm/boot/dts/sunxi-h3-h5.dtsi | 11 +++++++----
1 file changed, 7 insertions(+), 4 deletions(-)
--- a/arch/arm/boot/dts/sunxi-h3-h5.dtsi
+++ b/arch/arm/boot/dts/sunxi-h3-h5.dtsi
@@ -840,8 +840,9 @@
rtc: rtc@1f00000 {
/* compatible is in per SoC .dtsi file */
reg = <0x01f00000 0x400>;
- interrupts = <GIC_SPI 40 IRQ_TYPE_LEVEL_HIGH>,
- <GIC_SPI 41 IRQ_TYPE_LEVEL_HIGH>;
+ interrupt-parent = <&r_intc>;
+ interrupts = <8 IRQ_TYPE_LEVEL_HIGH>,
+ <9 IRQ_TYPE_LEVEL_HIGH>;
clock-output-names = "osc32k", "osc32k-out", "iosc";
clocks = <&osc32k>;
#clock-cells = <1>;
@@ -877,7 +878,8 @@
clocks = <&r_ccu CLK_APB0_IR>, <&r_ccu CLK_IR>;
clock-names = "apb", "ir";
resets = <&r_ccu RST_APB0_IR>;
- interrupts = <GIC_SPI 37 IRQ_TYPE_LEVEL_HIGH>;
+ interrupt-parent = <&r_intc>;
+ interrupts = <5 IRQ_TYPE_LEVEL_HIGH>;
reg = <0x01f02000 0x400>;
status = "disabled";
};
@@ -898,7 +900,8 @@
r_pio: pinctrl@1f02c00 {
compatible = "allwinner,sun8i-h3-r-pinctrl";
reg = <0x01f02c00 0x400>;
- interrupts = <GIC_SPI 45 IRQ_TYPE_LEVEL_HIGH>;
+ interrupt-parent = <&r_intc>;
+ interrupts = <13 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&r_ccu CLK_APB0_PIO>, <&osc24M>, <&rtc 0>;
clock-names = "apb", "hosc", "losc";
gpio-controller;

View File

@ -0,0 +1,51 @@
From 79d2176f5cf2902afd677a7fa8b5247e5710c132 Mon Sep 17 00:00:00 2001
From: Samuel Holland <samuel@sholland.org>
Date: Sun, 12 Jan 2020 20:32:15 -0600
Subject: [PATCH 22/31] arm64: dts: allwinner: a64: Move wakeup-capable IRQs to
r_intc
All IRQs that can be used to wake up the system must be routed through
r_intc, so they are visible to firmware while the system is suspended.
For the A64, r_intc IRQ numbers are offset by 32 from the GIC IRQ
numbers.
Signed-off-by: Samuel Holland <samuel@sholland.org>
---
arch/arm64/boot/dts/allwinner/sun50i-a64.dtsi | 11 +++++++----
1 file changed, 7 insertions(+), 4 deletions(-)
--- a/arch/arm64/boot/dts/allwinner/sun50i-a64.dtsi
+++ b/arch/arm64/boot/dts/allwinner/sun50i-a64.dtsi
@@ -1191,8 +1191,9 @@
compatible = "allwinner,sun50i-a64-rtc",
"allwinner,sun8i-h3-rtc";
reg = <0x01f00000 0x400>;
- interrupts = <GIC_SPI 40 IRQ_TYPE_LEVEL_HIGH>,
- <GIC_SPI 41 IRQ_TYPE_LEVEL_HIGH>;
+ interrupt-parent = <&r_intc>;
+ interrupts = <8 IRQ_TYPE_LEVEL_HIGH>,
+ <9 IRQ_TYPE_LEVEL_HIGH>;
clock-output-names = "osc32k", "osc32k-out", "iosc";
clocks = <&osc32k>;
#clock-cells = <1>;
@@ -1243,7 +1244,8 @@
clocks = <&r_ccu CLK_APB0_IR>, <&r_ccu CLK_IR>;
clock-names = "apb", "ir";
resets = <&r_ccu RST_APB0_IR>;
- interrupts = <GIC_SPI 37 IRQ_TYPE_LEVEL_HIGH>;
+ interrupt-parent = <&r_intc>;
+ interrupts = <5 IRQ_TYPE_LEVEL_HIGH>;
pinctrl-names = "default";
pinctrl-0 = <&r_ir_rx_pin>;
status = "disabled";
@@ -1263,7 +1265,8 @@
r_pio: pinctrl@1f02c00 {
compatible = "allwinner,sun50i-a64-r-pinctrl";
reg = <0x01f02c00 0x400>;
- interrupts = <GIC_SPI 45 IRQ_TYPE_LEVEL_HIGH>;
+ interrupt-parent = <&r_intc>;
+ interrupts = <13 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&r_ccu CLK_APB0_PIO>, <&osc24M>, <&osc32k>;
clock-names = "apb", "hosc", "losc";
gpio-controller;

View File

@ -0,0 +1,44 @@
From 537cf6daced84919286130c68735a248c62c186d Mon Sep 17 00:00:00 2001
From: Samuel Holland <samuel@sholland.org>
Date: Sun, 12 Jan 2020 20:33:18 -0600
Subject: [PATCH 23/31] arm64: dts: allwinner: h6: Fix indentation of IR node
This node was indented by two tabs when added instead of one.
Remove the extra tab.
Signed-off-by: Samuel Holland <samuel@sholland.org>
---
arch/arm64/boot/dts/allwinner/sun50i-h6.dtsi | 22 ++++++++++----------
1 file changed, 11 insertions(+), 11 deletions(-)
--- a/arch/arm64/boot/dts/allwinner/sun50i-h6.dtsi
+++ b/arch/arm64/boot/dts/allwinner/sun50i-h6.dtsi
@@ -941,17 +941,17 @@
};
r_ir: ir@7040000 {
- compatible = "allwinner,sun50i-h6-ir",
- "allwinner,sun6i-a31-ir";
- reg = <0x07040000 0x400>;
- interrupts = <GIC_SPI 109 IRQ_TYPE_LEVEL_HIGH>;
- clocks = <&r_ccu CLK_R_APB1_IR>,
- <&r_ccu CLK_IR>;
- clock-names = "apb", "ir";
- resets = <&r_ccu RST_R_APB1_IR>;
- pinctrl-names = "default";
- pinctrl-0 = <&r_ir_rx_pin>;
- status = "disabled";
+ compatible = "allwinner,sun50i-h6-ir",
+ "allwinner,sun6i-a31-ir";
+ reg = <0x07040000 0x400>;
+ interrupts = <GIC_SPI 109 IRQ_TYPE_LEVEL_HIGH>;
+ clocks = <&r_ccu CLK_R_APB1_IR>,
+ <&r_ccu CLK_IR>;
+ clock-names = "apb", "ir";
+ resets = <&r_ccu RST_R_APB1_IR>;
+ pinctrl-names = "default";
+ pinctrl-0 = <&r_ir_rx_pin>;
+ status = "disabled";
};
r_i2c: i2c@7081400 {

View File

@ -0,0 +1,53 @@
From d05c22e9079675ba1834ddac322897b45026ff90 Mon Sep 17 00:00:00 2001
From: Samuel Holland <samuel@sholland.org>
Date: Sun, 25 Aug 2019 05:35:23 -0500
Subject: [PATCH 24/31] arm64: dts: allwinner: h6: Move wakeup-capable IRQs to
r_intc
All IRQs that can be used to wake up the system must be routed through
r_intc, so they are visible to firmware while the system is suspended.
For the H6, r_intc IRQ numbers are offset by 96 from the GIC IRQ
numbers.
Signed-off-by: Samuel Holland <samuel@sholland.org>
---
arch/arm64/boot/dts/allwinner/sun50i-h6.dtsi | 13 ++++++++-----
1 file changed, 8 insertions(+), 5 deletions(-)
--- a/arch/arm64/boot/dts/allwinner/sun50i-h6.dtsi
+++ b/arch/arm64/boot/dts/allwinner/sun50i-h6.dtsi
@@ -883,8 +883,9 @@
rtc: rtc@7000000 {
compatible = "allwinner,sun50i-h6-rtc";
reg = <0x07000000 0x400>;
- interrupts = <GIC_SPI 101 IRQ_TYPE_LEVEL_HIGH>,
- <GIC_SPI 102 IRQ_TYPE_LEVEL_HIGH>;
+ interrupt-parent = <&r_intc>;
+ interrupts = <5 IRQ_TYPE_LEVEL_HIGH>,
+ <6 IRQ_TYPE_LEVEL_HIGH>;
clock-output-names = "osc32k", "osc32k-out", "iosc";
#clock-cells = <1>;
};
@@ -920,8 +921,9 @@
r_pio: pinctrl@7022000 {
compatible = "allwinner,sun50i-h6-r-pinctrl";
reg = <0x07022000 0x400>;
- interrupts = <GIC_SPI 105 IRQ_TYPE_LEVEL_HIGH>,
- <GIC_SPI 111 IRQ_TYPE_LEVEL_HIGH>;
+ interrupt-parent = <&r_intc>;
+ interrupts = < 9 IRQ_TYPE_LEVEL_HIGH>,
+ <15 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&r_ccu CLK_R_APB1>, <&osc24M>, <&rtc 0>;
clock-names = "apb", "hosc", "losc";
gpio-controller;
@@ -944,7 +946,8 @@
compatible = "allwinner,sun50i-h6-ir",
"allwinner,sun6i-a31-ir";
reg = <0x07040000 0x400>;
- interrupts = <GIC_SPI 109 IRQ_TYPE_LEVEL_HIGH>;
+ interrupt-parent = <&r_intc>;
+ interrupts = <13 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&r_ccu CLK_R_APB1_IR>,
<&r_ccu CLK_IR>;
clock-names = "apb", "ir";

View File

@ -0,0 +1,78 @@
From 882c9a65a1e6bb9bf1ddee8b2512f57b72cb9860 Mon Sep 17 00:00:00 2001
From: Samuel Holland <samuel@sholland.org>
Date: Sun, 25 Aug 2019 05:36:21 -0500
Subject: [PATCH 25/31] rtc: sun6i: Use wake IRQ instead of device PM ops
Since the RTC has a single IRQ, we can use the generic wake IRQ
implementation instead of defining our own device PM ops. This has the
same effect with quite a bit less code.
Signed-off-by: Samuel Holland <samuel@sholland.org>
---
drivers/rtc/rtc-sun6i.c | 34 ++++++----------------------------
1 file changed, 6 insertions(+), 28 deletions(-)
--- a/drivers/rtc/rtc-sun6i.c
+++ b/drivers/rtc/rtc-sun6i.c
@@ -25,6 +25,7 @@
#include <linux/of_address.h>
#include <linux/of_device.h>
#include <linux/platform_device.h>
+#include <linux/pm_wakeirq.h>
#include <linux/rtc.h>
#include <linux/slab.h>
#include <linux/types.h>
@@ -639,33 +640,6 @@ static const struct rtc_class_ops sun6i_
.alarm_irq_enable = sun6i_rtc_alarm_irq_enable
};
-#ifdef CONFIG_PM_SLEEP
-/* Enable IRQ wake on suspend, to wake up from RTC. */
-static int sun6i_rtc_suspend(struct device *dev)
-{
- struct sun6i_rtc_dev *chip = dev_get_drvdata(dev);
-
- if (device_may_wakeup(dev))
- enable_irq_wake(chip->irq);
-
- return 0;
-}
-
-/* Disable IRQ wake on resume. */
-static int sun6i_rtc_resume(struct device *dev)
-{
- struct sun6i_rtc_dev *chip = dev_get_drvdata(dev);
-
- if (device_may_wakeup(dev))
- disable_irq_wake(chip->irq);
-
- return 0;
-}
-#endif
-
-static SIMPLE_DEV_PM_OPS(sun6i_rtc_pm_ops,
- sun6i_rtc_suspend, sun6i_rtc_resume);
-
static int sun6i_rtc_probe(struct platform_device *pdev)
{
struct sun6i_rtc_dev *chip = sun6i_rtc;
@@ -716,6 +690,11 @@ static int sun6i_rtc_probe(struct platfo
clk_prepare_enable(chip->losc);
device_init_wakeup(&pdev->dev, 1);
+ ret = dev_pm_set_wake_irq(&pdev->dev, chip->irq);
+ if (ret) {
+ dev_err(&pdev->dev, "Could not set wake IRQ\n");
+ return ret;
+ }
chip->rtc = devm_rtc_allocate_device(&pdev->dev);
if (IS_ERR(chip->rtc))
@@ -756,7 +735,6 @@ static struct platform_driver sun6i_rtc_
.driver = {
.name = "sun6i-rtc",
.of_match_table = sun6i_rtc_dt_ids,
- .pm = &sun6i_rtc_pm_ops,
},
};
builtin_platform_driver(sun6i_rtc_driver);

View File

@ -0,0 +1,28 @@
From fe9d1c2c4700b8a8a31ee7d431cd9993b26f8953 Mon Sep 17 00:00:00 2001
From: Samuel Holland <samuel@sholland.org>
Date: Sat, 18 Jul 2020 17:18:23 -0500
Subject: [PATCH 26/31] dt-bindings: sram: Add ARM SCP SRAM compatible
As of commit a90b15e0ad72 ("Documentation: bindings: decouple juno
specific details from generic binding"), the SCPI binding in
Documentation/devicetree/bindings/arm/arm,scpi.txt mandates that SRAM
sections used for SCPI shared memory are compatible with arm,scp-shmem.
However, this compatible is missing from the SRAM binding.
Add the arm,scp-shmem compatible here to match the SCPI binding.
Signed-off-by: Samuel Holland <samuel@sholland.org>
---
Documentation/devicetree/bindings/sram/sram.yaml | 1 +
1 file changed, 1 insertion(+)
--- a/Documentation/devicetree/bindings/sram/sram.yaml
+++ b/Documentation/devicetree/bindings/sram/sram.yaml
@@ -76,6 +76,7 @@ patternProperties:
- amlogic,meson8b-smp-sram
- amlogic,meson-gxbb-scp-shmem
- amlogic,meson-axg-scp-shmem
+ - arm,scp-shmem
- renesas,smp-sram
- rockchip,rk3066-smp-sram
- samsung,exynos4210-sysram

View File

@ -0,0 +1,123 @@
From fef8b000f7e05397399b78891f26c53a2581863a Mon Sep 17 00:00:00 2001
From: Samuel Holland <samuel@sholland.org>
Date: Tue, 5 Mar 2019 22:02:41 -0600
Subject: [PATCH 27/31] firmware: arm_scpi: Support unidirectional mailbox
channels
Some mailbox controllers have only unidirectional channels, so we need a
pair of them for each SCPI channel. If a mbox-names property is present,
look for "rx" and "tx" mbox channels; otherwise, the existing behavior
is preserved, and a single mbox channel is used for each SCPI channel.
Note that since the mailbox framework only supports a single phandle
with each name (mbox_request_channel_byname always returns the first
one), this new mode only supports a single SCPI channel.
Signed-off-by: Samuel Holland <samuel@sholland.org>
---
drivers/firmware/arm_scpi.c | 58 +++++++++++++++++++++++++++++--------
1 file changed, 46 insertions(+), 12 deletions(-)
--- a/drivers/firmware/arm_scpi.c
+++ b/drivers/firmware/arm_scpi.c
@@ -231,7 +231,8 @@ struct scpi_xfer {
struct scpi_chan {
struct mbox_client cl;
- struct mbox_chan *chan;
+ struct mbox_chan *rx_chan;
+ struct mbox_chan *tx_chan;
void __iomem *tx_payload;
void __iomem *rx_payload;
struct list_head rx_pending;
@@ -505,7 +506,7 @@ static int scpi_send_message(u8 idx, voi
msg->rx_len = rx_len;
reinit_completion(&msg->done);
- ret = mbox_send_message(scpi_chan->chan, msg);
+ ret = mbox_send_message(scpi_chan->tx_chan, msg);
if (ret < 0 || !rx_buf)
goto out;
@@ -854,8 +855,13 @@ static void scpi_free_channels(void *dat
struct scpi_drvinfo *info = data;
int i;
- for (i = 0; i < info->num_chans; i++)
- mbox_free_channel(info->channels[i].chan);
+ for (i = 0; i < info->num_chans; i++) {
+ struct scpi_chan *pchan = &info->channels[i];
+
+ if (pchan->tx_chan != pchan->rx_chan)
+ mbox_free_channel(pchan->tx_chan);
+ mbox_free_channel(pchan->rx_chan);
+ }
}
static int scpi_remove(struct platform_device *pdev)
@@ -903,6 +909,7 @@ static int scpi_probe(struct platform_de
struct resource res;
struct device *dev = &pdev->dev;
struct device_node *np = dev->of_node;
+ bool use_mbox_names = false;
scpi_info = devm_kzalloc(dev, sizeof(*scpi_info), GFP_KERNEL);
if (!scpi_info)
@@ -916,6 +923,14 @@ static int scpi_probe(struct platform_de
dev_err(dev, "no mboxes property in '%pOF'\n", np);
return -ENODEV;
}
+ if (of_get_property(dev->of_node, "mbox-names", NULL)) {
+ use_mbox_names = true;
+ if (count != 2) {
+ dev_err(dev, "need exactly 2 mboxes with mbox-names\n");
+ return -ENODEV;
+ }
+ count /= 2;
+ }
scpi_info->channels = devm_kcalloc(dev, count, sizeof(struct scpi_chan),
GFP_KERNEL);
@@ -961,15 +976,34 @@ static int scpi_probe(struct platform_de
mutex_init(&pchan->xfers_lock);
ret = scpi_alloc_xfer_list(dev, pchan);
- if (!ret) {
- pchan->chan = mbox_request_channel(cl, idx);
- if (!IS_ERR(pchan->chan))
- continue;
- ret = PTR_ERR(pchan->chan);
- if (ret != -EPROBE_DEFER)
- dev_err(dev, "failed to get channel%d err %d\n",
- idx, ret);
+ if (ret)
+ return ret;
+
+ if (use_mbox_names) {
+ pchan->rx_chan = mbox_request_channel_byname(cl, "rx");
+ if (IS_ERR(pchan->rx_chan)) {
+ ret = PTR_ERR(pchan->rx_chan);
+ goto fail;
+ }
+ pchan->tx_chan = mbox_request_channel_byname(cl, "tx");
+ if (IS_ERR(pchan->rx_chan)) {
+ ret = PTR_ERR(pchan->tx_chan);
+ goto fail;
+ }
+ } else {
+ pchan->rx_chan = mbox_request_channel(cl, idx);
+ if (IS_ERR(pchan->rx_chan)) {
+ ret = PTR_ERR(pchan->rx_chan);
+ goto fail;
+ }
+ pchan->tx_chan = pchan->rx_chan;
}
+ continue;
+
+fail:
+ if (ret != -EPROBE_DEFER)
+ dev_err(dev, "failed to get channel%d err %d\n",
+ idx, ret);
return ret;
}

View File

@ -0,0 +1,72 @@
From 13bd8de3e31b42b4a44310294a29c620f36936ba Mon Sep 17 00:00:00 2001
From: Samuel Holland <samuel@sholland.org>
Date: Wed, 1 Jan 2020 16:12:36 -0600
Subject: [PATCH 29/31] ARM: dts: sunxi: h3/h5: Add SCPI protocol
Signed-off-by: Samuel Holland <samuel@sholland.org>
---
arch/arm/boot/dts/sun8i-h3.dtsi | 13 +++++++++++++
arch/arm/boot/dts/sunxi-h3-h5.dtsi | 7 +++++++
arch/arm64/boot/dts/allwinner/sun50i-h5.dtsi | 13 +++++++++++++
3 files changed, 33 insertions(+)
--- a/arch/arm/boot/dts/sun8i-h3.dtsi
+++ b/arch/arm/boot/dts/sun8i-h3.dtsi
@@ -170,6 +170,19 @@
#size-cells = <1>;
ranges;
+ sram_a2: sram@40000 {
+ compatible = "mmio-sram";
+ reg = <0x00040000 0xc000>;
+ #address-cells = <1>;
+ #size-cells = <1>;
+ ranges = <0 0x00040000 0xc000>;
+
+ scpi_sram: scpi-sram@bc00 {
+ compatible = "arm,scp-shmem";
+ reg = <0xbc00 0x200>;
+ };
+ };
+
sram_c: sram@1d00000 {
compatible = "mmio-sram";
reg = <0x01d00000 0x80000>;
--- a/arch/arm/boot/dts/sunxi-h3-h5.dtsi
+++ b/arch/arm/boot/dts/sunxi-h3-h5.dtsi
@@ -105,6 +105,13 @@
status = "disabled";
};
+ scpi_protocol: scpi {
+ compatible = "arm,scpi";
+ mboxes = <&msgbox 2>, <&msgbox 3>;
+ mbox-names = "tx", "rx";
+ shmem = <&scpi_sram>;
+ };
+
soc {
compatible = "simple-bus";
#address-cells = <1>;
--- a/arch/arm64/boot/dts/allwinner/sun50i-h5.dtsi
+++ b/arch/arm64/boot/dts/allwinner/sun50i-h5.dtsi
@@ -86,6 +86,19 @@
#size-cells = <1>;
ranges;
+ sram_a2: sram@40000 {
+ compatible = "mmio-sram";
+ reg = <0x00040000 0x14000>;
+ #address-cells = <1>;
+ #size-cells = <1>;
+ ranges = <0 0x00040000 0x14000>;
+
+ scpi_sram: scpi-sram@13c00 {
+ compatible = "arm,scp-shmem";
+ reg = <0x13c00 0x200>;
+ };
+ };
+
sram_c1: sram@18000 {
compatible = "mmio-sram";
reg = <0x00018000 0x1c000>;

View File

@ -0,0 +1,46 @@
From f0550a4bd9eefe64469813d81a1549ba31182ad7 Mon Sep 17 00:00:00 2001
From: Samuel Holland <samuel@sholland.org>
Date: Sat, 14 Dec 2019 20:52:53 -0600
Subject: [PATCH 30/31] arm64: dts: allwinner: a64: Add SCPI protocol
Signed-off-by: Samuel Holland <samuel@sholland.org>
---
arch/arm64/boot/dts/allwinner/sun50i-a64.dtsi | 20 +++++++++++++++++++
1 file changed, 20 insertions(+)
--- a/arch/arm64/boot/dts/allwinner/sun50i-a64.dtsi
+++ b/arch/arm64/boot/dts/allwinner/sun50i-a64.dtsi
@@ -130,6 +130,13 @@
method = "smc";
};
+ scpi_protocol: scpi {
+ compatible = "arm,scpi";
+ mboxes = <&msgbox 2>, <&msgbox 3>;
+ mbox-names = "tx", "rx";
+ shmem = <&scpi_sram>;
+ };
+
sound: sound {
compatible = "simple-audio-card";
simple-audio-card,name = "sun50i-a64-audio";
@@ -339,6 +346,19 @@
#size-cells = <1>;
ranges;
+ sram_a2: sram@40000 {
+ compatible = "mmio-sram";
+ reg = <0x00040000 0x14000>;
+ #address-cells = <1>;
+ #size-cells = <1>;
+ ranges = <0 0x00040000 0x14000>;
+
+ scpi_sram: scpi-sram@13c00 {
+ compatible = "arm,scp-shmem";
+ reg = <0x13c00 0x200>;
+ };
+ };
+
sram_c: sram@18000 {
compatible = "mmio-sram";
reg = <0x00018000 0x28000>;

View File

@ -0,0 +1,46 @@
From d39c105f72223beb931088032b82114c6ad01cba Mon Sep 17 00:00:00 2001
From: Samuel Holland <samuel@sholland.org>
Date: Sat, 14 Dec 2019 20:54:40 -0600
Subject: [PATCH 31/31] arm64: dts: allwinner: h6: Add SCPI protocol
Signed-off-by: Samuel Holland <samuel@sholland.org>
---
arch/arm64/boot/dts/allwinner/sun50i-h6.dtsi | 20 ++++++++++++++++++++
1 file changed, 20 insertions(+)
--- a/arch/arm64/boot/dts/allwinner/sun50i-h6.dtsi
+++ b/arch/arm64/boot/dts/allwinner/sun50i-h6.dtsi
@@ -88,6 +88,13 @@
method = "smc";
};
+ scpi_protocol: scpi {
+ compatible = "arm,scpi";
+ mboxes = <&msgbox 2>, <&msgbox 3>;
+ mbox-names = "tx", "rx";
+ shmem = <&scpi_sram>;
+ };
+
timer {
compatible = "arm,armv8-timer";
arm,no-tick-in-suspend;
@@ -196,6 +203,19 @@
#size-cells = <1>;
ranges;
+ sram_a2: sram@100000 {
+ compatible = "mmio-sram";
+ reg = <0x00100000 0x18000>;
+ #address-cells = <1>;
+ #size-cells = <1>;
+ ranges = <0 0x00100000 0x18000>;
+
+ scpi_sram: scpi-sram@17c00 {
+ compatible = "arm,scp-shmem";
+ reg = <0x17c00 0x200>;
+ };
+ };
+
sram_c: sram@28000 {
compatible = "mmio-sram";
reg = <0x00028000 0x1e000>;

View File

@ -24,7 +24,8 @@ devices = \
'A64': {
'orangepi-win': {
'dtb': 'sun50i-a64-orangepi-win.dtb',
'config': 'orangepi_win_defconfig'
'config': 'orangepi_win_defconfig',
'crust_config': 'orangepi_win_defconfig',
},
'pine64': {
'dtb': 'sun50i-a64-pine64.dtb',
@ -36,7 +37,8 @@ devices = \
},
'pine64-plus': {
'dtb': 'sun50i-a64-pine64-plus.dtb',
'config': 'pine64_plus_defconfig'
'config': 'pine64_plus_defconfig',
'crust_config': 'pine64_plus_defconfig',
},
},
'H2-plus': {
@ -64,29 +66,35 @@ devices = \
},
'orangepi-2': {
'dtb': 'sun8i-h3-orangepi-2.dtb',
'config': 'orangepi_2_defconfig'
'config': 'orangepi_2_defconfig',
'crust_config': 'orangepi_2_defconfig',
},
'orangepi-pc': {
'dtb': 'sun8i-h3-orangepi-pc.dtb',
'config': 'orangepi_pc_defconfig'
'config': 'orangepi_pc_defconfig',
'crust_config': 'orangepi_pc_defconfig',
},
'orangepi-pc-plus': {
'dtb': 'sun8i-h3-orangepi-pc-plus.dtb',
'config': 'orangepi_pc_plus_defconfig'
'config': 'orangepi_pc_plus_defconfig',
'crust_config': 'orangepi_pc_plus_defconfig',
},
'orangepi-plus2e': {
'dtb': 'sun8i-h3-orangepi-plus2e.dtb',
'config': 'orangepi_plus2e_defconfig'
'config': 'orangepi_plus2e_defconfig',
'crust_config': 'orangepi_plus2e_defconfig',
},
'orangepi-plus': {
'dtb': 'sun8i-h3-orangepi-plus.dtb',
'config': 'orangepi_plus_defconfig'
'config': 'orangepi_plus_defconfig',
'crust_config': 'orangepi_plus_defconfig',
},
},
'H5' : {
'orangepi-pc2': {
'dtb': 'sun50i-h5-orangepi-pc2.dtb',
'config': 'orangepi_pc2_defconfig'
'config': 'orangepi_pc2_defconfig',
'crust_config': 'orangepi_pc2_defconfig',
},
'tritium-h5': {
'dtb': 'sun50i-h5-libretech-all-h3-cc.dtb',
@ -100,7 +108,8 @@ devices = \
},
'orangepi-3': {
'dtb': 'sun50i-h6-orangepi-3.dtb',
'config': 'orangepi_3_defconfig'
'config': 'orangepi_3_defconfig',
'crust_config': 'orangepi_3_defconfig',
},
'orangepi-lite2': {
'dtb': 'sun50i-h6-orangepi-lite2.dtb',
@ -112,7 +121,8 @@ devices = \
},
'pine-h64': {
'dtb': 'sun50i-h6-pine-h64.dtb',
'config': 'pine_h64_defconfig'
'config': 'pine_h64_defconfig',
'crust_config': 'pine_h64_defconfig',
},
'pine-h64-model-b': {
'dtb': 'sun50i-h6-pine-h64-model-b.dtb',
@ -380,7 +390,7 @@ if len(sys.argv) > 3 and sys.argv[3] not in devices[sys.argv[1]][sys.argv[2]]:
if len(sys.argv) == 4:
exit_error('Invalid option: must specify dtb or config', PROJECT=sys.argv[1], SOC=sys.argv[2])
elif len(sys.argv) > 4 and sys.argv[4] not in ['dtb', 'config']:
elif len(sys.argv) > 4 and sys.argv[4] not in ['dtb', 'config', 'crust_config']:
exit_error('Invalid option: %s' % sys.argv[4], PROJECT=sys.argv[1], SOC=sys.argv[2])
if len(sys.argv) > 5:
@ -389,7 +399,8 @@ if len(sys.argv) > 5:
# Get dtb or u-boot config for a given project, soc, and board
# ./scripts/uboot_helper project device board-name dtb|config
if len(sys.argv) == 5:
print(devices[sys.argv[1]][sys.argv[2]][sys.argv[3]][sys.argv[4]])
if sys.argv[4] in devices[sys.argv[1]][sys.argv[2]][sys.argv[3]]:
print(devices[sys.argv[1]][sys.argv[2]][sys.argv[3]][sys.argv[4]])
# List boards supported by a given project and soc
# ./scripts/uboot_helper project device