mirror of
https://github.com/LibreELEC/LibreELEC.tv.git
synced 2025-07-30 06:06:43 +00:00
Merge pull request #4762 from dhewg/crust
Allwinner: add support for crust SCP firmware
This commit is contained in:
commit
753893dde1
36
packages/lang/gcc-or1k-linux/package.mk
Normal file
36
packages/lang/gcc-or1k-linux/package.mk
Normal 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++"
|
||||
}
|
@ -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"
|
||||
|
@ -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"
|
||||
|
43
packages/tools/crust/package.mk
Normal file
43
packages/tools/crust/package.mk
Normal 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
|
||||
}
|
@ -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"
|
||||
|
@ -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 .
|
@ -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,
|
||||
},
|
||||
};
|
@ -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
|
@ -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)
|
@ -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>;
|
||||
};
|
@ -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>;
|
||||
};
|
@ -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>;
|
||||
};
|
@ -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);
|
||||
}
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
}
|
@ -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,
|
@ -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;
|
||||
}
|
@ -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,
|
||||
},
|
||||
};
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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:
|
@ -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)
|
||||
{
|
@ -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);
|
@ -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
|
@ -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>;
|
@ -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;
|
@ -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;
|
@ -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 {
|
@ -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";
|
@ -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);
|
@ -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
|
@ -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;
|
||||
}
|
||||
|
@ -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>;
|
@ -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>;
|
@ -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>;
|
@ -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
|
||||
|
Loading…
x
Reference in New Issue
Block a user