mirror of
https://abf.rosa.ru/djam/kernel-5.15.git
synced 2025-02-23 10:32:54 +00:00
Update to 5.15.53, format Baikal patches more clearly
This commit is contained in:
parent
9388a6c209
commit
8f19ac0825
44 changed files with 15593 additions and 25418 deletions
2
.abf.yml
2
.abf.yml
|
@ -1,6 +1,6 @@
|
||||||
sources:
|
sources:
|
||||||
linux-5.15.tar.xz: ac61f2459040c09af1d5abd4ed100c3d316b443e
|
linux-5.15.tar.xz: ac61f2459040c09af1d5abd4ed100c3d316b443e
|
||||||
patch-5.15.43.xz: 798d06ee77587a2d13c09b6631292a179b45040a
|
patch-5.15.53.xz: d2acd58c14818c60e663c909706484a01080da1c
|
||||||
public_key_GOST_1.pem: b4fb6bf1cf73824944931a8f0c2cb7bf427e0774
|
public_key_GOST_1.pem: b4fb6bf1cf73824944931a8f0c2cb7bf427e0774
|
||||||
public_key_GOST_2.pem: cba209bd331f29031c5d945949b230a8d7a4dc12
|
public_key_GOST_2.pem: cba209bd331f29031c5d945949b230a8d7a4dc12
|
||||||
public_key_GOST_3.pem: e5a223dd7c556d4d0cac326f5ed9fc12dd769afb
|
public_key_GOST_3.pem: e5a223dd7c556d4d0cac326f5ed9fc12dd769afb
|
||||||
|
|
93
0600-drm-panfrost-initial-dual-core-group-GPUs-support.patch
Normal file
93
0600-drm-panfrost-initial-dual-core-group-GPUs-support.patch
Normal file
|
@ -0,0 +1,93 @@
|
||||||
|
From dda52f2d95223cc0695588595dffc5e4576b67a3 Mon Sep 17 00:00:00 2001
|
||||||
|
From: Alexey Sheplyakov <asheplyakov@basealt.ru>
|
||||||
|
Date: Sat, 15 Jan 2022 20:06:58 +0400
|
||||||
|
Subject: [PATCH 600/634] drm/panfrost: initial dual core group GPUs support
|
||||||
|
|
||||||
|
On a dual core group GPUs (such as T628) fragment shading can be
|
||||||
|
performed over all cores (because a fragment shader job doesn't
|
||||||
|
need coherency between threads), however vertex shading requires
|
||||||
|
to be run on the same core group as the tiler (which always lives
|
||||||
|
in core group 0).
|
||||||
|
|
||||||
|
As a first step to support T628 power on only the first core group
|
||||||
|
(so no jobs are scheduled on the second one). This makes T628 look
|
||||||
|
like every other Midgard GPU (and throws away up to half the cores).
|
||||||
|
|
||||||
|
With this patch panfrost is able to drive T628 (r1p0) GPU on some
|
||||||
|
armv8 SoCs (in particular BE-M1000). Without the patch rendering
|
||||||
|
is horribly broken (desktop is completely unusable) and eventually
|
||||||
|
the GPU locks up (it takes from a few seconds to a couple of
|
||||||
|
minutes).
|
||||||
|
|
||||||
|
Using the second core group requires support in Mesa (and an UABI
|
||||||
|
change): the userspace should
|
||||||
|
|
||||||
|
1) set PANFROST_JD_DOESNT_NEED_COHERENCY_ON_GPU flag to opt-in
|
||||||
|
to allowing the job to run across all cores.
|
||||||
|
2) set PANFROST_RUN_ON_SECOND_CORE_GROUP flag to allow compute
|
||||||
|
jobs to be run on the second core group (at the moment Mesa
|
||||||
|
does not advertise compute support on anything older than
|
||||||
|
Mali T760)
|
||||||
|
|
||||||
|
But there's little point adding such flags until someone (myself)
|
||||||
|
steps up to do the Mesa work.
|
||||||
|
|
||||||
|
Signed-off-by: Alexey Sheplyakov <asheplyakov@basealt.ru>
|
||||||
|
Signed-off-by: Vadim V. Vlasov <vadim.vlasov@elpitech.ru>
|
||||||
|
Tested-by: Alexey Sheplyakov <asheplyakov@basealt.ru>
|
||||||
|
Co-developed-by: Steven Price <steven.price@arm.com>
|
||||||
|
Signed-off-by: Steven Price <steven.price@arm.com>
|
||||||
|
Link: https://patchwork.freedesktop.org/patch/msgid/20220115160658.582646-1-asheplyakov@basealt.ru
|
||||||
|
---
|
||||||
|
drivers/gpu/drm/panfrost/panfrost_gpu.c | 27 ++++++++++++++++++++-----
|
||||||
|
1 file changed, 22 insertions(+), 5 deletions(-)
|
||||||
|
|
||||||
|
diff --git a/drivers/gpu/drm/panfrost/panfrost_gpu.c b/drivers/gpu/drm/panfrost/panfrost_gpu.c
|
||||||
|
index f8355de6e..15cec831a 100644
|
||||||
|
--- a/drivers/gpu/drm/panfrost/panfrost_gpu.c
|
||||||
|
+++ b/drivers/gpu/drm/panfrost/panfrost_gpu.c
|
||||||
|
@@ -320,19 +320,36 @@ void panfrost_gpu_power_on(struct panfrost_device *pfdev)
|
||||||
|
{
|
||||||
|
int ret;
|
||||||
|
u32 val;
|
||||||
|
+ u64 core_mask = U64_MAX;
|
||||||
|
|
||||||
|
panfrost_gpu_init_quirks(pfdev);
|
||||||
|
|
||||||
|
- /* Just turn on everything for now */
|
||||||
|
- gpu_write(pfdev, L2_PWRON_LO, pfdev->features.l2_present);
|
||||||
|
+ if (pfdev->features.l2_present != 1) {
|
||||||
|
+ /*
|
||||||
|
+ * Only support one core group now.
|
||||||
|
+ * ~(l2_present - 1) unsets all bits in l2_present except
|
||||||
|
+ * the bottom bit. (l2_present - 2) has all the bits in
|
||||||
|
+ * the first core group set. AND them together to generate
|
||||||
|
+ * a mask of cores in the first core group.
|
||||||
|
+ */
|
||||||
|
+ core_mask = ~(pfdev->features.l2_present - 1) &
|
||||||
|
+ (pfdev->features.l2_present - 2);
|
||||||
|
+ dev_info_once(pfdev->dev, "using only 1st core group (%lu cores from %lu)\n",
|
||||||
|
+ hweight64(core_mask),
|
||||||
|
+ hweight64(pfdev->features.shader_present));
|
||||||
|
+ }
|
||||||
|
+ gpu_write(pfdev, L2_PWRON_LO, pfdev->features.l2_present & core_mask);
|
||||||
|
ret = readl_relaxed_poll_timeout(pfdev->iomem + L2_READY_LO,
|
||||||
|
- val, val == pfdev->features.l2_present, 100, 20000);
|
||||||
|
+ val, val == (pfdev->features.l2_present & core_mask),
|
||||||
|
+ 100, 20000);
|
||||||
|
if (ret)
|
||||||
|
dev_err(pfdev->dev, "error powering up gpu L2");
|
||||||
|
|
||||||
|
- gpu_write(pfdev, SHADER_PWRON_LO, pfdev->features.shader_present);
|
||||||
|
+ gpu_write(pfdev, SHADER_PWRON_LO,
|
||||||
|
+ pfdev->features.shader_present & core_mask);
|
||||||
|
ret = readl_relaxed_poll_timeout(pfdev->iomem + SHADER_READY_LO,
|
||||||
|
- val, val == pfdev->features.shader_present, 100, 20000);
|
||||||
|
+ val, val == (pfdev->features.shader_present & core_mask),
|
||||||
|
+ 100, 20000);
|
||||||
|
if (ret)
|
||||||
|
dev_err(pfdev->dev, "error powering up gpu shader");
|
||||||
|
|
||||||
|
--
|
||||||
|
2.33.2
|
||||||
|
|
23032
0601-baikalm.patch
23032
0601-baikalm.patch
File diff suppressed because it is too large
Load diff
537
0601-net-stmmac-inital-support-of-Baikal-T1-M-SoCs-GMAC.patch
Normal file
537
0601-net-stmmac-inital-support-of-Baikal-T1-M-SoCs-GMAC.patch
Normal file
|
@ -0,0 +1,537 @@
|
||||||
|
From 1f302590ebe02909de1e5781a69089848abcca6a Mon Sep 17 00:00:00 2001
|
||||||
|
From: Alexey Sheplyakov <asheplyakov@basealt.ru>
|
||||||
|
Date: Mon, 24 Jan 2022 13:23:05 +0400
|
||||||
|
Subject: [PATCH 601/634] net: stmmac: inital support of Baikal-T1/M SoCs GMAC
|
||||||
|
|
||||||
|
The gigabit Ethernet controller available in Baikal-T1 and Baikal-M
|
||||||
|
SoCs is a Synopsys DesignWare MAC IP core, already supported by
|
||||||
|
the stmmac driver.
|
||||||
|
|
||||||
|
This patch implements some SoC specific operations (DMA reset and
|
||||||
|
speed fixup) necessary (but in general not sufficient) for
|
||||||
|
Baikal-T1/M variants.
|
||||||
|
|
||||||
|
Note that this driver does NOT cover all the IP blocks and platform
|
||||||
|
setup peculiarities. It's known to work on some Baikal-T1 boards
|
||||||
|
(including BFK3.1 reference board) and some Baikal-M based boards:
|
||||||
|
(TF307 revision D, LGP-16, AQBM1000), however it might or might not
|
||||||
|
work with other boards.
|
||||||
|
|
||||||
|
Changes since v2:
|
||||||
|
|
||||||
|
* Clearly explained the status of the driver (initial support),
|
||||||
|
mentioned the boards it known to work with.
|
||||||
|
* Increased timeouts in baikal_dma_reset so they are enough for
|
||||||
|
many PHYs, explained why such timeouts are necessary.
|
||||||
|
|
||||||
|
Changes since v1:
|
||||||
|
|
||||||
|
* The code compiles with -Werror
|
||||||
|
|
||||||
|
Signed-off-by: Alexey Sheplyakov <asheplyakov@basealt.ru>
|
||||||
|
Co-developed-by: Dmitry Dunaev <dmitry.dunaev@baikalelectronics.ru>
|
||||||
|
Co-developed-by: Vasiliy Vinogradov <v.vinogradov@aq.ru>
|
||||||
|
Tested-by: Alexey Sheplyakov <asheplyakov@basealt.ru>
|
||||||
|
---
|
||||||
|
drivers/net/ethernet/stmicro/stmmac/Kconfig | 11 +
|
||||||
|
drivers/net/ethernet/stmicro/stmmac/Makefile | 1 +
|
||||||
|
.../ethernet/stmicro/stmmac/dwmac-baikal.c | 214 ++++++++++++++++++
|
||||||
|
.../ethernet/stmicro/stmmac/dwmac1000_core.c | 1 +
|
||||||
|
.../ethernet/stmicro/stmmac/dwmac1000_dma.c | 46 ++--
|
||||||
|
.../ethernet/stmicro/stmmac/dwmac1000_dma.h | 26 +++
|
||||||
|
.../net/ethernet/stmicro/stmmac/dwmac_lib.c | 8 +
|
||||||
|
7 files changed, 289 insertions(+), 18 deletions(-)
|
||||||
|
create mode 100644 drivers/net/ethernet/stmicro/stmmac/dwmac-baikal.c
|
||||||
|
create mode 100644 drivers/net/ethernet/stmicro/stmmac/dwmac1000_dma.h
|
||||||
|
|
||||||
|
diff --git a/drivers/net/ethernet/stmicro/stmmac/Kconfig b/drivers/net/ethernet/stmicro/stmmac/Kconfig
|
||||||
|
index 929cfc22c..d8e6dcb98 100644
|
||||||
|
--- a/drivers/net/ethernet/stmicro/stmmac/Kconfig
|
||||||
|
+++ b/drivers/net/ethernet/stmicro/stmmac/Kconfig
|
||||||
|
@@ -66,6 +66,17 @@ config DWMAC_ANARION
|
||||||
|
|
||||||
|
This selects the Anarion SoC glue layer support for the stmmac driver.
|
||||||
|
|
||||||
|
+config DWMAC_BAIKAL
|
||||||
|
+ tristate "Baikal Electronics GMAC support"
|
||||||
|
+ default MIPS_BAIKAL_T1
|
||||||
|
+ depends on OF && (MIPS || ARM64 || COMPILE_TEST)
|
||||||
|
+ help
|
||||||
|
+ Support for gigabit ethernet controller on Baikal Electronics SoCs.
|
||||||
|
+
|
||||||
|
+ This selects the Baikal Electronics SoCs glue layer support for
|
||||||
|
+ the stmmac driver. This driver is used for Baikal-T1 and Baikal-M
|
||||||
|
+ SoCs gigabit ethernet controller.
|
||||||
|
+
|
||||||
|
config DWMAC_INGENIC
|
||||||
|
tristate "Ingenic MAC support"
|
||||||
|
default MACH_INGENIC
|
||||||
|
diff --git a/drivers/net/ethernet/stmicro/stmmac/Makefile b/drivers/net/ethernet/stmicro/stmmac/Makefile
|
||||||
|
index d4e12e9ac..ad138062e 100644
|
||||||
|
--- a/drivers/net/ethernet/stmicro/stmmac/Makefile
|
||||||
|
+++ b/drivers/net/ethernet/stmicro/stmmac/Makefile
|
||||||
|
@@ -14,6 +14,7 @@ stmmac-$(CONFIG_STMMAC_SELFTESTS) += stmmac_selftests.o
|
||||||
|
# Ordering matters. Generic driver must be last.
|
||||||
|
obj-$(CONFIG_STMMAC_PLATFORM) += stmmac-platform.o
|
||||||
|
obj-$(CONFIG_DWMAC_ANARION) += dwmac-anarion.o
|
||||||
|
+obj-$(CONFIG_DWMAC_BAIKAL) += dwmac-baikal.o
|
||||||
|
obj-$(CONFIG_DWMAC_INGENIC) += dwmac-ingenic.o
|
||||||
|
obj-$(CONFIG_DWMAC_IPQ806X) += dwmac-ipq806x.o
|
||||||
|
obj-$(CONFIG_DWMAC_LPC18XX) += dwmac-lpc18xx.o
|
||||||
|
diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac-baikal.c b/drivers/net/ethernet/stmicro/stmmac/dwmac-baikal.c
|
||||||
|
new file mode 100644
|
||||||
|
index 000000000..95ef0e144
|
||||||
|
--- /dev/null
|
||||||
|
+++ b/drivers/net/ethernet/stmicro/stmmac/dwmac-baikal.c
|
||||||
|
@@ -0,0 +1,214 @@
|
||||||
|
+// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
+/*
|
||||||
|
+ * Baikal-T1/M SoCs DWMAC glue layer
|
||||||
|
+ *
|
||||||
|
+ * Copyright (C) 2015,2016,2021 Baikal Electronics JSC
|
||||||
|
+ * Copyright (C) 2020-2022 BaseALT Ltd
|
||||||
|
+ * Authors: Dmitry Dunaev <dmitry.dunaev@baikalelectronics.ru>
|
||||||
|
+ * Alexey Sheplyakov <asheplyakov@basealt.ru>
|
||||||
|
+ */
|
||||||
|
+
|
||||||
|
+#include <linux/clk.h>
|
||||||
|
+#include <linux/iopoll.h>
|
||||||
|
+#include <linux/module.h>
|
||||||
|
+#include <linux/of.h>
|
||||||
|
+#include <linux/platform_device.h>
|
||||||
|
+
|
||||||
|
+#include "stmmac.h"
|
||||||
|
+#include "stmmac_platform.h"
|
||||||
|
+#include "common.h"
|
||||||
|
+#include "dwmac_dma.h"
|
||||||
|
+#include "dwmac1000_dma.h"
|
||||||
|
+
|
||||||
|
+#define MAC_GPIO 0x00e0 /* GPIO register */
|
||||||
|
+#define MAC_GPIO_GPO BIT(8) /* Output port */
|
||||||
|
+
|
||||||
|
+struct baikal_dwmac {
|
||||||
|
+ struct device *dev;
|
||||||
|
+ struct clk *tx2_clk;
|
||||||
|
+};
|
||||||
|
+
|
||||||
|
+static int baikal_dwmac_dma_reset(void __iomem *ioaddr)
|
||||||
|
+{
|
||||||
|
+ u32 value;
|
||||||
|
+
|
||||||
|
+ /* DMA SW reset */
|
||||||
|
+ value = readl(ioaddr + DMA_BUS_MODE);
|
||||||
|
+ value |= DMA_BUS_MODE_SFT_RESET;
|
||||||
|
+ writel(value, ioaddr + DMA_BUS_MODE);
|
||||||
|
+
|
||||||
|
+ /* Software DMA reset also resets MAC, so GP_OUT is set to zero.
|
||||||
|
+ * Which resets PHY as a side effect (if GP_OUT is connected directly
|
||||||
|
+ * to PHY reset).
|
||||||
|
+ * TODO: read the PHY reset duration from the device tree.
|
||||||
|
+ * Meanwhile use 100 milliseconds which seems to be enough for
|
||||||
|
+ * most PHYs
|
||||||
|
+ */
|
||||||
|
+ usleep_range(100000, 120000);
|
||||||
|
+
|
||||||
|
+ /* Clear PHY reset */
|
||||||
|
+ value = readl(ioaddr + MAC_GPIO);
|
||||||
|
+ value |= MAC_GPIO_GPO;
|
||||||
|
+ writel(value, ioaddr + MAC_GPIO);
|
||||||
|
+
|
||||||
|
+ /* Many PHYs need ~ 100 milliseconds to calm down after PHY reset
|
||||||
|
+ * has been cleared. And check for DMA reset below might return
|
||||||
|
+ * much earlier (i.e. in ~ 20 milliseconds). As a result reading
|
||||||
|
+ * PHY registers (after this function returns) might return garbage.
|
||||||
|
+ * Wait a bit to avoid the problem.
|
||||||
|
+ * TODO: read PHY post-reset delay from the device tree.
|
||||||
|
+ */
|
||||||
|
+ usleep_range(100000, 150000);
|
||||||
|
+
|
||||||
|
+ return readl_poll_timeout(ioaddr + DMA_BUS_MODE, value,
|
||||||
|
+ !(value & DMA_BUS_MODE_SFT_RESET),
|
||||||
|
+ 10000, 1000000);
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
+static const struct stmmac_dma_ops baikal_dwmac_dma_ops = {
|
||||||
|
+ .reset = baikal_dwmac_dma_reset,
|
||||||
|
+ .init = dwmac1000_dma_init,
|
||||||
|
+ .init_rx_chan = dwmac1000_dma_init_rx,
|
||||||
|
+ .init_tx_chan = dwmac1000_dma_init_tx,
|
||||||
|
+ .axi = dwmac1000_dma_axi,
|
||||||
|
+ .dump_regs = dwmac1000_dump_dma_regs,
|
||||||
|
+ .dma_rx_mode = dwmac1000_dma_operation_mode_rx,
|
||||||
|
+ .dma_tx_mode = dwmac1000_dma_operation_mode_tx,
|
||||||
|
+ .enable_dma_transmission = dwmac_enable_dma_transmission,
|
||||||
|
+ .enable_dma_irq = dwmac_enable_dma_irq,
|
||||||
|
+ .disable_dma_irq = dwmac_disable_dma_irq,
|
||||||
|
+ .start_tx = dwmac_dma_start_tx,
|
||||||
|
+ .stop_tx = dwmac_dma_stop_tx,
|
||||||
|
+ .start_rx = dwmac_dma_start_rx,
|
||||||
|
+ .stop_rx = dwmac_dma_stop_rx,
|
||||||
|
+ .dma_interrupt = dwmac_dma_interrupt,
|
||||||
|
+ .get_hw_feature = dwmac1000_get_hw_feature,
|
||||||
|
+ .rx_watchdog = dwmac1000_rx_watchdog
|
||||||
|
+};
|
||||||
|
+
|
||||||
|
+static struct mac_device_info *baikal_dwmac_setup(void *ppriv)
|
||||||
|
+{
|
||||||
|
+ struct mac_device_info *mac;
|
||||||
|
+ struct stmmac_priv *priv = ppriv;
|
||||||
|
+ int ret;
|
||||||
|
+ u32 value;
|
||||||
|
+
|
||||||
|
+ mac = devm_kzalloc(priv->device, sizeof(*mac), GFP_KERNEL);
|
||||||
|
+ if (!mac)
|
||||||
|
+ return NULL;
|
||||||
|
+
|
||||||
|
+ /* Clear PHY reset */
|
||||||
|
+ value = readl(priv->ioaddr + MAC_GPIO);
|
||||||
|
+ value |= MAC_GPIO_GPO;
|
||||||
|
+ writel(value, priv->ioaddr + MAC_GPIO);
|
||||||
|
+
|
||||||
|
+ mac->dma = &baikal_dwmac_dma_ops;
|
||||||
|
+ priv->hw = mac;
|
||||||
|
+ ret = dwmac1000_setup(priv);
|
||||||
|
+ if (ret) {
|
||||||
|
+ dev_err(priv->device, "dwmac1000_setup: error %d", ret);
|
||||||
|
+ return NULL;
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ return mac;
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
+static void baikal_dwmac_fix_mac_speed(void *priv, unsigned int speed)
|
||||||
|
+{
|
||||||
|
+ struct baikal_dwmac *dwmac = priv;
|
||||||
|
+ unsigned long tx2_clk_freq;
|
||||||
|
+
|
||||||
|
+ switch (speed) {
|
||||||
|
+ case SPEED_1000:
|
||||||
|
+ tx2_clk_freq = 250000000;
|
||||||
|
+ break;
|
||||||
|
+ case SPEED_100:
|
||||||
|
+ tx2_clk_freq = 50000000;
|
||||||
|
+ break;
|
||||||
|
+ case SPEED_10:
|
||||||
|
+ tx2_clk_freq = 5000000;
|
||||||
|
+ break;
|
||||||
|
+ default:
|
||||||
|
+ dev_warn(dwmac->dev, "invalid speed: %u\n", speed);
|
||||||
|
+ return;
|
||||||
|
+ }
|
||||||
|
+ dev_dbg(dwmac->dev, "speed %u, setting TX2 clock frequency to %lu\n",
|
||||||
|
+ speed, tx2_clk_freq);
|
||||||
|
+ clk_set_rate(dwmac->tx2_clk, tx2_clk_freq);
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
+static int dwmac_baikal_probe(struct platform_device *pdev)
|
||||||
|
+{
|
||||||
|
+ struct plat_stmmacenet_data *plat_dat;
|
||||||
|
+ struct stmmac_resources stmmac_res;
|
||||||
|
+ struct baikal_dwmac *dwmac;
|
||||||
|
+ int ret;
|
||||||
|
+
|
||||||
|
+ dwmac = devm_kzalloc(&pdev->dev, sizeof(*dwmac), GFP_KERNEL);
|
||||||
|
+ if (!dwmac)
|
||||||
|
+ return -ENOMEM;
|
||||||
|
+
|
||||||
|
+ ret = stmmac_get_platform_resources(pdev, &stmmac_res);
|
||||||
|
+ if (ret)
|
||||||
|
+ return ret;
|
||||||
|
+
|
||||||
|
+ ret = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(32));
|
||||||
|
+ if (ret) {
|
||||||
|
+ dev_err(&pdev->dev, "no suitable DMA available\n");
|
||||||
|
+ return ret;
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ plat_dat = stmmac_probe_config_dt(pdev, stmmac_res.mac);
|
||||||
|
+ if (IS_ERR(plat_dat)) {
|
||||||
|
+ dev_err(&pdev->dev, "dt configuration failed\n");
|
||||||
|
+ return PTR_ERR(plat_dat);
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ dwmac->dev = &pdev->dev;
|
||||||
|
+ dwmac->tx2_clk = devm_clk_get_optional(dwmac->dev, "tx2_clk");
|
||||||
|
+ if (IS_ERR(dwmac->tx2_clk)) {
|
||||||
|
+ ret = PTR_ERR(dwmac->tx2_clk);
|
||||||
|
+ dev_err(&pdev->dev, "couldn't get TX2 clock: %d\n", ret);
|
||||||
|
+ goto err_remove_config_dt;
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ if (dwmac->tx2_clk)
|
||||||
|
+ plat_dat->fix_mac_speed = baikal_dwmac_fix_mac_speed;
|
||||||
|
+ plat_dat->bsp_priv = dwmac;
|
||||||
|
+ plat_dat->has_gmac = 1;
|
||||||
|
+ plat_dat->enh_desc = 1;
|
||||||
|
+ plat_dat->tx_coe = 1;
|
||||||
|
+ plat_dat->rx_coe = 1;
|
||||||
|
+ plat_dat->clk_csr = 3;
|
||||||
|
+ plat_dat->setup = baikal_dwmac_setup;
|
||||||
|
+
|
||||||
|
+ ret = stmmac_dvr_probe(&pdev->dev, plat_dat, &stmmac_res);
|
||||||
|
+ if (ret)
|
||||||
|
+ goto err_remove_config_dt;
|
||||||
|
+
|
||||||
|
+ return 0;
|
||||||
|
+
|
||||||
|
+err_remove_config_dt:
|
||||||
|
+ stmmac_remove_config_dt(pdev, plat_dat);
|
||||||
|
+ return ret;
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
+static const struct of_device_id dwmac_baikal_match[] = {
|
||||||
|
+ { .compatible = "baikal,dwmac" },
|
||||||
|
+ { }
|
||||||
|
+};
|
||||||
|
+MODULE_DEVICE_TABLE(of, dwmac_baikal_match);
|
||||||
|
+
|
||||||
|
+static struct platform_driver dwmac_baikal_driver = {
|
||||||
|
+ .probe = dwmac_baikal_probe,
|
||||||
|
+ .remove = stmmac_pltfr_remove,
|
||||||
|
+ .driver = {
|
||||||
|
+ .name = "baikal-dwmac",
|
||||||
|
+ .pm = &stmmac_pltfr_pm_ops,
|
||||||
|
+ .of_match_table = of_match_ptr(dwmac_baikal_match)
|
||||||
|
+ }
|
||||||
|
+};
|
||||||
|
+module_platform_driver(dwmac_baikal_driver);
|
||||||
|
+
|
||||||
|
+MODULE_DESCRIPTION("Baikal-T1/M DWMAC driver");
|
||||||
|
+MODULE_LICENSE("GPL");
|
||||||
|
diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac1000_core.c b/drivers/net/ethernet/stmicro/stmmac/dwmac1000_core.c
|
||||||
|
index fc8759f14..bf4f79ef3 100644
|
||||||
|
--- a/drivers/net/ethernet/stmicro/stmmac/dwmac1000_core.c
|
||||||
|
+++ b/drivers/net/ethernet/stmicro/stmmac/dwmac1000_core.c
|
||||||
|
@@ -563,3 +563,4 @@ int dwmac1000_setup(struct stmmac_priv *priv)
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
+EXPORT_SYMBOL_GPL(dwmac1000_setup);
|
||||||
|
diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac1000_dma.c b/drivers/net/ethernet/stmicro/stmmac/dwmac1000_dma.c
|
||||||
|
index f5581db0b..1782a65cc 100644
|
||||||
|
--- a/drivers/net/ethernet/stmicro/stmmac/dwmac1000_dma.c
|
||||||
|
+++ b/drivers/net/ethernet/stmicro/stmmac/dwmac1000_dma.c
|
||||||
|
@@ -15,8 +15,9 @@
|
||||||
|
#include <asm/io.h>
|
||||||
|
#include "dwmac1000.h"
|
||||||
|
#include "dwmac_dma.h"
|
||||||
|
+#include "dwmac1000_dma.h"
|
||||||
|
|
||||||
|
-static void dwmac1000_dma_axi(void __iomem *ioaddr, struct stmmac_axi *axi)
|
||||||
|
+void dwmac1000_dma_axi(void __iomem *ioaddr, struct stmmac_axi *axi)
|
||||||
|
{
|
||||||
|
u32 value = readl(ioaddr + DMA_AXI_BUS_MODE);
|
||||||
|
int i;
|
||||||
|
@@ -69,9 +70,10 @@ static void dwmac1000_dma_axi(void __iomem *ioaddr, struct stmmac_axi *axi)
|
||||||
|
|
||||||
|
writel(value, ioaddr + DMA_AXI_BUS_MODE);
|
||||||
|
}
|
||||||
|
+EXPORT_SYMBOL_GPL(dwmac1000_dma_axi);
|
||||||
|
|
||||||
|
-static void dwmac1000_dma_init(void __iomem *ioaddr,
|
||||||
|
- struct stmmac_dma_cfg *dma_cfg, int atds)
|
||||||
|
+void dwmac1000_dma_init(void __iomem *ioaddr,
|
||||||
|
+ struct stmmac_dma_cfg *dma_cfg, int atds)
|
||||||
|
{
|
||||||
|
u32 value = readl(ioaddr + DMA_BUS_MODE);
|
||||||
|
int txpbl = dma_cfg->txpbl ?: dma_cfg->pbl;
|
||||||
|
@@ -109,22 +111,25 @@ static void dwmac1000_dma_init(void __iomem *ioaddr,
|
||||||
|
/* Mask interrupts by writing to CSR7 */
|
||||||
|
writel(DMA_INTR_DEFAULT_MASK, ioaddr + DMA_INTR_ENA);
|
||||||
|
}
|
||||||
|
+EXPORT_SYMBOL_GPL(dwmac1000_dma_init);
|
||||||
|
|
||||||
|
-static void dwmac1000_dma_init_rx(void __iomem *ioaddr,
|
||||||
|
- struct stmmac_dma_cfg *dma_cfg,
|
||||||
|
- dma_addr_t dma_rx_phy, u32 chan)
|
||||||
|
+void dwmac1000_dma_init_rx(void __iomem *ioaddr,
|
||||||
|
+ struct stmmac_dma_cfg *dma_cfg,
|
||||||
|
+ dma_addr_t dma_rx_phy, u32 chan)
|
||||||
|
{
|
||||||
|
/* RX descriptor base address list must be written into DMA CSR3 */
|
||||||
|
writel(lower_32_bits(dma_rx_phy), ioaddr + DMA_RCV_BASE_ADDR);
|
||||||
|
}
|
||||||
|
+EXPORT_SYMBOL_GPL(dwmac1000_dma_init_rx);
|
||||||
|
|
||||||
|
-static void dwmac1000_dma_init_tx(void __iomem *ioaddr,
|
||||||
|
- struct stmmac_dma_cfg *dma_cfg,
|
||||||
|
- dma_addr_t dma_tx_phy, u32 chan)
|
||||||
|
+void dwmac1000_dma_init_tx(void __iomem *ioaddr,
|
||||||
|
+ struct stmmac_dma_cfg *dma_cfg,
|
||||||
|
+ dma_addr_t dma_tx_phy, u32 chan)
|
||||||
|
{
|
||||||
|
/* TX descriptor base address list must be written into DMA CSR4 */
|
||||||
|
writel(lower_32_bits(dma_tx_phy), ioaddr + DMA_TX_BASE_ADDR);
|
||||||
|
}
|
||||||
|
+EXPORT_SYMBOL_GPL(dwmac1000_dma_init_tx);
|
||||||
|
|
||||||
|
static u32 dwmac1000_configure_fc(u32 csr6, int rxfifosz)
|
||||||
|
{
|
||||||
|
@@ -147,8 +152,8 @@ static u32 dwmac1000_configure_fc(u32 csr6, int rxfifosz)
|
||||||
|
return csr6;
|
||||||
|
}
|
||||||
|
|
||||||
|
-static void dwmac1000_dma_operation_mode_rx(void __iomem *ioaddr, int mode,
|
||||||
|
- u32 channel, int fifosz, u8 qmode)
|
||||||
|
+void dwmac1000_dma_operation_mode_rx(void __iomem *ioaddr, int mode,
|
||||||
|
+ u32 channel, int fifosz, u8 qmode)
|
||||||
|
{
|
||||||
|
u32 csr6 = readl(ioaddr + DMA_CONTROL);
|
||||||
|
|
||||||
|
@@ -174,9 +179,10 @@ static void dwmac1000_dma_operation_mode_rx(void __iomem *ioaddr, int mode,
|
||||||
|
|
||||||
|
writel(csr6, ioaddr + DMA_CONTROL);
|
||||||
|
}
|
||||||
|
+EXPORT_SYMBOL_GPL(dwmac1000_dma_operation_mode_rx);
|
||||||
|
|
||||||
|
-static void dwmac1000_dma_operation_mode_tx(void __iomem *ioaddr, int mode,
|
||||||
|
- u32 channel, int fifosz, u8 qmode)
|
||||||
|
+void dwmac1000_dma_operation_mode_tx(void __iomem *ioaddr, int mode,
|
||||||
|
+ u32 channel, int fifosz, u8 qmode)
|
||||||
|
{
|
||||||
|
u32 csr6 = readl(ioaddr + DMA_CONTROL);
|
||||||
|
|
||||||
|
@@ -207,8 +213,9 @@ static void dwmac1000_dma_operation_mode_tx(void __iomem *ioaddr, int mode,
|
||||||
|
|
||||||
|
writel(csr6, ioaddr + DMA_CONTROL);
|
||||||
|
}
|
||||||
|
+EXPORT_SYMBOL_GPL(dwmac1000_dma_operation_mode_tx);
|
||||||
|
|
||||||
|
-static void dwmac1000_dump_dma_regs(void __iomem *ioaddr, u32 *reg_space)
|
||||||
|
+void dwmac1000_dump_dma_regs(void __iomem *ioaddr, u32 *reg_space)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
|
||||||
|
@@ -217,9 +224,10 @@ static void dwmac1000_dump_dma_regs(void __iomem *ioaddr, u32 *reg_space)
|
||||||
|
reg_space[DMA_BUS_MODE / 4 + i] =
|
||||||
|
readl(ioaddr + DMA_BUS_MODE + i * 4);
|
||||||
|
}
|
||||||
|
+EXPORT_SYMBOL_GPL(dwmac1000_dump_dma_regs);
|
||||||
|
|
||||||
|
-static int dwmac1000_get_hw_feature(void __iomem *ioaddr,
|
||||||
|
- struct dma_features *dma_cap)
|
||||||
|
+int dwmac1000_get_hw_feature(void __iomem *ioaddr,
|
||||||
|
+ struct dma_features *dma_cap)
|
||||||
|
{
|
||||||
|
u32 hw_cap = readl(ioaddr + DMA_HW_FEATURE);
|
||||||
|
|
||||||
|
@@ -262,12 +270,14 @@ static int dwmac1000_get_hw_feature(void __iomem *ioaddr,
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
+EXPORT_SYMBOL_GPL(dwmac1000_get_hw_feature);
|
||||||
|
|
||||||
|
-static void dwmac1000_rx_watchdog(void __iomem *ioaddr, u32 riwt,
|
||||||
|
- u32 queue)
|
||||||
|
+void dwmac1000_rx_watchdog(void __iomem *ioaddr, u32 riwt,
|
||||||
|
+ u32 queue)
|
||||||
|
{
|
||||||
|
writel(riwt, ioaddr + DMA_RX_WATCHDOG);
|
||||||
|
}
|
||||||
|
+EXPORT_SYMBOL_GPL(dwmac1000_rx_watchdog);
|
||||||
|
|
||||||
|
const struct stmmac_dma_ops dwmac1000_dma_ops = {
|
||||||
|
.reset = dwmac_dma_reset,
|
||||||
|
diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac1000_dma.h b/drivers/net/ethernet/stmicro/stmmac/dwmac1000_dma.h
|
||||||
|
new file mode 100644
|
||||||
|
index 000000000..b254a0734
|
||||||
|
--- /dev/null
|
||||||
|
+++ b/drivers/net/ethernet/stmicro/stmmac/dwmac1000_dma.h
|
||||||
|
@@ -0,0 +1,26 @@
|
||||||
|
+/* SPDX-License-Identifier: GPL-2.0-only */
|
||||||
|
+
|
||||||
|
+#ifndef __DWMAC1000_DMA_H__
|
||||||
|
+#define __DWMAC1000_DMA_H__
|
||||||
|
+#include "dwmac1000.h"
|
||||||
|
+
|
||||||
|
+void dwmac1000_dma_axi(void __iomem *ioaddr, struct stmmac_axi *axi);
|
||||||
|
+void dwmac1000_dma_init(void __iomem *ioaddr,
|
||||||
|
+ struct stmmac_dma_cfg *dma_cfg, int atds);
|
||||||
|
+void dwmac1000_dma_init_rx(void __iomem *ioaddr,
|
||||||
|
+ struct stmmac_dma_cfg *dma_cfg,
|
||||||
|
+ dma_addr_t dma_rx_phy, u32 chan);
|
||||||
|
+void dwmac1000_dma_init_tx(void __iomem *ioaddr,
|
||||||
|
+ struct stmmac_dma_cfg *dma_cfg,
|
||||||
|
+ dma_addr_t dma_tx_phy, u32 chan);
|
||||||
|
+void dwmac1000_dma_operation_mode_rx(void __iomem *ioaddr, int mode,
|
||||||
|
+ u32 channel, int fifosz, u8 qmode);
|
||||||
|
+void dwmac1000_dma_operation_mode_tx(void __iomem *ioaddr, int mode,
|
||||||
|
+ u32 channel, int fifosz, u8 qmode);
|
||||||
|
+void dwmac1000_dump_dma_regs(void __iomem *ioaddr, u32 *reg_space);
|
||||||
|
+
|
||||||
|
+int dwmac1000_get_hw_feature(void __iomem *ioaddr,
|
||||||
|
+ struct dma_features *dma_cap);
|
||||||
|
+
|
||||||
|
+void dwmac1000_rx_watchdog(void __iomem *ioaddr, u32 riwt, u32 number_chan);
|
||||||
|
+#endif /* __DWMAC1000_DMA_H__ */
|
||||||
|
diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac_lib.c b/drivers/net/ethernet/stmicro/stmmac/dwmac_lib.c
|
||||||
|
index d1c31200b..b22e8f2f5 100644
|
||||||
|
--- a/drivers/net/ethernet/stmicro/stmmac/dwmac_lib.c
|
||||||
|
+++ b/drivers/net/ethernet/stmicro/stmmac/dwmac_lib.c
|
||||||
|
@@ -31,6 +31,7 @@ void dwmac_enable_dma_transmission(void __iomem *ioaddr)
|
||||||
|
{
|
||||||
|
writel(1, ioaddr + DMA_XMT_POLL_DEMAND);
|
||||||
|
}
|
||||||
|
+EXPORT_SYMBOL_GPL(dwmac_enable_dma_transmission);
|
||||||
|
|
||||||
|
void dwmac_enable_dma_irq(void __iomem *ioaddr, u32 chan, bool rx, bool tx)
|
||||||
|
{
|
||||||
|
@@ -43,6 +44,7 @@ void dwmac_enable_dma_irq(void __iomem *ioaddr, u32 chan, bool rx, bool tx)
|
||||||
|
|
||||||
|
writel(value, ioaddr + DMA_INTR_ENA);
|
||||||
|
}
|
||||||
|
+EXPORT_SYMBOL_GPL(dwmac_enable_dma_irq);
|
||||||
|
|
||||||
|
void dwmac_disable_dma_irq(void __iomem *ioaddr, u32 chan, bool rx, bool tx)
|
||||||
|
{
|
||||||
|
@@ -55,6 +57,7 @@ void dwmac_disable_dma_irq(void __iomem *ioaddr, u32 chan, bool rx, bool tx)
|
||||||
|
|
||||||
|
writel(value, ioaddr + DMA_INTR_ENA);
|
||||||
|
}
|
||||||
|
+EXPORT_SYMBOL_GPL(dwmac_disable_dma_irq);
|
||||||
|
|
||||||
|
void dwmac_dma_start_tx(void __iomem *ioaddr, u32 chan)
|
||||||
|
{
|
||||||
|
@@ -62,6 +65,7 @@ void dwmac_dma_start_tx(void __iomem *ioaddr, u32 chan)
|
||||||
|
value |= DMA_CONTROL_ST;
|
||||||
|
writel(value, ioaddr + DMA_CONTROL);
|
||||||
|
}
|
||||||
|
+EXPORT_SYMBOL_GPL(dwmac_dma_start_tx);
|
||||||
|
|
||||||
|
void dwmac_dma_stop_tx(void __iomem *ioaddr, u32 chan)
|
||||||
|
{
|
||||||
|
@@ -69,6 +73,7 @@ void dwmac_dma_stop_tx(void __iomem *ioaddr, u32 chan)
|
||||||
|
value &= ~DMA_CONTROL_ST;
|
||||||
|
writel(value, ioaddr + DMA_CONTROL);
|
||||||
|
}
|
||||||
|
+EXPORT_SYMBOL_GPL(dwmac_dma_stop_tx);
|
||||||
|
|
||||||
|
void dwmac_dma_start_rx(void __iomem *ioaddr, u32 chan)
|
||||||
|
{
|
||||||
|
@@ -76,6 +81,7 @@ void dwmac_dma_start_rx(void __iomem *ioaddr, u32 chan)
|
||||||
|
value |= DMA_CONTROL_SR;
|
||||||
|
writel(value, ioaddr + DMA_CONTROL);
|
||||||
|
}
|
||||||
|
+EXPORT_SYMBOL_GPL(dwmac_dma_start_rx);
|
||||||
|
|
||||||
|
void dwmac_dma_stop_rx(void __iomem *ioaddr, u32 chan)
|
||||||
|
{
|
||||||
|
@@ -83,6 +89,7 @@ void dwmac_dma_stop_rx(void __iomem *ioaddr, u32 chan)
|
||||||
|
value &= ~DMA_CONTROL_SR;
|
||||||
|
writel(value, ioaddr + DMA_CONTROL);
|
||||||
|
}
|
||||||
|
+EXPORT_SYMBOL_GPL(dwmac_dma_stop_rx);
|
||||||
|
|
||||||
|
#ifdef DWMAC_DMA_DEBUG
|
||||||
|
static void show_tx_process_state(unsigned int status)
|
||||||
|
@@ -230,6 +237,7 @@ int dwmac_dma_interrupt(void __iomem *ioaddr,
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
+EXPORT_SYMBOL_GPL(dwmac_dma_interrupt);
|
||||||
|
|
||||||
|
void dwmac_dma_flush_tx_fifo(void __iomem *ioaddr)
|
||||||
|
{
|
||||||
|
--
|
||||||
|
2.33.2
|
||||||
|
|
|
@ -0,0 +1,27 @@
|
||||||
|
From 8940498c4572f55a1744481f2788491c30cbe7f6 Mon Sep 17 00:00:00 2001
|
||||||
|
From: Alexey Sheplyakov <asheplyakov@basealt.ru>
|
||||||
|
Date: Mon, 24 Jan 2022 15:53:55 +0400
|
||||||
|
Subject: [PATCH 602/634] dt-bindings: dwmac: Add bindings for Baikal-T1/M SoCs
|
||||||
|
|
||||||
|
Added dwmac bindings for Baikal-T1 and Baikal-M SoCs
|
||||||
|
|
||||||
|
Signed-off-by: Alexey Sheplyakov <asheplyakov@basealt.ru>
|
||||||
|
---
|
||||||
|
Documentation/devicetree/bindings/net/snps,dwmac.yaml | 1 +
|
||||||
|
1 file changed, 1 insertion(+)
|
||||||
|
|
||||||
|
diff --git a/Documentation/devicetree/bindings/net/snps,dwmac.yaml b/Documentation/devicetree/bindings/net/snps,dwmac.yaml
|
||||||
|
index 5b8db76b6..61bb48b83 100644
|
||||||
|
--- a/Documentation/devicetree/bindings/net/snps,dwmac.yaml
|
||||||
|
+++ b/Documentation/devicetree/bindings/net/snps,dwmac.yaml
|
||||||
|
@@ -58,6 +58,7 @@ properties:
|
||||||
|
- amlogic,meson8m2-dwmac
|
||||||
|
- amlogic,meson-gxbb-dwmac
|
||||||
|
- amlogic,meson-axg-dwmac
|
||||||
|
+ - baikal,dwmac
|
||||||
|
- ingenic,jz4775-mac
|
||||||
|
- ingenic,x1000-mac
|
||||||
|
- ingenic,x1600-mac
|
||||||
|
--
|
||||||
|
2.33.2
|
||||||
|
|
|
@ -0,0 +1,98 @@
|
||||||
|
From fea81e39118424f92528d515e261acf10496f34e Mon Sep 17 00:00:00 2001
|
||||||
|
From: Alexey Sheplyakov <asheplyakov@altlinux.org>
|
||||||
|
Date: Fri, 21 Jan 2022 18:57:56 +0400
|
||||||
|
Subject: [PATCH 603/634] net: stmmac: custom mdio reset for some Baikal-M
|
||||||
|
boards
|
||||||
|
|
||||||
|
Signed-off-by: Alexey Sheplyakov <asheplyakov@basealt.ru>
|
||||||
|
Signed-off-by: Dmitry Dunaev <dmitry.dunaev@baikalelectronics.ru>
|
||||||
|
---
|
||||||
|
.../net/ethernet/stmicro/stmmac/stmmac_mdio.c | 63 +++++++++++++++++++
|
||||||
|
1 file changed, 63 insertions(+)
|
||||||
|
|
||||||
|
diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_mdio.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_mdio.c
|
||||||
|
index a5d150c5f..639bcc35e 100644
|
||||||
|
--- a/drivers/net/ethernet/stmicro/stmmac/stmmac_mdio.c
|
||||||
|
+++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_mdio.c
|
||||||
|
@@ -346,6 +346,63 @@ static int stmmac_mdio_write(struct mii_bus *bus, int phyaddr, int phyreg,
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
+#define MAC_GPIO 0xe0 /* GPIO register */
|
||||||
|
+#define MAC_GPIO_GPO BIT(8) /* output port */
|
||||||
|
+
|
||||||
|
+#if IS_ENABLED(CONFIG_STMMAC_PLATFORM) && IS_ENABLED(CONFIG_OF)
|
||||||
|
+/**
|
||||||
|
+ * Reset the MII bus via MAC GP_OUT pin
|
||||||
|
+ */
|
||||||
|
+static int stmmac_mdio_reset_gp_out(struct stmmac_priv *priv) {
|
||||||
|
+ u32 value, high, low;
|
||||||
|
+ u32 delays[3] = { 0, 0, 0 };
|
||||||
|
+ bool active_low = false;
|
||||||
|
+ struct device_node *np = priv->device->of_node;
|
||||||
|
+
|
||||||
|
+ if (!np)
|
||||||
|
+ return -ENODEV;
|
||||||
|
+
|
||||||
|
+ if (!of_property_read_bool(np, "snps,reset-gp-out")) {
|
||||||
|
+ dev_warn(priv->device, "snps,reset-gp-out is not set\n");
|
||||||
|
+ return -ENODEV;
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ dev_info(priv->device, "resetting MDIO via GP_OUT\n");
|
||||||
|
+ active_low = of_property_read_bool(np, "snsps,reset-active-low");
|
||||||
|
+ of_property_read_u32_array(np, "snps,reset-delays-us", delays, 3);
|
||||||
|
+
|
||||||
|
+ value = readl(priv->ioaddr + MAC_GPIO);
|
||||||
|
+ if (active_low) {
|
||||||
|
+ high = value | MAC_GPIO_GPO;
|
||||||
|
+ low = value & ~MAC_GPIO_GPO;
|
||||||
|
+ } else {
|
||||||
|
+ high = value & ~MAC_GPIO_GPO;
|
||||||
|
+ low = value | MAC_GPIO_GPO;
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ writel(high, priv->ioaddr + MAC_GPIO);
|
||||||
|
+ if (delays[0])
|
||||||
|
+ msleep(DIV_ROUND_UP(delays[0], 1000));
|
||||||
|
+
|
||||||
|
+ writel(low, priv->ioaddr + MAC_GPIO);
|
||||||
|
+ if (delays[1])
|
||||||
|
+ msleep(DIV_ROUND_UP(delays[1], 1000));
|
||||||
|
+
|
||||||
|
+ writel(high, priv->ioaddr + MAC_GPIO);
|
||||||
|
+ if (delays[2])
|
||||||
|
+ msleep(DIV_ROUND_UP(delays[2], 1000));
|
||||||
|
+
|
||||||
|
+ /* Clear PHY reset */
|
||||||
|
+ udelay(10);
|
||||||
|
+ value = readl(priv->ioaddr + MAC_GPIO);
|
||||||
|
+ value |= MAC_GPIO_GPO;
|
||||||
|
+ writel(value, priv->ioaddr + MAC_GPIO);
|
||||||
|
+ msleep(1000);
|
||||||
|
+ dev_info(priv->device, "mdio reset completed\n");
|
||||||
|
+ return 0;
|
||||||
|
+}
|
||||||
|
+#endif
|
||||||
|
+
|
||||||
|
/**
|
||||||
|
* stmmac_mdio_reset
|
||||||
|
* @bus: points to the mii_bus structure
|
||||||
|
@@ -361,8 +418,14 @@ int stmmac_mdio_reset(struct mii_bus *bus)
|
||||||
|
#ifdef CONFIG_OF
|
||||||
|
if (priv->device->of_node) {
|
||||||
|
struct gpio_desc *reset_gpio;
|
||||||
|
+ bool reset_gp_out;
|
||||||
|
u32 delays[3] = { 0, 0, 0 };
|
||||||
|
|
||||||
|
+ reset_gp_out = of_property_read_bool(priv->device->of_node,
|
||||||
|
+ "snps,reset-gp-out");
|
||||||
|
+ if (reset_gp_out)
|
||||||
|
+ return stmmac_mdio_reset_gp_out(priv);
|
||||||
|
+
|
||||||
|
reset_gpio = devm_gpiod_get_optional(priv->device,
|
||||||
|
"snps,reset",
|
||||||
|
GPIOD_OUT_LOW);
|
||||||
|
--
|
||||||
|
2.33.2
|
||||||
|
|
|
@ -1,27 +0,0 @@
|
||||||
From 33a36bf1bf71c1bffdb0631a09856a24ba3d8165 Mon Sep 17 00:00:00 2001
|
|
||||||
From: Alexey Sheplyakov <asheplyakov@altlinux.org>
|
|
||||||
Date: Fri, 20 Mar 2020 17:14:36 +0400
|
|
||||||
Subject: [PATCH 604/625] efi/arm-runtime: print EFI mapping
|
|
||||||
|
|
||||||
(cherry picked from commit 652f76ffabc0e987169934d924d9e34b99b4cca9)
|
|
||||||
---
|
|
||||||
drivers/firmware/efi/arm-runtime.c | 3 +++
|
|
||||||
1 file changed, 3 insertions(+)
|
|
||||||
|
|
||||||
diff --git a/drivers/firmware/efi/arm-runtime.c b/drivers/firmware/efi/arm-runtime.c
|
|
||||||
index 3359ae2adf24..5028cd1605f3 100644
|
|
||||||
--- a/drivers/firmware/efi/arm-runtime.c
|
|
||||||
+++ b/drivers/firmware/efi/arm-runtime.c
|
|
||||||
@@ -71,6 +71,9 @@ static bool __init efi_virtmap_init(void)
|
|
||||||
pr_warn(" EFI remap %pa: failed to create mapping (%d)\n",
|
|
||||||
&phys, ret);
|
|
||||||
return false;
|
|
||||||
+ } else {
|
|
||||||
+ pr_info(" EFI remap %pa => %llx\n",
|
|
||||||
+ &phys, (unsigned long long)md->virt_addr);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
--
|
|
||||||
2.31.1
|
|
||||||
|
|
26
0604-net-dwmac-baikal-added-compatible-strings.patch
Normal file
26
0604-net-dwmac-baikal-added-compatible-strings.patch
Normal file
|
@ -0,0 +1,26 @@
|
||||||
|
From f42e3ac9176206d78d6a103b62a6a7964723cc06 Mon Sep 17 00:00:00 2001
|
||||||
|
From: Alexey Sheplyakov <asheplyakov@altlinux.org>
|
||||||
|
Date: Mon, 24 Jan 2022 13:46:15 +0400
|
||||||
|
Subject: [PATCH 604/634] net: dwmac-baikal: added compatible strings...
|
||||||
|
|
||||||
|
... for AQBM1000 board, TF307 board with FDT from SDK-M 5.3
|
||||||
|
---
|
||||||
|
drivers/net/ethernet/stmicro/stmmac/dwmac-baikal.c | 2 ++
|
||||||
|
1 file changed, 2 insertions(+)
|
||||||
|
|
||||||
|
diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac-baikal.c b/drivers/net/ethernet/stmicro/stmmac/dwmac-baikal.c
|
||||||
|
index 95ef0e144..7dca7824b 100644
|
||||||
|
--- a/drivers/net/ethernet/stmicro/stmmac/dwmac-baikal.c
|
||||||
|
+++ b/drivers/net/ethernet/stmicro/stmmac/dwmac-baikal.c
|
||||||
|
@@ -195,6 +195,8 @@ static int dwmac_baikal_probe(struct platform_device *pdev)
|
||||||
|
|
||||||
|
static const struct of_device_id dwmac_baikal_match[] = {
|
||||||
|
{ .compatible = "baikal,dwmac" },
|
||||||
|
+ { .compatible = "be,dwmac" },
|
||||||
|
+ { .compatible = "aq,dwmac" },
|
||||||
|
{ }
|
||||||
|
};
|
||||||
|
MODULE_DEVICE_TABLE(of, dwmac_baikal_match);
|
||||||
|
--
|
||||||
|
2.33.2
|
||||||
|
|
255
0605-hwmon-bt1-pvt-access-registers-via-pvt_-readl-writel.patch
Normal file
255
0605-hwmon-bt1-pvt-access-registers-via-pvt_-readl-writel.patch
Normal file
|
@ -0,0 +1,255 @@
|
||||||
|
From 73b442bd59787a6b62de69290a152f839058d949 Mon Sep 17 00:00:00 2001
|
||||||
|
From: Alexey Sheplyakov <asheplyakov@basealt.ru>
|
||||||
|
Date: Tue, 25 Jan 2022 17:54:33 +0400
|
||||||
|
Subject: [PATCH 605/634] hwmon: bt1-pvt: access registers via
|
||||||
|
pvt_{readl,writel} helpers
|
||||||
|
|
||||||
|
Baikal-M SoC is equipped with PVT sensors too. However on Baikal-M
|
||||||
|
PVT registers (and clocks) are directly accessible to the secure
|
||||||
|
world only, so Linux has to call into firmware (ARM-TF) to
|
||||||
|
read/write registers.
|
||||||
|
|
||||||
|
This patch replaces readl/writel with pvt_readl/pvt_writel functions.
|
||||||
|
No functional changes intended. Subsequent patch will define
|
||||||
|
pvt_readl and pvt_writel functions for Baikal-M SoC.
|
||||||
|
|
||||||
|
Signed-off-by: Alexey Sheplyakov <asheplyakov@basealt.ru>
|
||||||
|
---
|
||||||
|
drivers/hwmon/bt1-pvt.c | 85 +++++++++++++++++++++++------------------
|
||||||
|
1 file changed, 48 insertions(+), 37 deletions(-)
|
||||||
|
|
||||||
|
diff --git a/drivers/hwmon/bt1-pvt.c b/drivers/hwmon/bt1-pvt.c
|
||||||
|
index 74ce5211e..c05fe8df0 100644
|
||||||
|
--- a/drivers/hwmon/bt1-pvt.c
|
||||||
|
+++ b/drivers/hwmon/bt1-pvt.c
|
||||||
|
@@ -138,12 +138,23 @@ static long pvt_calc_poly(const struct pvt_poly *poly, long data)
|
||||||
|
return ret / poly->total_divider;
|
||||||
|
}
|
||||||
|
|
||||||
|
-static inline u32 pvt_update(void __iomem *reg, u32 mask, u32 data)
|
||||||
|
+static inline u32 pvt_readl(struct pvt_hwmon const *pvt, int reg) {
|
||||||
|
+ return readl(pvt->regs + reg);
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
+static inline u32 pvt_readl_relaxed(struct pvt_hwmon const *pvt, int reg) {
|
||||||
|
+ return readl_relaxed(pvt->regs + reg);
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
+static inline void pvt_writel(u32 data, struct pvt_hwmon const *pvt, int reg) {
|
||||||
|
+ writel(data, pvt->regs + reg);
|
||||||
|
+}
|
||||||
|
+static inline u32 pvt_update(struct pvt_hwmon *pvt, int reg, u32 mask, u32 data)
|
||||||
|
{
|
||||||
|
u32 old;
|
||||||
|
|
||||||
|
- old = readl_relaxed(reg);
|
||||||
|
- writel((old & ~mask) | (data & mask), reg);
|
||||||
|
+ old = pvt_readl_relaxed(pvt, reg);
|
||||||
|
+ pvt_writel((old & ~mask) | (data & mask), pvt, reg);
|
||||||
|
|
||||||
|
return old & mask;
|
||||||
|
}
|
||||||
|
@@ -161,8 +172,8 @@ static inline void pvt_set_mode(struct pvt_hwmon *pvt, u32 mode)
|
||||||
|
|
||||||
|
mode = FIELD_PREP(PVT_CTRL_MODE_MASK, mode);
|
||||||
|
|
||||||
|
- old = pvt_update(pvt->regs + PVT_CTRL, PVT_CTRL_EN, 0);
|
||||||
|
- pvt_update(pvt->regs + PVT_CTRL, PVT_CTRL_MODE_MASK | PVT_CTRL_EN,
|
||||||
|
+ old = pvt_update(pvt, PVT_CTRL, PVT_CTRL_EN, 0);
|
||||||
|
+ pvt_update(pvt, PVT_CTRL, PVT_CTRL_MODE_MASK | PVT_CTRL_EN,
|
||||||
|
mode | old);
|
||||||
|
}
|
||||||
|
|
||||||
|
@@ -179,8 +190,8 @@ static inline void pvt_set_trim(struct pvt_hwmon *pvt, u32 trim)
|
||||||
|
|
||||||
|
trim = FIELD_PREP(PVT_CTRL_TRIM_MASK, trim);
|
||||||
|
|
||||||
|
- old = pvt_update(pvt->regs + PVT_CTRL, PVT_CTRL_EN, 0);
|
||||||
|
- pvt_update(pvt->regs + PVT_CTRL, PVT_CTRL_TRIM_MASK | PVT_CTRL_EN,
|
||||||
|
+ old = pvt_update(pvt, PVT_CTRL, PVT_CTRL_EN, 0);
|
||||||
|
+ pvt_update(pvt, PVT_CTRL, PVT_CTRL_TRIM_MASK | PVT_CTRL_EN,
|
||||||
|
trim | old);
|
||||||
|
}
|
||||||
|
|
||||||
|
@@ -188,9 +199,9 @@ static inline void pvt_set_tout(struct pvt_hwmon *pvt, u32 tout)
|
||||||
|
{
|
||||||
|
u32 old;
|
||||||
|
|
||||||
|
- old = pvt_update(pvt->regs + PVT_CTRL, PVT_CTRL_EN, 0);
|
||||||
|
- writel(tout, pvt->regs + PVT_TTIMEOUT);
|
||||||
|
- pvt_update(pvt->regs + PVT_CTRL, PVT_CTRL_EN, old);
|
||||||
|
+ old = pvt_update(pvt, PVT_CTRL, PVT_CTRL_EN, 0);
|
||||||
|
+ pvt_writel(tout, pvt, PVT_TTIMEOUT);
|
||||||
|
+ pvt_update(pvt, PVT_CTRL, PVT_CTRL_EN, old);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
@@ -237,7 +248,7 @@ static irqreturn_t pvt_soft_isr(int irq, void *data)
|
||||||
|
* status before the next conversion happens. Threshold events will be
|
||||||
|
* handled a bit later.
|
||||||
|
*/
|
||||||
|
- thres_sts = readl(pvt->regs + PVT_RAW_INTR_STAT);
|
||||||
|
+ thres_sts = pvt_readl(pvt, PVT_RAW_INTR_STAT);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Then lets recharge the PVT interface with the next sampling mode.
|
||||||
|
@@ -260,14 +271,14 @@ static irqreturn_t pvt_soft_isr(int irq, void *data)
|
||||||
|
*/
|
||||||
|
mutex_lock(&pvt->iface_mtx);
|
||||||
|
|
||||||
|
- old = pvt_update(pvt->regs + PVT_INTR_MASK, PVT_INTR_DVALID,
|
||||||
|
+ old = pvt_update(pvt, PVT_INTR_MASK, PVT_INTR_DVALID,
|
||||||
|
PVT_INTR_DVALID);
|
||||||
|
|
||||||
|
- val = readl(pvt->regs + PVT_DATA);
|
||||||
|
+ val = pvt_readl(pvt, PVT_DATA);
|
||||||
|
|
||||||
|
pvt_set_mode(pvt, pvt_info[pvt->sensor].mode);
|
||||||
|
|
||||||
|
- pvt_update(pvt->regs + PVT_INTR_MASK, PVT_INTR_DVALID, old);
|
||||||
|
+ pvt_update(pvt, PVT_INTR_MASK, PVT_INTR_DVALID, old);
|
||||||
|
|
||||||
|
mutex_unlock(&pvt->iface_mtx);
|
||||||
|
|
||||||
|
@@ -337,7 +348,7 @@ static int pvt_read_limit(struct pvt_hwmon *pvt, enum pvt_sensor_type type,
|
||||||
|
u32 data;
|
||||||
|
|
||||||
|
/* No need in serialization, since it is just read from MMIO. */
|
||||||
|
- data = readl(pvt->regs + pvt_info[type].thres_base);
|
||||||
|
+ data = pvt_readl(pvt, pvt_info[type].thres_base);
|
||||||
|
|
||||||
|
if (is_low)
|
||||||
|
data = FIELD_GET(PVT_THRES_LO_MASK, data);
|
||||||
|
@@ -372,7 +383,7 @@ static int pvt_write_limit(struct pvt_hwmon *pvt, enum pvt_sensor_type type,
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
/* Make sure the upper and lower ranges don't intersect. */
|
||||||
|
- limit = readl(pvt->regs + pvt_info[type].thres_base);
|
||||||
|
+ limit = pvt_readl(pvt, pvt_info[type].thres_base);
|
||||||
|
if (is_low) {
|
||||||
|
limit = FIELD_GET(PVT_THRES_HI_MASK, limit);
|
||||||
|
data = clamp_val(data, PVT_DATA_MIN, limit);
|
||||||
|
@@ -385,7 +396,7 @@ static int pvt_write_limit(struct pvt_hwmon *pvt, enum pvt_sensor_type type,
|
||||||
|
mask = PVT_THRES_HI_MASK;
|
||||||
|
}
|
||||||
|
|
||||||
|
- pvt_update(pvt->regs + pvt_info[type].thres_base, mask, data);
|
||||||
|
+ pvt_update(pvt, pvt_info[type].thres_base, mask, data);
|
||||||
|
|
||||||
|
mutex_unlock(&pvt->iface_mtx);
|
||||||
|
|
||||||
|
@@ -439,14 +450,14 @@ static irqreturn_t pvt_hard_isr(int irq, void *data)
|
||||||
|
* Mask the DVALID interrupt so after exiting from the handler a
|
||||||
|
* repeated conversion wouldn't happen.
|
||||||
|
*/
|
||||||
|
- pvt_update(pvt->regs + PVT_INTR_MASK, PVT_INTR_DVALID,
|
||||||
|
+ pvt_update(pvt, PVT_INTR_MASK, PVT_INTR_DVALID,
|
||||||
|
PVT_INTR_DVALID);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Nothing special for alarm-less driver. Just read the data, update
|
||||||
|
* the cache and notify a waiter of this event.
|
||||||
|
*/
|
||||||
|
- val = readl(pvt->regs + PVT_DATA);
|
||||||
|
+ val = pvt_readl(pvt, PVT_DATA);
|
||||||
|
if (!(val & PVT_DATA_VALID)) {
|
||||||
|
dev_err(pvt->dev, "Got IRQ when data isn't valid\n");
|
||||||
|
return IRQ_HANDLED;
|
||||||
|
@@ -498,8 +509,8 @@ static int pvt_read_data(struct pvt_hwmon *pvt, enum pvt_sensor_type type,
|
||||||
|
* Unmask the DVALID interrupt and enable the sensors conversions.
|
||||||
|
* Do the reverse procedure when conversion is done.
|
||||||
|
*/
|
||||||
|
- pvt_update(pvt->regs + PVT_INTR_MASK, PVT_INTR_DVALID, 0);
|
||||||
|
- pvt_update(pvt->regs + PVT_CTRL, PVT_CTRL_EN, PVT_CTRL_EN);
|
||||||
|
+ pvt_update(pvt, PVT_INTR_MASK, PVT_INTR_DVALID, 0);
|
||||||
|
+ pvt_update(pvt, PVT_CTRL, PVT_CTRL_EN, PVT_CTRL_EN);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Wait with timeout since in case if the sensor is suddenly powered
|
||||||
|
@@ -510,8 +521,8 @@ static int pvt_read_data(struct pvt_hwmon *pvt, enum pvt_sensor_type type,
|
||||||
|
timeout = 2 * usecs_to_jiffies(ktime_to_us(pvt->timeout));
|
||||||
|
ret = wait_for_completion_timeout(&cache->conversion, timeout);
|
||||||
|
|
||||||
|
- pvt_update(pvt->regs + PVT_CTRL, PVT_CTRL_EN, 0);
|
||||||
|
- pvt_update(pvt->regs + PVT_INTR_MASK, PVT_INTR_DVALID,
|
||||||
|
+ pvt_update(pvt, PVT_CTRL, PVT_CTRL_EN, 0);
|
||||||
|
+ pvt_update(pvt, PVT_INTR_MASK, PVT_INTR_DVALID,
|
||||||
|
PVT_INTR_DVALID);
|
||||||
|
|
||||||
|
data = READ_ONCE(cache->data);
|
||||||
|
@@ -637,7 +648,7 @@ static int pvt_read_trim(struct pvt_hwmon *pvt, long *val)
|
||||||
|
{
|
||||||
|
u32 data;
|
||||||
|
|
||||||
|
- data = readl(pvt->regs + PVT_CTRL);
|
||||||
|
+ data = pvt_readl(pvt, PVT_CTRL);
|
||||||
|
*val = FIELD_GET(PVT_CTRL_TRIM_MASK, data) * PVT_TRIM_STEP;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
@@ -981,21 +992,21 @@ static int pvt_check_pwr(struct pvt_hwmon *pvt)
|
||||||
|
* conversion. In the later case alas we won't be able to detect the
|
||||||
|
* problem.
|
||||||
|
*/
|
||||||
|
- pvt_update(pvt->regs + PVT_INTR_MASK, PVT_INTR_ALL, PVT_INTR_ALL);
|
||||||
|
- pvt_update(pvt->regs + PVT_CTRL, PVT_CTRL_EN, PVT_CTRL_EN);
|
||||||
|
+ pvt_update(pvt, PVT_INTR_MASK, PVT_INTR_ALL, PVT_INTR_ALL);
|
||||||
|
+ pvt_update(pvt, PVT_CTRL, PVT_CTRL_EN, PVT_CTRL_EN);
|
||||||
|
pvt_set_tout(pvt, 0);
|
||||||
|
- readl(pvt->regs + PVT_DATA);
|
||||||
|
+ pvt_readl(pvt, PVT_DATA);
|
||||||
|
|
||||||
|
tout = PVT_TOUT_MIN / NSEC_PER_USEC;
|
||||||
|
usleep_range(tout, 2 * tout);
|
||||||
|
|
||||||
|
- data = readl(pvt->regs + PVT_DATA);
|
||||||
|
+ data = pvt_readl(pvt, PVT_DATA);
|
||||||
|
if (!(data & PVT_DATA_VALID)) {
|
||||||
|
ret = -ENODEV;
|
||||||
|
dev_err(pvt->dev, "Sensor is powered down\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
- pvt_update(pvt->regs + PVT_CTRL, PVT_CTRL_EN, 0);
|
||||||
|
+ pvt_update(pvt, PVT_CTRL, PVT_CTRL_EN, 0);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
@@ -1016,10 +1027,10 @@ static int pvt_init_iface(struct pvt_hwmon *pvt)
|
||||||
|
* accidentally have ISR executed before the driver data is fully
|
||||||
|
* initialized. Clear the IRQ status as well.
|
||||||
|
*/
|
||||||
|
- pvt_update(pvt->regs + PVT_INTR_MASK, PVT_INTR_ALL, PVT_INTR_ALL);
|
||||||
|
- pvt_update(pvt->regs + PVT_CTRL, PVT_CTRL_EN, 0);
|
||||||
|
- readl(pvt->regs + PVT_CLR_INTR);
|
||||||
|
- readl(pvt->regs + PVT_DATA);
|
||||||
|
+ pvt_update(pvt, PVT_INTR_MASK, PVT_INTR_ALL, PVT_INTR_ALL);
|
||||||
|
+ pvt_update(pvt, PVT_CTRL, PVT_CTRL_EN, 0);
|
||||||
|
+ pvt_readl(pvt, PVT_CLR_INTR);
|
||||||
|
+ pvt_readl(pvt, PVT_DATA);
|
||||||
|
|
||||||
|
/* Setup default sensor mode, timeout and temperature trim. */
|
||||||
|
pvt_set_mode(pvt, pvt_info[pvt->sensor].mode);
|
||||||
|
@@ -1103,8 +1114,8 @@ static void pvt_disable_iface(void *data)
|
||||||
|
struct pvt_hwmon *pvt = data;
|
||||||
|
|
||||||
|
mutex_lock(&pvt->iface_mtx);
|
||||||
|
- pvt_update(pvt->regs + PVT_CTRL, PVT_CTRL_EN, 0);
|
||||||
|
- pvt_update(pvt->regs + PVT_INTR_MASK, PVT_INTR_DVALID,
|
||||||
|
+ pvt_update(pvt, PVT_CTRL, PVT_CTRL_EN, 0);
|
||||||
|
+ pvt_update(pvt, PVT_INTR_MASK, PVT_INTR_DVALID,
|
||||||
|
PVT_INTR_DVALID);
|
||||||
|
mutex_unlock(&pvt->iface_mtx);
|
||||||
|
}
|
||||||
|
@@ -1126,8 +1137,8 @@ static int pvt_enable_iface(struct pvt_hwmon *pvt)
|
||||||
|
* which theoretically may cause races.
|
||||||
|
*/
|
||||||
|
mutex_lock(&pvt->iface_mtx);
|
||||||
|
- pvt_update(pvt->regs + PVT_INTR_MASK, PVT_INTR_DVALID, 0);
|
||||||
|
- pvt_update(pvt->regs + PVT_CTRL, PVT_CTRL_EN, PVT_CTRL_EN);
|
||||||
|
+ pvt_update(pvt, PVT_INTR_MASK, PVT_INTR_DVALID, 0);
|
||||||
|
+ pvt_update(pvt, PVT_CTRL, PVT_CTRL_EN, PVT_CTRL_EN);
|
||||||
|
mutex_unlock(&pvt->iface_mtx);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
--
|
||||||
|
2.33.2
|
||||||
|
|
|
@ -0,0 +1,92 @@
|
||||||
|
From b81e26c4ffd2546847bb09194b02107f65e23a00 Mon Sep 17 00:00:00 2001
|
||||||
|
From: Alexey Sheplyakov <asheplyakov@basealt.ru>
|
||||||
|
Date: Tue, 25 Jan 2022 17:55:03 +0400
|
||||||
|
Subject: [PATCH 606/634] hwmon: bt1-pvt: define pvt_readl/pvt_writel for
|
||||||
|
Baikal-M SoC
|
||||||
|
|
||||||
|
On Baikal-M Linux has to call into firmware (ARM-TF) to access
|
||||||
|
PVT registers.
|
||||||
|
|
||||||
|
Signed-off-by: Alexey Sheplyakov <asheplyakov@basealt.ru>
|
||||||
|
---
|
||||||
|
drivers/hwmon/bt1-pvt.c | 23 +++++++++++++++++++++++
|
||||||
|
drivers/hwmon/bt1-pvt.h | 8 ++++++++
|
||||||
|
2 files changed, 31 insertions(+)
|
||||||
|
|
||||||
|
diff --git a/drivers/hwmon/bt1-pvt.c b/drivers/hwmon/bt1-pvt.c
|
||||||
|
index c05fe8df0..2f2a222bd 100644
|
||||||
|
--- a/drivers/hwmon/bt1-pvt.c
|
||||||
|
+++ b/drivers/hwmon/bt1-pvt.c
|
||||||
|
@@ -29,6 +29,9 @@
|
||||||
|
#include <linux/seqlock.h>
|
||||||
|
#include <linux/sysfs.h>
|
||||||
|
#include <linux/types.h>
|
||||||
|
+#ifdef CONFIG_ARM64
|
||||||
|
+#include <linux/arm-smccc.h>
|
||||||
|
+#endif
|
||||||
|
|
||||||
|
#include "bt1-pvt.h"
|
||||||
|
|
||||||
|
@@ -138,6 +141,7 @@ static long pvt_calc_poly(const struct pvt_poly *poly, long data)
|
||||||
|
return ret / poly->total_divider;
|
||||||
|
}
|
||||||
|
|
||||||
|
+#ifdef BT1_PVT_DIRECT_REG_ACCESS
|
||||||
|
static inline u32 pvt_readl(struct pvt_hwmon const *pvt, int reg) {
|
||||||
|
return readl(pvt->regs + reg);
|
||||||
|
}
|
||||||
|
@@ -149,6 +153,25 @@ static inline u32 pvt_readl_relaxed(struct pvt_hwmon const *pvt, int reg) {
|
||||||
|
static inline void pvt_writel(u32 data, struct pvt_hwmon const *pvt, int reg) {
|
||||||
|
writel(data, pvt->regs + reg);
|
||||||
|
}
|
||||||
|
+#else
|
||||||
|
+static inline u32 pvt_readl(struct pvt_hwmon const *pvt, int reg) {
|
||||||
|
+ struct arm_smccc_res res;
|
||||||
|
+ arm_smccc_smc(BAIKAL_SMC_PVT_ID, PVT_READ, pvt->pvt_id, reg,
|
||||||
|
+ 0, 0, 0, 0, &res);
|
||||||
|
+ return res.a0;
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
+static inline u32 pvt_readl_relaxed(struct pvt_hwmon const *pvt, int reg) {
|
||||||
|
+ return pvt_readl(pvt, reg);
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
+static inline void pvt_writel(u32 data, struct pvt_hwmon const *pvt, int reg) {
|
||||||
|
+ struct arm_smccc_res res;
|
||||||
|
+ arm_smccc_smc(BAIKAL_SMC_PVT_ID, PVT_WRITE, pvt->pvt_id, reg,
|
||||||
|
+ data, 0, 0, 0, &res);
|
||||||
|
+}
|
||||||
|
+#endif
|
||||||
|
+
|
||||||
|
static inline u32 pvt_update(struct pvt_hwmon *pvt, int reg, u32 mask, u32 data)
|
||||||
|
{
|
||||||
|
u32 old;
|
||||||
|
diff --git a/drivers/hwmon/bt1-pvt.h b/drivers/hwmon/bt1-pvt.h
|
||||||
|
index 93b8dd5e7..0cea95b01 100644
|
||||||
|
--- a/drivers/hwmon/bt1-pvt.h
|
||||||
|
+++ b/drivers/hwmon/bt1-pvt.h
|
||||||
|
@@ -101,6 +101,13 @@
|
||||||
|
# define PVT_TOUT_DEF 0
|
||||||
|
#endif
|
||||||
|
|
||||||
|
+#define BAIKAL_SMC_PVT_ID 0x82000001
|
||||||
|
+#define PVT_READ 0
|
||||||
|
+#define PVT_WRITE 1
|
||||||
|
+#ifndef CONFIG_ARM64
|
||||||
|
+#define BT1_PVT_DIRECT_REG_ACCESS
|
||||||
|
+#endif
|
||||||
|
+
|
||||||
|
/*
|
||||||
|
* enum pvt_sensor_type - Baikal-T1 PVT sensor types (correspond to each PVT
|
||||||
|
* sampling mode)
|
||||||
|
@@ -217,6 +224,7 @@ struct pvt_hwmon {
|
||||||
|
enum pvt_sensor_type sensor;
|
||||||
|
struct pvt_cache cache[PVT_SENSORS_NUM];
|
||||||
|
ktime_t timeout;
|
||||||
|
+ int pvt_id;
|
||||||
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
--
|
||||||
|
2.33.2
|
||||||
|
|
143
0607-hwmon-bt1-pvt-adjusted-probing-for-Baikal-M-SoC.patch
Normal file
143
0607-hwmon-bt1-pvt-adjusted-probing-for-Baikal-M-SoC.patch
Normal file
|
@ -0,0 +1,143 @@
|
||||||
|
From a46cf223ff79cc8120590bb642df26d99a05431a Mon Sep 17 00:00:00 2001
|
||||||
|
From: Alexey Sheplyakov <asheplyakov@basealt.ru>
|
||||||
|
Date: Tue, 25 Jan 2022 17:55:26 +0400
|
||||||
|
Subject: [PATCH 607/634] hwmon: bt1-pvt: adjusted probing for Baikal-M SoC
|
||||||
|
|
||||||
|
PVT registers and clocks are managed by the firmware (ARM-TF) and
|
||||||
|
can't be accessed by Linux directly. Therefore skip enabling
|
||||||
|
(disabling) clocks and ioremapping registers on Baikal-M.
|
||||||
|
|
||||||
|
Also a sensor is identified by special `pvt_id' instead of registers
|
||||||
|
base address. pvt_id is initialized from the device tree.
|
||||||
|
|
||||||
|
Signed-off-by: Alexey Sheplyakov <asheplyakov@basealt.ru>
|
||||||
|
---
|
||||||
|
drivers/hwmon/Kconfig | 7 ++++---
|
||||||
|
drivers/hwmon/bt1-pvt.c | 30 ++++++++++++++++++++++++++----
|
||||||
|
2 files changed, 30 insertions(+), 7 deletions(-)
|
||||||
|
|
||||||
|
diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig
|
||||||
|
index ccdaeafed..030f023de 100644
|
||||||
|
--- a/drivers/hwmon/Kconfig
|
||||||
|
+++ b/drivers/hwmon/Kconfig
|
||||||
|
@@ -412,10 +412,11 @@ config SENSORS_ATXP1
|
||||||
|
will be called atxp1.
|
||||||
|
|
||||||
|
config SENSORS_BT1_PVT
|
||||||
|
- tristate "Baikal-T1 Process, Voltage, Temperature sensor driver"
|
||||||
|
- depends on MIPS_BAIKAL_T1 || COMPILE_TEST
|
||||||
|
+ tristate "Baikal-T1/M Process, Voltage, Temperature sensor driver"
|
||||||
|
+ depends on MIPS_BAIKAL_T1 || ARCH_BAIKAL || COMPILE_TEST
|
||||||
|
+ default m if MIPS_BAIKAL_T1 || ARCH_BAIKAL
|
||||||
|
help
|
||||||
|
- If you say yes here you get support for Baikal-T1 PVT sensor
|
||||||
|
+ If you say yes here you get support for Baikal-M or Baikal-T1 PVT sensor
|
||||||
|
embedded into the SoC.
|
||||||
|
|
||||||
|
This driver can also be built as a module. If so, the module will be
|
||||||
|
diff --git a/drivers/hwmon/bt1-pvt.c b/drivers/hwmon/bt1-pvt.c
|
||||||
|
index 2f2a222bd..e2237774c 100644
|
||||||
|
--- a/drivers/hwmon/bt1-pvt.c
|
||||||
|
+++ b/drivers/hwmon/bt1-pvt.c
|
||||||
|
@@ -950,6 +950,7 @@ static int pvt_request_regs(struct pvt_hwmon *pvt)
|
||||||
|
{
|
||||||
|
struct platform_device *pdev = to_platform_device(pvt->dev);
|
||||||
|
struct resource *res;
|
||||||
|
+ int err = 0;
|
||||||
|
|
||||||
|
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||||
|
if (!res) {
|
||||||
|
@@ -957,27 +958,38 @@ static int pvt_request_regs(struct pvt_hwmon *pvt)
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
+#ifdef BT1_PVT_DIRECT_REG_ACCESS
|
||||||
|
pvt->regs = devm_ioremap_resource(pvt->dev, res);
|
||||||
|
if (IS_ERR(pvt->regs))
|
||||||
|
return PTR_ERR(pvt->regs);
|
||||||
|
+#else
|
||||||
|
+ err = of_property_read_u32(pvt->dev->of_node, "pvt_id", &(pvt->pvt_id));
|
||||||
|
+ if (err) {
|
||||||
|
+ dev_err(pvt->dev, "couldn't find pvt_id\n");
|
||||||
|
+ return err;
|
||||||
|
+ }
|
||||||
|
+#endif
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
+#ifdef BT1_PVT_DIRECT_REG_ACCESS
|
||||||
|
static void pvt_disable_clks(void *data)
|
||||||
|
{
|
||||||
|
struct pvt_hwmon *pvt = data;
|
||||||
|
|
||||||
|
clk_bulk_disable_unprepare(PVT_CLOCK_NUM, pvt->clks);
|
||||||
|
}
|
||||||
|
+#endif
|
||||||
|
|
||||||
|
static int pvt_request_clks(struct pvt_hwmon *pvt)
|
||||||
|
{
|
||||||
|
- int ret;
|
||||||
|
+ int ret = 0;
|
||||||
|
|
||||||
|
pvt->clks[PVT_CLOCK_APB].id = "pclk";
|
||||||
|
pvt->clks[PVT_CLOCK_REF].id = "ref";
|
||||||
|
|
||||||
|
+#ifdef BT1_PVT_DIRECT_REG_ACCESS
|
||||||
|
ret = devm_clk_bulk_get(pvt->dev, PVT_CLOCK_NUM, pvt->clks);
|
||||||
|
if (ret) {
|
||||||
|
dev_err(pvt->dev, "Couldn't get PVT clocks descriptors\n");
|
||||||
|
@@ -995,8 +1007,11 @@ static int pvt_request_clks(struct pvt_hwmon *pvt)
|
||||||
|
dev_err(pvt->dev, "Can't add PVT clocks disable action\n");
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
-
|
||||||
|
- return 0;
|
||||||
|
+#else
|
||||||
|
+ pvt->clks[PVT_CLOCK_APB].clk = NULL;
|
||||||
|
+ pvt->clks[PVT_CLOCK_REF].clk = NULL;
|
||||||
|
+#endif
|
||||||
|
+ return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int pvt_check_pwr(struct pvt_hwmon *pvt)
|
||||||
|
@@ -1036,14 +1051,17 @@ static int pvt_check_pwr(struct pvt_hwmon *pvt)
|
||||||
|
|
||||||
|
static int pvt_init_iface(struct pvt_hwmon *pvt)
|
||||||
|
{
|
||||||
|
- unsigned long rate;
|
||||||
|
u32 trim, temp;
|
||||||
|
|
||||||
|
+#ifdef BT1_PVT_DIRECT_REG_ACCESS
|
||||||
|
+ unsigned long rate;
|
||||||
|
+
|
||||||
|
rate = clk_get_rate(pvt->clks[PVT_CLOCK_REF].clk);
|
||||||
|
if (!rate) {
|
||||||
|
dev_err(pvt->dev, "Invalid reference clock rate\n");
|
||||||
|
return -ENODEV;
|
||||||
|
}
|
||||||
|
+#endif
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Make sure all interrupts and controller are disabled so not to
|
||||||
|
@@ -1072,6 +1090,7 @@ static int pvt_init_iface(struct pvt_hwmon *pvt)
|
||||||
|
* polled. In that case the formulae will look a bit different:
|
||||||
|
* Ttotal = 5 * (N / Fclk + Tmin)
|
||||||
|
*/
|
||||||
|
+#if defined(BT1_PVT_DIRECT_REG_ACCESS)
|
||||||
|
#if defined(CONFIG_SENSORS_BT1_PVT_ALARMS)
|
||||||
|
pvt->timeout = ktime_set(PVT_SENSORS_NUM * PVT_TOUT_DEF, 0);
|
||||||
|
pvt->timeout = ktime_divns(pvt->timeout, rate);
|
||||||
|
@@ -1081,6 +1100,9 @@ static int pvt_init_iface(struct pvt_hwmon *pvt)
|
||||||
|
pvt->timeout = ktime_divns(pvt->timeout, rate);
|
||||||
|
pvt->timeout = ktime_add_ns(pvt->timeout, PVT_TOUT_MIN);
|
||||||
|
#endif
|
||||||
|
+#else
|
||||||
|
+ pvt->timeout = ktime_set(0, PVT_TOUT_MIN * PVT_SENSORS_NUM);
|
||||||
|
+#endif
|
||||||
|
|
||||||
|
trim = PVT_TRIM_DEF;
|
||||||
|
if (!of_property_read_u32(pvt->dev->of_node,
|
||||||
|
--
|
||||||
|
2.33.2
|
||||||
|
|
|
@ -1,161 +0,0 @@
|
||||||
Subject: [PATCH 608/625] Baikal-M: USB driver
|
|
||||||
|
|
||||||
Update for 5.15.105
|
|
||||||
|
|
||||||
diff --git a/drivers/usb/dwc3/Kconfig b/drivers/usb/dwc3/Kconfig
|
|
||||||
--- a/drivers/usb/dwc3/Kconfig
|
|
||||||
+++ b/drivers/usb/dwc3/Kconfig
|
|
||||||
@@ -158,4 +158,13 @@
|
|
||||||
This driver handles both ZynqMP and Versal SoC operations.
|
|
||||||
Say 'Y' or 'M' if you have one such device.
|
|
||||||
|
|
||||||
+config USB_DWC3_BAIKAL
|
|
||||||
+ tristate "Baikal Electronics Platforms"
|
|
||||||
+ depends on OF
|
|
||||||
+ default USB_DWC3
|
|
||||||
+ help
|
|
||||||
+ Baikal Electronics SoCs with one DesignWare Core USB3 IP
|
|
||||||
+ inside.
|
|
||||||
+ Say 'Y' or 'M' if you have one such device.
|
|
||||||
+
|
|
||||||
endif
|
|
||||||
diff --git a/drivers/usb/dwc3/Makefile b/drivers/usb/dwc3/Makefile
|
|
||||||
--- a/drivers/usb/dwc3/Makefile
|
|
||||||
+++ b/drivers/usb/dwc3/Makefile
|
|
||||||
@@ -53,3 +53,4 @@
|
|
||||||
obj-$(CONFIG_USB_DWC3_QCOM) += dwc3-qcom.o
|
|
||||||
obj-$(CONFIG_USB_DWC3_IMX8MP) += dwc3-imx8mp.o
|
|
||||||
obj-$(CONFIG_USB_DWC3_XILINX) += dwc3-xilinx.o
|
|
||||||
+obj-$(CONFIG_USB_DWC3_BAIKAL) += dwc3-baikal.o
|
|
||||||
diff --git a/drivers/usb/dwc3/dwc3-baikal.c b/drivers/usb/dwc3/dwc3-baikal.c
|
|
||||||
new file mode 100644
|
|
||||||
index 000000000000..2426dc49bd79
|
|
||||||
--- /dev/null
|
|
||||||
+++ b/drivers/usb/dwc3/dwc3-baikal.c
|
|
||||||
@@ -0,0 +1,126 @@
|
|
||||||
+/**
|
|
||||||
+ * dwc3-baikal.c - Baikal Electronics SoCs Specific Glue layer
|
|
||||||
+ *
|
|
||||||
+ * Copyright (C) 2015 Baikal Electronics JSC - http://www.baikalelectronics.ru
|
|
||||||
+ *
|
|
||||||
+ * Author: Dmitry Dunaev <dmitry.dunaev@baikalelectronics.ru>
|
|
||||||
+ *
|
|
||||||
+ * This program is free software: you can redistribute it and/or modify
|
|
||||||
+ * it under the terms of the GNU General Public License version 2 of
|
|
||||||
+ * the License as published by the Free Software Foundation.
|
|
||||||
+ *
|
|
||||||
+ * This program is distributed in the hope that it will be useful,
|
|
||||||
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
+ * GNU General Public License for more details.
|
|
||||||
+ */
|
|
||||||
+
|
|
||||||
+#include <linux/clk.h>
|
|
||||||
+#include <linux/module.h>
|
|
||||||
+#include <linux/kernel.h>
|
|
||||||
+#include <linux/interrupt.h>
|
|
||||||
+#include <linux/platform_device.h>
|
|
||||||
+#include <linux/dma-mapping.h>
|
|
||||||
+#include <linux/usb/usb_phy_generic.h>
|
|
||||||
+#include <linux/io.h>
|
|
||||||
+#include <linux/of_platform.h>
|
|
||||||
+
|
|
||||||
+struct dwc3_baikal {
|
|
||||||
+ struct device *dev;
|
|
||||||
+ struct clk *clk;
|
|
||||||
+};
|
|
||||||
+
|
|
||||||
+static int be_dwc3_probe(struct platform_device *pdev)
|
|
||||||
+{
|
|
||||||
+ struct device *dev = &pdev->dev;
|
|
||||||
+ struct device_node *node = pdev->dev.of_node;
|
|
||||||
+ struct dwc3_baikal *dwc;
|
|
||||||
+ int ret;
|
|
||||||
+
|
|
||||||
+ dwc = devm_kzalloc(dev, sizeof(*dwc), GFP_KERNEL);
|
|
||||||
+ if (!dwc)
|
|
||||||
+ return -ENOMEM;
|
|
||||||
+
|
|
||||||
+ ret = dma_coerce_mask_and_coherent(dev, DMA_BIT_MASK(64));
|
|
||||||
+ if (ret) {
|
|
||||||
+ dev_err(dev, "DMA mask error %d\n", ret);
|
|
||||||
+ return ret;
|
|
||||||
+ }
|
|
||||||
+
|
|
||||||
+ platform_set_drvdata(pdev, dwc);
|
|
||||||
+ dwc->dev = dev;
|
|
||||||
+
|
|
||||||
+ dwc->clk = devm_clk_get(dwc->dev, "usb");
|
|
||||||
+ if (IS_ERR(dwc->clk)) {
|
|
||||||
+ dev_err(dev, "no interface clk specified\n");
|
|
||||||
+ return -EINVAL;
|
|
||||||
+ }
|
|
||||||
+
|
|
||||||
+ ret = clk_prepare_enable(dwc->clk);
|
|
||||||
+ if (ret < 0) {
|
|
||||||
+ dev_err(dwc->dev, "unable to enable usb clock\n");
|
|
||||||
+ return ret;
|
|
||||||
+ }
|
|
||||||
+
|
|
||||||
+ if (node) {
|
|
||||||
+ ret = of_platform_populate(node, NULL, NULL, dev);
|
|
||||||
+ if (ret) {
|
|
||||||
+ dev_err(&pdev->dev, "failed to create dwc3 core\n");
|
|
||||||
+ goto __error;
|
|
||||||
+ }
|
|
||||||
+ } else {
|
|
||||||
+ dev_err(dev, "no device node, failed to add dwc3 core\n");
|
|
||||||
+ ret = -ENODEV;
|
|
||||||
+ goto __error;
|
|
||||||
+ }
|
|
||||||
+
|
|
||||||
+ return 0;
|
|
||||||
+
|
|
||||||
+__error:
|
|
||||||
+ clk_disable_unprepare(dwc->clk);
|
|
||||||
+
|
|
||||||
+ return ret;
|
|
||||||
+}
|
|
||||||
+
|
|
||||||
+static int be_dwc3_remove_core(struct device *dev, void *c)
|
|
||||||
+{
|
|
||||||
+ struct platform_device *pdev = to_platform_device(dev);
|
|
||||||
+
|
|
||||||
+ platform_device_unregister(pdev);
|
|
||||||
+
|
|
||||||
+ return 0;
|
|
||||||
+}
|
|
||||||
+
|
|
||||||
+static int be_dwc3_remove(struct platform_device *pdev)
|
|
||||||
+{
|
|
||||||
+ struct dwc3_baikal *dwc = platform_get_drvdata(pdev);
|
|
||||||
+
|
|
||||||
+ device_for_each_child(&pdev->dev, NULL, be_dwc3_remove_core);
|
|
||||||
+ clk_disable_unprepare(dwc->clk);
|
|
||||||
+
|
|
||||||
+ platform_set_drvdata(pdev, NULL);
|
|
||||||
+
|
|
||||||
+ return 0;
|
|
||||||
+}
|
|
||||||
+
|
|
||||||
+static const struct of_device_id be_dwc3_of_match[] = {
|
|
||||||
+ { .compatible = "be,baikal-dwc3", },
|
|
||||||
+ {},
|
|
||||||
+};
|
|
||||||
+MODULE_DEVICE_TABLE(of, be_dwc3_of_match);
|
|
||||||
+
|
|
||||||
+static struct platform_driver be_dwc3_driver = {
|
|
||||||
+ .probe = be_dwc3_probe,
|
|
||||||
+ .remove = be_dwc3_remove,
|
|
||||||
+ .driver = {
|
|
||||||
+ .name = "baikal-dwc3",
|
|
||||||
+ .of_match_table = be_dwc3_of_match,
|
|
||||||
+ },
|
|
||||||
+};
|
|
||||||
+
|
|
||||||
+module_platform_driver(be_dwc3_driver);
|
|
||||||
+
|
|
||||||
+MODULE_ALIAS("platform:baikal-dwc3");
|
|
||||||
+MODULE_AUTHOR("Dmitry Dunaev <dmitry.dunaev@baikalelectronics.ru>");
|
|
||||||
+MODULE_LICENSE("GPL v2");
|
|
||||||
+MODULE_DESCRIPTION("DesignWare USB3 Baikal SoCs Glue Layer");
|
|
25
0608-hwmon-bt1-pvt-added-compatible-baikal-pvt.patch
Normal file
25
0608-hwmon-bt1-pvt-added-compatible-baikal-pvt.patch
Normal file
|
@ -0,0 +1,25 @@
|
||||||
|
From 547e4d17296f6f927389cb6baa84ea67a43e738d Mon Sep 17 00:00:00 2001
|
||||||
|
From: Alexey Sheplyakov <asheplyakov@altlinux.org>
|
||||||
|
Date: Thu, 3 Dec 2020 19:24:00 +0400
|
||||||
|
Subject: [PATCH 608/634] hwmon: bt1-pvt: added compatible baikal,pvt
|
||||||
|
|
||||||
|
So the driver will be loaded on existing Baikal-M based boards.
|
||||||
|
---
|
||||||
|
drivers/hwmon/bt1-pvt.c | 1 +
|
||||||
|
1 file changed, 1 insertion(+)
|
||||||
|
|
||||||
|
diff --git a/drivers/hwmon/bt1-pvt.c b/drivers/hwmon/bt1-pvt.c
|
||||||
|
index e2237774c..a32ef9f93 100644
|
||||||
|
--- a/drivers/hwmon/bt1-pvt.c
|
||||||
|
+++ b/drivers/hwmon/bt1-pvt.c
|
||||||
|
@@ -1240,6 +1240,7 @@ static int pvt_probe(struct platform_device *pdev)
|
||||||
|
|
||||||
|
static const struct of_device_id pvt_of_match[] = {
|
||||||
|
{ .compatible = "baikal,bt1-pvt" },
|
||||||
|
+ { .compatible = "baikal,pvt" },
|
||||||
|
{ }
|
||||||
|
};
|
||||||
|
MODULE_DEVICE_TABLE(of, pvt_of_match);
|
||||||
|
--
|
||||||
|
2.33.2
|
||||||
|
|
|
@ -1,103 +0,0 @@
|
||||||
Subject: [PATCH 609/625] Baikal-M: video unit driver
|
|
||||||
|
|
||||||
Update for 5.15.28
|
|
||||||
|
|
||||||
diff --git a/drivers/gpu/drm/baikal/baikal_vdu_encoder.c b/drivers/gpu/drm/baikal/baikal_vdu_encoder.c
|
|
||||||
new file mode 100644
|
|
||||||
--- /dev/null
|
|
||||||
+++ b/drivers/gpu/drm/baikal/baikal_vdu_encoder.c
|
|
||||||
@@ -0,0 +1,51 @@
|
|
||||||
+/*
|
|
||||||
+ * Copyright (C) 2019-2020 Baikal Electronics JSC
|
|
||||||
+ *
|
|
||||||
+ * Author: Pavel Parkhomenko <Pavel.Parkhomenko@baikalelectronics.ru>
|
|
||||||
+ *
|
|
||||||
+ * Parts of this file were based on sources as follows:
|
|
||||||
+ *
|
|
||||||
+ * Copyright (c) 2006-2008 Intel Corporation
|
|
||||||
+ * Copyright (c) 2007 Dave Airlie <airlied@linux.ie>
|
|
||||||
+ * Copyright (C) 2011 Texas Instruments
|
|
||||||
+ * (C) COPYRIGHT 2012-2013 ARM Limited. All rights reserved.
|
|
||||||
+ *
|
|
||||||
+ * This program is free software and is provided to you under the terms of the
|
|
||||||
+ * GNU General Public License version 2 as published by the Free Software
|
|
||||||
+ * Foundation, and any use by you of this program is subject to the terms of
|
|
||||||
+ * such GNU licence.
|
|
||||||
+ *
|
|
||||||
+ */
|
|
||||||
+
|
|
||||||
+/**
|
|
||||||
+ * baikal_vdu_encoder.c
|
|
||||||
+ * Implementation of the encoder functions for Baikal Electronics BE-M1000 VDU driver
|
|
||||||
+ */
|
|
||||||
+#include <linux/version.h>
|
|
||||||
+#include <linux/shmem_fs.h>
|
|
||||||
+#include <linux/dma-buf.h>
|
|
||||||
+
|
|
||||||
+#include <drm/drm_crtc_helper.h>
|
|
||||||
+
|
|
||||||
+#include "baikal_vdu_drm.h"
|
|
||||||
+
|
|
||||||
+const struct drm_encoder_funcs baikal_vdu_encoder_funcs = {
|
|
||||||
+ .destroy = drm_encoder_cleanup,
|
|
||||||
+};
|
|
||||||
+
|
|
||||||
+int baikal_vdu_encoder_init(struct drm_device *dev)
|
|
||||||
+{
|
|
||||||
+ struct baikal_vdu_private *priv = dev->dev_private;
|
|
||||||
+ struct drm_encoder *encoder = &priv->encoder;
|
|
||||||
+ int ret;
|
|
||||||
+
|
|
||||||
+ ret = drm_encoder_init(dev, encoder, &baikal_vdu_encoder_funcs,
|
|
||||||
+ DRM_MODE_ENCODER_NONE, NULL);
|
|
||||||
+ if (ret)
|
|
||||||
+ return ret;
|
|
||||||
+
|
|
||||||
+ encoder->crtc = &priv->crtc;
|
|
||||||
+ encoder->possible_crtcs = BIT(drm_crtc_index(encoder->crtc));
|
|
||||||
+
|
|
||||||
+ return 0;
|
|
||||||
+}
|
|
||||||
diff --git a/drivers/gpu/drm/baikal/baikal_vdu_gem.c b/drivers/gpu/drm/baikal/baikal_vdu_gem.c
|
|
||||||
new file mode 100644
|
|
||||||
index 000000000000..b07566caf12c
|
|
||||||
--- /dev/null
|
|
||||||
+++ b/drivers/gpu/drm/baikal/baikal_vdu_gem.c
|
|
||||||
@@ -0,0 +1,37 @@
|
|
||||||
+/*
|
|
||||||
+ * Copyright (C) 2019-2020 Baikal Electronics JSC
|
|
||||||
+ *
|
|
||||||
+ * Author: Pavel Parkhomenko <Pavel.Parkhomenko@baikalelectronics.ru>
|
|
||||||
+ *
|
|
||||||
+ * Parts of this file were based on sources as follows:
|
|
||||||
+ *
|
|
||||||
+ * Copyright (c) 2006-2008 Intel Corporation
|
|
||||||
+ * Copyright (c) 2007 Dave Airlie <airlied@linux.ie>
|
|
||||||
+ * Copyright (C) 2011 Texas Instruments
|
|
||||||
+ * (C) COPYRIGHT 2012-2013 ARM Limited. All rights reserved.
|
|
||||||
+ *
|
|
||||||
+ * This program is free software and is provided to you under the terms of the
|
|
||||||
+ * GNU General Public License version 2 as published by the Free Software
|
|
||||||
+ * Foundation, and any use by you of this program is subject to the terms of
|
|
||||||
+ * such GNU licence.
|
|
||||||
+ *
|
|
||||||
+ */
|
|
||||||
+
|
|
||||||
+/**
|
|
||||||
+ * baikal_vdu_gem.c
|
|
||||||
+ * Implementation of the GEM functions for Baikal Electronics BE-M1000 VDU driver
|
|
||||||
+ */
|
|
||||||
+#include <linux/version.h>
|
|
||||||
+#include <linux/shmem_fs.h>
|
|
||||||
+#include <linux/dma-buf.h>
|
|
||||||
+#include <drm/drm_crtc_helper.h>
|
|
||||||
+#include <drm/drm_gem_cma_helper.h>
|
|
||||||
+#include "baikal_vdu_drm.h"
|
|
||||||
+
|
|
||||||
+int baikal_vdu_dumb_create(struct drm_file *file_priv,
|
|
||||||
+ struct drm_device *dev, struct drm_mode_create_dumb *args)
|
|
||||||
+{
|
|
||||||
+ args->pitch = DIV_ROUND_UP(args->width * args->bpp, 8);
|
|
||||||
+
|
|
||||||
+ return drm_gem_cma_dumb_create_internal(file_priv, dev, args);
|
|
||||||
+}
|
|
404
0609-clk-added-Baikal-M-clock-management-unit-driver.patch
Normal file
404
0609-clk-added-Baikal-M-clock-management-unit-driver.patch
Normal file
|
@ -0,0 +1,404 @@
|
||||||
|
From 08694e3dc351d1082df951f544942fe6c4f3d78d Mon Sep 17 00:00:00 2001
|
||||||
|
From: Alexey Sheplyakov <asheplyakov@basealt.ru>
|
||||||
|
Date: Tue, 25 Jan 2022 17:57:05 +0400
|
||||||
|
Subject: [PATCH 609/634] clk: added Baikal-M clock management unit driver
|
||||||
|
|
||||||
|
On Baikal-M SoC clock management unit (CMU) is controled by
|
||||||
|
the firmware (ARM-TF), since the registers of CMU are accessible
|
||||||
|
only to the secure world. This drivers is a shim which calls into
|
||||||
|
the firmware.
|
||||||
|
|
||||||
|
Signed-off-by: Alexey Sheplyakov <asheplyakov@basealt.ru>
|
||||||
|
Signed-off-by: Ekaterina Skachko <ekaterina.skachko@baikalelectronics.ru>
|
||||||
|
---
|
||||||
|
drivers/clk/Makefile | 1 +
|
||||||
|
drivers/clk/baikal-m/Makefile | 1 +
|
||||||
|
drivers/clk/baikal-m/clk-baikal.c | 355 ++++++++++++++++++++++++++++++
|
||||||
|
3 files changed, 357 insertions(+)
|
||||||
|
create mode 100644 drivers/clk/baikal-m/Makefile
|
||||||
|
create mode 100644 drivers/clk/baikal-m/clk-baikal.c
|
||||||
|
|
||||||
|
diff --git a/drivers/clk/Makefile b/drivers/clk/Makefile
|
||||||
|
index e42312121..2764f731d 100644
|
||||||
|
--- a/drivers/clk/Makefile
|
||||||
|
+++ b/drivers/clk/Makefile
|
||||||
|
@@ -75,6 +75,7 @@ obj-y += analogbits/
|
||||||
|
obj-$(CONFIG_COMMON_CLK_AT91) += at91/
|
||||||
|
obj-$(CONFIG_ARCH_ARTPEC) += axis/
|
||||||
|
obj-$(CONFIG_ARC_PLAT_AXS10X) += axs10x/
|
||||||
|
+obj-$(CONFIG_ARCH_BAIKAL) += baikal-m/
|
||||||
|
obj-$(CONFIG_CLK_BAIKAL_T1) += baikal-t1/
|
||||||
|
obj-y += bcm/
|
||||||
|
obj-$(CONFIG_ARCH_BERLIN) += berlin/
|
||||||
|
diff --git a/drivers/clk/baikal-m/Makefile b/drivers/clk/baikal-m/Makefile
|
||||||
|
new file mode 100644
|
||||||
|
index 000000000..56aa4de40
|
||||||
|
--- /dev/null
|
||||||
|
+++ b/drivers/clk/baikal-m/Makefile
|
||||||
|
@@ -0,0 +1 @@
|
||||||
|
+obj-y += clk-baikal.o
|
||||||
|
\ No newline at end of file
|
||||||
|
diff --git a/drivers/clk/baikal-m/clk-baikal.c b/drivers/clk/baikal-m/clk-baikal.c
|
||||||
|
new file mode 100644
|
||||||
|
index 000000000..a52cf8da7
|
||||||
|
--- /dev/null
|
||||||
|
+++ b/drivers/clk/baikal-m/clk-baikal.c
|
||||||
|
@@ -0,0 +1,355 @@
|
||||||
|
+// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
+/*
|
||||||
|
+ * clk-baikal.c - Baikal-M clock driver.
|
||||||
|
+ *
|
||||||
|
+ * Copyright (C) 2015,2016,2020,2021 Baikal Electronics JSC
|
||||||
|
+ * Authors:
|
||||||
|
+ * Ekaterina Skachko <ekaterina.skachko@baikalelectronics.ru>
|
||||||
|
+ * Alexey Sheplyakov <asheplyakov@basealt.ru>
|
||||||
|
+ */
|
||||||
|
+
|
||||||
|
+#include <linux/arm-smccc.h>
|
||||||
|
+#include <linux/clk-provider.h>
|
||||||
|
+#include <linux/clk.h>
|
||||||
|
+#include <linux/clkdev.h>
|
||||||
|
+#include <linux/delay.h>
|
||||||
|
+#include <linux/io.h>
|
||||||
|
+#include <linux/of.h>
|
||||||
|
+#include <linux/of_address.h>
|
||||||
|
+#include <linux/platform_device.h>
|
||||||
|
+
|
||||||
|
+#define CMU_PLL_SET_RATE 0
|
||||||
|
+#define CMU_PLL_GET_RATE 1
|
||||||
|
+#define CMU_PLL_ENABLE 2
|
||||||
|
+#define CMU_PLL_DISABLE 3
|
||||||
|
+#define CMU_PLL_ROUND_RATE 4
|
||||||
|
+#define CMU_PLL_IS_ENABLED 5
|
||||||
|
+#define CMU_CLK_CH_SET_RATE 6
|
||||||
|
+#define CMU_CLK_CH_GET_RATE 7
|
||||||
|
+#define CMU_CLK_CH_ENABLE 8
|
||||||
|
+#define CMU_CLK_CH_DISABLE 9
|
||||||
|
+#define CMU_CLK_CH_ROUND_RATE 10
|
||||||
|
+#define CMU_CLK_CH_IS_ENABLED 11
|
||||||
|
+
|
||||||
|
+struct baikal_clk_cmu {
|
||||||
|
+ struct clk_hw hw;
|
||||||
|
+ uint32_t cmu_id;
|
||||||
|
+ unsigned int parent;
|
||||||
|
+ const char *name;
|
||||||
|
+ uint32_t is_clk_ch;
|
||||||
|
+};
|
||||||
|
+
|
||||||
|
+#define to_baikal_cmu(_hw) container_of(_hw, struct baikal_clk_cmu, hw)
|
||||||
|
+
|
||||||
|
+/* Pointer to the place on handling SMC CMU calls in monitor */
|
||||||
|
+#define BAIKAL_SMC_LCRU_ID 0x82000000
|
||||||
|
+
|
||||||
|
+static int baikal_clk_enable(struct clk_hw *hw)
|
||||||
|
+{
|
||||||
|
+ struct arm_smccc_res res;
|
||||||
|
+ struct baikal_clk_cmu *pclk = to_baikal_cmu(hw);
|
||||||
|
+ uint32_t cmd;
|
||||||
|
+
|
||||||
|
+ if (pclk->is_clk_ch) {
|
||||||
|
+ cmd = CMU_CLK_CH_ENABLE;
|
||||||
|
+ } else {
|
||||||
|
+ cmd = CMU_PLL_ENABLE;
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ arm_smccc_smc(BAIKAL_SMC_LCRU_ID, pclk->cmu_id, cmd, 0,
|
||||||
|
+ pclk->parent, 0, 0, 0, &res);
|
||||||
|
+
|
||||||
|
+ pr_debug("%s(%s, %s@0x%x): %s\n",
|
||||||
|
+ __func__,
|
||||||
|
+ pclk->name,
|
||||||
|
+ pclk->is_clk_ch ? "clkch" : "pll",
|
||||||
|
+ pclk->cmu_id,
|
||||||
|
+ res.a0 ? "error" : "ok");
|
||||||
|
+
|
||||||
|
+ return res.a0;
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
+static void baikal_clk_disable(struct clk_hw *hw)
|
||||||
|
+{
|
||||||
|
+ struct arm_smccc_res res;
|
||||||
|
+ struct baikal_clk_cmu *pclk = to_baikal_cmu(hw);
|
||||||
|
+ uint32_t cmd;
|
||||||
|
+
|
||||||
|
+ if (pclk->is_clk_ch) {
|
||||||
|
+ cmd = CMU_CLK_CH_DISABLE;
|
||||||
|
+ } else {
|
||||||
|
+ cmd = CMU_PLL_DISABLE;
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ arm_smccc_smc(BAIKAL_SMC_LCRU_ID, pclk->cmu_id, cmd, 0,
|
||||||
|
+ pclk->parent, 0, 0, 0, &res);
|
||||||
|
+
|
||||||
|
+ pr_debug("%s(%s, %s@0x%x): %s\n",
|
||||||
|
+ __func__,
|
||||||
|
+ pclk->name,
|
||||||
|
+ pclk->is_clk_ch ? "clkch" : "pll",
|
||||||
|
+ pclk->cmu_id,
|
||||||
|
+ res.a0 ? "error" : "ok");
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
+static int baikal_clk_is_enabled(struct clk_hw *hw)
|
||||||
|
+{
|
||||||
|
+ struct arm_smccc_res res;
|
||||||
|
+ struct baikal_clk_cmu *pclk = to_baikal_cmu(hw);
|
||||||
|
+ uint32_t cmd;
|
||||||
|
+
|
||||||
|
+ if (pclk->is_clk_ch) {
|
||||||
|
+ cmd = CMU_CLK_CH_IS_ENABLED;
|
||||||
|
+ } else {
|
||||||
|
+ cmd = CMU_PLL_IS_ENABLED;
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ arm_smccc_smc(BAIKAL_SMC_LCRU_ID, pclk->cmu_id, cmd, 0,
|
||||||
|
+ pclk->parent, 0, 0, 0, &res);
|
||||||
|
+
|
||||||
|
+ pr_debug("%s(%s, %s@0x%x): %s\n",
|
||||||
|
+ __func__,
|
||||||
|
+ pclk->name,
|
||||||
|
+ pclk->is_clk_ch ? "clkch" : "pll",
|
||||||
|
+ pclk->cmu_id,
|
||||||
|
+ res.a0 ? "true" : "false");
|
||||||
|
+
|
||||||
|
+ return res.a0;
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
+static unsigned long baikal_clk_recalc_rate(struct clk_hw *hw,
|
||||||
|
+ unsigned long parent_rate)
|
||||||
|
+{
|
||||||
|
+ struct arm_smccc_res res;
|
||||||
|
+ struct baikal_clk_cmu *pclk = to_baikal_cmu(hw);
|
||||||
|
+ uint32_t cmd;
|
||||||
|
+ unsigned long parent;
|
||||||
|
+
|
||||||
|
+ if (pclk->is_clk_ch) {
|
||||||
|
+ cmd = CMU_CLK_CH_GET_RATE;
|
||||||
|
+ parent = pclk->parent;
|
||||||
|
+ } else {
|
||||||
|
+ cmd = CMU_PLL_GET_RATE;
|
||||||
|
+ parent= parent_rate;
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ arm_smccc_smc(BAIKAL_SMC_LCRU_ID, pclk->cmu_id, cmd, 0,
|
||||||
|
+ parent, 0, 0, 0, &res);
|
||||||
|
+
|
||||||
|
+ pr_debug("%s(%s, %s@0x%x): %ld Hz\n",
|
||||||
|
+ __func__,
|
||||||
|
+ pclk->name,
|
||||||
|
+ pclk->is_clk_ch ? "clkch" : "pll",
|
||||||
|
+ pclk->cmu_id,
|
||||||
|
+ res.a0);
|
||||||
|
+
|
||||||
|
+ return res.a0;
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
+static int baikal_clk_set_rate(struct clk_hw *hw, unsigned long rate,
|
||||||
|
+ unsigned long parent_rate)
|
||||||
|
+{
|
||||||
|
+ struct arm_smccc_res res;
|
||||||
|
+ struct baikal_clk_cmu *pclk = to_baikal_cmu(hw);
|
||||||
|
+ uint32_t cmd;
|
||||||
|
+ unsigned long parent;
|
||||||
|
+
|
||||||
|
+ if (pclk->is_clk_ch) {
|
||||||
|
+ cmd = CMU_CLK_CH_SET_RATE;
|
||||||
|
+ parent = pclk->parent;
|
||||||
|
+ } else {
|
||||||
|
+ cmd = CMU_PLL_SET_RATE;
|
||||||
|
+ parent = parent_rate;
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ arm_smccc_smc(BAIKAL_SMC_LCRU_ID, pclk->cmu_id, cmd, rate,
|
||||||
|
+ parent, 0, 0, 0, &res);
|
||||||
|
+
|
||||||
|
+ pr_debug("%s(%s, %s@0x%x, %ld Hz): %s\n",
|
||||||
|
+ __func__,
|
||||||
|
+ pclk->name,
|
||||||
|
+ pclk->is_clk_ch ? "clkch" : "pll",
|
||||||
|
+ pclk->cmu_id,
|
||||||
|
+ rate,
|
||||||
|
+ res.a0 ? "error" : "ok");
|
||||||
|
+
|
||||||
|
+ return res.a0;
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
+static long baikal_clk_round_rate(struct clk_hw *hw, unsigned long rate,
|
||||||
|
+ unsigned long *prate)
|
||||||
|
+{
|
||||||
|
+ struct arm_smccc_res res;
|
||||||
|
+ struct baikal_clk_cmu *pclk = to_baikal_cmu(hw);
|
||||||
|
+ unsigned long parent;
|
||||||
|
+ uint32_t cmd;
|
||||||
|
+
|
||||||
|
+ if (pclk->is_clk_ch) {
|
||||||
|
+ cmd = CMU_CLK_CH_ROUND_RATE;
|
||||||
|
+ parent = pclk->parent;
|
||||||
|
+ } else {
|
||||||
|
+ cmd = CMU_PLL_ROUND_RATE;
|
||||||
|
+ parent = *prate;
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ arm_smccc_smc(BAIKAL_SMC_LCRU_ID, pclk->cmu_id, cmd, rate,
|
||||||
|
+ parent, 0, 0, 0, &res);
|
||||||
|
+
|
||||||
|
+ pr_debug("%s(%s, %s@0x%x): %ld Hz\n",
|
||||||
|
+ __func__,
|
||||||
|
+ pclk->name,
|
||||||
|
+ pclk->is_clk_ch ? "clkch" : "pll",
|
||||||
|
+ pclk->cmu_id,
|
||||||
|
+ res.a0);
|
||||||
|
+
|
||||||
|
+ return res.a0;
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
+static const struct clk_ops be_clk_pll_ops = {
|
||||||
|
+ .enable = baikal_clk_enable,
|
||||||
|
+ .disable = baikal_clk_disable,
|
||||||
|
+ .is_enabled = baikal_clk_is_enabled,
|
||||||
|
+ .recalc_rate = baikal_clk_recalc_rate,
|
||||||
|
+ .set_rate = baikal_clk_set_rate,
|
||||||
|
+ .round_rate = baikal_clk_round_rate
|
||||||
|
+};
|
||||||
|
+
|
||||||
|
+static int __init baikal_clk_probe(struct device_node *node)
|
||||||
|
+{
|
||||||
|
+ struct clk_init_data init;
|
||||||
|
+ struct clk_init_data *init_ch;
|
||||||
|
+ struct baikal_clk_cmu *cmu;
|
||||||
|
+ struct baikal_clk_cmu **cmu_ch;
|
||||||
|
+
|
||||||
|
+ struct clk *clk;
|
||||||
|
+ struct clk_onecell_data *clk_ch;
|
||||||
|
+
|
||||||
|
+ int number, i = 0;
|
||||||
|
+ u32 rc, index;
|
||||||
|
+ struct property *prop;
|
||||||
|
+ const __be32 *p;
|
||||||
|
+ const char *clk_ch_name;
|
||||||
|
+ const char *parent_name;
|
||||||
|
+
|
||||||
|
+ cmu = kzalloc(sizeof(struct baikal_clk_cmu), GFP_KERNEL);
|
||||||
|
+ if (!cmu) {
|
||||||
|
+ pr_err("%s: could not allocate CMU clk\n", __func__);
|
||||||
|
+ return -ENOMEM;
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ of_property_read_string(node, "clock-output-names", &cmu->name);
|
||||||
|
+ of_property_read_u32(node, "clock-frequency", &cmu->parent);
|
||||||
|
+ of_property_read_u32(node, "cmu-id", &cmu->cmu_id);
|
||||||
|
+
|
||||||
|
+ parent_name = of_clk_get_parent_name(node, 0);
|
||||||
|
+
|
||||||
|
+ /* Setup clock init structure */
|
||||||
|
+ init.parent_names = &parent_name;
|
||||||
|
+ init.num_parents = 1;
|
||||||
|
+ init.name = cmu->name;
|
||||||
|
+ init.ops = &be_clk_pll_ops;
|
||||||
|
+ init.flags = CLK_IGNORE_UNUSED;
|
||||||
|
+
|
||||||
|
+ cmu->hw.init = &init;
|
||||||
|
+ cmu->is_clk_ch = 0;
|
||||||
|
+
|
||||||
|
+ /* Register the clock */
|
||||||
|
+ pr_debug("%s: add %s, parent %s\n", __func__, cmu->name, parent_name ? parent_name : "null");
|
||||||
|
+ clk = clk_register(NULL, &cmu->hw);
|
||||||
|
+
|
||||||
|
+ if (IS_ERR(clk)) {
|
||||||
|
+ pr_err("%s: could not register clk %s\n", __func__, cmu->name);
|
||||||
|
+ return -ENOMEM;
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ /* Register the clock for lookup */
|
||||||
|
+ rc = clk_register_clkdev(clk, cmu->name, NULL);
|
||||||
|
+ if (rc != 0) {
|
||||||
|
+ pr_err("%s: could not register lookup clk %s\n",
|
||||||
|
+ __func__, cmu->name);
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ clk_prepare_enable(clk);
|
||||||
|
+
|
||||||
|
+ number = of_property_count_u32_elems(node, "clock-indices");
|
||||||
|
+
|
||||||
|
+ if (number > 0) {
|
||||||
|
+ clk_ch = kmalloc(sizeof(struct clk_onecell_data), GFP_KERNEL);
|
||||||
|
+ if (!clk_ch) {
|
||||||
|
+ pr_err("%s: could not allocate CMU clk channel\n", __func__);
|
||||||
|
+ return -ENOMEM;
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ /* Get the last index to find out max number of children*/
|
||||||
|
+ of_property_for_each_u32(node, "clock-indices", prop, p, index) {
|
||||||
|
+ ;
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ clk_ch->clks = kcalloc(index + 1, sizeof(struct clk *), GFP_KERNEL);
|
||||||
|
+ clk_ch->clk_num = index + 1;
|
||||||
|
+ cmu_ch = kcalloc((index + 1), sizeof(struct baikal_clk_cmu *), GFP_KERNEL);
|
||||||
|
+ if (!cmu_ch) {
|
||||||
|
+ kfree(clk_ch);
|
||||||
|
+ return -ENOMEM;
|
||||||
|
+ }
|
||||||
|
+ init_ch = kcalloc((number + 1), sizeof(struct clk_init_data), GFP_KERNEL);
|
||||||
|
+ if (!init_ch) {
|
||||||
|
+ pr_err("%s: could not allocate CMU init structure \n", __func__);
|
||||||
|
+ kfree(cmu_ch);
|
||||||
|
+ kfree(clk_ch);
|
||||||
|
+ return -ENOMEM;
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ of_property_for_each_u32(node, "clock-indices", prop, p, index) {
|
||||||
|
+ of_property_read_string_index(node, "clock-names",
|
||||||
|
+ i, &clk_ch_name);
|
||||||
|
+ pr_info("%s: clkch <%s>, index %d, i %d\n", __func__, clk_ch_name, index, i);
|
||||||
|
+ init_ch[i].parent_names = &cmu->name;
|
||||||
|
+ init_ch[i].num_parents = 1;
|
||||||
|
+ init_ch[i].name = clk_ch_name;
|
||||||
|
+ init_ch[i].ops = &be_clk_pll_ops;
|
||||||
|
+ init_ch[i].flags = CLK_IGNORE_UNUSED;
|
||||||
|
+
|
||||||
|
+ cmu_ch[index] = kzalloc(sizeof(struct baikal_clk_cmu), GFP_KERNEL);
|
||||||
|
+ if (!cmu_ch[index]) {
|
||||||
|
+ pr_err("%s: could not allocate baikal_clk_cmu structure\n", __func__);
|
||||||
|
+ return -ENOMEM;
|
||||||
|
+ }
|
||||||
|
+ cmu_ch[index]->name = clk_ch_name;
|
||||||
|
+ cmu_ch[index]->cmu_id = index;
|
||||||
|
+ cmu_ch[index]->parent = cmu->cmu_id;
|
||||||
|
+ cmu_ch[index]->is_clk_ch = 1;
|
||||||
|
+ cmu_ch[index]->hw.init = &init_ch[i];
|
||||||
|
+ clk_ch->clks[index] = clk_register(NULL, &cmu_ch[index]->hw);
|
||||||
|
+
|
||||||
|
+ if (IS_ERR(clk_ch->clks[index])) {
|
||||||
|
+ pr_err("%s: could not register clk %s\n", __func__, clk_ch_name);
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ rc = clk_register_clkdev(clk_ch->clks[index], clk_ch_name, NULL);
|
||||||
|
+ if (rc != 0) {
|
||||||
|
+ pr_err("%s: could not register lookup clk %s\n",
|
||||||
|
+ __func__, clk_ch_name);
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ clk_prepare_enable(clk_ch->clks[index]);
|
||||||
|
+ i++;
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ return of_clk_add_provider(node, of_clk_src_onecell_get, clk_ch);
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ return of_clk_add_provider(node, of_clk_src_simple_get, clk);
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
+static void __init baikal_clk_init(struct device_node *np)
|
||||||
|
+{
|
||||||
|
+ int err;
|
||||||
|
+ err = baikal_clk_probe(np);
|
||||||
|
+ if (err) {
|
||||||
|
+ panic("%s: failed to probe clock %pOF: %d\n", __func__, np, err);
|
||||||
|
+ } else {
|
||||||
|
+ pr_info("%s: successfully probed %pOF\n", __func__, np);
|
||||||
|
+ }
|
||||||
|
+}
|
||||||
|
+CLK_OF_DECLARE_DRIVER(baikal_cmu, "baikal,cmu", baikal_clk_init);
|
||||||
|
--
|
||||||
|
2.33.2
|
||||||
|
|
29
0610-cpufreq-dt-don-t-load-on-Baikal-M-SoC.patch
Normal file
29
0610-cpufreq-dt-don-t-load-on-Baikal-M-SoC.patch
Normal file
|
@ -0,0 +1,29 @@
|
||||||
|
From af1139b62b63af63b2d3518284cbe6423b525e6b Mon Sep 17 00:00:00 2001
|
||||||
|
From: Alexey Sheplyakov <asheplyakov@basealt.ru>
|
||||||
|
Date: Tue, 25 Jan 2022 17:57:20 +0400
|
||||||
|
Subject: [PATCH 610/634] cpufreq-dt: don't load on Baikal-M SoC
|
||||||
|
|
||||||
|
Otherwise the system freezes in few minutes after the boot.
|
||||||
|
Proper cpufreq driver for Baikal-M will be implemented later on.
|
||||||
|
|
||||||
|
Signed-off-by: Alexey Sheplyakov <asheplyakov@basealt.ru>
|
||||||
|
---
|
||||||
|
drivers/cpufreq/cpufreq-dt-platdev.c | 2 ++
|
||||||
|
1 file changed, 2 insertions(+)
|
||||||
|
|
||||||
|
diff --git a/drivers/cpufreq/cpufreq-dt-platdev.c b/drivers/cpufreq/cpufreq-dt-platdev.c
|
||||||
|
index ca1d103ec..29b14e8d7 100644
|
||||||
|
--- a/drivers/cpufreq/cpufreq-dt-platdev.c
|
||||||
|
+++ b/drivers/cpufreq/cpufreq-dt-platdev.c
|
||||||
|
@@ -105,6 +105,8 @@ static const struct of_device_id blocklist[] __initconst = {
|
||||||
|
|
||||||
|
{ .compatible = "arm,vexpress", },
|
||||||
|
|
||||||
|
+ { .compatible = "baikal,baikal-m", },
|
||||||
|
+
|
||||||
|
{ .compatible = "calxeda,highbank", },
|
||||||
|
{ .compatible = "calxeda,ecx-2000", },
|
||||||
|
|
||||||
|
--
|
||||||
|
2.33.2
|
||||||
|
|
|
@ -0,0 +1,27 @@
|
||||||
|
From 23147d24b1b455958e034eaac105e7d38b0ad010 Mon Sep 17 00:00:00 2001
|
||||||
|
From: Alexey Sheplyakov <asheplyakov@basealt.ru>
|
||||||
|
Date: Tue, 23 Nov 2021 19:55:35 +0400
|
||||||
|
Subject: [PATCH 611/634] usb: dwc3: of-simple: added compatible string for
|
||||||
|
Baikal-M SoC
|
||||||
|
|
||||||
|
Signed-off-by: Alexey Sheplyakov <asheplyakov@basealt.ru>
|
||||||
|
---
|
||||||
|
drivers/usb/dwc3/dwc3-of-simple.c | 2 ++
|
||||||
|
1 file changed, 2 insertions(+)
|
||||||
|
|
||||||
|
diff --git a/drivers/usb/dwc3/dwc3-of-simple.c b/drivers/usb/dwc3/dwc3-of-simple.c
|
||||||
|
index 71fd620c5..63d6e4b05 100644
|
||||||
|
--- a/drivers/usb/dwc3/dwc3-of-simple.c
|
||||||
|
+++ b/drivers/usb/dwc3/dwc3-of-simple.c
|
||||||
|
@@ -177,6 +177,8 @@ static const struct of_device_id of_dwc3_simple_match[] = {
|
||||||
|
{ .compatible = "allwinner,sun50i-h6-dwc3" },
|
||||||
|
{ .compatible = "hisilicon,hi3670-dwc3" },
|
||||||
|
{ .compatible = "intel,keembay-dwc3" },
|
||||||
|
+ { .compatible = "baikal,baikal-dwc3" },
|
||||||
|
+ { .compatible = "be,baikal-dwc3" },
|
||||||
|
{ /* Sentinel */ }
|
||||||
|
};
|
||||||
|
MODULE_DEVICE_TABLE(of, of_dwc3_simple_match);
|
||||||
|
--
|
||||||
|
2.33.2
|
||||||
|
|
41
0612-arm64-Enable-armv8-based-Baikal-M-SoC-support.patch
Normal file
41
0612-arm64-Enable-armv8-based-Baikal-M-SoC-support.patch
Normal file
|
@ -0,0 +1,41 @@
|
||||||
|
From 72be8c51625033aa7d30de3d3a6ceadd2111fd12 Mon Sep 17 00:00:00 2001
|
||||||
|
From: Alexey Sheplyakov <asheplyakov@basealt.ru>
|
||||||
|
Date: Tue, 25 Jan 2022 17:58:48 +0400
|
||||||
|
Subject: [PATCH 612/634] arm64: Enable armv8 based Baikal-M SoC support
|
||||||
|
|
||||||
|
This patch adds Kconfig entries necessary to enable
|
||||||
|
Baikal Electronics' Baikal-M (also known as BE-M1000) SoC
|
||||||
|
support. Also it enables pinctrl, timers and GPIO drivers.
|
||||||
|
|
||||||
|
Signed-off-by: Alexey Sheplyakov <asheplyakov@basealt.ru>
|
||||||
|
---
|
||||||
|
arch/arm64/Kconfig.platforms | 13 +++++++++++++
|
||||||
|
1 file changed, 13 insertions(+)
|
||||||
|
|
||||||
|
diff --git a/arch/arm64/Kconfig.platforms b/arch/arm64/Kconfig.platforms
|
||||||
|
index b0ce18d4c..225b0f6dd 100644
|
||||||
|
--- a/arch/arm64/Kconfig.platforms
|
||||||
|
+++ b/arch/arm64/Kconfig.platforms
|
||||||
|
@@ -34,6 +34,19 @@ config ARCH_APPLE
|
||||||
|
This enables support for Apple's in-house ARM SoC family, starting
|
||||||
|
with the Apple M1.
|
||||||
|
|
||||||
|
+config ARCH_BAIKAL
|
||||||
|
+ bool "Baikal Electronics Baikal-M SoC Family"
|
||||||
|
+ select GPIOLIB
|
||||||
|
+ select PINCTRL
|
||||||
|
+ select OF_GPIO
|
||||||
|
+ select GPIO_SYSFS
|
||||||
|
+ select GPIO_DWAPB
|
||||||
|
+ select GPIO_GENERIC
|
||||||
|
+ select DW_APB_TIMER
|
||||||
|
+ select DW_APB_TIMER_OF
|
||||||
|
+ help
|
||||||
|
+ This enables support for Baikal Electronics Baikal-M SoC Family
|
||||||
|
+
|
||||||
|
config ARCH_BCM2835
|
||||||
|
bool "Broadcom BCM2835 family"
|
||||||
|
select TIMER_OF
|
||||||
|
--
|
||||||
|
2.33.2
|
||||||
|
|
545
0613-drm-bridge-New-bridge-driver-stdp4028.patch
Normal file
545
0613-drm-bridge-New-bridge-driver-stdp4028.patch
Normal file
|
@ -0,0 +1,545 @@
|
||||||
|
From b9614bda923d424af282753f5919ddd686230941 Mon Sep 17 00:00:00 2001
|
||||||
|
From: "Vadim V. Vlasov" <vvv19xx@gmail.com>
|
||||||
|
Date: Thu, 1 Oct 2020 20:27:58 +0300
|
||||||
|
Subject: [PATCH 613/634] drm/bridge: New bridge driver - stdp4028
|
||||||
|
|
||||||
|
MegaChips stdp4028 is LVDS to DP bridge.
|
||||||
|
|
||||||
|
The driver can work in interrupt or poll mode.
|
||||||
|
Videomodes may be specified in the devicetree or read from EDID.
|
||||||
|
|
||||||
|
Signed-off-by: Vadim V. Vlasov <vvv19xx@gmail.com>
|
||||||
|
Signed-off-by: Alexey Sheplyakov <asheplyakov@basealt.ru>
|
||||||
|
---
|
||||||
|
drivers/gpu/drm/bridge/Kconfig | 8 +
|
||||||
|
drivers/gpu/drm/bridge/Makefile | 1 +
|
||||||
|
drivers/gpu/drm/bridge/stdp4028.c | 486 ++++++++++++++++++++++++++++++
|
||||||
|
3 files changed, 495 insertions(+)
|
||||||
|
create mode 100644 drivers/gpu/drm/bridge/stdp4028.c
|
||||||
|
|
||||||
|
diff --git a/drivers/gpu/drm/bridge/Kconfig b/drivers/gpu/drm/bridge/Kconfig
|
||||||
|
index 68ec45abc..5f1778c36 100644
|
||||||
|
--- a/drivers/gpu/drm/bridge/Kconfig
|
||||||
|
+++ b/drivers/gpu/drm/bridge/Kconfig
|
||||||
|
@@ -315,6 +315,14 @@ config DRM_TI_TPD12S015
|
||||||
|
Texas Instruments TPD12S015 HDMI level shifter and ESD protection
|
||||||
|
driver.
|
||||||
|
|
||||||
|
+config DRM_STDP4028
|
||||||
|
+ tristate "MegaChips STDP4028 DP bridge"
|
||||||
|
+ depends on OF
|
||||||
|
+ select DRM_KMS_HELPER
|
||||||
|
+ select DRM_PANEL
|
||||||
|
+ help
|
||||||
|
+ MegaChips STDP4028 DP bridge driver
|
||||||
|
+
|
||||||
|
source "drivers/gpu/drm/bridge/analogix/Kconfig"
|
||||||
|
|
||||||
|
source "drivers/gpu/drm/bridge/adv7511/Kconfig"
|
||||||
|
diff --git a/drivers/gpu/drm/bridge/Makefile b/drivers/gpu/drm/bridge/Makefile
|
||||||
|
index f2c73683c..9faf98509 100644
|
||||||
|
--- a/drivers/gpu/drm/bridge/Makefile
|
||||||
|
+++ b/drivers/gpu/drm/bridge/Makefile
|
||||||
|
@@ -29,6 +29,7 @@ obj-$(CONFIG_DRM_TI_TFP410) += ti-tfp410.o
|
||||||
|
obj-$(CONFIG_DRM_TI_TPD12S015) += ti-tpd12s015.o
|
||||||
|
obj-$(CONFIG_DRM_NWL_MIPI_DSI) += nwl-dsi.o
|
||||||
|
obj-$(CONFIG_DRM_ITE_IT66121) += ite-it66121.o
|
||||||
|
+obj-$(CONFIG_DRM_STDP4028) += stdp4028.o
|
||||||
|
|
||||||
|
obj-y += analogix/
|
||||||
|
obj-y += cadence/
|
||||||
|
diff --git a/drivers/gpu/drm/bridge/stdp4028.c b/drivers/gpu/drm/bridge/stdp4028.c
|
||||||
|
new file mode 100644
|
||||||
|
index 000000000..12b3ff31b
|
||||||
|
--- /dev/null
|
||||||
|
+++ b/drivers/gpu/drm/bridge/stdp4028.c
|
||||||
|
@@ -0,0 +1,486 @@
|
||||||
|
+// SPDX-License-Identifier: GPL-2.0
|
||||||
|
+/*
|
||||||
|
+ * Driver for MegaChips STDP4028 LVDS to DP display bridge
|
||||||
|
+ */
|
||||||
|
+
|
||||||
|
+#include <linux/delay.h>
|
||||||
|
+#include <linux/gpio.h>
|
||||||
|
+#include <linux/i2c.h>
|
||||||
|
+#include <linux/module.h>
|
||||||
|
+#include <linux/of.h>
|
||||||
|
+#include <linux/of_gpio.h>
|
||||||
|
+#include <drm/drm_atomic.h>
|
||||||
|
+#include <drm/drm_atomic_helper.h>
|
||||||
|
+#include <drm/drm_bridge.h>
|
||||||
|
+#include <drm/drm_crtc_helper.h>
|
||||||
|
+#include <drm/drm_edid.h>
|
||||||
|
+#include <drm/drm_print.h>
|
||||||
|
+#include <drm/drm_probe_helper.h>
|
||||||
|
+
|
||||||
|
+/* video modes */
|
||||||
|
+#include <video/display_timing.h>
|
||||||
|
+#include <video/of_display_timing.h>
|
||||||
|
+#include <video/videomode.h>
|
||||||
|
+
|
||||||
|
+#define MAX_PIXEL_CLOCK 330000
|
||||||
|
+
|
||||||
|
+#define EDID_EXT_BLOCK_CNT 0x7E
|
||||||
|
+
|
||||||
|
+#define STDP4028_PRODUCT_ID_REG 0x00
|
||||||
|
+#define STDP4028_IRQ_OUT_CONF_REG 0x02
|
||||||
|
+#define STDP4028_IRQ_STS_REG 0x03
|
||||||
|
+#define STDP4028_I2C_CTRL_REG 0x08
|
||||||
|
+#define STDP4028_LVDS_FMT_REG 0x0B
|
||||||
|
+#define STDP4028_LVDS_CTRL0_REG 0x0C
|
||||||
|
+#define STDP4028_DPTX_IRQ_EN_REG 0x3C
|
||||||
|
+#define STDP4028_DPTX_IRQ_STS_REG 0x3D
|
||||||
|
+#define STDP4028_DPTX_STS_REG 0x3E
|
||||||
|
+
|
||||||
|
+#define STDP4028_DPTX_DP_IRQ_EN 0x10
|
||||||
|
+
|
||||||
|
+#define STDP4028_DPTX_HOTPLUG_IRQ_EN 0x04
|
||||||
|
+#define STDP4028_DPTX_LINK_CH_IRQ_EN 0x20
|
||||||
|
+#define STDP4028_DPTX_IRQ_CONFIG \
|
||||||
|
+ (STDP4028_DPTX_LINK_CH_IRQ_EN | STDP4028_DPTX_HOTPLUG_IRQ_EN)
|
||||||
|
+
|
||||||
|
+#define STDP4028_DPTX_HOTPLUG_STS 0x02
|
||||||
|
+#define STDP4028_DPTX_LINK_STS 0x10
|
||||||
|
+#define STDP4028_CON_STATE_CONNECTED \
|
||||||
|
+ (STDP4028_DPTX_HOTPLUG_STS | STDP4028_DPTX_LINK_STS)
|
||||||
|
+
|
||||||
|
+#define STDP4028_DPTX_HOTPLUG_CH_STS 0x04
|
||||||
|
+#define STDP4028_DPTX_LINK_CH_STS 0x20
|
||||||
|
+#define STDP4028_DPTX_IRQ_CLEAR \
|
||||||
|
+ (STDP4028_DPTX_LINK_CH_STS | STDP4028_DPTX_HOTPLUG_CH_STS)
|
||||||
|
+
|
||||||
|
+struct stdp4028 {
|
||||||
|
+ struct drm_connector connector;
|
||||||
|
+ struct drm_bridge bridge;
|
||||||
|
+ struct i2c_client *stdp4028_i2c;
|
||||||
|
+ struct i2c_client *edid_i2c;
|
||||||
|
+ struct edid *edid;
|
||||||
|
+ struct gpio_desc *reset_gpio;
|
||||||
|
+ struct mutex lock;
|
||||||
|
+ int channels;
|
||||||
|
+ int chan_cfg;
|
||||||
|
+};
|
||||||
|
+
|
||||||
|
+static inline int stdp_read(struct stdp4028 *stdp, int reg)
|
||||||
|
+{
|
||||||
|
+ int ret;
|
||||||
|
+
|
||||||
|
+ ret = i2c_smbus_read_word_data(stdp->stdp4028_i2c, reg);
|
||||||
|
+ if (ret < 0)
|
||||||
|
+ return ret;
|
||||||
|
+ return be16_to_cpu(ret);
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
+static inline int stdp_write(struct stdp4028 *stdp, int reg, u16 val)
|
||||||
|
+{
|
||||||
|
+ val = cpu_to_be16(val);
|
||||||
|
+ return i2c_smbus_write_word_data(stdp->stdp4028_i2c, reg, val);
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
+#define bridge_to_stdp4028(bridge) \
|
||||||
|
+ container_of(bridge, struct stdp4028, bridge)
|
||||||
|
+
|
||||||
|
+#define connector_to_stdp4028(connector) \
|
||||||
|
+ container_of(connector, struct stdp4028, connector)
|
||||||
|
+
|
||||||
|
+static u8 *stdp4028_get_edid(struct i2c_client *client)
|
||||||
|
+{
|
||||||
|
+ struct i2c_adapter *adapter = client->adapter;
|
||||||
|
+ unsigned char start = 0x00;
|
||||||
|
+ unsigned int total_size;
|
||||||
|
+ u8 *block = kmalloc(EDID_LENGTH, GFP_KERNEL);
|
||||||
|
+
|
||||||
|
+ struct i2c_msg msgs[] = {
|
||||||
|
+ {
|
||||||
|
+ .addr = client->addr,
|
||||||
|
+ .flags = 0,
|
||||||
|
+ .len = 1,
|
||||||
|
+ .buf = &start,
|
||||||
|
+ }, {
|
||||||
|
+ .addr = client->addr,
|
||||||
|
+ .flags = I2C_M_RD,
|
||||||
|
+ .len = EDID_LENGTH,
|
||||||
|
+ .buf = block,
|
||||||
|
+ }
|
||||||
|
+ };
|
||||||
|
+
|
||||||
|
+ if (!block)
|
||||||
|
+ return NULL;
|
||||||
|
+
|
||||||
|
+ if (i2c_transfer(adapter, msgs, 2) != 2) {
|
||||||
|
+ DRM_ERROR("Unable to read EDID.\n");
|
||||||
|
+ goto err;
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ if (!drm_edid_block_valid(block, 0, false, NULL)) {
|
||||||
|
+ DRM_ERROR("Invalid EDID data\n");
|
||||||
|
+ goto err;
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ total_size = (block[EDID_EXT_BLOCK_CNT] + 1) * EDID_LENGTH;
|
||||||
|
+ if (total_size > EDID_LENGTH) {
|
||||||
|
+ kfree(block);
|
||||||
|
+ block = kmalloc(total_size, GFP_KERNEL);
|
||||||
|
+ if (!block)
|
||||||
|
+ return NULL;
|
||||||
|
+
|
||||||
|
+ /* Yes, read the entire buffer, and do not skip the first
|
||||||
|
+ * EDID_LENGTH bytes.
|
||||||
|
+ */
|
||||||
|
+ start = 0x00;
|
||||||
|
+ msgs[1].len = total_size;
|
||||||
|
+ msgs[1].buf = block;
|
||||||
|
+
|
||||||
|
+ if (i2c_transfer(adapter, msgs, 2) != 2) {
|
||||||
|
+ DRM_ERROR("Unable to read EDID extension blocks.\n");
|
||||||
|
+ goto err;
|
||||||
|
+ }
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ return block;
|
||||||
|
+
|
||||||
|
+err:
|
||||||
|
+ kfree(block);
|
||||||
|
+ return NULL;
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
+/*
|
||||||
|
+ * Get videomode specified in the devicetree.
|
||||||
|
+ * Return 1 on success, 0 otherwise.
|
||||||
|
+ */
|
||||||
|
+static int stdp4028_get_of_modes(struct drm_connector *connector)
|
||||||
|
+{
|
||||||
|
+ struct stdp4028 *stdp = connector_to_stdp4028(connector);
|
||||||
|
+ struct i2c_client *client = stdp->stdp4028_i2c;
|
||||||
|
+ struct drm_display_mode *mode;
|
||||||
|
+ struct device_node *np = client->dev.of_node;
|
||||||
|
+ struct display_timing timing;
|
||||||
|
+ struct videomode video_mode;
|
||||||
|
+ int ret;
|
||||||
|
+
|
||||||
|
+ ret = of_get_display_timing(np, "panel-timing", &timing);
|
||||||
|
+ if (ret < 0)
|
||||||
|
+ return 0;
|
||||||
|
+
|
||||||
|
+ videomode_from_timing(&timing, &video_mode);
|
||||||
|
+
|
||||||
|
+ mode = drm_mode_create(connector->dev);
|
||||||
|
+ if (!mode)
|
||||||
|
+ return 0;
|
||||||
|
+ drm_display_mode_from_videomode(&video_mode, mode);
|
||||||
|
+ mode->type |= DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED;
|
||||||
|
+
|
||||||
|
+ drm_mode_probed_add(connector, mode);
|
||||||
|
+ return 1;
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
+static int stdp4028_get_modes(struct drm_connector *connector)
|
||||||
|
+{
|
||||||
|
+ struct stdp4028 *stdp;
|
||||||
|
+ struct i2c_client *client;
|
||||||
|
+ int num_modes = 0;
|
||||||
|
+
|
||||||
|
+ stdp = connector_to_stdp4028(connector);
|
||||||
|
+ client = stdp->edid_i2c;
|
||||||
|
+
|
||||||
|
+ mutex_lock(&stdp->lock);
|
||||||
|
+
|
||||||
|
+ num_modes = stdp4028_get_of_modes(connector);
|
||||||
|
+ if (num_modes > 0) {
|
||||||
|
+ mutex_unlock(&stdp->lock);
|
||||||
|
+ return num_modes;
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ kfree(stdp->edid);
|
||||||
|
+ stdp->edid = (struct edid *) stdp4028_get_edid(client);
|
||||||
|
+
|
||||||
|
+ if (stdp->edid) {
|
||||||
|
+ drm_connector_update_edid_property(connector, stdp->edid);
|
||||||
|
+ num_modes = drm_add_edid_modes(connector, stdp->edid);
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ mutex_unlock(&stdp->lock);
|
||||||
|
+
|
||||||
|
+ return num_modes;
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
+
|
||||||
|
+static enum drm_mode_status stdp4028_mode_valid(
|
||||||
|
+ struct drm_connector *connector, struct drm_display_mode *mode)
|
||||||
|
+{
|
||||||
|
+ if (mode->clock > MAX_PIXEL_CLOCK) {
|
||||||
|
+ DRM_INFO("The pixel clock for the mode %s is too high, and not supported.",
|
||||||
|
+ mode->name);
|
||||||
|
+ return MODE_CLOCK_HIGH;
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ return MODE_OK;
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
+static const struct
|
||||||
|
+drm_connector_helper_funcs stdp4028_connector_helper_funcs = {
|
||||||
|
+ .get_modes = stdp4028_get_modes,
|
||||||
|
+ .mode_valid = stdp4028_mode_valid,
|
||||||
|
+};
|
||||||
|
+
|
||||||
|
+static enum drm_connector_status stdp4028_detect(
|
||||||
|
+ struct drm_connector *connector, bool force)
|
||||||
|
+{
|
||||||
|
+ struct stdp4028 *stdp = connector_to_stdp4028(connector);
|
||||||
|
+ s32 link_state;
|
||||||
|
+
|
||||||
|
+ link_state = stdp_read(stdp, STDP4028_DPTX_STS_REG);
|
||||||
|
+
|
||||||
|
+ if (link_state == STDP4028_CON_STATE_CONNECTED)
|
||||||
|
+ return connector_status_connected;
|
||||||
|
+
|
||||||
|
+ if (link_state == 0)
|
||||||
|
+ return connector_status_disconnected;
|
||||||
|
+
|
||||||
|
+ return connector_status_unknown;
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
+static const struct drm_connector_funcs stdp4028_connector_funcs = {
|
||||||
|
+ .fill_modes = drm_helper_probe_single_connector_modes,
|
||||||
|
+ .detect = stdp4028_detect,
|
||||||
|
+ .destroy = drm_connector_cleanup,
|
||||||
|
+ .reset = drm_atomic_helper_connector_reset,
|
||||||
|
+ .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
|
||||||
|
+ .atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
|
||||||
|
+};
|
||||||
|
+
|
||||||
|
+static irqreturn_t stdp4028_irq_handler(int irq, void *dev_id)
|
||||||
|
+{
|
||||||
|
+ struct stdp4028 *stdp = dev_id;
|
||||||
|
+
|
||||||
|
+ mutex_lock(&stdp->lock);
|
||||||
|
+
|
||||||
|
+ stdp_write(stdp, STDP4028_DPTX_IRQ_STS_REG, STDP4028_DPTX_IRQ_CLEAR);
|
||||||
|
+
|
||||||
|
+ mutex_unlock(&stdp->lock);
|
||||||
|
+
|
||||||
|
+ if (stdp->connector.dev)
|
||||||
|
+ drm_kms_helper_hotplug_event(stdp->connector.dev);
|
||||||
|
+
|
||||||
|
+ return IRQ_HANDLED;
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
+static int stdp4028_create_connector(struct drm_bridge *bridge)
|
||||||
|
+{
|
||||||
|
+ int ret;
|
||||||
|
+ struct stdp4028 *stdp
|
||||||
|
+ = bridge_to_stdp4028(bridge);
|
||||||
|
+ struct drm_connector *connector = &stdp->connector;
|
||||||
|
+
|
||||||
|
+ if (!bridge->encoder) {
|
||||||
|
+ DRM_ERROR("Parent encoder object not found");
|
||||||
|
+ return -ENODEV;
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ if (stdp->stdp4028_i2c->irq)
|
||||||
|
+ connector->polled = DRM_CONNECTOR_POLL_HPD;
|
||||||
|
+ else
|
||||||
|
+ connector->polled = DRM_CONNECTOR_POLL_CONNECT |
|
||||||
|
+ DRM_CONNECTOR_POLL_DISCONNECT;
|
||||||
|
+
|
||||||
|
+ drm_connector_helper_add(connector, &stdp4028_connector_helper_funcs);
|
||||||
|
+
|
||||||
|
+ ret = drm_connector_init(bridge->dev, connector,
|
||||||
|
+ &stdp4028_connector_funcs,
|
||||||
|
+ DRM_MODE_CONNECTOR_DisplayPort);
|
||||||
|
+ if (ret) {
|
||||||
|
+ DRM_ERROR("Failed to initialize connector with drm\n");
|
||||||
|
+ return ret;
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ return drm_connector_attach_encoder(connector, bridge->encoder);
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
+static int stdp4028_attach(struct drm_bridge *bridge,
|
||||||
|
+ enum drm_bridge_attach_flags flags)
|
||||||
|
+{
|
||||||
|
+ struct stdp4028 *stdp
|
||||||
|
+ = bridge_to_stdp4028(bridge);
|
||||||
|
+
|
||||||
|
+ /* Configures the bridge to re-enable interrupts after each ack. */
|
||||||
|
+ stdp_write(stdp, STDP4028_IRQ_OUT_CONF_REG, STDP4028_DPTX_DP_IRQ_EN);
|
||||||
|
+
|
||||||
|
+ /* Enable interrupts */
|
||||||
|
+ stdp_write(stdp, STDP4028_DPTX_IRQ_EN_REG, STDP4028_DPTX_IRQ_CONFIG);
|
||||||
|
+
|
||||||
|
+ if (flags & DRM_BRIDGE_ATTACH_NO_CONNECTOR)
|
||||||
|
+ return 0;
|
||||||
|
+
|
||||||
|
+ return stdp4028_create_connector(bridge);
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
+static const struct drm_bridge_funcs stdp4028_funcs = {
|
||||||
|
+ .attach = stdp4028_attach,
|
||||||
|
+};
|
||||||
|
+
|
||||||
|
+static int stdp4028_probe(struct i2c_client *stdp4028_i2c,
|
||||||
|
+ const struct i2c_device_id *id)
|
||||||
|
+{
|
||||||
|
+ struct device *dev = &stdp4028_i2c->dev;
|
||||||
|
+ struct stdp4028 *bridge;
|
||||||
|
+ int ret;
|
||||||
|
+ u32 edid_i2c_reg, channels, chan_cfg;
|
||||||
|
+ enum of_gpio_flags flags;
|
||||||
|
+ int reset_gpio, i;
|
||||||
|
+ int reg;
|
||||||
|
+
|
||||||
|
+ bridge = devm_kzalloc(dev, sizeof(*bridge), GFP_KERNEL);
|
||||||
|
+ if (!bridge)
|
||||||
|
+ return -ENOMEM;
|
||||||
|
+
|
||||||
|
+ mutex_init(&bridge->lock);
|
||||||
|
+
|
||||||
|
+ bridge->stdp4028_i2c = stdp4028_i2c;
|
||||||
|
+ bridge->bridge.driver_private = bridge;
|
||||||
|
+ i2c_set_clientdata(stdp4028_i2c, bridge);
|
||||||
|
+
|
||||||
|
+ reset_gpio = of_get_named_gpio_flags(dev->of_node,
|
||||||
|
+ "reset-gpios", 0, &flags);
|
||||||
|
+ if (gpio_is_valid(reset_gpio)) {
|
||||||
|
+ unsigned long gpio_flags;
|
||||||
|
+
|
||||||
|
+ /*
|
||||||
|
+ * We will set GPIO to "inactive" state instead of toggling
|
||||||
|
+ * reset. If the chip is not ready we will return -EPROBE_DEFER
|
||||||
|
+ * and retry later.
|
||||||
|
+ */
|
||||||
|
+ if (!(flags & OF_GPIO_ACTIVE_LOW))
|
||||||
|
+ gpio_flags = GPIOF_ACTIVE_LOW | GPIOF_OUT_INIT_LOW;
|
||||||
|
+ else
|
||||||
|
+ gpio_flags = GPIOF_OUT_INIT_HIGH;
|
||||||
|
+ ret = devm_gpio_request_one(dev, reset_gpio, gpio_flags,
|
||||||
|
+ "stdp-reset");
|
||||||
|
+ if (ret) {
|
||||||
|
+ dev_err(dev, "request GPIO failed (%d)\n", ret);
|
||||||
|
+ /* continue anyway */
|
||||||
|
+ } else {
|
||||||
|
+ bridge->reset_gpio = gpio_to_desc(reset_gpio);
|
||||||
|
+ udelay(100);
|
||||||
|
+ }
|
||||||
|
+ } else if (reset_gpio == -EPROBE_DEFER) {
|
||||||
|
+ return -EPROBE_DEFER;
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ ret = of_property_read_u32(dev->of_node, "channels", &channels);
|
||||||
|
+ if (ret)
|
||||||
|
+ channels = 1;
|
||||||
|
+ bridge->channels = channels;
|
||||||
|
+
|
||||||
|
+ ret = of_property_read_u32(dev->of_node, "chan-cfg", &chan_cfg);
|
||||||
|
+ if (ret)
|
||||||
|
+ chan_cfg = 0;
|
||||||
|
+ bridge->chan_cfg = chan_cfg;
|
||||||
|
+
|
||||||
|
+ ret = of_property_read_u32(dev->of_node, "edid-reg", &edid_i2c_reg);
|
||||||
|
+ if (ret) {
|
||||||
|
+ dev_warn(dev, "edid-reg not specified, assuming 0x50...\n");
|
||||||
|
+ edid_i2c_reg = 0x50;
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ /* Configure stdp registers */
|
||||||
|
+ reg = stdp_read(bridge, STDP4028_PRODUCT_ID_REG);
|
||||||
|
+ if (reg < 0) {
|
||||||
|
+ dev_err(dev, "Can't read stdp id (%d)\n", reg);
|
||||||
|
+ return -EPROBE_DEFER; /* probably, reset not complete */
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ dev_info(dev, "stdp id word: %x\n", reg);
|
||||||
|
+
|
||||||
|
+ for (i = 0; i < 10; i++) {
|
||||||
|
+ reg = stdp_read(bridge, STDP4028_IRQ_STS_REG);
|
||||||
|
+ if (reg > 0 && reg & 0x800)
|
||||||
|
+ break;
|
||||||
|
+ usleep_range(1000, 1500);
|
||||||
|
+ }
|
||||||
|
+ dev_dbg(dev, "STDP status word %x (i = %d)\n", reg, i);
|
||||||
|
+ stdp_write(bridge, STDP4028_IRQ_STS_REG, 0x800); //clear
|
||||||
|
+ /* enable edid addr */
|
||||||
|
+ stdp_write(bridge, STDP4028_I2C_CTRL_REG, (edid_i2c_reg << 1) | 0x400);
|
||||||
|
+
|
||||||
|
+ if (channels == 4)
|
||||||
|
+ reg = 2;
|
||||||
|
+ else if (channels == 2)
|
||||||
|
+ reg = 1;
|
||||||
|
+ else
|
||||||
|
+ reg = 0;
|
||||||
|
+ reg |= chan_cfg << 2;
|
||||||
|
+ stdp_write(bridge, STDP4028_LVDS_CTRL0_REG, reg);
|
||||||
|
+
|
||||||
|
+ bridge->edid_i2c = i2c_new_dummy_device(stdp4028_i2c->adapter, edid_i2c_reg);
|
||||||
|
+
|
||||||
|
+ if (!bridge->edid_i2c)
|
||||||
|
+ return -ENOMEM;
|
||||||
|
+
|
||||||
|
+ bridge->bridge.funcs = &stdp4028_funcs;
|
||||||
|
+ bridge->bridge.of_node = dev->of_node;
|
||||||
|
+ drm_bridge_add(&bridge->bridge);
|
||||||
|
+
|
||||||
|
+ /* Clear pending interrupts since power up. */
|
||||||
|
+ stdp_write(bridge, STDP4028_DPTX_IRQ_STS_REG, STDP4028_DPTX_IRQ_CLEAR);
|
||||||
|
+
|
||||||
|
+ if (stdp4028_i2c->irq) {
|
||||||
|
+ ret = devm_request_threaded_irq(&stdp4028_i2c->dev,
|
||||||
|
+ stdp4028_i2c->irq, NULL,
|
||||||
|
+ stdp4028_irq_handler,
|
||||||
|
+ IRQF_TRIGGER_HIGH | IRQF_ONESHOT,
|
||||||
|
+ "stdp-lvds-dp", bridge);
|
||||||
|
+ if (ret)
|
||||||
|
+ return ret;
|
||||||
|
+
|
||||||
|
+ /* enable DPTX IRQs */
|
||||||
|
+ stdp_write(bridge, STDP4028_IRQ_OUT_CONF_REG,
|
||||||
|
+ STDP4028_DPTX_DP_IRQ_EN);
|
||||||
|
+ stdp_write(bridge, STDP4028_DPTX_IRQ_EN_REG,
|
||||||
|
+ STDP4028_DPTX_IRQ_CONFIG);
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ return 0;
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
+static int stdp4028_remove(struct i2c_client *stdp4028_i2c)
|
||||||
|
+{
|
||||||
|
+ struct stdp4028 *stdp = i2c_get_clientdata(stdp4028_i2c);
|
||||||
|
+
|
||||||
|
+ drm_bridge_remove(&stdp->bridge);
|
||||||
|
+ i2c_unregister_device(stdp->edid_i2c);
|
||||||
|
+
|
||||||
|
+ kfree(stdp->edid);
|
||||||
|
+
|
||||||
|
+ return 0;
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
+static const struct i2c_device_id stdp4028_i2c_table[] = {
|
||||||
|
+ {"stdp4028-lvds-dp", 0},
|
||||||
|
+ {},
|
||||||
|
+};
|
||||||
|
+MODULE_DEVICE_TABLE(i2c, stdp4028_i2c_table);
|
||||||
|
+
|
||||||
|
+static const struct of_device_id stdp4028_match[] = {
|
||||||
|
+ { .compatible = "megachips,stdp4028-lvds-dp" },
|
||||||
|
+ {},
|
||||||
|
+};
|
||||||
|
+MODULE_DEVICE_TABLE(of, stdp4028_match);
|
||||||
|
+
|
||||||
|
+static struct i2c_driver stdp4028_driver = {
|
||||||
|
+ .id_table = stdp4028_i2c_table,
|
||||||
|
+ .probe = stdp4028_probe,
|
||||||
|
+ .remove = stdp4028_remove,
|
||||||
|
+ .driver = {
|
||||||
|
+ .name = "stdp4028-lvds-dp",
|
||||||
|
+ .of_match_table = stdp4028_match,
|
||||||
|
+ },
|
||||||
|
+};
|
||||||
|
+module_i2c_driver(stdp4028_driver);
|
||||||
|
+
|
||||||
|
+MODULE_AUTHOR("Vadim V. Vlasov <vvv19xx at gmail.com>");
|
||||||
|
+MODULE_DESCRIPTION("STDP4028 LVDS to DP display bridge)");
|
||||||
|
+MODULE_LICENSE("GPL v2");
|
||||||
|
--
|
||||||
|
2.33.2
|
||||||
|
|
1597
0614-drm-added-Baikal-M-SoC-video-display-unit-driver.patch
Normal file
1597
0614-drm-added-Baikal-M-SoC-video-display-unit-driver.patch
Normal file
File diff suppressed because it is too large
Load diff
121
0615-baikal_vdu-et101-display-port-support.patch
Normal file
121
0615-baikal_vdu-et101-display-port-support.patch
Normal file
|
@ -0,0 +1,121 @@
|
||||||
|
From 4dbd82fde8dde03d7921d630ceecd74bdda62692 Mon Sep 17 00:00:00 2001
|
||||||
|
From: Alexey Sheplyakov <asheplyakov@altlinux.org>
|
||||||
|
Date: Thu, 10 Mar 2022 04:33:35 +0400
|
||||||
|
Subject: [PATCH 615/634] baikal_vdu: et101 display port support
|
||||||
|
|
||||||
|
LVDS is connected via a bridge on et101, and current code
|
||||||
|
(baikal_vdu_crtc_helper_mode_set_nofb, baikal_vdu_crtc_helper_enable)
|
||||||
|
assumes that LVDS is connected directly to a panel. To solve
|
||||||
|
the problem
|
||||||
|
|
||||||
|
- check for output type instead of checking for panel
|
||||||
|
- adjust hsync/vsync parameters calculation (in baikal_vdu_crtc_helper_mode_set_nofb)
|
||||||
|
|
||||||
|
Note: due to hardware peculiarities using HDMI and DP is possible
|
||||||
|
only with some constraints:
|
||||||
|
|
||||||
|
- Resolution of both displays should be exactly the same
|
||||||
|
- HDMI viewport must be above or below the display port one
|
||||||
|
---
|
||||||
|
drivers/gpu/drm/baikal/baikal_vdu_crtc.c | 32 +++++++++++++++---------
|
||||||
|
drivers/gpu/drm/baikal/baikal_vdu_drv.c | 5 ++--
|
||||||
|
2 files changed, 23 insertions(+), 14 deletions(-)
|
||||||
|
|
||||||
|
diff --git a/drivers/gpu/drm/baikal/baikal_vdu_crtc.c b/drivers/gpu/drm/baikal/baikal_vdu_crtc.c
|
||||||
|
index 039150c59..e338ff8b3 100644
|
||||||
|
--- a/drivers/gpu/drm/baikal/baikal_vdu_crtc.c
|
||||||
|
+++ b/drivers/gpu/drm/baikal/baikal_vdu_crtc.c
|
||||||
|
@@ -134,14 +134,15 @@ static void baikal_vdu_crtc_helper_mode_set_nofb(struct drm_crtc *crtc)
|
||||||
|
drm_mode_debug_printmodeline(mode);
|
||||||
|
|
||||||
|
ppl = mode->hdisplay / 16;
|
||||||
|
- if (priv->panel) {
|
||||||
|
+ if (priv->type == VDU_TYPE_LVDS) {
|
||||||
|
hsw = mode->hsync_end - mode->hsync_start;
|
||||||
|
hfp = mode->hsync_start - mode->hdisplay - 1;
|
||||||
|
+ hbp = mode->htotal - mode->hsync_end;
|
||||||
|
} else {
|
||||||
|
hsw = mode->hsync_end - mode->hsync_start - 1;
|
||||||
|
hfp = mode->hsync_start - mode->hdisplay;
|
||||||
|
+ hbp = mode->htotal - mode->hsync_end - 1;
|
||||||
|
}
|
||||||
|
- hbp = mode->htotal - mode->hsync_end;
|
||||||
|
|
||||||
|
lpp = mode->vdisplay;
|
||||||
|
vsw = mode->vsync_end - mode->vsync_start;
|
||||||
|
@@ -194,13 +195,13 @@ static void baikal_vdu_crtc_helper_enable(struct drm_crtc *crtc,
|
||||||
|
{
|
||||||
|
struct baikal_vdu_private *priv = crtc->dev->dev_private;
|
||||||
|
struct drm_panel *panel = priv->panel;
|
||||||
|
- struct device_node *panel_node;
|
||||||
|
- const char *data_mapping;
|
||||||
|
+ const char *data_mapping = NULL;
|
||||||
|
u32 cntl, gpio;
|
||||||
|
|
||||||
|
+ DRM_DEV_DEBUG_DRIVER(crtc->dev->dev, "priv = %px\n", priv);
|
||||||
|
DRM_DEV_DEBUG_DRIVER(crtc->dev->dev, "enabling pixel clock\n");
|
||||||
|
clk_prepare_enable(priv->clk);
|
||||||
|
-
|
||||||
|
+ DRM_DEV_DEBUG_DRIVER(crtc->dev->dev, "panel = %px\n", panel);
|
||||||
|
drm_panel_prepare(panel);
|
||||||
|
|
||||||
|
writel(ISCR_VSC_VFP, priv->regs + ISCR);
|
||||||
|
@@ -217,17 +218,24 @@ static void baikal_vdu_crtc_helper_enable(struct drm_crtc *crtc,
|
||||||
|
cntl |= CR1_LCE + CR1_FDW_16_WORDS;
|
||||||
|
|
||||||
|
if (priv->type == VDU_TYPE_LVDS) {
|
||||||
|
- panel_node = panel->dev->of_node;
|
||||||
|
- if (of_property_read_string(panel_node, "data-mapping", &data_mapping)) {
|
||||||
|
+ if (panel) {
|
||||||
|
+ of_property_read_string(panel->dev->of_node,
|
||||||
|
+ "data-mapping", &data_mapping);
|
||||||
|
+ }
|
||||||
|
+ if (!data_mapping) {
|
||||||
|
cntl |= CR1_OPS_LCD18;
|
||||||
|
- } else if (strncmp(data_mapping, "vesa-24", 7))
|
||||||
|
+ dev_dbg(crtc->dev->dev, "data mapping not specified, using jeida-18");
|
||||||
|
+ } else if (!strncmp(data_mapping, "vesa-24", 7)) {
|
||||||
|
cntl |= CR1_OPS_LCD24;
|
||||||
|
- else if (strncmp(data_mapping, "jeida-18", 8))
|
||||||
|
- cntl |= CR1_OPS_LCD18;
|
||||||
|
- else {
|
||||||
|
- dev_warn(crtc->dev->dev, "%s data mapping is not supported, vesa-24 is set\n", data_mapping);
|
||||||
|
+ dev_dbg(crtc->dev->dev, "using vesa-24 mapping\n");
|
||||||
|
+ } else if (!strncmp(data_mapping, "jeida-18", 8)) {
|
||||||
|
+ cntl |= CR1_OPS_LCD18;
|
||||||
|
+ dev_dbg(crtc->dev->dev, "using jeida-18 mapping\n");
|
||||||
|
+ } else {
|
||||||
|
+ dev_warn(crtc->dev->dev, "unsupported data mapping '%s', using vesa-24\n", data_mapping);
|
||||||
|
cntl |= CR1_OPS_LCD24;
|
||||||
|
}
|
||||||
|
+
|
||||||
|
gpio = GPIOR_UHD_ENB;
|
||||||
|
if (priv->ep_count == 4)
|
||||||
|
gpio |= GPIOR_UHD_QUAD_PORT;
|
||||||
|
diff --git a/drivers/gpu/drm/baikal/baikal_vdu_drv.c b/drivers/gpu/drm/baikal/baikal_vdu_drv.c
|
||||||
|
index af0e0ca19..cedf7962d 100644
|
||||||
|
--- a/drivers/gpu/drm/baikal/baikal_vdu_drv.c
|
||||||
|
+++ b/drivers/gpu/drm/baikal/baikal_vdu_drv.c
|
||||||
|
@@ -95,9 +95,9 @@ static int vdu_modeset_init(struct drm_device *dev)
|
||||||
|
mode_config = &dev->mode_config;
|
||||||
|
mode_config->funcs = &mode_config_funcs;
|
||||||
|
mode_config->min_width = 1;
|
||||||
|
- mode_config->max_width = 4096;
|
||||||
|
+ mode_config->max_width = 4095;
|
||||||
|
mode_config->min_height = 1;
|
||||||
|
- mode_config->max_height = 4096;
|
||||||
|
+ mode_config->max_height = 4095;
|
||||||
|
|
||||||
|
ret = baikal_vdu_primary_plane_init(dev);
|
||||||
|
if (ret != 0) {
|
||||||
|
@@ -124,6 +124,7 @@ static int vdu_modeset_init(struct drm_device *dev)
|
||||||
|
goto out_config;
|
||||||
|
}
|
||||||
|
priv->ep_count = ep_count;
|
||||||
|
+ dev_dbg(dev->dev, "panel/bridge has %d endpoints\n", priv->ep_count);
|
||||||
|
|
||||||
|
if (priv->bridge) {
|
||||||
|
struct drm_encoder *encoder = &priv->encoder;
|
||||||
|
--
|
||||||
|
2.33.2
|
||||||
|
|
File diff suppressed because it is too large
Load diff
251
0616-dw-hdmi-ahb-audio-support-Baikal-M-SoC.patch
Normal file
251
0616-dw-hdmi-ahb-audio-support-Baikal-M-SoC.patch
Normal file
|
@ -0,0 +1,251 @@
|
||||||
|
From dee6fe243a31bfc024020876697fd2e31017b099 Mon Sep 17 00:00:00 2001
|
||||||
|
From: Alexey Sheplyakov <asheplyakov@altlinux.org>
|
||||||
|
Date: Wed, 2 Sep 2020 16:00:38 +0400
|
||||||
|
Subject: [PATCH 616/634] dw-hdmi-ahb-audio: support Baikal-M SoC
|
||||||
|
|
||||||
|
---
|
||||||
|
.../drm/bridge/synopsys/dw-hdmi-ahb-audio.c | 106 ++++++++++++------
|
||||||
|
.../gpu/drm/bridge/synopsys/dw-hdmi-audio.h | 1 +
|
||||||
|
drivers/gpu/drm/bridge/synopsys/dw-hdmi.c | 6 +
|
||||||
|
3 files changed, 78 insertions(+), 35 deletions(-)
|
||||||
|
|
||||||
|
diff --git a/drivers/gpu/drm/bridge/synopsys/dw-hdmi-ahb-audio.c b/drivers/gpu/drm/bridge/synopsys/dw-hdmi-ahb-audio.c
|
||||||
|
index 7d2ed0ed2..8ea999aac 100644
|
||||||
|
--- a/drivers/gpu/drm/bridge/synopsys/dw-hdmi-ahb-audio.c
|
||||||
|
+++ b/drivers/gpu/drm/bridge/synopsys/dw-hdmi-ahb-audio.c
|
||||||
|
@@ -132,12 +132,45 @@ struct snd_dw_hdmi {
|
||||||
|
u8 cs[192][8];
|
||||||
|
};
|
||||||
|
|
||||||
|
-static void dw_hdmi_writel(u32 val, void __iomem *ptr)
|
||||||
|
+static inline void dw_hdmi_writeb_relaxed(u8 value, const struct dw_hdmi_audio_data *data, int offset)
|
||||||
|
{
|
||||||
|
- writeb_relaxed(val, ptr);
|
||||||
|
- writeb_relaxed(val >> 8, ptr + 1);
|
||||||
|
- writeb_relaxed(val >> 16, ptr + 2);
|
||||||
|
- writeb_relaxed(val >> 24, ptr + 3);
|
||||||
|
+ void __iomem *base = data->base;
|
||||||
|
+ if (data->reg_offset != 0)
|
||||||
|
+ offset <<= data->reg_offset;
|
||||||
|
+ writeb_relaxed(value, base + offset);
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
+static inline void dw_hdmi_writeb(u8 value, const struct dw_hdmi_audio_data *data, int offset)
|
||||||
|
+{
|
||||||
|
+ void __iomem *base = data->base;
|
||||||
|
+ if (data->reg_offset != 0)
|
||||||
|
+ offset <<= data->reg_offset;
|
||||||
|
+ writeb(value, base + offset);
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
+static inline u8 dw_hdmi_readb(const struct dw_hdmi_audio_data *data, int offset)
|
||||||
|
+{
|
||||||
|
+ void __iomem *base = data->base;
|
||||||
|
+ if (data->reg_offset != 0)
|
||||||
|
+ offset <<= data->reg_offset;
|
||||||
|
+ return readb(base + offset);
|
||||||
|
+
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
+static inline u8 dw_hdmi_readb_relaxed(const struct dw_hdmi_audio_data *data, int offset)
|
||||||
|
+{
|
||||||
|
+ void __iomem *base = data->base;
|
||||||
|
+ if (data->reg_offset != 0)
|
||||||
|
+ offset <<= data->reg_offset;
|
||||||
|
+ return readb_relaxed(base + offset);
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
+static void dw_hdmi_writel(u32 val, const struct dw_hdmi_audio_data *data, int offset)
|
||||||
|
+{
|
||||||
|
+ dw_hdmi_writeb_relaxed(val, data, offset);
|
||||||
|
+ dw_hdmi_writeb_relaxed(val >> 8, data, offset + 1);
|
||||||
|
+ dw_hdmi_writeb_relaxed(val >> 16, data, offset + 2);
|
||||||
|
+ dw_hdmi_writeb_relaxed(val >> 24, data, offset + 3);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
@@ -232,7 +265,6 @@ static void dw_hdmi_create_cs(struct snd_dw_hdmi *dw,
|
||||||
|
|
||||||
|
static void dw_hdmi_start_dma(struct snd_dw_hdmi *dw)
|
||||||
|
{
|
||||||
|
- void __iomem *base = dw->data.base;
|
||||||
|
unsigned offset = dw->buf_offset;
|
||||||
|
unsigned period = dw->buf_period;
|
||||||
|
u32 start, stop;
|
||||||
|
@@ -240,18 +272,18 @@ static void dw_hdmi_start_dma(struct snd_dw_hdmi *dw)
|
||||||
|
dw->reformat(dw, offset, period);
|
||||||
|
|
||||||
|
/* Clear all irqs before enabling irqs and starting DMA */
|
||||||
|
- writeb_relaxed(HDMI_IH_AHBDMAAUD_STAT0_ALL,
|
||||||
|
- base + HDMI_IH_AHBDMAAUD_STAT0);
|
||||||
|
+ dw_hdmi_writeb_relaxed(HDMI_IH_AHBDMAAUD_STAT0_ALL,
|
||||||
|
+ &dw->data, HDMI_IH_AHBDMAAUD_STAT0);
|
||||||
|
|
||||||
|
start = dw->buf_addr + offset;
|
||||||
|
stop = start + period - 1;
|
||||||
|
|
||||||
|
/* Setup the hardware start/stop addresses */
|
||||||
|
- dw_hdmi_writel(start, base + HDMI_AHB_DMA_STRADDR0);
|
||||||
|
- dw_hdmi_writel(stop, base + HDMI_AHB_DMA_STPADDR0);
|
||||||
|
+ dw_hdmi_writel(start, &dw->data, HDMI_AHB_DMA_STRADDR0);
|
||||||
|
+ dw_hdmi_writel(stop, &dw->data, HDMI_AHB_DMA_STPADDR0);
|
||||||
|
|
||||||
|
- writeb_relaxed((u8)~HDMI_AHB_DMA_MASK_DONE, base + HDMI_AHB_DMA_MASK);
|
||||||
|
- writeb(HDMI_AHB_DMA_START_START, base + HDMI_AHB_DMA_START);
|
||||||
|
+ dw_hdmi_writeb_relaxed((u8)~HDMI_AHB_DMA_MASK_DONE, &dw->data, HDMI_AHB_DMA_MASK);
|
||||||
|
+ dw_hdmi_writeb(HDMI_AHB_DMA_START_START, &dw->data, HDMI_AHB_DMA_START);
|
||||||
|
|
||||||
|
offset += period;
|
||||||
|
if (offset >= dw->buf_size)
|
||||||
|
@@ -262,8 +294,8 @@ static void dw_hdmi_start_dma(struct snd_dw_hdmi *dw)
|
||||||
|
static void dw_hdmi_stop_dma(struct snd_dw_hdmi *dw)
|
||||||
|
{
|
||||||
|
/* Disable interrupts before disabling DMA */
|
||||||
|
- writeb_relaxed(~0, dw->data.base + HDMI_AHB_DMA_MASK);
|
||||||
|
- writeb_relaxed(HDMI_AHB_DMA_STOP_STOP, dw->data.base + HDMI_AHB_DMA_STOP);
|
||||||
|
+ dw_hdmi_writeb_relaxed(~0, &dw->data, HDMI_AHB_DMA_MASK);
|
||||||
|
+ dw_hdmi_writeb_relaxed(HDMI_AHB_DMA_STOP_STOP, &dw->data, HDMI_AHB_DMA_STOP);
|
||||||
|
}
|
||||||
|
|
||||||
|
static irqreturn_t snd_dw_hdmi_irq(int irq, void *data)
|
||||||
|
@@ -272,11 +304,11 @@ static irqreturn_t snd_dw_hdmi_irq(int irq, void *data)
|
||||||
|
struct snd_pcm_substream *substream;
|
||||||
|
unsigned stat;
|
||||||
|
|
||||||
|
- stat = readb_relaxed(dw->data.base + HDMI_IH_AHBDMAAUD_STAT0);
|
||||||
|
+ stat = dw_hdmi_readb_relaxed(&dw->data, HDMI_IH_AHBDMAAUD_STAT0);
|
||||||
|
if (!stat)
|
||||||
|
return IRQ_NONE;
|
||||||
|
|
||||||
|
- writeb_relaxed(stat, dw->data.base + HDMI_IH_AHBDMAAUD_STAT0);
|
||||||
|
+ dw_hdmi_writeb_relaxed(stat, &dw->data, HDMI_IH_AHBDMAAUD_STAT0);
|
||||||
|
|
||||||
|
substream = dw->substream;
|
||||||
|
if (stat & HDMI_IH_AHBDMAAUD_STAT0_DONE && substream) {
|
||||||
|
@@ -319,7 +351,6 @@ static int dw_hdmi_open(struct snd_pcm_substream *substream)
|
||||||
|
{
|
||||||
|
struct snd_pcm_runtime *runtime = substream->runtime;
|
||||||
|
struct snd_dw_hdmi *dw = substream->private_data;
|
||||||
|
- void __iomem *base = dw->data.base;
|
||||||
|
u8 *eld;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
@@ -349,16 +380,16 @@ static int dw_hdmi_open(struct snd_pcm_substream *substream)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
/* Clear FIFO */
|
||||||
|
- writeb_relaxed(HDMI_AHB_DMA_CONF0_SW_FIFO_RST,
|
||||||
|
- base + HDMI_AHB_DMA_CONF0);
|
||||||
|
+ dw_hdmi_writeb_relaxed(HDMI_AHB_DMA_CONF0_SW_FIFO_RST,
|
||||||
|
+ &dw->data, HDMI_AHB_DMA_CONF0);
|
||||||
|
|
||||||
|
/* Configure interrupt polarities */
|
||||||
|
- writeb_relaxed(~0, base + HDMI_AHB_DMA_POL);
|
||||||
|
- writeb_relaxed(~0, base + HDMI_AHB_DMA_BUFFPOL);
|
||||||
|
+ dw_hdmi_writeb_relaxed(~0, &dw->data, HDMI_AHB_DMA_POL);
|
||||||
|
+ dw_hdmi_writeb_relaxed(~0, &dw->data, HDMI_AHB_DMA_BUFFPOL);
|
||||||
|
|
||||||
|
/* Keep interrupts masked, and clear any pending */
|
||||||
|
- writeb_relaxed(~0, base + HDMI_AHB_DMA_MASK);
|
||||||
|
- writeb_relaxed(~0, base + HDMI_IH_AHBDMAAUD_STAT0);
|
||||||
|
+ dw_hdmi_writeb_relaxed(~0, &dw->data, HDMI_AHB_DMA_MASK);
|
||||||
|
+ dw_hdmi_writeb_relaxed(~0, &dw->data, HDMI_IH_AHBDMAAUD_STAT0);
|
||||||
|
|
||||||
|
ret = request_irq(dw->data.irq, snd_dw_hdmi_irq, IRQF_SHARED,
|
||||||
|
"dw-hdmi-audio", dw);
|
||||||
|
@@ -366,9 +397,9 @@ static int dw_hdmi_open(struct snd_pcm_substream *substream)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
/* Un-mute done interrupt */
|
||||||
|
- writeb_relaxed(HDMI_IH_MUTE_AHBDMAAUD_STAT0_ALL &
|
||||||
|
- ~HDMI_IH_MUTE_AHBDMAAUD_STAT0_DONE,
|
||||||
|
- base + HDMI_IH_MUTE_AHBDMAAUD_STAT0);
|
||||||
|
+ dw_hdmi_writeb_relaxed(HDMI_IH_MUTE_AHBDMAAUD_STAT0_ALL &
|
||||||
|
+ ~HDMI_IH_MUTE_AHBDMAAUD_STAT0_DONE,
|
||||||
|
+ &dw->data, HDMI_IH_MUTE_AHBDMAAUD_STAT0);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
@@ -378,8 +409,8 @@ static int dw_hdmi_close(struct snd_pcm_substream *substream)
|
||||||
|
struct snd_dw_hdmi *dw = substream->private_data;
|
||||||
|
|
||||||
|
/* Mute all interrupts */
|
||||||
|
- writeb_relaxed(HDMI_IH_MUTE_AHBDMAAUD_STAT0_ALL,
|
||||||
|
- dw->data.base + HDMI_IH_MUTE_AHBDMAAUD_STAT0);
|
||||||
|
+ dw_hdmi_writeb_relaxed(HDMI_IH_MUTE_AHBDMAAUD_STAT0_ALL,
|
||||||
|
+ &dw->data, HDMI_IH_MUTE_AHBDMAAUD_STAT0);
|
||||||
|
|
||||||
|
free_irq(dw->data.irq, dw);
|
||||||
|
|
||||||
|
@@ -420,6 +451,11 @@ static int dw_hdmi_prepare(struct snd_pcm_substream *substream)
|
||||||
|
HDMI_AHB_DMA_CONF0_INCR8;
|
||||||
|
threshold = 128;
|
||||||
|
break;
|
||||||
|
+ case 0x2a: /* this revision is used in Baikal-M SoC */
|
||||||
|
+ conf0 = HDMI_AHB_DMA_CONF0_BURST_MODE |
|
||||||
|
+ HDMI_AHB_DMA_CONF0_INCR16;
|
||||||
|
+ threshold = 128;
|
||||||
|
+ break;
|
||||||
|
default:
|
||||||
|
/* NOTREACHED */
|
||||||
|
return -EINVAL;
|
||||||
|
@@ -434,9 +470,9 @@ static int dw_hdmi_prepare(struct snd_pcm_substream *substream)
|
||||||
|
conf1 = default_hdmi_channel_config[runtime->channels - 2].conf1;
|
||||||
|
ca = default_hdmi_channel_config[runtime->channels - 2].ca;
|
||||||
|
|
||||||
|
- writeb_relaxed(threshold, dw->data.base + HDMI_AHB_DMA_THRSLD);
|
||||||
|
- writeb_relaxed(conf0, dw->data.base + HDMI_AHB_DMA_CONF0);
|
||||||
|
- writeb_relaxed(conf1, dw->data.base + HDMI_AHB_DMA_CONF1);
|
||||||
|
+ dw_hdmi_writeb_relaxed(threshold, &dw->data, HDMI_AHB_DMA_THRSLD);
|
||||||
|
+ dw_hdmi_writeb_relaxed(conf0, &dw->data, HDMI_AHB_DMA_CONF0);
|
||||||
|
+ dw_hdmi_writeb_relaxed(conf1, &dw->data, HDMI_AHB_DMA_CONF1);
|
||||||
|
|
||||||
|
dw_hdmi_set_channel_count(dw->data.hdmi, runtime->channels);
|
||||||
|
dw_hdmi_set_channel_allocation(dw->data.hdmi, ca);
|
||||||
|
@@ -528,10 +564,10 @@ static int snd_dw_hdmi_probe(struct platform_device *pdev)
|
||||||
|
unsigned revision;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
- writeb_relaxed(HDMI_IH_MUTE_AHBDMAAUD_STAT0_ALL,
|
||||||
|
- data->base + HDMI_IH_MUTE_AHBDMAAUD_STAT0);
|
||||||
|
- revision = readb_relaxed(data->base + HDMI_REVISION_ID);
|
||||||
|
- if (revision != 0x0a && revision != 0x1a) {
|
||||||
|
+ dw_hdmi_writeb_relaxed(HDMI_IH_MUTE_AHBDMAAUD_STAT0_ALL,
|
||||||
|
+ data, HDMI_IH_MUTE_AHBDMAAUD_STAT0);
|
||||||
|
+ revision = dw_hdmi_readb_relaxed(data, HDMI_REVISION_ID);
|
||||||
|
+ if (revision != 0x0a && revision != 0x1a && revision != 0x2a) {
|
||||||
|
dev_err(dev, "dw-hdmi-audio: unknown revision 0x%02x\n",
|
||||||
|
revision);
|
||||||
|
return -ENXIO;
|
||||||
|
diff --git a/drivers/gpu/drm/bridge/synopsys/dw-hdmi-audio.h b/drivers/gpu/drm/bridge/synopsys/dw-hdmi-audio.h
|
||||||
|
index f72d27208..bc8468fe5 100644
|
||||||
|
--- a/drivers/gpu/drm/bridge/synopsys/dw-hdmi-audio.h
|
||||||
|
+++ b/drivers/gpu/drm/bridge/synopsys/dw-hdmi-audio.h
|
||||||
|
@@ -10,6 +10,7 @@ struct dw_hdmi_audio_data {
|
||||||
|
int irq;
|
||||||
|
struct dw_hdmi *hdmi;
|
||||||
|
u8 *(*get_eld)(struct dw_hdmi *hdmi);
|
||||||
|
+ unsigned reg_offset;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct dw_hdmi_i2s_audio_data {
|
||||||
|
diff --git a/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c b/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c
|
||||||
|
index 25d58dcfc..573c98200 100644
|
||||||
|
--- a/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c
|
||||||
|
+++ b/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c
|
||||||
|
@@ -3441,6 +3441,12 @@ struct dw_hdmi *dw_hdmi_probe(struct platform_device *pdev,
|
||||||
|
audio.irq = irq;
|
||||||
|
audio.hdmi = hdmi;
|
||||||
|
audio.get_eld = hdmi_audio_get_eld;
|
||||||
|
+ audio.reg_offset = 0;
|
||||||
|
+ if (of_device_is_compatible(np, "baikal,hdmi")) {
|
||||||
|
+ audio.reg_offset = 2;
|
||||||
|
+ dev_info(dev, "setting audio.reg_offset=%d for BE-M1000 SoC\n",
|
||||||
|
+ audio.reg_offset);
|
||||||
|
+ }
|
||||||
|
hdmi->enable_audio = dw_hdmi_ahb_audio_enable;
|
||||||
|
hdmi->disable_audio = dw_hdmi_ahb_audio_disable;
|
||||||
|
|
||||||
|
--
|
||||||
|
2.33.2
|
||||||
|
|
703
0617-ALSA-hda-Baikal-M-SoC-support.patch
Normal file
703
0617-ALSA-hda-Baikal-M-SoC-support.patch
Normal file
|
@ -0,0 +1,703 @@
|
||||||
|
From 3321942c48f86879a4af5c035aca9ea49f33c083 Mon Sep 17 00:00:00 2001
|
||||||
|
From: Alexey Sheplyakov <asheplyakov@altlinux.org>
|
||||||
|
Date: Sat, 12 Mar 2022 18:37:29 +0400
|
||||||
|
Subject: [PATCH 617/634] ALSA: hda: Baikal-M SoC support
|
||||||
|
|
||||||
|
Known issues (et101 board):
|
||||||
|
* Probe fails to detect any outputs if headphones are connected
|
||||||
|
during the probe.
|
||||||
|
* Device must be configured as output only if no microphone is
|
||||||
|
connected. Otherwise a process which tries to *output* audio
|
||||||
|
blocks forever.
|
||||||
|
---
|
||||||
|
sound/hda/hdac_controller.c | 19 +-
|
||||||
|
sound/pci/hda/Kconfig | 14 +
|
||||||
|
sound/pci/hda/Makefile | 2 +
|
||||||
|
sound/pci/hda/hda_baikal.c | 525 +++++++++++++++++++++++++++++++++
|
||||||
|
sound/pci/hda/hda_controller.c | 19 +-
|
||||||
|
5 files changed, 573 insertions(+), 6 deletions(-)
|
||||||
|
create mode 100644 sound/pci/hda/hda_baikal.c
|
||||||
|
|
||||||
|
diff --git a/sound/hda/hdac_controller.c b/sound/hda/hdac_controller.c
|
||||||
|
index f7bd6e2db..71cf723fd 100644
|
||||||
|
--- a/sound/hda/hdac_controller.c
|
||||||
|
+++ b/sound/hda/hdac_controller.c
|
||||||
|
@@ -6,6 +6,7 @@
|
||||||
|
#include <linux/kernel.h>
|
||||||
|
#include <linux/delay.h>
|
||||||
|
#include <linux/export.h>
|
||||||
|
+#include <linux/of.h>
|
||||||
|
#include <sound/core.h>
|
||||||
|
#include <sound/hdaudio.h>
|
||||||
|
#include <sound/hda_register.h>
|
||||||
|
@@ -31,7 +32,8 @@ static void azx_clear_corbrp(struct hdac_bus *bus)
|
||||||
|
break;
|
||||||
|
udelay(1);
|
||||||
|
}
|
||||||
|
- if (timeout <= 0)
|
||||||
|
+ if (timeout <= 0
|
||||||
|
+ && !of_device_is_compatible(bus->dev->of_node, "be,cw-hda"))
|
||||||
|
dev_err(bus->dev, "CORB reset timeout#2, CORBRP = %d\n",
|
||||||
|
snd_hdac_chip_readw(bus, CORBRP));
|
||||||
|
}
|
||||||
|
@@ -42,6 +44,7 @@ static void azx_clear_corbrp(struct hdac_bus *bus)
|
||||||
|
*/
|
||||||
|
void snd_hdac_bus_init_cmd_io(struct hdac_bus *bus)
|
||||||
|
{
|
||||||
|
+ u8 rirbctl;
|
||||||
|
WARN_ON_ONCE(!bus->rb.area);
|
||||||
|
|
||||||
|
spin_lock_irq(&bus->reg_lock);
|
||||||
|
@@ -78,8 +81,13 @@ void snd_hdac_bus_init_cmd_io(struct hdac_bus *bus)
|
||||||
|
snd_hdac_chip_writew(bus, RIRBWP, AZX_RIRBWP_RST);
|
||||||
|
/* set N=1, get RIRB response interrupt for new entry */
|
||||||
|
snd_hdac_chip_writew(bus, RINTCNT, 1);
|
||||||
|
- /* enable rirb dma and response irq */
|
||||||
|
- snd_hdac_chip_writeb(bus, RIRBCTL, AZX_RBCTL_DMA_EN | AZX_RBCTL_IRQ_EN);
|
||||||
|
+ rirbctl = AZX_RBCTL_DMA_EN | AZX_RBCTL_IRQ_EN;
|
||||||
|
+ if (of_device_is_compatible(bus->dev->of_node, "be,cw-hda")) {
|
||||||
|
+ /* response IRQ does not work in Baikal-M HDA controller */
|
||||||
|
+ rirbctl = AZX_RBCTL_DMA_EN;
|
||||||
|
+ }
|
||||||
|
+ /* enable rirb dma and response irq (if supported) */
|
||||||
|
+ snd_hdac_chip_writeb(bus, RIRBCTL, rirbctl);
|
||||||
|
/* Accept unsolicited responses */
|
||||||
|
snd_hdac_chip_updatel(bus, GCTL, AZX_GCTL_UNSOL, AZX_GCTL_UNSOL);
|
||||||
|
spin_unlock_irq(&bus->reg_lock);
|
||||||
|
@@ -144,6 +152,11 @@ int snd_hdac_bus_send_cmd(struct hdac_bus *bus, unsigned int val)
|
||||||
|
unsigned int addr = azx_command_addr(val);
|
||||||
|
unsigned int wp, rp;
|
||||||
|
|
||||||
|
+ if (of_device_is_compatible(bus->dev->of_node, "be,cw-hda")) {
|
||||||
|
+ /* force first codec address because wrong codec init */
|
||||||
|
+ val |= 0x10000000U;
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
spin_lock_irq(&bus->reg_lock);
|
||||||
|
|
||||||
|
bus->last_cmd[azx_command_addr(val)] = val;
|
||||||
|
diff --git a/sound/pci/hda/Kconfig b/sound/pci/hda/Kconfig
|
||||||
|
index ab9d2746e..aaf8a66e1 100644
|
||||||
|
--- a/sound/pci/hda/Kconfig
|
||||||
|
+++ b/sound/pci/hda/Kconfig
|
||||||
|
@@ -42,6 +42,20 @@ config SND_HDA_TEGRA
|
||||||
|
To compile this driver as a module, choose M here: the module
|
||||||
|
will be called snd-hda-tegra.
|
||||||
|
|
||||||
|
+config SND_HDA_BAIKAL_M
|
||||||
|
+ tristate "Baikal-M HD Audio"
|
||||||
|
+ depends on ARCH_BAIKAL
|
||||||
|
+ select SND_HDA
|
||||||
|
+ select SND_HDA_ALIGNED_MMIO
|
||||||
|
+ help
|
||||||
|
+ Say Y here to support the HDA controller present in
|
||||||
|
+ Baikalm-M SoC
|
||||||
|
+
|
||||||
|
+
|
||||||
|
+ This option enables support for the HD Audio controller
|
||||||
|
+ present in Baikal-M SoC, used to communicate audio
|
||||||
|
+ to the mezzanine board outputs.
|
||||||
|
+
|
||||||
|
if SND_HDA
|
||||||
|
|
||||||
|
config SND_HDA_HWDEP
|
||||||
|
diff --git a/sound/pci/hda/Makefile b/sound/pci/hda/Makefile
|
||||||
|
index b8fa682ce..d12d417f5 100644
|
||||||
|
--- a/sound/pci/hda/Makefile
|
||||||
|
+++ b/sound/pci/hda/Makefile
|
||||||
|
@@ -1,6 +1,7 @@
|
||||||
|
# SPDX-License-Identifier: GPL-2.0
|
||||||
|
snd-hda-intel-objs := hda_intel.o
|
||||||
|
snd-hda-tegra-objs := hda_tegra.o
|
||||||
|
+snd-hda-baikal-m-objs := hda_baikal.o
|
||||||
|
|
||||||
|
snd-hda-codec-y := hda_bind.o hda_codec.o hda_jack.o hda_auto_parser.o hda_sysfs.o
|
||||||
|
snd-hda-codec-y += hda_controller.o
|
||||||
|
@@ -50,3 +51,4 @@ obj-$(CONFIG_SND_HDA_CODEC_HDMI) += snd-hda-codec-hdmi.o
|
||||||
|
# when built in kernel
|
||||||
|
obj-$(CONFIG_SND_HDA_INTEL) += snd-hda-intel.o
|
||||||
|
obj-$(CONFIG_SND_HDA_TEGRA) += snd-hda-tegra.o
|
||||||
|
+obj-$(CONFIG_SND_HDA_BAIKAL_M) += snd-hda-baikal-m.o
|
||||||
|
diff --git a/sound/pci/hda/hda_baikal.c b/sound/pci/hda/hda_baikal.c
|
||||||
|
new file mode 100644
|
||||||
|
index 000000000..5032d78e4
|
||||||
|
--- /dev/null
|
||||||
|
+++ b/sound/pci/hda/hda_baikal.c
|
||||||
|
@@ -0,0 +1,525 @@
|
||||||
|
+// SPDX-License-Identifier: GPL-2.0-only
|
||||||
|
+/*
|
||||||
|
+ *
|
||||||
|
+ * Implementation of primary ALSA driver code base for Baikal-M HDA controller.
|
||||||
|
+ */
|
||||||
|
+
|
||||||
|
+#include <linux/clk.h>
|
||||||
|
+#include <linux/clk-provider.h>
|
||||||
|
+#include <linux/clocksource.h>
|
||||||
|
+#include <linux/completion.h>
|
||||||
|
+#include <linux/delay.h>
|
||||||
|
+#include <linux/dma-mapping.h>
|
||||||
|
+#include <linux/init.h>
|
||||||
|
+#include <linux/interrupt.h>
|
||||||
|
+#include <linux/io.h>
|
||||||
|
+#include <linux/kernel.h>
|
||||||
|
+#include <linux/module.h>
|
||||||
|
+#include <linux/moduleparam.h>
|
||||||
|
+#include <linux/mutex.h>
|
||||||
|
+#include <linux/of_device.h>
|
||||||
|
+#include <linux/slab.h>
|
||||||
|
+#include <linux/time.h>
|
||||||
|
+#include <linux/string.h>
|
||||||
|
+#include <linux/pm_runtime.h>
|
||||||
|
+
|
||||||
|
+#include <sound/core.h>
|
||||||
|
+#include <sound/initval.h>
|
||||||
|
+
|
||||||
|
+#include <sound/hda_codec.h>
|
||||||
|
+#include "hda_controller.h"
|
||||||
|
+
|
||||||
|
+#ifdef CONFIG_PM
|
||||||
|
+static int power_save = CONFIG_SND_HDA_POWER_SAVE_DEFAULT;
|
||||||
|
+module_param(power_save, bint, 0644);
|
||||||
|
+MODULE_PARM_DESC(power_save,
|
||||||
|
+ "Automatic power-saving timeout (in seconds, 0 = disable).");
|
||||||
|
+#else
|
||||||
|
+#define power_save 0
|
||||||
|
+#endif
|
||||||
|
+
|
||||||
|
+/* max number of SDs */
|
||||||
|
+#define NUM_CAPTURE_SD 4
|
||||||
|
+#define NUM_PLAYBACK_SD 4
|
||||||
|
+
|
||||||
|
+struct hda_baikal {
|
||||||
|
+ struct azx chip;
|
||||||
|
+ struct device *dev;
|
||||||
|
+ void __iomem *regs;
|
||||||
|
+ struct work_struct probe_work;
|
||||||
|
+ struct work_struct irq_pending_work;
|
||||||
|
+ unsigned int irq_pending_warned:1;
|
||||||
|
+};
|
||||||
|
+
|
||||||
|
+static int azx_position_ok(struct azx *chip, struct azx_dev *azx_dev);
|
||||||
|
+static const struct hda_controller_ops hda_baikal_ops;
|
||||||
|
+
|
||||||
|
+/* calculate runtime delay from LPIB */
|
||||||
|
+static int azx_get_delay_from_lpib(struct azx *chip, struct azx_dev *azx_dev,
|
||||||
|
+ unsigned int pos)
|
||||||
|
+{
|
||||||
|
+ struct snd_pcm_substream *substream = azx_dev->core.substream;
|
||||||
|
+ int stream = substream->stream;
|
||||||
|
+ unsigned int lpib_pos = azx_get_pos_lpib(chip, azx_dev);
|
||||||
|
+ int delay;
|
||||||
|
+
|
||||||
|
+ if (stream == SNDRV_PCM_STREAM_PLAYBACK)
|
||||||
|
+ delay = pos - lpib_pos;
|
||||||
|
+ else
|
||||||
|
+ delay = lpib_pos - pos;
|
||||||
|
+ if (delay < 0) {
|
||||||
|
+ if (delay >= azx_dev->core.delay_negative_threshold)
|
||||||
|
+ delay = 0;
|
||||||
|
+ else
|
||||||
|
+ delay += azx_dev->core.bufsize;
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ if (delay >= azx_dev->core.period_bytes) {
|
||||||
|
+ dev_info(chip->card->dev,
|
||||||
|
+ "Unstable LPIB (%d >= %d); disabling LPIB delay counting\n",
|
||||||
|
+ delay, azx_dev->core.period_bytes);
|
||||||
|
+ delay = 0;
|
||||||
|
+ chip->driver_caps &= ~AZX_DCAPS_COUNT_LPIB_DELAY;
|
||||||
|
+ chip->get_delay[stream] = NULL;
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ return bytes_to_frames(substream->runtime, delay);
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
+/* called from IRQ */
|
||||||
|
+static int azx_position_check(struct azx *chip, struct azx_dev *azx_dev)
|
||||||
|
+{
|
||||||
|
+ struct hda_baikal *hda = container_of(chip, struct hda_baikal, chip);
|
||||||
|
+ int ok;
|
||||||
|
+
|
||||||
|
+ ok = azx_position_ok(chip, azx_dev);
|
||||||
|
+
|
||||||
|
+ if (ok == 1) {
|
||||||
|
+ azx_dev->irq_pending = 0;
|
||||||
|
+ return ok;
|
||||||
|
+ } else if (ok == 0) {
|
||||||
|
+ /* bogus IRQ, process it later */
|
||||||
|
+ azx_dev->irq_pending = 1;
|
||||||
|
+ schedule_work(&hda->irq_pending_work);
|
||||||
|
+ }
|
||||||
|
+ return 0;
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
+/*
|
||||||
|
+ * Check whether the current DMA position is acceptable for updating
|
||||||
|
+ * periods. Returns non-zero if it's OK.
|
||||||
|
+ *
|
||||||
|
+ * Many HD-audio controllers appear pretty inaccurate about
|
||||||
|
+ * the update-IRQ timing. The IRQ is issued before actually the
|
||||||
|
+ * data is processed. So, we need to process it afterwords in a
|
||||||
|
+ * workqueue.
|
||||||
|
+ */
|
||||||
|
+static int azx_position_ok(struct azx *chip, struct azx_dev *azx_dev)
|
||||||
|
+{
|
||||||
|
+ struct snd_pcm_substream *substream = azx_dev->core.substream;
|
||||||
|
+ int stream = substream->stream;
|
||||||
|
+ u32 wallclk;
|
||||||
|
+ unsigned int pos;
|
||||||
|
+
|
||||||
|
+ wallclk = azx_readl(chip, WALLCLK) - azx_dev->core.start_wallclk;
|
||||||
|
+ if (wallclk < (azx_dev->core.period_wallclk * 2) / 3)
|
||||||
|
+ return -1; /* bogus (too early) interrupt */
|
||||||
|
+
|
||||||
|
+ if (chip->get_position[stream])
|
||||||
|
+ pos = chip->get_position[stream](chip, azx_dev);
|
||||||
|
+ else { /* use the position buffer as default */
|
||||||
|
+ pos = azx_get_pos_posbuf(chip, azx_dev);
|
||||||
|
+ if (!pos || pos == (u32)-1) {
|
||||||
|
+ dev_info(chip->card->dev,
|
||||||
|
+ "Invalid position buffer, using LPIB read method instead.\n");
|
||||||
|
+ chip->get_position[stream] = azx_get_pos_lpib;
|
||||||
|
+ if (chip->get_position[0] == azx_get_pos_lpib &&
|
||||||
|
+ chip->get_position[1] == azx_get_pos_lpib)
|
||||||
|
+ azx_bus(chip)->use_posbuf = false;
|
||||||
|
+ pos = azx_get_pos_lpib(chip, azx_dev);
|
||||||
|
+ chip->get_delay[stream] = NULL;
|
||||||
|
+ } else {
|
||||||
|
+ chip->get_position[stream] = azx_get_pos_posbuf;
|
||||||
|
+ if (chip->driver_caps & AZX_DCAPS_COUNT_LPIB_DELAY)
|
||||||
|
+ chip->get_delay[stream] = azx_get_delay_from_lpib;
|
||||||
|
+ }
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ if (pos >= azx_dev->core.bufsize)
|
||||||
|
+ pos = 0;
|
||||||
|
+
|
||||||
|
+ if (WARN_ONCE(!azx_dev->core.period_bytes,
|
||||||
|
+ "hda-baikal: zero azx_dev->period_bytes"))
|
||||||
|
+ return -1; /* this shouldn't happen! */
|
||||||
|
+ if (wallclk < (azx_dev->core.period_wallclk * 5) / 4 &&
|
||||||
|
+ pos % azx_dev->core.period_bytes > azx_dev->core.period_bytes / 2)
|
||||||
|
+ /* NG - it's below the first next period boundary */
|
||||||
|
+ return chip->bdl_pos_adj ? 0 : -1;
|
||||||
|
+ azx_dev->core.start_wallclk += wallclk;
|
||||||
|
+ return 1; /* OK, it's fine */
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
+/*
|
||||||
|
+ * The work for pending PCM period updates.
|
||||||
|
+ */
|
||||||
|
+static void azx_irq_pending_work(struct work_struct *work)
|
||||||
|
+{
|
||||||
|
+ struct hda_baikal *hda = container_of(work, struct hda_baikal, irq_pending_work);
|
||||||
|
+ struct azx *chip = &hda->chip;
|
||||||
|
+ struct hdac_bus *bus = azx_bus(chip);
|
||||||
|
+ struct hdac_stream *s;
|
||||||
|
+ int pending, ok;
|
||||||
|
+
|
||||||
|
+ if (!hda->irq_pending_warned) {
|
||||||
|
+ dev_info(chip->card->dev,
|
||||||
|
+ "IRQ timing workaround is activated for card #%d. Suggest a bigger bdl_pos_adj.\n",
|
||||||
|
+ chip->card->number);
|
||||||
|
+ hda->irq_pending_warned = 1;
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ for (;;) {
|
||||||
|
+ pending = 0;
|
||||||
|
+ spin_lock_irq(&bus->reg_lock);
|
||||||
|
+ list_for_each_entry(s, &bus->stream_list, list) {
|
||||||
|
+ struct azx_dev *azx_dev = stream_to_azx_dev(s);
|
||||||
|
+ if (!azx_dev->irq_pending ||
|
||||||
|
+ !s->substream ||
|
||||||
|
+ !s->running)
|
||||||
|
+ continue;
|
||||||
|
+ ok = azx_position_ok(chip, azx_dev);
|
||||||
|
+ if (ok > 0) {
|
||||||
|
+ azx_dev->irq_pending = 0;
|
||||||
|
+ spin_unlock(&bus->reg_lock);
|
||||||
|
+ snd_pcm_period_elapsed(s->substream);
|
||||||
|
+ spin_lock(&bus->reg_lock);
|
||||||
|
+ } else if (ok < 0) {
|
||||||
|
+ pending = 0; /* too early */
|
||||||
|
+ } else
|
||||||
|
+ pending++;
|
||||||
|
+ }
|
||||||
|
+ spin_unlock_irq(&bus->reg_lock);
|
||||||
|
+ if (!pending)
|
||||||
|
+ return;
|
||||||
|
+ msleep(1);
|
||||||
|
+ }
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
+/* clear irq_pending flags and assure no on-going workq */
|
||||||
|
+static void azx_clear_irq_pending(struct azx *chip)
|
||||||
|
+{
|
||||||
|
+ struct hdac_bus *bus = azx_bus(chip);
|
||||||
|
+ struct hdac_stream *s;
|
||||||
|
+
|
||||||
|
+ spin_lock_irq(&bus->reg_lock);
|
||||||
|
+ list_for_each_entry(s, &bus->stream_list, list) {
|
||||||
|
+ struct azx_dev *azx_dev = stream_to_azx_dev(s);
|
||||||
|
+ azx_dev->irq_pending = 0;
|
||||||
|
+ }
|
||||||
|
+ spin_unlock_irq(&bus->reg_lock);
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
+static int hda_baikal_dev_disconnect(struct snd_device *device)
|
||||||
|
+{
|
||||||
|
+ struct azx *chip = device->device_data;
|
||||||
|
+
|
||||||
|
+ chip->bus.shutdown = 1;
|
||||||
|
+ return 0;
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
+static int hda_baikal_dev_free(struct snd_device *device)
|
||||||
|
+{
|
||||||
|
+ struct azx *chip = device->device_data;
|
||||||
|
+ struct hda_baikal *hda = container_of(chip, struct hda_baikal, chip);
|
||||||
|
+
|
||||||
|
+ cancel_work_sync(&hda->probe_work);
|
||||||
|
+ if (azx_bus(chip)->chip_init) {
|
||||||
|
+ azx_clear_irq_pending(chip);
|
||||||
|
+ azx_stop_all_streams(chip);
|
||||||
|
+ azx_stop_chip(chip);
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ azx_free_stream_pages(chip);
|
||||||
|
+ azx_free_streams(chip);
|
||||||
|
+ snd_hdac_bus_exit(azx_bus(chip));
|
||||||
|
+
|
||||||
|
+ return 0;
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
+static int hda_baikal_init_chip(struct azx *chip, struct platform_device *pdev)
|
||||||
|
+{
|
||||||
|
+ struct hda_baikal *hda = container_of(chip, struct hda_baikal, chip);
|
||||||
|
+ struct hdac_bus *bus = azx_bus(chip);
|
||||||
|
+ struct device *dev = hda->dev;
|
||||||
|
+ struct resource *res;
|
||||||
|
+
|
||||||
|
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||||
|
+ hda->regs = devm_ioremap_resource(dev, res);
|
||||||
|
+ if (IS_ERR(hda->regs))
|
||||||
|
+ return PTR_ERR(hda->regs);
|
||||||
|
+
|
||||||
|
+ bus->remap_addr = hda->regs;
|
||||||
|
+ bus->addr = res->start;
|
||||||
|
+
|
||||||
|
+ return 0;
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
+static int hda_baikal_first_init(struct azx *chip, struct platform_device *pdev)
|
||||||
|
+{
|
||||||
|
+ struct hdac_bus *bus = azx_bus(chip);
|
||||||
|
+ struct snd_card *card = chip->card;
|
||||||
|
+ int err;
|
||||||
|
+ unsigned short gcap;
|
||||||
|
+ int irq_id = platform_get_irq(pdev, 0);
|
||||||
|
+ const char *sname, *drv_name = "baikal-hda";
|
||||||
|
+ struct device_node *np = pdev->dev.of_node;
|
||||||
|
+
|
||||||
|
+ err = hda_baikal_init_chip(chip, pdev);
|
||||||
|
+ if (err)
|
||||||
|
+ return err;
|
||||||
|
+
|
||||||
|
+ err = devm_request_irq(chip->card->dev, irq_id, azx_interrupt,
|
||||||
|
+ IRQF_SHARED, KBUILD_MODNAME, chip);
|
||||||
|
+ if (err) {
|
||||||
|
+ dev_err(chip->card->dev,
|
||||||
|
+ "unable to request IRQ %d, disabling device\n",
|
||||||
|
+ irq_id);
|
||||||
|
+ return err;
|
||||||
|
+ }
|
||||||
|
+ bus->irq = irq_id;
|
||||||
|
+
|
||||||
|
+ synchronize_irq(bus->irq);
|
||||||
|
+
|
||||||
|
+ gcap = azx_readw(chip, GCAP);
|
||||||
|
+ dev_dbg(card->dev, "chipset global capabilities = 0x%x\n", gcap);
|
||||||
|
+
|
||||||
|
+ /* force polling mode, because RIRB interrupts don't working */
|
||||||
|
+ bus->polling_mode = 1;
|
||||||
|
+
|
||||||
|
+ /* read number of streams from GCAP register instead of using
|
||||||
|
+ * hardcoded value
|
||||||
|
+ */
|
||||||
|
+ chip->capture_streams = (gcap >> 8) & 0x0f;
|
||||||
|
+ chip->playback_streams = (gcap >> 12) & 0x0f;
|
||||||
|
+ if (!chip->playback_streams && !chip->capture_streams) {
|
||||||
|
+ /* gcap didn't give any info, switching to old method */
|
||||||
|
+ chip->playback_streams = NUM_PLAYBACK_SD;
|
||||||
|
+ chip->capture_streams = NUM_CAPTURE_SD;
|
||||||
|
+ }
|
||||||
|
+ chip->capture_index_offset = 0;
|
||||||
|
+ chip->playback_index_offset = chip->capture_streams;
|
||||||
|
+ chip->num_streams = chip->playback_streams + chip->capture_streams;
|
||||||
|
+
|
||||||
|
+ /* initialize streams */
|
||||||
|
+ err = azx_init_streams(chip);
|
||||||
|
+ if (err < 0) {
|
||||||
|
+ dev_err(card->dev, "failed to initialize streams: %d\n", err);
|
||||||
|
+ return err;
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ err = azx_alloc_stream_pages(chip);
|
||||||
|
+ if (err < 0) {
|
||||||
|
+ dev_err(card->dev, "failed to allocate stream pages: %d\n",
|
||||||
|
+ err);
|
||||||
|
+ return err;
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ /* initialize chip */
|
||||||
|
+ azx_init_chip(chip, 1);
|
||||||
|
+
|
||||||
|
+ /* codec detection */
|
||||||
|
+ if (!bus->codec_mask) {
|
||||||
|
+ dev_err(card->dev, "no codecs found!\n");
|
||||||
|
+ return -ENODEV;
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ /* driver name */
|
||||||
|
+ strncpy(card->driver, drv_name, sizeof(card->driver));
|
||||||
|
+ /* shortname for card */
|
||||||
|
+ sname = of_get_property(np, "baikal,model", NULL);
|
||||||
|
+ if (!sname)
|
||||||
|
+ sname = drv_name;
|
||||||
|
+ if (strlen(sname) > sizeof(card->shortname))
|
||||||
|
+ dev_info(card->dev, "truncating shortname for card\n");
|
||||||
|
+ strncpy(card->shortname, sname, sizeof(card->shortname));
|
||||||
|
+
|
||||||
|
+ /* longname for card */
|
||||||
|
+ snprintf(card->longname, sizeof(card->longname),
|
||||||
|
+ "%s at 0x%lx irq %i",
|
||||||
|
+ card->shortname, bus->addr, bus->irq);
|
||||||
|
+
|
||||||
|
+ return 0;
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
+static void hda_baikal_probe_work(struct work_struct *work);
|
||||||
|
+
|
||||||
|
+static int hda_baikal_create(struct snd_card *card,
|
||||||
|
+ unsigned int driver_caps,
|
||||||
|
+ struct hda_baikal *hda)
|
||||||
|
+{
|
||||||
|
+ static struct snd_device_ops ops = {
|
||||||
|
+ .dev_disconnect = hda_baikal_dev_disconnect,
|
||||||
|
+ .dev_free = hda_baikal_dev_free,
|
||||||
|
+ };
|
||||||
|
+ struct azx *chip;
|
||||||
|
+ int err;
|
||||||
|
+
|
||||||
|
+ chip = &hda->chip;
|
||||||
|
+
|
||||||
|
+ mutex_init(&chip->open_mutex);
|
||||||
|
+ chip->card = card;
|
||||||
|
+ chip->ops = &hda_baikal_ops;
|
||||||
|
+ chip->driver_caps = driver_caps;
|
||||||
|
+ chip->driver_type = driver_caps & 0xff;
|
||||||
|
+ chip->dev_index = 0;
|
||||||
|
+ INIT_LIST_HEAD(&chip->pcm_list);
|
||||||
|
+ INIT_WORK(&hda->irq_pending_work, azx_irq_pending_work);
|
||||||
|
+
|
||||||
|
+ chip->codec_probe_mask = 3; /* two codecs: first and second bits */
|
||||||
|
+
|
||||||
|
+ chip->single_cmd = false;
|
||||||
|
+ chip->snoop = true;
|
||||||
|
+
|
||||||
|
+ chip->get_position[0] = chip->get_position[1] = azx_get_pos_lpib;
|
||||||
|
+ chip->get_delay[0] = chip->get_delay[1] = azx_get_delay_from_lpib;
|
||||||
|
+
|
||||||
|
+ INIT_WORK(&hda->probe_work, hda_baikal_probe_work);
|
||||||
|
+
|
||||||
|
+ err = azx_bus_init(chip, NULL);
|
||||||
|
+ if (err < 0)
|
||||||
|
+ return err;
|
||||||
|
+
|
||||||
|
+ chip->bus.core.needs_damn_long_delay = 1;
|
||||||
|
+ chip->bus.core.aligned_mmio = 1;
|
||||||
|
+
|
||||||
|
+ err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, chip, &ops);
|
||||||
|
+ if (err < 0) {
|
||||||
|
+ dev_err(card->dev, "Error creating device\n");
|
||||||
|
+ return err;
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ return 0;
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
+static int hda_baikal_probe(struct platform_device *pdev)
|
||||||
|
+{
|
||||||
|
+ const unsigned int driver_flags = AZX_DCAPS_PM_RUNTIME |
|
||||||
|
+ AZX_DCAPS_NO_64BIT |
|
||||||
|
+ AZX_DCAPS_4K_BDLE_BOUNDARY |
|
||||||
|
+ AZX_DCAPS_COUNT_LPIB_DELAY;
|
||||||
|
+ struct snd_card *card;
|
||||||
|
+ struct azx *chip;
|
||||||
|
+ struct hda_baikal *hda;
|
||||||
|
+ int err;
|
||||||
|
+
|
||||||
|
+ hda = devm_kzalloc(&pdev->dev, sizeof(*hda), GFP_KERNEL);
|
||||||
|
+ if (!hda)
|
||||||
|
+ return -ENOMEM;
|
||||||
|
+ hda->dev = &pdev->dev;
|
||||||
|
+ chip = &hda->chip;
|
||||||
|
+
|
||||||
|
+ err = snd_card_new(&pdev->dev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1,
|
||||||
|
+ THIS_MODULE, 0, &card);
|
||||||
|
+ if (err < 0) {
|
||||||
|
+ dev_err(&pdev->dev, "Error creating card!\n");
|
||||||
|
+ return err;
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ err = hda_baikal_create(card, driver_flags, hda);
|
||||||
|
+ if (err < 0)
|
||||||
|
+ goto out_free;
|
||||||
|
+ card->private_data = chip;
|
||||||
|
+
|
||||||
|
+ dev_set_drvdata(&pdev->dev, card);
|
||||||
|
+
|
||||||
|
+ pm_runtime_enable(hda->dev);
|
||||||
|
+ if (!azx_has_pm_runtime(chip))
|
||||||
|
+ pm_runtime_forbid(hda->dev);
|
||||||
|
+
|
||||||
|
+ schedule_work(&hda->probe_work);
|
||||||
|
+
|
||||||
|
+ return 0;
|
||||||
|
+
|
||||||
|
+out_free:
|
||||||
|
+ snd_card_free(card);
|
||||||
|
+ return err;
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
+static void hda_baikal_probe_work(struct work_struct *work)
|
||||||
|
+{
|
||||||
|
+ struct hda_baikal *hda = container_of(work, struct hda_baikal, probe_work);
|
||||||
|
+ struct azx *chip = &hda->chip;
|
||||||
|
+ struct platform_device *pdev = to_platform_device(hda->dev);
|
||||||
|
+ int err;
|
||||||
|
+
|
||||||
|
+ pm_runtime_get_sync(hda->dev);
|
||||||
|
+ err = hda_baikal_first_init(chip, pdev);
|
||||||
|
+ if (err < 0)
|
||||||
|
+ goto out_free;
|
||||||
|
+
|
||||||
|
+ /* create codec instances */
|
||||||
|
+ err = azx_probe_codecs(chip, 1);
|
||||||
|
+ if (err < 0)
|
||||||
|
+ goto out_free;
|
||||||
|
+
|
||||||
|
+ err = azx_codec_configure(chip);
|
||||||
|
+ if (err < 0)
|
||||||
|
+ goto out_free;
|
||||||
|
+
|
||||||
|
+ err = snd_card_register(chip->card);
|
||||||
|
+ if (err < 0)
|
||||||
|
+ goto out_free;
|
||||||
|
+
|
||||||
|
+ chip->running = 1;
|
||||||
|
+
|
||||||
|
+ snd_hda_set_power_save(&chip->bus, power_save * 1000);
|
||||||
|
+
|
||||||
|
+ out_free:
|
||||||
|
+ pm_runtime_put(hda->dev);
|
||||||
|
+ return; /* no error return from async probe */
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
+static int hda_baikal_remove(struct platform_device *pdev)
|
||||||
|
+{
|
||||||
|
+ int ret;
|
||||||
|
+
|
||||||
|
+ ret = snd_card_free(dev_get_drvdata(&pdev->dev));
|
||||||
|
+ pm_runtime_disable(&pdev->dev);
|
||||||
|
+
|
||||||
|
+ return ret;
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
+static void hda_baikal_shutdown(struct platform_device *pdev)
|
||||||
|
+{
|
||||||
|
+ struct snd_card *card = dev_get_drvdata(&pdev->dev);
|
||||||
|
+ struct azx *chip;
|
||||||
|
+
|
||||||
|
+ if (!card)
|
||||||
|
+ return;
|
||||||
|
+ chip = card->private_data;
|
||||||
|
+ if (chip && chip->running)
|
||||||
|
+ azx_stop_chip(chip);
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
+static const struct hda_controller_ops hda_baikal_ops = {
|
||||||
|
+ .position_check = azx_position_check,
|
||||||
|
+};
|
||||||
|
+
|
||||||
|
+static const struct of_device_id hda_baikal_match[] = {
|
||||||
|
+ { .compatible = "be,cw-hda" },
|
||||||
|
+ {},
|
||||||
|
+};
|
||||||
|
+MODULE_DEVICE_TABLE(of, hda_baikal_match);
|
||||||
|
+
|
||||||
|
+static struct platform_driver baikal_platform_hda = {
|
||||||
|
+ .driver = {
|
||||||
|
+ .name = "baikal-hda",
|
||||||
|
+ .of_match_table = hda_baikal_match,
|
||||||
|
+ },
|
||||||
|
+ .probe = hda_baikal_probe,
|
||||||
|
+ .remove = hda_baikal_remove,
|
||||||
|
+ .shutdown = hda_baikal_shutdown,
|
||||||
|
+};
|
||||||
|
+module_platform_driver(baikal_platform_hda);
|
||||||
|
+
|
||||||
|
+MODULE_DESCRIPTION("Baikal HDA bus driver");
|
||||||
|
+MODULE_LICENSE("GPL v2");
|
||||||
|
diff --git a/sound/pci/hda/hda_controller.c b/sound/pci/hda/hda_controller.c
|
||||||
|
index 75dcb14ff..5137c8cef 100644
|
||||||
|
--- a/sound/pci/hda/hda_controller.c
|
||||||
|
+++ b/sound/pci/hda/hda_controller.c
|
||||||
|
@@ -1196,21 +1196,25 @@ int azx_probe_codecs(struct azx *chip, unsigned int max_slots)
|
||||||
|
{
|
||||||
|
struct hdac_bus *bus = azx_bus(chip);
|
||||||
|
int c, codecs, err;
|
||||||
|
+ int retry_count, max_probe_retries = 1;
|
||||||
|
|
||||||
|
codecs = 0;
|
||||||
|
if (!max_slots)
|
||||||
|
max_slots = AZX_DEFAULT_CODECS;
|
||||||
|
|
||||||
|
+ if (of_device_is_compatible(chip->card->dev->of_node, "be,cw-hda"))
|
||||||
|
+ max_probe_retries = 100;
|
||||||
|
+
|
||||||
|
/* First try to probe all given codec slots */
|
||||||
|
for (c = 0; c < max_slots; c++) {
|
||||||
|
if ((bus->codec_mask & (1 << c)) & chip->codec_probe_mask) {
|
||||||
|
+ retry_count = 0;
|
||||||
|
+probe_retry:
|
||||||
|
if (probe_codec(chip, c) < 0) {
|
||||||
|
+ retry_count++;
|
||||||
|
/* Some BIOSen give you wrong codec addresses
|
||||||
|
* that don't exist
|
||||||
|
*/
|
||||||
|
- dev_warn(chip->card->dev,
|
||||||
|
- "Codec #%d probe error; disabling it...\n", c);
|
||||||
|
- bus->codec_mask &= ~(1 << c);
|
||||||
|
/* More badly, accessing to a non-existing
|
||||||
|
* codec often screws up the controller chip,
|
||||||
|
* and disturbs the further communications.
|
||||||
|
@@ -1220,6 +1224,15 @@ int azx_probe_codecs(struct azx *chip, unsigned int max_slots)
|
||||||
|
*/
|
||||||
|
azx_stop_chip(chip);
|
||||||
|
azx_init_chip(chip, true);
|
||||||
|
+ if (retry_count < max_probe_retries)
|
||||||
|
+ goto probe_retry;
|
||||||
|
+ dev_warn(chip->card->dev,
|
||||||
|
+ "Codec #%d probe error; disabling it...\n", c);
|
||||||
|
+ bus->codec_mask &= ~(1 << c);
|
||||||
|
+ } else {
|
||||||
|
+ dev_info(chip->card->dev,
|
||||||
|
+ "Codec #%d successfully probed, retry count = %d\n",
|
||||||
|
+ c, retry_count);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
--
|
||||||
|
2.33.2
|
||||||
|
|
|
@ -1,850 +0,0 @@
|
||||||
Subject: [PATCH 617/625] Baikal-M: PCIe driver from SDK-M 4.4
|
|
||||||
|
|
||||||
Appears to successfully probe the hardware, lspci -vv output
|
|
||||||
looks good. No other testing has been done yet.
|
|
||||||
|
|
||||||
Update for 5.15.28
|
|
||||||
|
|
||||||
diff --git a/drivers/pci/controller/dwc/Makefile b/drivers/pci/controller/dwc/Makefile
|
|
||||||
--- a/drivers/pci/controller/dwc/Makefile
|
|
||||||
+++ b/drivers/pci/controller/dwc/Makefile
|
|
||||||
@@ -24,7 +24,7 @@
|
|
||||||
obj-$(CONFIG_PCIE_UNIPHIER) += pcie-uniphier.o
|
|
||||||
obj-$(CONFIG_PCIE_UNIPHIER_EP) += pcie-uniphier-ep.o
|
|
||||||
obj-$(CONFIG_PCIE_VISCONTI_HOST) += pcie-visconti.o
|
|
||||||
-obj-$(CONFIG_PCI_BAIKAL) += pcie-baikal.o
|
|
||||||
+obj-$(CONFIG_PCI_BAIKAL) += pcie-baikal.o pcie-baikal-v44.o
|
|
||||||
|
|
||||||
# The following drivers are for devices that use the generic ACPI
|
|
||||||
# pci_root.c driver but don't support standard ECAM config access.
|
|
||||||
diff --git a/drivers/pci/controller/dwc/pcie-baikal-v44.c b/drivers/pci/controller/dwc/pcie-baikal-v44.c
|
|
||||||
new file mode 100644
|
|
||||||
--- /dev/null
|
|
||||||
+++ b/drivers/pci/controller/dwc/pcie-baikal-v44.c
|
|
||||||
@@ -0,0 +1,826 @@
|
|
||||||
+/*
|
|
||||||
+ * PCIe root complex driver for Baikal SoCs
|
|
||||||
+ *
|
|
||||||
+ * Copyright (C) 2019-2020 Baikal Electronics, JSC
|
|
||||||
+ * Authors: Pavel Parkhomenko <pavel.parkhomenko@baikalelectronics.ru>
|
|
||||||
+ * Mikhail Ivanov <michail.ivanov@baikalelectronics.ru>
|
|
||||||
+ *
|
|
||||||
+ * This program is free software; you can redistribute it and/or modify
|
|
||||||
+ * it under the terms of the GNU General Public License version 2 as
|
|
||||||
+ * published by the Free Software Foundation.
|
|
||||||
+ */
|
|
||||||
+
|
|
||||||
+#include <linux/interrupt.h>
|
|
||||||
+#include <linux/module.h>
|
|
||||||
+#include <linux/irqchip/arm-gic-v3.h>
|
|
||||||
+#include <linux/mfd/baikal/lcru-pcie.h>
|
|
||||||
+#include <linux/mfd/syscon.h>
|
|
||||||
+#include <linux/of_address.h>
|
|
||||||
+#include <linux/of_device.h>
|
|
||||||
+#include <linux/of_gpio.h>
|
|
||||||
+#include <linux/pci.h>
|
|
||||||
+#include <linux/phy/phy.h>
|
|
||||||
+
|
|
||||||
+#include "pcie-designware.h"
|
|
||||||
+
|
|
||||||
+#ifdef CONFIG_PCI_DEBUG
|
|
||||||
+#define DEBUG
|
|
||||||
+#endif
|
|
||||||
+
|
|
||||||
+struct baikal_pcie {
|
|
||||||
+ struct dw_pcie pp;
|
|
||||||
+ unsigned bus_nr;
|
|
||||||
+ struct regmap *lcru;
|
|
||||||
+ struct gpio_desc *reset_gpio;
|
|
||||||
+ unsigned reset_active_low;
|
|
||||||
+ char reset_name[32];
|
|
||||||
+ unsigned retrained;
|
|
||||||
+ unsigned num_lanes;
|
|
||||||
+};
|
|
||||||
+
|
|
||||||
+#define to_baikal_pcie(x) container_of((x), struct baikal_pcie, pp)
|
|
||||||
+
|
|
||||||
+#define PCIE_DEVICE_CONTROL_DEVICE_STATUS_REG 0x78
|
|
||||||
+#define PCIE_CAP_CORR_ERR_REPORT_EN BIT(0)
|
|
||||||
+#define PCIE_CAP_NON_FATAL_ERR_REPORT_EN BIT(1)
|
|
||||||
+#define PCIE_CAP_FATAL_ERR_REPORT_EN BIT(2)
|
|
||||||
+#define PCIE_CAP_UNSUPPORT_REQ_REP_EN BIT(3)
|
|
||||||
+
|
|
||||||
+#define PCIE_LINK_CAPABILITIES_REG 0x7c
|
|
||||||
+#define PCIE_CAP_MAX_LINK_WIDTH_MASK 0x3f0
|
|
||||||
+#define PCIE_CAP_MAX_LINK_WIDTH_SHIFT 4
|
|
||||||
+
|
|
||||||
+#define PCIE_LINK_CONTROL_LINK_STATUS_REG 0x80
|
|
||||||
+#define PCIE_CAP_LINK_SPEED_MASK 0xf0000
|
|
||||||
+#define PCIE_CAP_LINK_SPEED_SHIFT 16
|
|
||||||
+#define PCIE_CAP_NEGO_LINK_WIDTH_MASK 0x3f00000
|
|
||||||
+#define PCIE_CAP_NEGO_LINK_WIDTH_SHIFT 20
|
|
||||||
+#define PCIE_CAP_LINK_TRAINING BIT(27)
|
|
||||||
+
|
|
||||||
+#define PCIE_ROOT_CONTROL_ROOT_CAPABILITIES_REG 0x8c
|
|
||||||
+#define PCIE_CAP_SYS_ERR_ON_CORR_ERR_EN BIT(0)
|
|
||||||
+#define PCIE_CAP_SYS_ERR_ON_NON_FATAL_ERR_EN BIT(1)
|
|
||||||
+#define PCIE_CAP_SYS_ERR_ON_FATAL_ERR_EN BIT(2)
|
|
||||||
+#define PCIE_CAP_PME_INT_EN BIT(3)
|
|
||||||
+
|
|
||||||
+#define PCIE_LINK_CONTROL2_LINK_STATUS2_REG 0xa0
|
|
||||||
+#define PCIE_CAP_TARGET_LINK_SPEED_MASK 0xf
|
|
||||||
+
|
|
||||||
+#define PCIE_UNCORR_ERR_STATUS_REG 0x104
|
|
||||||
+#define PCIE_CORR_ERR_STATUS_REG 0x110
|
|
||||||
+
|
|
||||||
+#define PCIE_ROOT_ERR_CMD_REG 0x12c
|
|
||||||
+#define PCIE_CORR_ERR_REPORTING_EN BIT(0)
|
|
||||||
+#define PCIE_NON_FATAL_ERR_REPORTING_EN BIT(1)
|
|
||||||
+#define PCIE_FATAL_ERR_REPORTING_EN BIT(2)
|
|
||||||
+
|
|
||||||
+#define PCIE_ROOT_ERR_STATUS_REG 0x130
|
|
||||||
+
|
|
||||||
+#define PCIE_GEN2_CTRL_REG 0x80c
|
|
||||||
+#define PCIE_DIRECT_SPEED_CHANGE BIT(17)
|
|
||||||
+
|
|
||||||
+#define PCIE_MSI_CTRL_ADDR_LO_REG 0x820
|
|
||||||
+#define PCIE_MSI_CTRL_ADDR_HI_REG 0x824
|
|
||||||
+
|
|
||||||
+#define PCIE_MISC_CONTROL_1_REG 0x8bc
|
|
||||||
+#define PCIE_DBI_RO_RW_EN BIT(0)
|
|
||||||
+
|
|
||||||
+#define PCIE_IATU_VIEWPORT_REG 0x900
|
|
||||||
+#define PCIE_IATU_REGION_INBOUND BIT(31)
|
|
||||||
+#define PCIE_IATU_REGION_OUTBOUND 0
|
|
||||||
+#define PCIE_IATU_REGION_CTRL_2_REG 0x908
|
|
||||||
+
|
|
||||||
+static const struct of_device_id of_baikal_pcie_match[] = {
|
|
||||||
+ { .compatible = "baikal,pcie-m", },
|
|
||||||
+ {},
|
|
||||||
+};
|
|
||||||
+
|
|
||||||
+static unsigned baikal_pcie_link_is_training(struct dw_pcie *pp)
|
|
||||||
+{
|
|
||||||
+ unsigned long reg;
|
|
||||||
+ reg = dw_pcie_readl_dbi(pp, PCIE_LINK_CONTROL_LINK_STATUS_REG);
|
|
||||||
+ return reg & PCIE_CAP_LINK_TRAINING;
|
|
||||||
+}
|
|
||||||
+
|
|
||||||
+static bool baikal_wait_pcie_link_training_done(struct dw_pcie *pp)
|
|
||||||
+{
|
|
||||||
+ unsigned long start_jiffies = jiffies;
|
|
||||||
+ while (baikal_pcie_link_is_training(pp)) {
|
|
||||||
+ if (time_after(jiffies, start_jiffies + HZ)) {
|
|
||||||
+ pr_err("%s: link retrained for too long, timeout occured\n", __func__);
|
|
||||||
+ return false;
|
|
||||||
+ }
|
|
||||||
+ udelay(100);
|
|
||||||
+ }
|
|
||||||
+ return true;
|
|
||||||
+}
|
|
||||||
+
|
|
||||||
+static void baikal_print_link_status(struct dw_pcie *pp)
|
|
||||||
+{
|
|
||||||
+ unsigned long reg;
|
|
||||||
+ unsigned speed, width;
|
|
||||||
+
|
|
||||||
+ reg = dw_pcie_readl_dbi(pp, PCIE_LINK_CONTROL_LINK_STATUS_REG);
|
|
||||||
+ speed = (reg & PCIE_CAP_LINK_SPEED_MASK) >> PCIE_CAP_LINK_SPEED_SHIFT;
|
|
||||||
+ width = (reg & PCIE_CAP_NEGO_LINK_WIDTH_MASK) >>
|
|
||||||
+ PCIE_CAP_NEGO_LINK_WIDTH_SHIFT;
|
|
||||||
+
|
|
||||||
+ dev_info(pp->dev, "link status is Gen%u, x%u\n", speed, width);
|
|
||||||
+}
|
|
||||||
+
|
|
||||||
+static void baikal_pcie_link_retrain(struct dw_pcie *pp, int target_speed)
|
|
||||||
+{
|
|
||||||
+ unsigned long reg;
|
|
||||||
+ unsigned long start_jiffies;
|
|
||||||
+
|
|
||||||
+ dev_info(pp->dev, "retrain link to Gen%u\n", target_speed);
|
|
||||||
+
|
|
||||||
+ /* In case link is already training wait for training to complete */
|
|
||||||
+ baikal_wait_pcie_link_training_done(pp);
|
|
||||||
+
|
|
||||||
+ /* Set desired speed */
|
|
||||||
+ reg = dw_pcie_readl_dbi(pp, PCIE_LINK_CONTROL2_LINK_STATUS2_REG);
|
|
||||||
+ reg &= ~PCIE_CAP_TARGET_LINK_SPEED_MASK;
|
|
||||||
+ reg |= target_speed;
|
|
||||||
+ dw_pcie_writel_dbi(pp, PCIE_LINK_CONTROL2_LINK_STATUS2_REG, reg);
|
|
||||||
+
|
|
||||||
+ /* Set DIRECT_SPEED_CHANGE bit */
|
|
||||||
+ reg = dw_pcie_readl_dbi(pp, PCIE_GEN2_CTRL_REG);
|
|
||||||
+ reg &= ~PCIE_DIRECT_SPEED_CHANGE;
|
|
||||||
+ dw_pcie_writel_dbi(pp, PCIE_GEN2_CTRL_REG, reg);
|
|
||||||
+ reg |= PCIE_DIRECT_SPEED_CHANGE;
|
|
||||||
+ dw_pcie_writel_dbi(pp, PCIE_GEN2_CTRL_REG, reg);
|
|
||||||
+
|
|
||||||
+ /* Wait for link training begin */
|
|
||||||
+ start_jiffies = jiffies;
|
|
||||||
+ while (baikal_pcie_link_is_training(pp) == 0) {
|
|
||||||
+ if (time_after(jiffies, start_jiffies + HZ)) {
|
|
||||||
+ pr_err("%s: link retrained for too long, timeout occured\n", __func__);
|
|
||||||
+ break;
|
|
||||||
+ }
|
|
||||||
+ udelay(100);
|
|
||||||
+ }
|
|
||||||
+
|
|
||||||
+ /* Wait for link training end */
|
|
||||||
+ baikal_wait_pcie_link_training_done(pp);
|
|
||||||
+
|
|
||||||
+ if (dw_pcie_wait_for_link(pp) == 0) {
|
|
||||||
+ baikal_print_link_status(pp);
|
|
||||||
+ }
|
|
||||||
+}
|
|
||||||
+
|
|
||||||
+static void baikal_pcie_link_speed_fixup(struct pci_dev *pdev)
|
|
||||||
+{
|
|
||||||
+ struct pcie_port *portp = pdev->bus->sysdata;
|
|
||||||
+ struct dw_pcie *pp = to_dw_pcie_from_pp(portp);
|
|
||||||
+ struct baikal_pcie *pcie = to_baikal_pcie(pp);
|
|
||||||
+ unsigned dev_lnkcap_speed;
|
|
||||||
+ unsigned dev_lnkcap_width;
|
|
||||||
+ unsigned rc_lnkcap_speed;
|
|
||||||
+ unsigned rc_lnksta_speed;
|
|
||||||
+ unsigned rc_target_speed;
|
|
||||||
+ u32 reg;
|
|
||||||
+
|
|
||||||
+ /* Skip Root Bridge */
|
|
||||||
+ if (!pdev->bus->self) {
|
|
||||||
+ return;
|
|
||||||
+ }
|
|
||||||
+
|
|
||||||
+ /* Skip any devices not directly connected to the RC */
|
|
||||||
+ if (pdev->bus->self->bus->number != portp->bridge->bus->number) {
|
|
||||||
+ return;
|
|
||||||
+ }
|
|
||||||
+
|
|
||||||
+ /* Skip if the bus has already been retrained */
|
|
||||||
+ if (pcie->retrained) {
|
|
||||||
+ return;
|
|
||||||
+ }
|
|
||||||
+
|
|
||||||
+ reg = dw_pcie_readl_dbi(pp, PCIE_LINK_CAPABILITIES_REG);
|
|
||||||
+ rc_lnkcap_speed = reg & PCI_EXP_LNKCAP_SLS;
|
|
||||||
+
|
|
||||||
+ reg = dw_pcie_readl_dbi(pp, PCIE_LINK_CONTROL_LINK_STATUS_REG);
|
|
||||||
+ rc_lnksta_speed = (reg & PCIE_CAP_LINK_SPEED_MASK) >>
|
|
||||||
+ PCIE_CAP_LINK_SPEED_SHIFT;
|
|
||||||
+
|
|
||||||
+ rc_target_speed = rc_lnksta_speed < 3? rc_lnksta_speed + 1 : 3;
|
|
||||||
+
|
|
||||||
+ pcie_capability_read_dword(pdev, PCI_EXP_LNKCAP, ®);
|
|
||||||
+ dev_lnkcap_speed = (reg & PCI_EXP_LNKCAP_SLS);
|
|
||||||
+ dev_lnkcap_width = (reg & PCI_EXP_LNKCAP_MLW) >> PCI_EXP_LNKSTA_NLW_SHIFT;
|
|
||||||
+
|
|
||||||
+ baikal_print_link_status(pp);
|
|
||||||
+ dev_info(&pdev->dev, "device link capability is Gen%u, x%u\n",
|
|
||||||
+ dev_lnkcap_speed, dev_lnkcap_width);
|
|
||||||
+
|
|
||||||
+ while (rc_target_speed > rc_lnksta_speed &&
|
|
||||||
+ rc_target_speed <= rc_lnkcap_speed &&
|
|
||||||
+ rc_target_speed <= dev_lnkcap_speed) {
|
|
||||||
+ /* Try to change link speed */
|
|
||||||
+ baikal_pcie_link_retrain(pp, rc_target_speed);
|
|
||||||
+ reg = dw_pcie_readl_dbi(pp, PCIE_LINK_CONTROL_LINK_STATUS_REG);
|
|
||||||
+ rc_lnksta_speed = (reg & PCIE_CAP_LINK_SPEED_MASK) >>
|
|
||||||
+ PCIE_CAP_LINK_SPEED_SHIFT;
|
|
||||||
+
|
|
||||||
+ /* Check if the targeted speed has not been reached */
|
|
||||||
+ if (rc_lnksta_speed < rc_target_speed) {
|
|
||||||
+ /* Check if the retraining has led to speed regression */
|
|
||||||
+ if (rc_lnksta_speed < (rc_target_speed - 1)) {
|
|
||||||
+ /* Fall back to the previous speed */
|
|
||||||
+ --rc_target_speed;
|
|
||||||
+ baikal_pcie_link_retrain(pp, rc_target_speed);
|
|
||||||
+ }
|
|
||||||
+
|
|
||||||
+ break;
|
|
||||||
+ }
|
|
||||||
+
|
|
||||||
+ ++rc_target_speed;
|
|
||||||
+ }
|
|
||||||
+
|
|
||||||
+ pcie->retrained = 1;
|
|
||||||
+}
|
|
||||||
+
|
|
||||||
+static void baikal_pcie_retrain_links(const struct pci_bus *bus)
|
|
||||||
+{
|
|
||||||
+ struct pci_dev *dev;
|
|
||||||
+ struct pci_bus *child;
|
|
||||||
+
|
|
||||||
+ list_for_each_entry(dev, &bus->devices, bus_list)
|
|
||||||
+ baikal_pcie_link_speed_fixup(dev);
|
|
||||||
+
|
|
||||||
+ list_for_each_entry(dev, &bus->devices, bus_list) {
|
|
||||||
+ child = dev->subordinate;
|
|
||||||
+ if (child)
|
|
||||||
+ baikal_pcie_retrain_links(child);
|
|
||||||
+ }
|
|
||||||
+}
|
|
||||||
+
|
|
||||||
+static int baikal_pcie_link_up(struct dw_pcie *pp)
|
|
||||||
+{
|
|
||||||
+ struct baikal_pcie *pcie = to_baikal_pcie(pp);
|
|
||||||
+ unsigned long reg;
|
|
||||||
+
|
|
||||||
+ reg = baikal_pcie_lcru_readl(pcie->lcru,
|
|
||||||
+ BAIKAL_LCRU_PCIE_GEN_CTL(pcie->bus_nr));
|
|
||||||
+
|
|
||||||
+ if ((reg & BAIKAL_PCIE_LTSSM_ENABLE) == 0) {
|
|
||||||
+ return 0;
|
|
||||||
+ }
|
|
||||||
+
|
|
||||||
+ reg = baikal_pcie_lcru_readl(pcie->lcru,
|
|
||||||
+ BAIKAL_LCRU_PCIE_STATUS(pcie->bus_nr));
|
|
||||||
+
|
|
||||||
+ return (reg & BAIKAL_PCIE_SMLH_LINKUP) &&
|
|
||||||
+ (reg & BAIKAL_PCIE_RDLH_LINKUP);
|
|
||||||
+}
|
|
||||||
+
|
|
||||||
+static int baikal_pcie_get_msi(struct baikal_pcie *pcie,
|
|
||||||
+ struct device_node *msi_node,
|
|
||||||
+ u64 *msi_addr)
|
|
||||||
+{
|
|
||||||
+ struct device *dev = pcie->pp.dev;
|
|
||||||
+ int ret;
|
|
||||||
+ struct resource res;
|
|
||||||
+
|
|
||||||
+ /*
|
|
||||||
+ * Check if 'msi-parent' points to ARM GICv3 ITS, which is the only
|
|
||||||
+ * supported MSI controller.
|
|
||||||
+ */
|
|
||||||
+ if (!of_device_is_compatible(msi_node, "arm,gic-v3-its")) {
|
|
||||||
+ dev_err(dev, "unable to find compatible MSI controller\n");
|
|
||||||
+ return -ENODEV;
|
|
||||||
+ }
|
|
||||||
+
|
|
||||||
+ /* Derive GITS_TRANSLATER address from GICv3 */
|
|
||||||
+ ret = of_address_to_resource(msi_node, 0, &res);
|
|
||||||
+ if (ret < 0) {
|
|
||||||
+ dev_err(dev, "unable to obtain MSI controller resources\n");
|
|
||||||
+ return ret;
|
|
||||||
+ }
|
|
||||||
+
|
|
||||||
+ *msi_addr = res.start + GITS_TRANSLATER;
|
|
||||||
+ return 0;
|
|
||||||
+}
|
|
||||||
+
|
|
||||||
+static int baikal_pcie_msi_steer(struct baikal_pcie *pcie,
|
|
||||||
+ struct device_node *msi_node)
|
|
||||||
+{
|
|
||||||
+ struct dw_pcie *pp = &pcie->pp;
|
|
||||||
+ struct device *dev = pp->dev;
|
|
||||||
+ int ret;
|
|
||||||
+ u64 msi_addr;
|
|
||||||
+
|
|
||||||
+ ret = baikal_pcie_get_msi(pcie, msi_node, &msi_addr);
|
|
||||||
+ if (ret < 0) {
|
|
||||||
+ dev_err(dev, "MSI steering failed\n");
|
|
||||||
+ return ret;
|
|
||||||
+ }
|
|
||||||
+
|
|
||||||
+ /* Program the msi_data */
|
|
||||||
+ dw_pcie_write(pp->dbi_base + PCIE_MSI_CTRL_ADDR_LO_REG, 4,
|
|
||||||
+ lower_32_bits(msi_addr));
|
|
||||||
+ dw_pcie_write(pp->dbi_base + PCIE_MSI_CTRL_ADDR_HI_REG, 4,
|
|
||||||
+ upper_32_bits(msi_addr));
|
|
||||||
+ return 0;
|
|
||||||
+}
|
|
||||||
+
|
|
||||||
+static int baikal_pcie_msi_enable(struct baikal_pcie *pcie)
|
|
||||||
+{
|
|
||||||
+ struct device *dev = pcie->pp.dev;
|
|
||||||
+ struct device_node *msi_node;
|
|
||||||
+ int ret;
|
|
||||||
+
|
|
||||||
+ /*
|
|
||||||
+ * The "msi-parent" phandle needs to exist
|
|
||||||
+ * for us to obtain the MSI node.
|
|
||||||
+ */
|
|
||||||
+ msi_node = of_parse_phandle(dev->of_node, "msi-parent", 0);
|
|
||||||
+ if (!msi_node) {
|
|
||||||
+ dev_err(dev, "failed to read msi-parent node from FDT\n");
|
|
||||||
+ return -ENODEV;
|
|
||||||
+ }
|
|
||||||
+
|
|
||||||
+ ret = baikal_pcie_msi_steer(pcie, msi_node);
|
|
||||||
+ if (ret) {
|
|
||||||
+ goto out_put_node;
|
|
||||||
+ }
|
|
||||||
+
|
|
||||||
+out_put_node:
|
|
||||||
+ of_node_put(msi_node);
|
|
||||||
+ return ret;
|
|
||||||
+}
|
|
||||||
+
|
|
||||||
+static irqreturn_t baikal_pcie_err_irq_handler(int irq, void *priv)
|
|
||||||
+{
|
|
||||||
+ struct baikal_pcie *pcie = priv;
|
|
||||||
+ struct device *dev = pcie->pp.dev;
|
|
||||||
+ unsigned long corr_err_status;
|
|
||||||
+ unsigned long dev_ctrl_dev_status;
|
|
||||||
+ unsigned long root_err_status;
|
|
||||||
+ unsigned long uncorr_err_status;
|
|
||||||
+
|
|
||||||
+ uncorr_err_status = dw_pcie_readl_dbi(&pcie->pp,
|
|
||||||
+ PCIE_UNCORR_ERR_STATUS_REG);
|
|
||||||
+ corr_err_status = dw_pcie_readl_dbi(&pcie->pp,
|
|
||||||
+ PCIE_CORR_ERR_STATUS_REG);
|
|
||||||
+ root_err_status = dw_pcie_readl_dbi(&pcie->pp,
|
|
||||||
+ PCIE_ROOT_ERR_STATUS_REG);
|
|
||||||
+ dev_ctrl_dev_status = dw_pcie_readl_dbi(&pcie->pp,
|
|
||||||
+ PCIE_DEVICE_CONTROL_DEVICE_STATUS_REG);
|
|
||||||
+ dev_err(dev,
|
|
||||||
+ "dev_err:0x%lx root_err:0x%lx uncorr_err:0x%lx corr_err:0x%lx\n",
|
|
||||||
+ (dev_ctrl_dev_status & 0xf0000) >> 16,
|
|
||||||
+ root_err_status, uncorr_err_status, corr_err_status);
|
|
||||||
+
|
|
||||||
+ dw_pcie_writel_dbi(&pcie->pp,
|
|
||||||
+ PCIE_UNCORR_ERR_STATUS_REG, uncorr_err_status);
|
|
||||||
+ dw_pcie_writel_dbi(&pcie->pp,
|
|
||||||
+ PCIE_CORR_ERR_STATUS_REG, corr_err_status);
|
|
||||||
+ dw_pcie_writel_dbi(&pcie->pp,
|
|
||||||
+ PCIE_ROOT_ERR_STATUS_REG, root_err_status);
|
|
||||||
+ dw_pcie_writel_dbi(&pcie->pp,
|
|
||||||
+ PCIE_DEVICE_CONTROL_DEVICE_STATUS_REG, dev_ctrl_dev_status);
|
|
||||||
+
|
|
||||||
+ return IRQ_HANDLED;
|
|
||||||
+}
|
|
||||||
+
|
|
||||||
+static int baikal_pcie_host_init(struct pcie_port *port)
|
|
||||||
+{
|
|
||||||
+ struct dw_pcie* pp = to_dw_pcie_from_pp(port);
|
|
||||||
+ struct device *dev = pp->dev;
|
|
||||||
+ struct baikal_pcie *pcie = to_baikal_pcie(pp);
|
|
||||||
+ int err;
|
|
||||||
+ int linkup;
|
|
||||||
+ unsigned idx;
|
|
||||||
+ unsigned long reg;
|
|
||||||
+
|
|
||||||
+ /* Disable access to PHY registers and DBI2 mode */
|
|
||||||
+ reg = baikal_pcie_lcru_readl(pcie->lcru,
|
|
||||||
+ BAIKAL_LCRU_PCIE_GEN_CTL(pcie->bus_nr));
|
|
||||||
+
|
|
||||||
+ reg &= ~(BAIKAL_PCIE_PHY_MGMT_ENABLE |
|
|
||||||
+ BAIKAL_PCIE_DBI2_MODE);
|
|
||||||
+
|
|
||||||
+ baikal_pcie_lcru_writel(pcie->lcru,
|
|
||||||
+ BAIKAL_LCRU_PCIE_GEN_CTL(pcie->bus_nr), reg);
|
|
||||||
+
|
|
||||||
+ pcie->retrained = 0;
|
|
||||||
+ linkup = baikal_pcie_link_up(pp);
|
|
||||||
+
|
|
||||||
+ /* If link is not established yet, reset the RC */
|
|
||||||
+ if (!linkup) {
|
|
||||||
+ /* Disable link training */
|
|
||||||
+ reg = baikal_pcie_lcru_readl(pcie->lcru,
|
|
||||||
+ BAIKAL_LCRU_PCIE_GEN_CTL(pcie->bus_nr));
|
|
||||||
+
|
|
||||||
+ reg &= ~BAIKAL_PCIE_LTSSM_ENABLE;
|
|
||||||
+ baikal_pcie_lcru_writel(pcie->lcru,
|
|
||||||
+ BAIKAL_LCRU_PCIE_GEN_CTL(pcie->bus_nr), reg);
|
|
||||||
+
|
|
||||||
+ /* Assert PERST pin */
|
|
||||||
+ if (pcie->reset_gpio != NULL) {
|
|
||||||
+ unsigned long gpio_flags;
|
|
||||||
+
|
|
||||||
+ if (pcie->reset_active_low) {
|
|
||||||
+ gpio_flags = GPIOF_ACTIVE_LOW |
|
|
||||||
+ GPIOF_OUT_INIT_LOW;
|
|
||||||
+ } else {
|
|
||||||
+ gpio_flags = GPIOF_OUT_INIT_HIGH;
|
|
||||||
+ }
|
|
||||||
+
|
|
||||||
+ err = devm_gpio_request_one(dev,
|
|
||||||
+ desc_to_gpio(pcie->reset_gpio),
|
|
||||||
+ gpio_flags, pcie->reset_name);
|
|
||||||
+
|
|
||||||
+ if (err) {
|
|
||||||
+ dev_err(dev, "request GPIO failed (%d)\n", err);
|
|
||||||
+ return -ENODEV;
|
|
||||||
+ }
|
|
||||||
+ }
|
|
||||||
+
|
|
||||||
+ /* Reset the RC */
|
|
||||||
+ reg = baikal_pcie_lcru_readl(pcie->lcru,
|
|
||||||
+ BAIKAL_LCRU_PCIE_RESET(pcie->bus_nr));
|
|
||||||
+
|
|
||||||
+ reg |= BAIKAL_PCIE_NONSTICKY_RST |
|
|
||||||
+ BAIKAL_PCIE_STICKY_RST |
|
|
||||||
+ BAIKAL_PCIE_PWR_RST |
|
|
||||||
+ BAIKAL_PCIE_CORE_RST |
|
|
||||||
+ BAIKAL_PCIE_PHY_RESET;
|
|
||||||
+
|
|
||||||
+ /* If the RC is PCIe x8, reset PIPE0 and PIPE1 */
|
|
||||||
+ if (pcie->bus_nr == 2) {
|
|
||||||
+ reg |= BAIKAL_PCIE_PIPE0_RESET |
|
|
||||||
+ BAIKAL_PCIE_PIPE1_RESET;
|
|
||||||
+ } else {
|
|
||||||
+ reg |= BAIKAL_PCIE_PIPE_RESET;
|
|
||||||
+ }
|
|
||||||
+
|
|
||||||
+ baikal_pcie_lcru_writel(pcie->lcru,
|
|
||||||
+ BAIKAL_LCRU_PCIE_RESET(pcie->bus_nr), reg);
|
|
||||||
+
|
|
||||||
+ usleep_range(20000, 30000);
|
|
||||||
+
|
|
||||||
+ if (pcie->reset_gpio != NULL) {
|
|
||||||
+ /* Deassert PERST pin */
|
|
||||||
+ gpiod_set_value_cansleep(pcie->reset_gpio, 0);
|
|
||||||
+ }
|
|
||||||
+
|
|
||||||
+ /* Deassert PHY reset */
|
|
||||||
+ reg = baikal_pcie_lcru_readl(pcie->lcru,
|
|
||||||
+ BAIKAL_LCRU_PCIE_RESET(pcie->bus_nr));
|
|
||||||
+
|
|
||||||
+ reg &= ~BAIKAL_PCIE_PHY_RESET;
|
|
||||||
+ baikal_pcie_lcru_writel(pcie->lcru,
|
|
||||||
+ BAIKAL_LCRU_PCIE_RESET(pcie->bus_nr), reg);
|
|
||||||
+
|
|
||||||
+ /* Deassert all software controlled resets */
|
|
||||||
+ reg = baikal_pcie_lcru_readl(pcie->lcru,
|
|
||||||
+ BAIKAL_LCRU_PCIE_RESET(pcie->bus_nr));
|
|
||||||
+
|
|
||||||
+ reg &= ~(BAIKAL_PCIE_ADB_PWRDWN |
|
|
||||||
+ BAIKAL_PCIE_HOT_RESET |
|
|
||||||
+ BAIKAL_PCIE_NONSTICKY_RST |
|
|
||||||
+ BAIKAL_PCIE_STICKY_RST |
|
|
||||||
+ BAIKAL_PCIE_PWR_RST |
|
|
||||||
+ BAIKAL_PCIE_CORE_RST |
|
|
||||||
+ BAIKAL_PCIE_PHY_RESET);
|
|
||||||
+
|
|
||||||
+ if (pcie->bus_nr == 2) {
|
|
||||||
+ reg &= ~(BAIKAL_PCIE_PIPE0_RESET |
|
|
||||||
+ BAIKAL_PCIE_PIPE1_RESET);
|
|
||||||
+ } else {
|
|
||||||
+ reg &= ~BAIKAL_PCIE_PIPE_RESET;
|
|
||||||
+ }
|
|
||||||
+
|
|
||||||
+ baikal_pcie_lcru_writel(pcie->lcru,
|
|
||||||
+ BAIKAL_LCRU_PCIE_RESET(pcie->bus_nr), reg);
|
|
||||||
+ }
|
|
||||||
+
|
|
||||||
+ /* Deinitialise all iATU regions */
|
|
||||||
+// for (idx = 0; idx < pp->num_viewport; ++idx) {
|
|
||||||
+// dw_pcie_writel_dbi(pp, PCIE_IATU_VIEWPORT_REG,
|
|
||||||
+// PCIE_IATU_REGION_OUTBOUND | idx);
|
|
||||||
+// dw_pcie_writel_dbi(pp, PCIE_IATU_REGION_CTRL_2_REG, 0);
|
|
||||||
+// }
|
|
||||||
+
|
|
||||||
+ /*
|
|
||||||
+ * Enable writing to config regs. This is required as the DW driver
|
|
||||||
+ * changes the class code. That register needs DBI write enable.
|
|
||||||
+ */
|
|
||||||
+ reg = dw_pcie_readl_dbi(pp, PCIE_MISC_CONTROL_1_REG);
|
|
||||||
+ reg |= PCIE_DBI_RO_RW_EN;
|
|
||||||
+ dw_pcie_writel_dbi(pp, PCIE_MISC_CONTROL_1_REG, reg);
|
|
||||||
+
|
|
||||||
+ dw_pcie_setup_rc(port);
|
|
||||||
+
|
|
||||||
+ /* Set prog-if = 1 */
|
|
||||||
+ reg = dw_pcie_readl_dbi(pp, PCI_CLASS_REVISION);
|
|
||||||
+ reg = (1 << 8) | (reg & 0xffff00ff);
|
|
||||||
+ dw_pcie_writel_dbi(pp, PCI_CLASS_REVISION, reg);
|
|
||||||
+
|
|
||||||
+ /* Set max link width in accordance with 'num-lanes' value */
|
|
||||||
+ reg = dw_pcie_readl_dbi(pp, PCIE_LINK_CAPABILITIES_REG);
|
|
||||||
+ reg &= ~PCIE_CAP_MAX_LINK_WIDTH_MASK;
|
|
||||||
+ reg |= (pcie->num_lanes) << PCIE_CAP_MAX_LINK_WIDTH_SHIFT;
|
|
||||||
+ dw_pcie_writel_dbi(pp, PCIE_LINK_CAPABILITIES_REG, reg);
|
|
||||||
+
|
|
||||||
+ /* Disable writing to config regs */
|
|
||||||
+ reg = dw_pcie_readl_dbi(pp, PCIE_MISC_CONTROL_1_REG);
|
|
||||||
+ reg &= ~PCIE_DBI_RO_RW_EN;
|
|
||||||
+ dw_pcie_writel_dbi(pp, PCIE_MISC_CONTROL_1_REG, reg);
|
|
||||||
+
|
|
||||||
+ if (IS_ENABLED(CONFIG_PCI_MSI)) {
|
|
||||||
+ err = baikal_pcie_msi_enable(pcie);
|
|
||||||
+ if (err) {
|
|
||||||
+ dev_err(pp->dev, "failed to initialize MSI\n");
|
|
||||||
+ return -EIO;
|
|
||||||
+ }
|
|
||||||
+ }
|
|
||||||
+
|
|
||||||
+ /* Enable error reporting */
|
|
||||||
+ reg = dw_pcie_readl_dbi(pp, PCIE_ROOT_ERR_CMD_REG);
|
|
||||||
+ reg |= PCIE_CORR_ERR_REPORTING_EN |
|
|
||||||
+ PCIE_NON_FATAL_ERR_REPORTING_EN |
|
|
||||||
+ PCIE_FATAL_ERR_REPORTING_EN;
|
|
||||||
+ dw_pcie_writel_dbi(pp, PCIE_ROOT_ERR_CMD_REG, reg);
|
|
||||||
+
|
|
||||||
+ reg = dw_pcie_readl_dbi(pp, PCIE_DEVICE_CONTROL_DEVICE_STATUS_REG);
|
|
||||||
+ reg |= PCIE_CAP_CORR_ERR_REPORT_EN |
|
|
||||||
+ PCIE_CAP_NON_FATAL_ERR_REPORT_EN |
|
|
||||||
+ PCIE_CAP_FATAL_ERR_REPORT_EN |
|
|
||||||
+ PCIE_CAP_UNSUPPORT_REQ_REP_EN;
|
|
||||||
+ dw_pcie_writel_dbi(pp, PCIE_DEVICE_CONTROL_DEVICE_STATUS_REG, reg);
|
|
||||||
+
|
|
||||||
+ reg = dw_pcie_readl_dbi(pp, PCIE_ROOT_CONTROL_ROOT_CAPABILITIES_REG);
|
|
||||||
+ reg |= PCIE_CAP_SYS_ERR_ON_CORR_ERR_EN |
|
|
||||||
+ PCIE_CAP_SYS_ERR_ON_NON_FATAL_ERR_EN |
|
|
||||||
+ PCIE_CAP_SYS_ERR_ON_FATAL_ERR_EN |
|
|
||||||
+ PCIE_CAP_PME_INT_EN;
|
|
||||||
+ dw_pcie_writel_dbi(pp, PCIE_ROOT_CONTROL_ROOT_CAPABILITIES_REG, reg);
|
|
||||||
+
|
|
||||||
+ if (linkup) {
|
|
||||||
+ dev_info(dev, "link is already up\n");
|
|
||||||
+ return 0;
|
|
||||||
+ }
|
|
||||||
+
|
|
||||||
+ /* Use Gen1 mode for link establishing */
|
|
||||||
+ reg = dw_pcie_readl_dbi(pp, PCIE_LINK_CONTROL2_LINK_STATUS2_REG);
|
|
||||||
+ reg &= ~PCIE_CAP_TARGET_LINK_SPEED_MASK;
|
|
||||||
+ reg |= 1;
|
|
||||||
+ dw_pcie_writel_dbi(pp, PCIE_LINK_CONTROL2_LINK_STATUS2_REG, reg);
|
|
||||||
+
|
|
||||||
+ /*
|
|
||||||
+ * Clear DIRECT_SPEED_CHANGE bit. It has been set by dw_pcie_setup_rc.
|
|
||||||
+ * This bit causes link retraining. However, link retraining should be
|
|
||||||
+ * performed later by calling a speed fixup function.
|
|
||||||
+ */
|
|
||||||
+ reg = dw_pcie_readl_dbi(pp, PCIE_GEN2_CTRL_REG);
|
|
||||||
+ reg &= ~PCIE_DIRECT_SPEED_CHANGE;
|
|
||||||
+ dw_pcie_writel_dbi(pp, PCIE_GEN2_CTRL_REG, reg);
|
|
||||||
+
|
|
||||||
+ /* Establish link */
|
|
||||||
+ reg = baikal_pcie_lcru_readl(pcie->lcru,
|
|
||||||
+ BAIKAL_LCRU_PCIE_GEN_CTL(pcie->bus_nr));
|
|
||||||
+
|
|
||||||
+ reg |= BAIKAL_PCIE_LTSSM_ENABLE;
|
|
||||||
+ baikal_pcie_lcru_writel(pcie->lcru,
|
|
||||||
+ BAIKAL_LCRU_PCIE_GEN_CTL(pcie->bus_nr), reg);
|
|
||||||
+
|
|
||||||
+ dw_pcie_wait_for_link(pp);
|
|
||||||
+ /* XXX:
|
|
||||||
+ * - return OK even if the link is down
|
|
||||||
+ * - on error dw_pcie_wait_for_link prints a warning on its own,
|
|
||||||
+ * there's no need to print more messages here
|
|
||||||
+ */
|
|
||||||
+ return 0;
|
|
||||||
+}
|
|
||||||
+
|
|
||||||
+static int baikal_pcie_msi_host_init(struct pcie_port *pp)
|
|
||||||
+{
|
|
||||||
+ struct dw_pcie* pcie = to_dw_pcie_from_pp(pp);
|
|
||||||
+ struct device *dev = pcie->dev;
|
|
||||||
+ struct device_node *np = dev->of_node;
|
|
||||||
+ struct device_node *msi_node;
|
|
||||||
+
|
|
||||||
+ /*
|
|
||||||
+ * The MSI domain is set by the generic of_msi_configure(). This
|
|
||||||
+ * .msi_host_init() function keeps us from doing the default MSI domain
|
|
||||||
+ * setup in dw_pcie_host_init() and also enforces the requirement that
|
|
||||||
+ * "msi-parent" exists.
|
|
||||||
+ */
|
|
||||||
+ msi_node = of_parse_phandle(np, "msi-parent", 0);
|
|
||||||
+ if (!msi_node) {
|
|
||||||
+ dev_err(dev, "failed to find msi-parent\n");
|
|
||||||
+ return -EINVAL;
|
|
||||||
+ }
|
|
||||||
+
|
|
||||||
+ return 0;
|
|
||||||
+}
|
|
||||||
+
|
|
||||||
+static const struct dw_pcie_host_ops baikal_pcie_host_ops = {
|
|
||||||
+ .host_init = baikal_pcie_host_init,
|
|
||||||
+ .msi_host_init = baikal_pcie_msi_host_init,
|
|
||||||
+};
|
|
||||||
+
|
|
||||||
+static const struct dw_pcie_ops baikal_pcie_ops = {
|
|
||||||
+ .link_up = baikal_pcie_link_up,
|
|
||||||
+};
|
|
||||||
+
|
|
||||||
+static int baikal_add_pcie_port(struct baikal_pcie *pcie,
|
|
||||||
+ struct platform_device *pdev)
|
|
||||||
+{
|
|
||||||
+ struct dw_pcie *dw_pci = &pcie->pp;
|
|
||||||
+ struct pcie_port *pp = &dw_pci->pp;
|
|
||||||
+ struct resource *res;
|
|
||||||
+ int irq;
|
|
||||||
+ int ret;
|
|
||||||
+
|
|
||||||
+ dw_pci->dev = &pdev->dev;
|
|
||||||
+
|
|
||||||
+ res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "dbi");
|
|
||||||
+ if (res) {
|
|
||||||
+ devm_request_resource(dw_pci->dev, &iomem_resource, res);
|
|
||||||
+ dw_pci->dbi_base = devm_ioremap_resource(dw_pci->dev, res);
|
|
||||||
+ if (IS_ERR(dw_pci->dbi_base)) {
|
|
||||||
+ dev_err(dw_pci->dev, "error with ioremap\n");
|
|
||||||
+ return -ENOMEM;
|
|
||||||
+ }
|
|
||||||
+ } else {
|
|
||||||
+ dev_err(dw_pci->dev, "missing *dbi* reg space\n");
|
|
||||||
+ return -EINVAL;
|
|
||||||
+ }
|
|
||||||
+
|
|
||||||
+ irq = platform_get_irq(pdev, 0);
|
|
||||||
+ if (irq < 0) {
|
|
||||||
+ dev_err(dw_pci->dev, "missing IRQ resource: %d\n", irq);
|
|
||||||
+ return irq;
|
|
||||||
+ }
|
|
||||||
+
|
|
||||||
+ ret = devm_request_irq(dw_pci->dev, irq, baikal_pcie_err_irq_handler,
|
|
||||||
+ IRQF_SHARED | IRQF_NO_THREAD,
|
|
||||||
+ "baikal-pcie-error-irq", pcie);
|
|
||||||
+ if (ret) {
|
|
||||||
+ dev_err(dw_pci->dev, "failed to request IRQ\n");
|
|
||||||
+ return ret;
|
|
||||||
+ }
|
|
||||||
+
|
|
||||||
+ pp->ops = &baikal_pcie_host_ops;
|
|
||||||
+ ret = dw_pcie_host_init(pp);
|
|
||||||
+ if (ret) {
|
|
||||||
+ dev_err(dw_pci->dev, "failed to initialize host\n");
|
|
||||||
+ return ret;
|
|
||||||
+ }
|
|
||||||
+ baikal_pcie_retrain_links(pp->bridge->bus);
|
|
||||||
+
|
|
||||||
+ return 0;
|
|
||||||
+}
|
|
||||||
+
|
|
||||||
+static bool has_incompat_firmware(void) {
|
|
||||||
+ bool gotcha = false;
|
|
||||||
+ struct device_node *np = NULL;
|
|
||||||
+ np = of_find_node_by_path("/soc");
|
|
||||||
+ if (np) {
|
|
||||||
+ of_node_put(np);
|
|
||||||
+ } else {
|
|
||||||
+ gotcha = true;
|
|
||||||
+ }
|
|
||||||
+ return gotcha;
|
|
||||||
+}
|
|
||||||
+
|
|
||||||
+static int baikal_pcie_probe(struct platform_device *pdev)
|
|
||||||
+{
|
|
||||||
+ struct device *dev = &pdev->dev;
|
|
||||||
+ struct baikal_pcie *pcie;
|
|
||||||
+ int err;
|
|
||||||
+ u32 index[2];
|
|
||||||
+ enum of_gpio_flags gpio_flags;
|
|
||||||
+ int reset_gpio;
|
|
||||||
+
|
|
||||||
+ if (has_incompat_firmware()) {
|
|
||||||
+ dev_err(dev, "detected incompatible firmware, bailing out\n");
|
|
||||||
+ return -EINVAL;
|
|
||||||
+ }
|
|
||||||
+ if (!of_match_device(of_baikal_pcie_match, dev)) {
|
|
||||||
+ return -EINVAL;
|
|
||||||
+ }
|
|
||||||
+
|
|
||||||
+ pcie = devm_kzalloc(dev, sizeof(*pcie), GFP_KERNEL);
|
|
||||||
+ if (!pcie) {
|
|
||||||
+ return -ENOMEM;
|
|
||||||
+ }
|
|
||||||
+ pcie->pp.dev = dev;
|
|
||||||
+ pcie->pp.ops = &baikal_pcie_ops;
|
|
||||||
+
|
|
||||||
+ err = of_property_read_u32(dev->of_node, "num-lanes", &pcie->num_lanes);
|
|
||||||
+ if (err) {
|
|
||||||
+ dev_err(dev, "property num-lanes isn't found\n");
|
|
||||||
+ return -EINVAL;
|
|
||||||
+ }
|
|
||||||
+ pcie->lcru = syscon_regmap_lookup_by_phandle(dev->of_node,
|
|
||||||
+ "baikal,pcie-lcru");
|
|
||||||
+ if (IS_ERR(pcie->lcru)) {
|
|
||||||
+ dev_err(dev, "No LCRU phandle specified\n");
|
|
||||||
+ pcie->lcru = NULL;
|
|
||||||
+ return -EINVAL;
|
|
||||||
+ }
|
|
||||||
+
|
|
||||||
+ if (of_property_read_u32_array(dev->of_node, "baikal,pcie-lcru", index, 2)) {
|
|
||||||
+ pcie->lcru = NULL;
|
|
||||||
+ return -EINVAL;
|
|
||||||
+ }
|
|
||||||
+
|
|
||||||
+ pcie->bus_nr = index[1];
|
|
||||||
+
|
|
||||||
+ pm_runtime_enable(dev);
|
|
||||||
+ err = pm_runtime_get_sync(dev);
|
|
||||||
+ if (err < 0) {
|
|
||||||
+ dev_err(dev, "pm_runtime_get_sync failed\n");
|
|
||||||
+ goto err_pm_disable;
|
|
||||||
+ }
|
|
||||||
+
|
|
||||||
+ reset_gpio = of_get_named_gpio_flags(dev->of_node, "reset-gpios", 0,
|
|
||||||
+ &gpio_flags);
|
|
||||||
+ if (gpio_is_valid(reset_gpio)) {
|
|
||||||
+ pcie->reset_gpio = gpio_to_desc(reset_gpio);
|
|
||||||
+ pcie->reset_active_low = gpio_flags & OF_GPIO_ACTIVE_LOW;
|
|
||||||
+ snprintf(pcie->reset_name, sizeof pcie->reset_name,
|
|
||||||
+ "pcie%u-reset", pcie->bus_nr);
|
|
||||||
+ } else {
|
|
||||||
+ pcie->reset_gpio = NULL;
|
|
||||||
+ }
|
|
||||||
+
|
|
||||||
+ err = baikal_add_pcie_port(pcie, pdev);
|
|
||||||
+ if (err < 0) {
|
|
||||||
+ goto err_pm_put;
|
|
||||||
+ }
|
|
||||||
+
|
|
||||||
+ platform_set_drvdata(pdev, pcie);
|
|
||||||
+ return 0;
|
|
||||||
+
|
|
||||||
+err_pm_put:
|
|
||||||
+ pm_runtime_put(dev);
|
|
||||||
+
|
|
||||||
+err_pm_disable:
|
|
||||||
+ pm_runtime_disable(dev);
|
|
||||||
+
|
|
||||||
+ return err;
|
|
||||||
+}
|
|
||||||
+
|
|
||||||
+#ifdef CONFIG_PM_SLEEP
|
|
||||||
+static int baikal_pcie_suspend(struct device *dev)
|
|
||||||
+{
|
|
||||||
+ struct baikal_pcie *pcie = dev_get_drvdata(dev);
|
|
||||||
+ struct dw_pcie *pp = &pcie->pp;
|
|
||||||
+ u32 val;
|
|
||||||
+
|
|
||||||
+ /* Clear Memory Space Enable (MSE) bit */
|
|
||||||
+ val = dw_pcie_readl_dbi(pp, PCI_COMMAND);
|
|
||||||
+ val &= ~PCI_COMMAND_MEMORY;
|
|
||||||
+ dw_pcie_writel_dbi(pp, PCI_COMMAND, val);
|
|
||||||
+ return 0;
|
|
||||||
+}
|
|
||||||
+
|
|
||||||
+static int baikal_pcie_resume(struct device *dev)
|
|
||||||
+{
|
|
||||||
+ struct baikal_pcie *pcie = dev_get_drvdata(dev);
|
|
||||||
+ struct dw_pcie *pp = &pcie->pp;
|
|
||||||
+ u32 val;
|
|
||||||
+
|
|
||||||
+ /* Set Memory Space Enable (MSE) bit */
|
|
||||||
+ val = dw_pcie_readl_dbi(pp, PCI_COMMAND);
|
|
||||||
+ val |= PCI_COMMAND_MEMORY;
|
|
||||||
+ dw_pcie_writel_dbi(pp, PCI_COMMAND, val);
|
|
||||||
+ return 0;
|
|
||||||
+}
|
|
||||||
+
|
|
||||||
+static int baikal_pcie_suspend_noirq(struct device *dev)
|
|
||||||
+{
|
|
||||||
+ return 0;
|
|
||||||
+}
|
|
||||||
+
|
|
||||||
+static int baikal_pcie_resume_noirq(struct device *dev)
|
|
||||||
+{
|
|
||||||
+ return 0;
|
|
||||||
+}
|
|
||||||
+#endif
|
|
||||||
+
|
|
||||||
+static const struct dev_pm_ops baikal_pcie_pm_ops = {
|
|
||||||
+ SET_SYSTEM_SLEEP_PM_OPS(baikal_pcie_suspend, baikal_pcie_resume)
|
|
||||||
+ SET_NOIRQ_SYSTEM_SLEEP_PM_OPS(baikal_pcie_suspend_noirq,
|
|
||||||
+ baikal_pcie_resume_noirq)
|
|
||||||
+};
|
|
||||||
+
|
|
||||||
+static struct platform_driver baikal_pcie_driver = {
|
|
||||||
+ .driver = {
|
|
||||||
+ .name = "baikal-pcie-v2",
|
|
||||||
+ .of_match_table = of_baikal_pcie_match,
|
|
||||||
+ .suppress_bind_attrs = true,
|
|
||||||
+ .pm = &baikal_pcie_pm_ops,
|
|
||||||
+ },
|
|
||||||
+ .probe = baikal_pcie_probe,
|
|
||||||
+};
|
|
||||||
+
|
|
||||||
+MODULE_DEVICE_TABLE(of, of_baikal_pcie_match);
|
|
||||||
+module_platform_driver(baikal_pcie_driver);
|
|
||||||
+MODULE_LICENSE("GPL v2");
|
|
808
0618-Added-TF307-TF306-board-management-controller-driver.patch
Normal file
808
0618-Added-TF307-TF306-board-management-controller-driver.patch
Normal file
|
@ -0,0 +1,808 @@
|
||||||
|
From bc1902da2625cbb283b6ee6fc512206d8e75689c Mon Sep 17 00:00:00 2001
|
||||||
|
From: Alexey Sheplyakov <asheplyakov@altlinux.org>
|
||||||
|
Date: Tue, 9 Jun 2020 10:37:23 +0400
|
||||||
|
Subject: [PATCH 618/634] Added TF307/TF306 board management controller driver
|
||||||
|
|
||||||
|
The board management controller (BMC) device is responsible for CPU
|
||||||
|
kick-starting, controlling power button, and a full board poweroff.
|
||||||
|
---
|
||||||
|
drivers/misc/Kconfig | 18 +
|
||||||
|
drivers/misc/Makefile | 1 +
|
||||||
|
drivers/misc/tp_bmc.c | 747 ++++++++++++++++++++++++++++++++++++++++++
|
||||||
|
3 files changed, 766 insertions(+)
|
||||||
|
create mode 100644 drivers/misc/tp_bmc.c
|
||||||
|
|
||||||
|
diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig
|
||||||
|
index 0f5a49fc7..42a679533 100644
|
||||||
|
--- a/drivers/misc/Kconfig
|
||||||
|
+++ b/drivers/misc/Kconfig
|
||||||
|
@@ -470,6 +470,24 @@ config HISI_HIKEY_USB
|
||||||
|
switching between the dual-role USB-C port and the USB-A host ports
|
||||||
|
using only one USB controller.
|
||||||
|
|
||||||
|
+config TP_BMC
|
||||||
|
+ tristate "TF307/TF306 board management controller"
|
||||||
|
+ depends on I2C
|
||||||
|
+ depends on OF
|
||||||
|
+ select PINCTRL
|
||||||
|
+ select GENERIC_PINCONF
|
||||||
|
+ select SERIO
|
||||||
|
+ default y if ARCH_BAIKAL
|
||||||
|
+ help
|
||||||
|
+ Say Y here if you want to build a driver for BMC devices embedded into
|
||||||
|
+ some boards with Baikal BE-M1000 and BE-T1000 processors. The device main
|
||||||
|
+ purpose is the CPU kick-starting as well as some additional side-way
|
||||||
|
+ functionality like power on/off buttons state tracing and full device
|
||||||
|
+ powering off.
|
||||||
|
+
|
||||||
|
+ If you choose to build module, its name will be tp-bmc. If unsure,
|
||||||
|
+ say N here.
|
||||||
|
+
|
||||||
|
source "drivers/misc/c2port/Kconfig"
|
||||||
|
source "drivers/misc/eeprom/Kconfig"
|
||||||
|
source "drivers/misc/cb710/Kconfig"
|
||||||
|
diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile
|
||||||
|
index a086197af..ae7bd2a8c 100644
|
||||||
|
--- a/drivers/misc/Makefile
|
||||||
|
+++ b/drivers/misc/Makefile
|
||||||
|
@@ -59,3 +59,4 @@ obj-$(CONFIG_UACCE) += uacce/
|
||||||
|
obj-$(CONFIG_XILINX_SDFEC) += xilinx_sdfec.o
|
||||||
|
obj-$(CONFIG_HISI_HIKEY_USB) += hisi_hikey_usb.o
|
||||||
|
obj-$(CONFIG_HI6421V600_IRQ) += hi6421v600-irq.o
|
||||||
|
+obj-$(CONFIG_TP_BMC) += tp_bmc.o
|
||||||
|
diff --git a/drivers/misc/tp_bmc.c b/drivers/misc/tp_bmc.c
|
||||||
|
new file mode 100644
|
||||||
|
index 000000000..0b320d3ff
|
||||||
|
--- /dev/null
|
||||||
|
+++ b/drivers/misc/tp_bmc.c
|
||||||
|
@@ -0,0 +1,747 @@
|
||||||
|
+#include <linux/i2c.h>
|
||||||
|
+#include <linux/bcd.h>
|
||||||
|
+#include <linux/slab.h>
|
||||||
|
+#include <linux/module.h>
|
||||||
|
+#include <linux/of.h>
|
||||||
|
+#include <linux/input.h>
|
||||||
|
+#include <linux/regmap.h>
|
||||||
|
+#include <linux/delay.h>
|
||||||
|
+#include <linux/kthread.h>
|
||||||
|
+#include <linux/pm.h>
|
||||||
|
+#include <linux/rtc.h>
|
||||||
|
+#include <linux/serio.h>
|
||||||
|
+#include <linux/platform_device.h>
|
||||||
|
+#include <linux/pinctrl/pinctrl.h>
|
||||||
|
+#include <linux/pinctrl/pinconf.h>
|
||||||
|
+#include <linux/pinctrl/pinconf-generic.h>
|
||||||
|
+
|
||||||
|
+enum I2C_REGS {
|
||||||
|
+ R_ID1 = 0,
|
||||||
|
+ R_ID2,
|
||||||
|
+ R_ID3,
|
||||||
|
+ R_ID4,
|
||||||
|
+ R_SOFTOFF_RQ,
|
||||||
|
+ R_PWROFF_RQ,
|
||||||
|
+ R_PWRBTN_STATE,
|
||||||
|
+ R_VERSION1,
|
||||||
|
+ R_VERSION2,
|
||||||
|
+ R_BOOTREASON,
|
||||||
|
+ R_BOOTREASON_ARG,
|
||||||
|
+ R_SCRATCH1,
|
||||||
|
+ R_SCRATCH2,
|
||||||
|
+ R_SCRATCH3,
|
||||||
|
+ R_SCRATCH4,
|
||||||
|
+ R_CAP,
|
||||||
|
+ R_GPIODIR0,
|
||||||
|
+ R_GPIODIR1,
|
||||||
|
+ R_GPIODIR2,
|
||||||
|
+ R_COUNT
|
||||||
|
+};
|
||||||
|
+
|
||||||
|
+#define BMC_ID1_VAL 0x49
|
||||||
|
+#define BMC_ID2_VAL 0x54
|
||||||
|
+#define BMC_ID3_VAL 0x58
|
||||||
|
+#define BMC_ID4_VAL0 0x32
|
||||||
|
+#define BMC_ID4_VAL1 0x2
|
||||||
|
+
|
||||||
|
+#define BMC_VERSION1 0
|
||||||
|
+#define BMC_VERSION2 2
|
||||||
|
+#define BMC_VERSION2_3 3
|
||||||
|
+
|
||||||
|
+#define BMC_CAP_PWRBTN 0x1
|
||||||
|
+#define BMC_CAP_TOUCHPAD 0x2
|
||||||
|
+#define BMC_CAP_RTC 0x4
|
||||||
|
+#define BMC_CAP_FRU 0x8
|
||||||
|
+#define BMC_CAP_GPIODIR 0x10
|
||||||
|
+
|
||||||
|
+#define BMC_SERIO_BUFSIZE 7
|
||||||
|
+
|
||||||
|
+#define POLL_JIFFIES 100
|
||||||
|
+
|
||||||
|
+struct bmc_poll_data {
|
||||||
|
+ struct i2c_client *c;
|
||||||
|
+};
|
||||||
|
+
|
||||||
|
+static struct i2c_client *bmc_i2c;
|
||||||
|
+static struct i2c_client *rtc_i2c;
|
||||||
|
+static struct i2c_driver mitx2_bmc_i2c_driver;
|
||||||
|
+static struct input_dev *button_dev;
|
||||||
|
+static struct bmc_poll_data poll_data;
|
||||||
|
+static struct task_struct *polling_task;
|
||||||
|
+#ifdef CONFIG_SERIO
|
||||||
|
+static struct i2c_client *serio_i2c;
|
||||||
|
+static struct task_struct *touchpad_task;
|
||||||
|
+#endif
|
||||||
|
+static u8 bmc_proto_version[3];
|
||||||
|
+static u8 bmc_bootreason[2];
|
||||||
|
+static u8 bmc_scratch[4];
|
||||||
|
+static int bmc_cap;
|
||||||
|
+static const char input_name[] = "BMC input dev";
|
||||||
|
+static u8 prev_ret;
|
||||||
|
+
|
||||||
|
+/* BMC RTC */
|
||||||
|
+static int
|
||||||
|
+bmc_rtc_read_time(struct device *dev, struct rtc_time *tm)
|
||||||
|
+{
|
||||||
|
+ struct i2c_client *client = to_i2c_client(dev);
|
||||||
|
+ uint8_t rtc_buf[8];
|
||||||
|
+ struct i2c_msg msg;
|
||||||
|
+ int t;
|
||||||
|
+ int rc;
|
||||||
|
+
|
||||||
|
+ msg.addr = client->addr;
|
||||||
|
+ msg.flags = I2C_M_RD;
|
||||||
|
+ msg.len = 8;
|
||||||
|
+ msg.buf = rtc_buf;
|
||||||
|
+ rc = i2c_transfer(client->adapter, &msg, 1);
|
||||||
|
+ if (rc != 1) {
|
||||||
|
+ dev_err(dev, "rtc_read_time: i2c_transfer error %d\n", rc);
|
||||||
|
+ return rc;
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ tm->tm_sec = bcd2bin(rtc_buf[0] & 0x7f);
|
||||||
|
+ tm->tm_min = bcd2bin(rtc_buf[1] & 0x7f);
|
||||||
|
+ tm->tm_hour = bcd2bin(rtc_buf[2] & 0x3f);
|
||||||
|
+ if (rtc_buf[3] & (1 << 6)) /* PM */
|
||||||
|
+ tm->tm_hour += 12;
|
||||||
|
+ tm->tm_mday = bcd2bin(rtc_buf[4] & 0x3f);
|
||||||
|
+ tm->tm_mon = bcd2bin(rtc_buf[5] & 0x1f);
|
||||||
|
+ t = rtc_buf[5] >> 5;
|
||||||
|
+ tm->tm_wday = (t == 7) ? 0 : t;
|
||||||
|
+ tm->tm_year = bcd2bin(rtc_buf[6]) + 100; /* year since 1900 */
|
||||||
|
+ tm->tm_yday = rtc_year_days(tm->tm_mday, tm->tm_mon, tm->tm_year);
|
||||||
|
+ tm->tm_isdst = 0;
|
||||||
|
+
|
||||||
|
+ return rtc_valid_tm(tm);
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
+static int
|
||||||
|
+bmc_rtc_set_time(struct device *dev, struct rtc_time *tm)
|
||||||
|
+{
|
||||||
|
+ struct i2c_client *client = to_i2c_client(dev);
|
||||||
|
+ uint8_t rtc_buf[8];
|
||||||
|
+ struct i2c_msg msg;
|
||||||
|
+ int rc;
|
||||||
|
+ uint8_t seconds, minutes, hours, wday, mday, month, years;
|
||||||
|
+
|
||||||
|
+ seconds = bin2bcd(tm->tm_sec);
|
||||||
|
+ minutes = bin2bcd(tm->tm_min);
|
||||||
|
+ hours = bin2bcd(tm->tm_hour);
|
||||||
|
+ wday = tm->tm_wday ? tm->tm_wday : 0x7;
|
||||||
|
+ mday = bin2bcd(tm->tm_mday);
|
||||||
|
+ month = bin2bcd(tm->tm_mon);
|
||||||
|
+ years = bin2bcd(tm->tm_year % 100);
|
||||||
|
+
|
||||||
|
+ /* Need sanity check??? */
|
||||||
|
+ rtc_buf[0] = seconds;
|
||||||
|
+ rtc_buf[1] = minutes;
|
||||||
|
+ rtc_buf[2] = hours;
|
||||||
|
+ rtc_buf[3] = 0;
|
||||||
|
+ rtc_buf[4] = mday;
|
||||||
|
+ rtc_buf[5] = month | (wday << 5);
|
||||||
|
+ rtc_buf[6] = years;
|
||||||
|
+ rtc_buf[7] = 0;
|
||||||
|
+
|
||||||
|
+ msg.addr = client->addr;
|
||||||
|
+ msg.flags = 0;
|
||||||
|
+ msg.len = 8;
|
||||||
|
+ msg.buf = rtc_buf;
|
||||||
|
+ dev_dbg(dev, "rtc_set_time: %08x-%08x\n", *(uint32_t *)&rtc_buf[0],
|
||||||
|
+ *(uint32_t *)&rtc_buf[4]);
|
||||||
|
+ rc = i2c_transfer(client->adapter, &msg, 1);
|
||||||
|
+ if (rc != 1)
|
||||||
|
+ dev_err(dev, "i2c write: %d\n", rc);
|
||||||
|
+
|
||||||
|
+ return (rc == 1) ? 0 : -EIO;
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
+static const struct rtc_class_ops
|
||||||
|
+bmc_rtc_ops = {
|
||||||
|
+ .read_time = bmc_rtc_read_time,
|
||||||
|
+ .set_time = bmc_rtc_set_time,
|
||||||
|
+};
|
||||||
|
+
|
||||||
|
+#ifdef CONFIG_SERIO
|
||||||
|
+/* BMC serio (PS/2 touchpad) interface */
|
||||||
|
+
|
||||||
|
+static int bmc_serio_write(struct serio *id, unsigned char val)
|
||||||
|
+{
|
||||||
|
+ struct i2c_client *client = id->port_data;
|
||||||
|
+ uint8_t buf[4];
|
||||||
|
+ struct i2c_msg msg;
|
||||||
|
+ int rc;
|
||||||
|
+
|
||||||
|
+ buf[0] = val;
|
||||||
|
+ msg.addr = client->addr;
|
||||||
|
+ msg.flags = 0;
|
||||||
|
+ msg.len = 1;
|
||||||
|
+ msg.buf = buf;
|
||||||
|
+ dev_dbg(&client->dev, "bmc_serio_write: %02x\n", val);
|
||||||
|
+ rc = i2c_transfer(client->adapter, &msg, 1);
|
||||||
|
+ if (rc != 1)
|
||||||
|
+ dev_err(&client->dev, "i2c write: %d\n", rc);
|
||||||
|
+
|
||||||
|
+ return (rc == 1) ? 0 : -EIO;
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
+/* returns: -1 on error, +1 if more data available, 0 otherwise */
|
||||||
|
+static int bmc_serio_read(struct i2c_client *client)
|
||||||
|
+{
|
||||||
|
+ struct serio *serio = dev_get_drvdata(&client->dev);
|
||||||
|
+ int i, rc, cnt;
|
||||||
|
+ uint8_t buf[BMC_SERIO_BUFSIZE];
|
||||||
|
+ struct i2c_msg msg;
|
||||||
|
+
|
||||||
|
+ msg.addr = client->addr;
|
||||||
|
+ msg.flags = I2C_M_RD;
|
||||||
|
+ msg.len = BMC_SERIO_BUFSIZE;
|
||||||
|
+ msg.buf = buf;
|
||||||
|
+ rc = i2c_transfer(client->adapter, &msg, 1);
|
||||||
|
+ if (rc != 1) {
|
||||||
|
+ dev_err(&client->dev, "bmc_serio_read: i2c_transfer error %d\n", rc);
|
||||||
|
+ return -1;
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ cnt = buf[0];
|
||||||
|
+ rc = 0;
|
||||||
|
+ if (cnt > BMC_SERIO_BUFSIZE - 1) {
|
||||||
|
+ cnt = BMC_SERIO_BUFSIZE - 1;
|
||||||
|
+ rc = 1;
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ for (i = 0; i < cnt; i++) {
|
||||||
|
+ serio_interrupt(serio, buf[i + 1], 0);
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ return 0;
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
+int
|
||||||
|
+touchpad_poll_fn(void *data) {
|
||||||
|
+ int ret;
|
||||||
|
+
|
||||||
|
+ while (1) {
|
||||||
|
+ if (kthread_should_stop())
|
||||||
|
+ break;
|
||||||
|
+ while ((ret = bmc_serio_read(serio_i2c)) > 0)
|
||||||
|
+ ;
|
||||||
|
+ if (ret < 0) {
|
||||||
|
+ msleep_interruptible(10000);
|
||||||
|
+ }
|
||||||
|
+ msleep_interruptible(10);
|
||||||
|
+ }
|
||||||
|
+ return 0;
|
||||||
|
+}
|
||||||
|
+#endif /* CONFIG_SERIO */
|
||||||
|
+
|
||||||
|
+#ifdef CONFIG_PINCTRL
|
||||||
|
+static uint8_t bmc_pincf_state [3];
|
||||||
|
+#define BMC_NPINS (sizeof(bmc_pincf_state) * 8)
|
||||||
|
+
|
||||||
|
+static struct pinctrl_pin_desc bmc_pin_desc[BMC_NPINS] = {
|
||||||
|
+ PINCTRL_PIN(0, "P0"),
|
||||||
|
+ PINCTRL_PIN(1, "P1"),
|
||||||
|
+ PINCTRL_PIN(2, "P2"),
|
||||||
|
+ PINCTRL_PIN(3, "P3"),
|
||||||
|
+ PINCTRL_PIN(4, "P4"),
|
||||||
|
+ PINCTRL_PIN(5, "P5"),
|
||||||
|
+ PINCTRL_PIN(6, "P6"),
|
||||||
|
+ PINCTRL_PIN(7, "P7"),
|
||||||
|
+ PINCTRL_PIN(8, "P8"),
|
||||||
|
+ PINCTRL_PIN(9, "P9"),
|
||||||
|
+ PINCTRL_PIN(10, "P10"),
|
||||||
|
+ PINCTRL_PIN(11, "P11"),
|
||||||
|
+ PINCTRL_PIN(12, "P12"),
|
||||||
|
+ PINCTRL_PIN(13, "P13"),
|
||||||
|
+ PINCTRL_PIN(14, "P14"),
|
||||||
|
+ PINCTRL_PIN(15, "P15"),
|
||||||
|
+ PINCTRL_PIN(16, "P16"),
|
||||||
|
+ PINCTRL_PIN(17, "P17"),
|
||||||
|
+ PINCTRL_PIN(18, "P18"),
|
||||||
|
+ PINCTRL_PIN(19, "P19"),
|
||||||
|
+ PINCTRL_PIN(20, "P20"),
|
||||||
|
+ PINCTRL_PIN(21, "P21"),
|
||||||
|
+ PINCTRL_PIN(22, "P22"),
|
||||||
|
+ PINCTRL_PIN(23, "P23"),
|
||||||
|
+};
|
||||||
|
+
|
||||||
|
+#define PCTRL_DEV "bmc_pinctrl"
|
||||||
|
+
|
||||||
|
+static int bmc_pin_config_get(struct pinctrl_dev *pctldev,
|
||||||
|
+ unsigned pin,
|
||||||
|
+ unsigned long *config)
|
||||||
|
+{
|
||||||
|
+ int idx, bit;
|
||||||
|
+
|
||||||
|
+ if (pin > BMC_NPINS)
|
||||||
|
+ return -EINVAL;
|
||||||
|
+
|
||||||
|
+ idx = pin >> 3;
|
||||||
|
+ bit = pin & 7;
|
||||||
|
+
|
||||||
|
+ *config = !!(bmc_pincf_state[idx] & (1 << bit));
|
||||||
|
+ return 0;
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
+static int bmc_pin_config_set(struct pinctrl_dev *pctldev,
|
||||||
|
+ unsigned pin,
|
||||||
|
+ unsigned long *config,
|
||||||
|
+ unsigned nc)
|
||||||
|
+{
|
||||||
|
+ int idx, bit;
|
||||||
|
+ enum pin_config_param param;
|
||||||
|
+ int arg;
|
||||||
|
+
|
||||||
|
+ if (pin > BMC_NPINS)
|
||||||
|
+ return -EINVAL;
|
||||||
|
+
|
||||||
|
+ idx = pin >> 3;
|
||||||
|
+ bit = pin & 7;
|
||||||
|
+
|
||||||
|
+ param = pinconf_to_config_param (*config);
|
||||||
|
+ arg = pinconf_to_config_argument (*config);
|
||||||
|
+ if (param != PIN_CONFIG_OUTPUT)
|
||||||
|
+ return -EINVAL;
|
||||||
|
+
|
||||||
|
+ if (arg)
|
||||||
|
+ bmc_pincf_state[idx] |= (1 << bit);
|
||||||
|
+ else
|
||||||
|
+ bmc_pincf_state[idx] &= ~(1 << bit);
|
||||||
|
+dev_dbg(&bmc_i2c->dev, "bmc_pin_config_set: pin %u, dir %lu\n", pin, *config);
|
||||||
|
+
|
||||||
|
+ return i2c_smbus_write_byte_data(bmc_i2c, R_GPIODIR0 + idx, bmc_pincf_state[idx]);
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
+void pinconf_generic_dump_config(struct pinctrl_dev *pctldev,
|
||||||
|
+ struct seq_file *s, unsigned long config);
|
||||||
|
+
|
||||||
|
+void pinctrl_utils_free_map(struct pinctrl_dev *pctldev,
|
||||||
|
+ struct pinctrl_map *map, unsigned num_maps);
|
||||||
|
+
|
||||||
|
+static const struct pinconf_ops bmc_confops = {
|
||||||
|
+ .pin_config_get = bmc_pin_config_get,
|
||||||
|
+ .pin_config_set = bmc_pin_config_set,
|
||||||
|
+ .pin_config_config_dbg_show = pinconf_generic_dump_config,
|
||||||
|
+};
|
||||||
|
+
|
||||||
|
+static int bmc_groups_count(struct pinctrl_dev *pctldev)
|
||||||
|
+{
|
||||||
|
+ return 0;
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
+static const char *bmc_group_name(struct pinctrl_dev *pctldev,
|
||||||
|
+ unsigned selector)
|
||||||
|
+{
|
||||||
|
+ return NULL;
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
+static const struct pinctrl_ops bmc_ctrl_ops = {
|
||||||
|
+ .get_groups_count = bmc_groups_count,
|
||||||
|
+ .get_group_name = bmc_group_name,
|
||||||
|
+ .dt_node_to_map = pinconf_generic_dt_node_to_map_pin,
|
||||||
|
+ .dt_free_map = pinctrl_utils_free_map,
|
||||||
|
+};
|
||||||
|
+
|
||||||
|
+static struct pinctrl_desc bmc_pincrtl_desc = {
|
||||||
|
+ .name = PCTRL_DEV,
|
||||||
|
+ .pins = bmc_pin_desc,
|
||||||
|
+ .pctlops = &bmc_ctrl_ops,
|
||||||
|
+ .npins = BMC_NPINS,
|
||||||
|
+ .confops = &bmc_confops,
|
||||||
|
+};
|
||||||
|
+
|
||||||
|
+static struct pinctrl_dev *bmc_pinctrl_dev;
|
||||||
|
+
|
||||||
|
+static int bmc_pinctrl_register(struct device *dev)
|
||||||
|
+{
|
||||||
|
+ struct pinctrl_dev *pctrl_dev;
|
||||||
|
+ struct platform_device *pbdev;
|
||||||
|
+
|
||||||
|
+ pbdev = platform_device_alloc(PCTRL_DEV, -1);
|
||||||
|
+ pbdev->dev.parent = dev;
|
||||||
|
+ pbdev->dev.of_node = of_find_node_by_name(dev->of_node, "bmc_pinctrl");
|
||||||
|
+ platform_device_add(pbdev);
|
||||||
|
+ pctrl_dev = devm_pinctrl_register(&pbdev->dev, &bmc_pincrtl_desc, NULL);
|
||||||
|
+ if (IS_ERR(pctrl_dev)) {
|
||||||
|
+ dev_err(&pbdev->dev, "Can't register pinctrl (%ld)\n", PTR_ERR(pctrl_dev));
|
||||||
|
+ return PTR_ERR(pctrl_dev);
|
||||||
|
+ } else {
|
||||||
|
+ dev_info(&pbdev->dev, "BMC pinctrl registered\n");
|
||||||
|
+ bmc_pinctrl_dev = pctrl_dev;
|
||||||
|
+ }
|
||||||
|
+ /* reset all pins to default state */
|
||||||
|
+ i2c_smbus_write_byte_data(to_i2c_client(dev), R_GPIODIR0, 0);
|
||||||
|
+ i2c_smbus_write_byte_data(to_i2c_client(dev), R_GPIODIR1, 0);
|
||||||
|
+ i2c_smbus_write_byte_data(to_i2c_client(dev), R_GPIODIR2, 0);
|
||||||
|
+ return 0;
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
+static void bmc_pinctrl_unregister(void)
|
||||||
|
+{
|
||||||
|
+ if (bmc_pinctrl_dev)
|
||||||
|
+ devm_pinctrl_unregister(&bmc_i2c->dev, bmc_pinctrl_dev);
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
+#endif
|
||||||
|
+
|
||||||
|
+void
|
||||||
|
+bmc_pwroff_rq(void) {
|
||||||
|
+ int ret = 0;
|
||||||
|
+
|
||||||
|
+ dev_info(&bmc_i2c->dev, "Write reg R_PWROFF_RQ\n");
|
||||||
|
+ ret = i2c_smbus_write_byte_data(bmc_i2c, R_PWROFF_RQ, 0x01);
|
||||||
|
+ dev_info(&bmc_i2c->dev, "ret: %i\n", ret);
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
+int
|
||||||
|
+pwroff_rq_poll_fn(void *data) {
|
||||||
|
+ int ret;
|
||||||
|
+
|
||||||
|
+ while (1) {
|
||||||
|
+ if (kthread_should_stop())
|
||||||
|
+ break;
|
||||||
|
+ dev_dbg(&poll_data.c->dev, "Polling\n");
|
||||||
|
+ ret = i2c_smbus_read_byte_data(poll_data.c, R_SOFTOFF_RQ);
|
||||||
|
+ dev_dbg(&poll_data.c->dev, "Polling returned: %i\n", ret);
|
||||||
|
+ if (prev_ret != ret) {
|
||||||
|
+ dev_info(&poll_data.c->dev, "key change [%i]\n", ret);
|
||||||
|
+ if (ret < 0) {
|
||||||
|
+ dev_err(&poll_data.c->dev,
|
||||||
|
+ "Could not read register %x\n",
|
||||||
|
+ R_SOFTOFF_RQ);
|
||||||
|
+ return -EIO;
|
||||||
|
+ } else if (ret != 0) {
|
||||||
|
+ dev_info(&poll_data.c->dev,
|
||||||
|
+ "PWROFF \"irq\" detected [%i]\n", ret);
|
||||||
|
+ input_event(button_dev, EV_KEY, KEY_POWER, 1);
|
||||||
|
+ } else {
|
||||||
|
+ input_event(button_dev, EV_KEY, KEY_POWER, 0);
|
||||||
|
+ }
|
||||||
|
+ input_sync(button_dev);
|
||||||
|
+ }
|
||||||
|
+ prev_ret = ret;
|
||||||
|
+
|
||||||
|
+ msleep_interruptible(100);
|
||||||
|
+ }
|
||||||
|
+ do_exit(1);
|
||||||
|
+ return 0;
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
+static int
|
||||||
|
+mitx2_bmc_validate(struct i2c_client *client) {
|
||||||
|
+ int ret = 0;
|
||||||
|
+ int i = 0;
|
||||||
|
+ static const u8 regs[] = {R_ID1, R_ID2, R_ID3};
|
||||||
|
+ static const u8 vals[] = {BMC_ID1_VAL, BMC_ID2_VAL, BMC_ID3_VAL};
|
||||||
|
+
|
||||||
|
+ bmc_proto_version[0] = 0;
|
||||||
|
+ bmc_proto_version[1] = 0;
|
||||||
|
+ bmc_proto_version[2] = 0;
|
||||||
|
+
|
||||||
|
+ for (i = 0; i < ARRAY_SIZE(regs); i++) {
|
||||||
|
+ ret = i2c_smbus_read_byte_data(client, regs[i]);
|
||||||
|
+ if (ret < 0) {
|
||||||
|
+ dev_err(&client->dev, "Could not read register %x\n",
|
||||||
|
+ regs[i]);
|
||||||
|
+ return -EIO;
|
||||||
|
+ }
|
||||||
|
+ if (ret != vals[i]) {
|
||||||
|
+ dev_err(&client->dev,
|
||||||
|
+ "Bad value [0x%02x] in register 0x%02x, should be [0x%02x]\n",
|
||||||
|
+ ret, regs[i], vals[i]);
|
||||||
|
+
|
||||||
|
+ return -ENODEV;
|
||||||
|
+ }
|
||||||
|
+ }
|
||||||
|
+ ret = i2c_smbus_read_byte_data(client, R_ID4);
|
||||||
|
+ if (ret < 0) {
|
||||||
|
+ dev_err(&client->dev, "Could not read register %x\n", R_ID4);
|
||||||
|
+ return -EIO;
|
||||||
|
+ }
|
||||||
|
+ if (ret == BMC_ID4_VAL0) {
|
||||||
|
+ bmc_proto_version[0] = 0;
|
||||||
|
+ } else if (ret == BMC_ID4_VAL1) {
|
||||||
|
+ bmc_proto_version[0] = 2;
|
||||||
|
+ ret = i2c_smbus_read_byte_data(client, R_VERSION1);
|
||||||
|
+ if (ret < 0) {
|
||||||
|
+ dev_err(&client->dev, "Could not read register %x\n",
|
||||||
|
+ R_VERSION1);
|
||||||
|
+ return -EIO;
|
||||||
|
+ }
|
||||||
|
+ bmc_proto_version[1] = ret;
|
||||||
|
+ ret = i2c_smbus_read_byte_data(client, R_VERSION2);
|
||||||
|
+ if (ret < 0) {
|
||||||
|
+ dev_err(&client->dev, "Could not read register %x\n",
|
||||||
|
+ R_VERSION2);
|
||||||
|
+ return -EIO;
|
||||||
|
+ }
|
||||||
|
+ bmc_proto_version[2] = ret;
|
||||||
|
+ ret = i2c_smbus_read_byte_data(client, R_BOOTREASON);
|
||||||
|
+ if (ret < 0) {
|
||||||
|
+ dev_err(&client->dev, "Could not read register %x\n",
|
||||||
|
+ R_BOOTREASON);
|
||||||
|
+ return -EIO;
|
||||||
|
+ }
|
||||||
|
+ bmc_bootreason[0] = ret;
|
||||||
|
+ dev_info(&client->dev, "BMC bootreason[0]->%i\n", ret);
|
||||||
|
+ ret = i2c_smbus_read_byte_data(client, R_BOOTREASON_ARG);
|
||||||
|
+ if (ret < 0) {
|
||||||
|
+ dev_err(&client->dev, "Could not read register %x\n",
|
||||||
|
+ R_BOOTREASON_ARG);
|
||||||
|
+ return -EIO;
|
||||||
|
+ }
|
||||||
|
+ bmc_bootreason[1] = ret;
|
||||||
|
+ dev_info(&client->dev, "BMC bootreason[1]->%i\n", ret);
|
||||||
|
+ for (i = R_SCRATCH1; i <= R_SCRATCH4; i++) {
|
||||||
|
+ ret = i2c_smbus_read_byte_data(client, i);
|
||||||
|
+ if (ret < 0) {
|
||||||
|
+ dev_err(&client->dev,
|
||||||
|
+ "Could not read register %x\n", i);
|
||||||
|
+ return -EIO;
|
||||||
|
+ }
|
||||||
|
+ bmc_scratch[i - R_SCRATCH1] = ret;
|
||||||
|
+ }
|
||||||
|
+ if (bmc_proto_version[2] >= BMC_VERSION2_3) {
|
||||||
|
+ ret = i2c_smbus_read_byte_data(client, R_CAP);
|
||||||
|
+ if (ret >= 0)
|
||||||
|
+ bmc_cap = ret;
|
||||||
|
+ dev_info(&client->dev,
|
||||||
|
+ "BMC extended capabilities %x\n", bmc_cap);
|
||||||
|
+ } else {
|
||||||
|
+ bmc_cap = BMC_CAP_PWRBTN;
|
||||||
|
+ }
|
||||||
|
+ } else {
|
||||||
|
+ dev_err(&client->dev, "Bad value [0x%02x] in register 0x%02x\n",
|
||||||
|
+ ret, R_ID4);
|
||||||
|
+ return -ENODEV;
|
||||||
|
+ }
|
||||||
|
+ dev_info(&client->dev, "BMC seems to be valid\n");
|
||||||
|
+ return 0;
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
+static int
|
||||||
|
+bmc_create_client_devices(struct device *bmc_dev)
|
||||||
|
+{
|
||||||
|
+ int ret = 0;
|
||||||
|
+ struct rtc_device *rtc_dev;
|
||||||
|
+ struct i2c_client *client = to_i2c_client(bmc_dev);
|
||||||
|
+ int client_addr = client->addr + 1;
|
||||||
|
+
|
||||||
|
+ if (bmc_cap & BMC_CAP_TOUCHPAD) {
|
||||||
|
+#ifdef CONFIG_SERIO
|
||||||
|
+ struct serio *serio;
|
||||||
|
+ serio_i2c = i2c_new_ancillary_device(client,
|
||||||
|
+ "bmc_serio", client_addr);
|
||||||
|
+ if (IS_ERR(serio_i2c)) {
|
||||||
|
+ dev_err(&client->dev, "Can't get serio secondary\n");
|
||||||
|
+ serio_i2c = NULL;
|
||||||
|
+ ret = -ENOMEM;
|
||||||
|
+ goto fail;
|
||||||
|
+ }
|
||||||
|
+ serio = devm_kzalloc(&serio_i2c->dev, sizeof(struct serio), GFP_KERNEL);
|
||||||
|
+ if (!serio) {
|
||||||
|
+ dev_err(&serio_i2c->dev, "Can't allocate serio\n");
|
||||||
|
+ ret = -ENOMEM;
|
||||||
|
+ i2c_unregister_device(serio_i2c);
|
||||||
|
+ serio_i2c = NULL;
|
||||||
|
+ goto skip_tp;
|
||||||
|
+ }
|
||||||
|
+ serio->write = bmc_serio_write;
|
||||||
|
+ serio->port_data = serio_i2c;
|
||||||
|
+ serio->id.type = SERIO_PS_PSTHRU;
|
||||||
|
+ serio_register_port(serio);
|
||||||
|
+ dev_set_drvdata(&serio_i2c->dev, serio);
|
||||||
|
+ touchpad_task = kthread_run(touchpad_poll_fn, NULL, "BMC serio poll task");
|
||||||
|
+
|
||||||
|
+skip_tp:
|
||||||
|
+#endif
|
||||||
|
+ client_addr++;
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ if (bmc_cap & BMC_CAP_RTC) {
|
||||||
|
+ rtc_i2c = i2c_new_ancillary_device(client,
|
||||||
|
+ "bmc_rtc", client_addr);
|
||||||
|
+ if (IS_ERR(rtc_i2c)) {
|
||||||
|
+ dev_err(&client->dev, "Can't get RTC secondary\n");
|
||||||
|
+ rtc_i2c = NULL;
|
||||||
|
+ ret = -ENOMEM;
|
||||||
|
+ goto fail;
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ rtc_dev = devm_rtc_device_register(&rtc_i2c->dev, "bmc_rtc",
|
||||||
|
+ &bmc_rtc_ops, THIS_MODULE);
|
||||||
|
+ if (IS_ERR(rtc_dev)) {
|
||||||
|
+ ret = PTR_ERR(rtc_dev);
|
||||||
|
+ dev_err(&client->dev, "Failed to register RTC device: %d\n",
|
||||||
|
+ ret);
|
||||||
|
+ i2c_unregister_device(rtc_i2c);
|
||||||
|
+ rtc_i2c = NULL;
|
||||||
|
+ }
|
||||||
|
+fail:
|
||||||
|
+ client_addr++;
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+#ifdef CONFIG_PINCTRL
|
||||||
|
+ if (bmc_cap & BMC_CAP_GPIODIR || 1 /*vvv*/)
|
||||||
|
+ bmc_pinctrl_register(bmc_dev);
|
||||||
|
+#endif
|
||||||
|
+
|
||||||
|
+ return ret;
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
+static int
|
||||||
|
+mitx2_bmc_i2c_probe(struct i2c_client *client,
|
||||||
|
+ const struct i2c_device_id *id)
|
||||||
|
+{
|
||||||
|
+ int err = 0;
|
||||||
|
+ int i = 0;
|
||||||
|
+
|
||||||
|
+ dev_info(&client->dev, "mitx2 bmc probe\n");
|
||||||
|
+
|
||||||
|
+ if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C))
|
||||||
|
+ return -ENODEV;
|
||||||
|
+
|
||||||
|
+ for (i = 0; i < 10; i++) {
|
||||||
|
+ err = mitx2_bmc_validate(client);
|
||||||
|
+ if (!err)
|
||||||
|
+ break;
|
||||||
|
+ msleep_interruptible(20);
|
||||||
|
+ }
|
||||||
|
+ if (err)
|
||||||
|
+ return err;
|
||||||
|
+
|
||||||
|
+ if (bmc_cap & BMC_CAP_PWRBTN) {
|
||||||
|
+ button_dev = input_allocate_device();
|
||||||
|
+ if (!button_dev) {
|
||||||
|
+ dev_err(&client->dev, "Not enough memory\n");
|
||||||
|
+ return -ENOMEM;
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ button_dev->id.bustype = BUS_I2C;
|
||||||
|
+ button_dev->dev.parent = &client->dev;
|
||||||
|
+ button_dev->name = input_name;
|
||||||
|
+ button_dev->phys = "bmc-input0";
|
||||||
|
+ button_dev->evbit[0] = BIT_MASK(EV_KEY);
|
||||||
|
+ button_dev->keybit[BIT_WORD(KEY_POWER)] = BIT_MASK(KEY_POWER);
|
||||||
|
+
|
||||||
|
+ err = input_register_device(button_dev);
|
||||||
|
+ if (err) {
|
||||||
|
+ dev_err(&client->dev, "Failed to register device\n");
|
||||||
|
+ input_free_device(button_dev);
|
||||||
|
+ return err;
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ dev_info(&client->dev, "Starting polling thread\n");
|
||||||
|
+ poll_data.c = client;
|
||||||
|
+ polling_task = kthread_run(pwroff_rq_poll_fn, NULL, "BMC poll task");
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ if (bmc_cap || 1 /*vvv*/)
|
||||||
|
+ err = bmc_create_client_devices(&client->dev);
|
||||||
|
+
|
||||||
|
+ bmc_i2c = client;
|
||||||
|
+ /* register as poweroff handler */
|
||||||
|
+ pm_power_off = bmc_pwroff_rq;
|
||||||
|
+
|
||||||
|
+ return 0;
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
+static int
|
||||||
|
+mitx2_bmc_i2c_remove(struct i2c_client *client)
|
||||||
|
+{
|
||||||
|
+#ifdef CONFIG_SERIO
|
||||||
|
+ struct serio *serio;
|
||||||
|
+#endif
|
||||||
|
+
|
||||||
|
+ if (button_dev) {
|
||||||
|
+ kthread_stop(polling_task);
|
||||||
|
+ input_unregister_device(button_dev);
|
||||||
|
+ }
|
||||||
|
+#ifdef CONFIG_SERIO
|
||||||
|
+ if (serio_i2c) {
|
||||||
|
+ kthread_stop(touchpad_task);
|
||||||
|
+ serio = dev_get_drvdata(&serio_i2c->dev);
|
||||||
|
+ serio_unregister_port(serio);
|
||||||
|
+ i2c_unregister_device(serio_i2c);
|
||||||
|
+ }
|
||||||
|
+#endif
|
||||||
|
+ if (rtc_i2c)
|
||||||
|
+ i2c_unregister_device(rtc_i2c);
|
||||||
|
+#ifdef CONFIG_PINCTRL
|
||||||
|
+ bmc_pinctrl_unregister();
|
||||||
|
+#endif
|
||||||
|
+
|
||||||
|
+ return 0;
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
+#ifdef CONFIG_OF
|
||||||
|
+static const struct of_device_id mitx2_bmc_of_match[] = {
|
||||||
|
+ { .compatible = "tp,mitx2-bmc" },
|
||||||
|
+ {}
|
||||||
|
+};
|
||||||
|
+MODULE_DEVICE_TABLE(of, mitx2_bmc_of_match);
|
||||||
|
+#endif
|
||||||
|
+
|
||||||
|
+static const struct i2c_device_id mitx2_bmc_i2c_id[] = {
|
||||||
|
+ { "mitx2_bmc", 0 },
|
||||||
|
+ { }
|
||||||
|
+};
|
||||||
|
+MODULE_DEVICE_TABLE(i2c, mitx2_bmc_i2c_id);
|
||||||
|
+
|
||||||
|
+static ssize_t
|
||||||
|
+version_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf)
|
||||||
|
+{
|
||||||
|
+ return sprintf(buf, "%i.%i.%i\n", bmc_proto_version[0],
|
||||||
|
+ bmc_proto_version[1], bmc_proto_version[2]);
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
+static struct kobj_attribute version_attribute =
|
||||||
|
+ __ATTR(version, 0664, version_show, NULL);
|
||||||
|
+
|
||||||
|
+static ssize_t
|
||||||
|
+bootreason_show(struct kobject *kobj,
|
||||||
|
+ struct kobj_attribute *attr, char *buf)
|
||||||
|
+{
|
||||||
|
+ return sprintf(buf, "%i\n", (bmc_bootreason[0] |
|
||||||
|
+ (bmc_bootreason[1] << 8)));
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
+static struct kobj_attribute bootreason_attribute =
|
||||||
|
+ __ATTR(bootreason, 0664, bootreason_show, NULL);
|
||||||
|
+
|
||||||
|
+static ssize_t
|
||||||
|
+scratch_show(struct kobject *kobj,
|
||||||
|
+ struct kobj_attribute *attr, char *buf)
|
||||||
|
+{
|
||||||
|
+ return sprintf(buf, "%i\n", (bmc_scratch[0] | (bmc_scratch[1] << 8) |
|
||||||
|
+ (bmc_scratch[2] << 16) | (bmc_scratch[3] << 24)));
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
+static struct kobj_attribute scratch_attribute =
|
||||||
|
+ __ATTR(scratch, 0664, scratch_show, NULL);
|
||||||
|
+
|
||||||
|
+static struct attribute *bmc_attrs[] = {
|
||||||
|
+ &version_attribute.attr,
|
||||||
|
+ &bootreason_attribute.attr,
|
||||||
|
+ &scratch_attribute.attr,
|
||||||
|
+ NULL,
|
||||||
|
+};
|
||||||
|
+
|
||||||
|
+ATTRIBUTE_GROUPS(bmc);
|
||||||
|
+
|
||||||
|
+static struct i2c_driver mitx2_bmc_i2c_driver = {
|
||||||
|
+ .driver = {
|
||||||
|
+ .name = "mitx2-bmc",
|
||||||
|
+ .of_match_table = of_match_ptr(mitx2_bmc_of_match),
|
||||||
|
+ .groups = bmc_groups,
|
||||||
|
+ },
|
||||||
|
+ .probe = mitx2_bmc_i2c_probe,
|
||||||
|
+ .remove = mitx2_bmc_i2c_remove,
|
||||||
|
+ .id_table = mitx2_bmc_i2c_id,
|
||||||
|
+};
|
||||||
|
+module_i2c_driver(mitx2_bmc_i2c_driver);
|
||||||
|
+
|
||||||
|
+MODULE_AUTHOR("Konstantin Kirik");
|
||||||
|
+MODULE_DESCRIPTION("mITX2 BMC driver");
|
||||||
|
+MODULE_LICENSE("GPL");
|
||||||
|
+MODULE_ALIAS("serial:bmc");
|
||||||
|
--
|
||||||
|
2.33.2
|
||||||
|
|
|
@ -1,22 +0,0 @@
|
||||||
Subject: [PATCH 619/625] panfrost: compatibility with Baikal-M firmware from
|
|
||||||
SDK-M 4.3
|
|
||||||
|
|
||||||
Added .compatible string so the driver can be used on Baikal-M
|
|
||||||
systems with firmware from SDK-M 4.3.
|
|
||||||
|
|
||||||
Note: the driver should be explicitly enabled with
|
|
||||||
enable_broken_machines=y module parameter
|
|
||||||
|
|
||||||
Update for 5.15.28
|
|
||||||
|
|
||||||
diff --git a/drivers/gpu/drm/panfrost/panfrost_drv.c b/drivers/gpu/drm/panfrost/panfrost_drv.c
|
|
||||||
--- a/drivers/gpu/drm/panfrost/panfrost_drv.c
|
|
||||||
+++ b/drivers/gpu/drm/panfrost/panfrost_drv.c
|
|
||||||
@@ -655,6 +655,7 @@
|
|
||||||
{ .compatible = "arm,mali-t880", .data = &default_data, },
|
|
||||||
{ .compatible = "arm,mali-bifrost", .data = &default_data, },
|
|
||||||
{ .compatible = "mediatek,mt8183-mali", .data = &mediatek_mt8183_data },
|
|
||||||
+ { .compatible = "arm,mali-midgard", .data = &default_data, },
|
|
||||||
{}
|
|
||||||
};
|
|
||||||
MODULE_DEVICE_TABLE(of, dt_match);
|
|
|
@ -0,0 +1,54 @@
|
||||||
|
From ac048f806b24c33a331673c6b00f7482adc5b7a0 Mon Sep 17 00:00:00 2001
|
||||||
|
From: Alexey Sheplyakov <asheplyakov@basealt.ru>
|
||||||
|
Date: Tue, 11 Jan 2022 15:49:19 +0400
|
||||||
|
Subject: [PATCH 619/634] [rejected] serial: 8250_dw: verify clock rate in
|
||||||
|
dw8250_set_termios
|
||||||
|
|
||||||
|
Refuse to change the clock rate if clk_round_rate() returns
|
||||||
|
a rate which is way too off (i.e. by more than 1/16 from the one
|
||||||
|
necessary for a given baud rate). In particular this happens if
|
||||||
|
the requested rate is below the minimum supported by the clock.
|
||||||
|
|
||||||
|
Fixes the UART console on Baikal-M SoC. Without this patch the
|
||||||
|
console gets garbled immediately after loading the driver.
|
||||||
|
dw8250_set_termios tries to configure the baud rate (115200),
|
||||||
|
and calls clk_round_rate to figure out the supported rate closest
|
||||||
|
to 1843200 Hz (which is 115200 * 16). However the (SoC-specific)
|
||||||
|
clock driver returns 4705882 Hz. This frequency is way too off,
|
||||||
|
hence after setting it the console gets garbled.
|
||||||
|
|
||||||
|
Signed-off-by: Alexey Sheplyakov <asheplyakov@basealt.ru>
|
||||||
|
Signed-off-by: Vadim V. Vlasov <vadim.vlasov@elpitech.ru>
|
||||||
|
|
||||||
|
Cc: Andy Shevchenko <andriy.shevchenko@linux.intel.com>
|
||||||
|
Cc: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
|
||||||
|
Cc: Serge Semin <Sergey.Semin@baikalelectronics.ru>
|
||||||
|
---
|
||||||
|
drivers/tty/serial/8250/8250_dw.c | 5 +++--
|
||||||
|
1 file changed, 3 insertions(+), 2 deletions(-)
|
||||||
|
|
||||||
|
diff --git a/drivers/tty/serial/8250/8250_dw.c b/drivers/tty/serial/8250/8250_dw.c
|
||||||
|
index 49559731b..b6d21ac44 100644
|
||||||
|
--- a/drivers/tty/serial/8250/8250_dw.c
|
||||||
|
+++ b/drivers/tty/serial/8250/8250_dw.c
|
||||||
|
@@ -329,14 +329,15 @@ dw8250_do_pm(struct uart_port *port, unsigned int state, unsigned int old)
|
||||||
|
static void dw8250_set_termios(struct uart_port *p, struct ktermios *termios,
|
||||||
|
struct ktermios *old)
|
||||||
|
{
|
||||||
|
- unsigned long newrate = tty_termios_baud_rate(termios) * 16;
|
||||||
|
+ unsigned long baud = tty_termios_baud_rate(termios);
|
||||||
|
+ unsigned long newrate = baud * 16;
|
||||||
|
struct dw8250_data *d = to_dw8250_data(p->private_data);
|
||||||
|
long rate;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
clk_disable_unprepare(d->clk);
|
||||||
|
rate = clk_round_rate(d->clk, newrate);
|
||||||
|
- if (rate > 0) {
|
||||||
|
+ if (rate > 0 && rate >= baud * 15 && rate <= baud * 17) {
|
||||||
|
/*
|
||||||
|
* Premilinary set the uartclk to the new clock rate so the
|
||||||
|
* clock update event handler caused by the clk_set_rate()
|
||||||
|
--
|
||||||
|
2.33.2
|
||||||
|
|
|
@ -0,0 +1,60 @@
|
||||||
|
From eb6abea1811676cfc548db499446b848c0221c40 Mon Sep 17 00:00:00 2001
|
||||||
|
From: Alexey Sheplyakov <asheplyakov@altlinux.org>
|
||||||
|
Date: Fri, 24 Dec 2021 19:55:09 +0400
|
||||||
|
Subject: [PATCH 620/634] drm/panfrost: forcibly set dma-coherent on Baikal-M
|
||||||
|
|
||||||
|
With memattr 0x888d88 (set by arm_mali_lpae_alloc_pgtable) GPU
|
||||||
|
(Mali T628 r1p0) experiences a lot of DATA_INVALID faults,
|
||||||
|
unhandled page faults, and other errors. Also the screen goes
|
||||||
|
black almost immediately.
|
||||||
|
|
||||||
|
On the other hand with memattr 0x484d48 (as set by mali_kbase)
|
||||||
|
the GPU appears to work just fine.
|
||||||
|
|
||||||
|
Robin Murphy <robin.murphy@arm.com> explains:
|
||||||
|
|
||||||
|
> using the outer-cacheable attribute is deliberate because it is necessary
|
||||||
|
> for I/O-coherent GPUs to work properly (and should be irrelevant for
|
||||||
|
> non-coherent integrations)
|
||||||
|
|
||||||
|
> I'd note that panfrost has been working OK - to the extent that Mesa
|
||||||
|
> supports its older ISA - on the T624 (single core group) in Arm's
|
||||||
|
> Juno SoC for over a year now since commit 268af50f38b1.
|
||||||
|
|
||||||
|
> If you have to force outer non-cacheable to avoid getting translation
|
||||||
|
> faults and other errors that look like the GPU is inexplicably seeing
|
||||||
|
> the wrong data, I'd check whether you have the same thing where your
|
||||||
|
> integration is actually I/O-coherent and you're missing the "dma-coherent"
|
||||||
|
> property in your DT.
|
||||||
|
|
||||||
|
Indeed setting the "gpu-coherent" property (and adjusting jobs affinity
|
||||||
|
for dual core group GPU) makes panfrost work just fine on Baikal-M.
|
||||||
|
|
||||||
|
However on Baikal-M the FDT is passed to the kernel by the firmware,
|
||||||
|
and replacing the FDT in the firmware is tricky.
|
||||||
|
Therefore set `coherent` property when running on Baikal-M (even
|
||||||
|
if the `dma-coherent` property is missing in the FDT).
|
||||||
|
|
||||||
|
X-DONTUPSTREAM
|
||||||
|
---
|
||||||
|
drivers/gpu/drm/panfrost/panfrost_drv.c | 4 ++++
|
||||||
|
1 file changed, 4 insertions(+)
|
||||||
|
|
||||||
|
diff --git a/drivers/gpu/drm/panfrost/panfrost_drv.c b/drivers/gpu/drm/panfrost/panfrost_drv.c
|
||||||
|
index 1ffaef5ec..ffe14ed9e 100644
|
||||||
|
--- a/drivers/gpu/drm/panfrost/panfrost_drv.c
|
||||||
|
+++ b/drivers/gpu/drm/panfrost/panfrost_drv.c
|
||||||
|
@@ -552,6 +552,10 @@ static int panfrost_probe(struct platform_device *pdev)
|
||||||
|
return -ENODEV;
|
||||||
|
|
||||||
|
pfdev->coherent = device_get_dma_attr(&pdev->dev) == DEV_DMA_COHERENT;
|
||||||
|
+ if (!pfdev->coherent && of_device_is_compatible(of_root, "baikal,baikal-m")) {
|
||||||
|
+ pfdev->coherent = true;
|
||||||
|
+ dev_warn(&pdev->dev, "marking as DMA coherent on BE-M1000");
|
||||||
|
+ }
|
||||||
|
|
||||||
|
/* Allocate and initialze the DRM device. */
|
||||||
|
ddev = drm_dev_alloc(&panfrost_drm_driver, &pdev->dev);
|
||||||
|
--
|
||||||
|
2.33.2
|
||||||
|
|
35
0621-drm-panfrost-disable-devfreq-on-Baikal-M.patch
Normal file
35
0621-drm-panfrost-disable-devfreq-on-Baikal-M.patch
Normal file
|
@ -0,0 +1,35 @@
|
||||||
|
From 531ced5d7d678240e6e18ddc89cb32bf74423b60 Mon Sep 17 00:00:00 2001
|
||||||
|
From: Alexey Sheplyakov <asheplyakov@altlinux.org>
|
||||||
|
Date: Mon, 21 Jun 2021 15:42:47 +0400
|
||||||
|
Subject: [PATCH 621/634] drm/panfrost: disable devfreq on Baikal-M
|
||||||
|
|
||||||
|
Enabling GPU frequency scaling on Baikal-M cases GPU MMU lockup:
|
||||||
|
|
||||||
|
[ 38.108633] panfrost 2a200000.gpu: AS_ACTIVE bit stuck
|
||||||
|
|
||||||
|
Since GPU and CPU share the memory this locks up the whole system.
|
||||||
|
Therefore disable devfreq on Baikal-M.
|
||||||
|
|
||||||
|
X-DONTUPSTREAM
|
||||||
|
---
|
||||||
|
drivers/gpu/drm/panfrost/panfrost_devfreq.c | 4 ++++
|
||||||
|
1 file changed, 4 insertions(+)
|
||||||
|
|
||||||
|
diff --git a/drivers/gpu/drm/panfrost/panfrost_devfreq.c b/drivers/gpu/drm/panfrost/panfrost_devfreq.c
|
||||||
|
index 194af7f60..fc0e586f8 100644
|
||||||
|
--- a/drivers/gpu/drm/panfrost/panfrost_devfreq.c
|
||||||
|
+++ b/drivers/gpu/drm/panfrost/panfrost_devfreq.c
|
||||||
|
@@ -100,6 +100,10 @@ int panfrost_devfreq_init(struct panfrost_device *pfdev)
|
||||||
|
DRM_DEV_INFO(dev, "More than 1 supply is not supported yet\n");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
+ if (of_device_is_compatible(of_root, "baikal,baikal-m")) {
|
||||||
|
+ dev_info(pfdev->dev, "disabling GPU devfreq on BE-M1000\n");
|
||||||
|
+ return 0;
|
||||||
|
+ }
|
||||||
|
|
||||||
|
ret = devm_pm_opp_set_regulators(dev, pfdev->comp->supply_names,
|
||||||
|
pfdev->comp->num_supplies);
|
||||||
|
--
|
||||||
|
2.33.2
|
||||||
|
|
|
@ -0,0 +1,61 @@
|
||||||
|
From cba93078ea9087b9d732882995c117fe5ef0edf9 Mon Sep 17 00:00:00 2001
|
||||||
|
From: Alexey Sheplyakov <asheplyakov@altlinux.org>
|
||||||
|
Date: Fri, 3 Sep 2021 19:41:08 +0400
|
||||||
|
Subject: [PATCH 622/634] pm: disable all sleep states on Baikal-M based boards
|
||||||
|
|
||||||
|
These days desktop environments try to put computer into a sleep
|
||||||
|
state after a certain period of inactivity. TF307 board is able
|
||||||
|
to enter a sleep state, however it does *NOT* wakeup via power
|
||||||
|
button or keyboard/mouse.
|
||||||
|
|
||||||
|
Apparently the only wakeup sources on TF307 board are
|
||||||
|
|
||||||
|
- Real time clock (RTC)
|
||||||
|
- Ethernet
|
||||||
|
|
||||||
|
Surprisingly BMC (board management controller) is NOT a wakeup
|
||||||
|
source. Also tp_bmc driver does not use interrupts, and polls
|
||||||
|
the device instead. Perhaps BMC is unable to generate interrupts
|
||||||
|
at all?
|
||||||
|
|
||||||
|
To avoid the problem disable all sleep states (including s2idle)
|
||||||
|
on Baikal-M systems
|
||||||
|
|
||||||
|
X-DONTUPSTREAM
|
||||||
|
---
|
||||||
|
kernel/power/suspend.c | 12 ++++++++++++
|
||||||
|
1 file changed, 12 insertions(+)
|
||||||
|
|
||||||
|
diff --git a/kernel/power/suspend.c b/kernel/power/suspend.c
|
||||||
|
index 13d905dd3..9eaf760db 100644
|
||||||
|
--- a/kernel/power/suspend.c
|
||||||
|
+++ b/kernel/power/suspend.c
|
||||||
|
@@ -30,6 +30,7 @@
|
||||||
|
#include <trace/events/power.h>
|
||||||
|
#include <linux/compiler.h>
|
||||||
|
#include <linux/moduleparam.h>
|
||||||
|
+#include <linux/of.h>
|
||||||
|
|
||||||
|
#include "power.h"
|
||||||
|
|
||||||
|
@@ -236,6 +237,17 @@ EXPORT_SYMBOL_GPL(suspend_valid_only_mem);
|
||||||
|
|
||||||
|
static bool sleep_state_supported(suspend_state_t state)
|
||||||
|
{
|
||||||
|
+#ifdef CONFIG_OF
|
||||||
|
+ if (of_device_is_compatible(of_root, "baikal,baikal-m")) {
|
||||||
|
+ /* XXX: there are no wakeup sources except RTC and Ethernet
|
||||||
|
+ * on BE-M1000 based boards. In other words, no way to wakeup
|
||||||
|
+ * system via the keyboard or power button.
|
||||||
|
+ * Thus even s2idle is unusable on BE-M1000 systems.
|
||||||
|
+ */
|
||||||
|
+ pr_info("%s: no useful wakeup sources on Baikal-M", __func__);
|
||||||
|
+ return false;
|
||||||
|
+ }
|
||||||
|
+#endif
|
||||||
|
return state == PM_SUSPEND_TO_IDLE || (suspend_ops && suspend_ops->enter);
|
||||||
|
}
|
||||||
|
|
||||||
|
--
|
||||||
|
2.33.2
|
||||||
|
|
147
0623-arm64-stub-fixed-secondary-cores-boot-on-Baikal-M-So.patch
Normal file
147
0623-arm64-stub-fixed-secondary-cores-boot-on-Baikal-M-So.patch
Normal file
|
@ -0,0 +1,147 @@
|
||||||
|
From 868a4a3d3aa09f2090b50be4ad18d3f623b60acd Mon Sep 17 00:00:00 2001
|
||||||
|
From: Alexey Sheplyakov <asheplyakov@altlinux.org>
|
||||||
|
Date: Tue, 10 Nov 2020 19:05:39 +0400
|
||||||
|
Subject: [PATCH 623/634] arm64-stub: fixed secondary cores boot on Baikal-M
|
||||||
|
SoC
|
||||||
|
|
||||||
|
Old versions of Baikal-M firmware (ARM-TF) deny execution attempts
|
||||||
|
outside of the (physical) address ranges
|
||||||
|
[0x80000000, 0x8FFFFFFF] and [0xA0000000, 0xBFFFFFFF]
|
||||||
|
Thus PSCI calls to boot secondary cores fail unless the kernel image
|
||||||
|
resides in one of these address ranges. However UEFI PE/COFF loader
|
||||||
|
puts the kernel image into the forbidden range. Since the alignment
|
||||||
|
is good enough EFI stub does not try to relocate the kernel.
|
||||||
|
As a result secondary CPUs fail to boot.
|
||||||
|
|
||||||
|
Relocation to a random address is not going to work either.
|
||||||
|
Therefore automatically disable kaslr on "known bad" systems (for
|
||||||
|
now only Baikal-M) and forcibly relocate the kernel to a low(er)
|
||||||
|
address.
|
||||||
|
|
||||||
|
This patch is necessary only for old firmware (pre SDK-M 5.1) and
|
||||||
|
prevents kalsr from working on Baikal-M systems.
|
||||||
|
|
||||||
|
X-DONTUPSTREAM
|
||||||
|
X-legacy
|
||||||
|
---
|
||||||
|
drivers/firmware/efi/libstub/arm64-stub.c | 62 ++++++++++++++++++++++-
|
||||||
|
1 file changed, 61 insertions(+), 1 deletion(-)
|
||||||
|
|
||||||
|
diff --git a/drivers/firmware/efi/libstub/arm64-stub.c b/drivers/firmware/efi/libstub/arm64-stub.c
|
||||||
|
index 9cc556013..5486a223a 100644
|
||||||
|
--- a/drivers/firmware/efi/libstub/arm64-stub.c
|
||||||
|
+++ b/drivers/firmware/efi/libstub/arm64-stub.c
|
||||||
|
@@ -11,6 +11,7 @@
|
||||||
|
#include <asm/efi.h>
|
||||||
|
#include <asm/memory.h>
|
||||||
|
#include <asm/sections.h>
|
||||||
|
+#include <linux/libfdt.h>
|
||||||
|
#include <asm/sysreg.h>
|
||||||
|
|
||||||
|
#include "efistub.h"
|
||||||
|
@@ -34,6 +35,31 @@ efi_status_t check_platform_features(void)
|
||||||
|
return EFI_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
+static const char* machines_need_low_alloc[] = {
|
||||||
|
+ "baikal,baikal-m",
|
||||||
|
+};
|
||||||
|
+
|
||||||
|
+static bool need_low_alloc(void) {
|
||||||
|
+ size_t i;
|
||||||
|
+ const void *fdt;
|
||||||
|
+ const char *match;
|
||||||
|
+
|
||||||
|
+ fdt = get_efi_config_table(DEVICE_TREE_GUID);
|
||||||
|
+ if (!fdt) {
|
||||||
|
+ efi_info("failed to retrive FDT from EFI\n");
|
||||||
|
+ return false;
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ for (i = 0; i < ARRAY_SIZE(machines_need_low_alloc); i++) {
|
||||||
|
+ match = machines_need_low_alloc[i];
|
||||||
|
+ if (fdt_node_check_compatible(fdt, 0, match) == 0) {
|
||||||
|
+ efi_info("machine %s: forcing kernel relocation to low address\n", match);
|
||||||
|
+ return true;
|
||||||
|
+ }
|
||||||
|
+ }
|
||||||
|
+ return false;
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
/*
|
||||||
|
* Distro versions of GRUB may ignore the BSS allocation entirely (i.e., fail
|
||||||
|
* to provide space, and fail to zero it). Check for this condition by double
|
||||||
|
@@ -79,6 +105,19 @@ static bool check_image_region(u64 base, u64 size)
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
+static inline efi_status_t efi_low_alloc(unsigned long size, unsigned long align,
|
||||||
|
+ unsigned long *addr)
|
||||||
|
+{
|
||||||
|
+ /*
|
||||||
|
+ * Don't allocate at 0x0. It will confuse code that
|
||||||
|
+ * checks pointers against NULL. Skip the first 8
|
||||||
|
+ * bytes so we start at a nice even number.
|
||||||
|
+ */
|
||||||
|
+ return efi_low_alloc_above(size, align, addr, 0x8);
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
+
|
||||||
|
+
|
||||||
|
efi_status_t handle_kernel_image(unsigned long *image_addr,
|
||||||
|
unsigned long *image_size,
|
||||||
|
unsigned long *reserve_addr,
|
||||||
|
@@ -99,6 +138,14 @@ efi_status_t handle_kernel_image(unsigned long *image_addr,
|
||||||
|
*/
|
||||||
|
u64 min_kimg_align = efi_nokaslr ? MIN_KIMG_ALIGN : EFI_KIMG_ALIGN;
|
||||||
|
|
||||||
|
+ bool force_low_reloc = need_low_alloc();
|
||||||
|
+ if (force_low_reloc) {
|
||||||
|
+ if (!efi_nokaslr) {
|
||||||
|
+ efi_info("booting on a broken firmware, KASLR will be disabled\n");
|
||||||
|
+ efi_nokaslr = true;
|
||||||
|
+ }
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
if (IS_ENABLED(CONFIG_RANDOMIZE_BASE)) {
|
||||||
|
if (!efi_nokaslr) {
|
||||||
|
status = efi_get_random_bytes(sizeof(phys_seed),
|
||||||
|
@@ -112,7 +159,8 @@ efi_status_t handle_kernel_image(unsigned long *image_addr,
|
||||||
|
efi_nokaslr = true;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
- efi_info("KASLR disabled on kernel command line\n");
|
||||||
|
+ if (!force_low_reloc)
|
||||||
|
+ efi_info("KASLR disabled on kernel command line\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@@ -140,6 +188,15 @@ efi_status_t handle_kernel_image(unsigned long *image_addr,
|
||||||
|
status = EFI_OUT_OF_RESOURCES;
|
||||||
|
}
|
||||||
|
|
||||||
|
+ if (force_low_reloc) {
|
||||||
|
+ status = efi_low_alloc(*reserve_size,
|
||||||
|
+ min_kimg_align,
|
||||||
|
+ reserve_addr);
|
||||||
|
+ if (status != EFI_SUCCESS) {
|
||||||
|
+ efi_err("Failed to relocate kernel, expect secondary CPUs boot failure\n");
|
||||||
|
+ }
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
if (status != EFI_SUCCESS) {
|
||||||
|
if (!check_image_region((u64)_text, kernel_memsize)) {
|
||||||
|
efi_err("FIRMWARE BUG: Image BSS overlaps adjacent EFI memory region\n");
|
||||||
|
@@ -164,6 +221,9 @@ efi_status_t handle_kernel_image(unsigned long *image_addr,
|
||||||
|
}
|
||||||
|
|
||||||
|
*image_addr = *reserve_addr;
|
||||||
|
+ if (efi_nokaslr) {
|
||||||
|
+ efi_info("relocating kernel to 0x%lx\n", *image_addr);
|
||||||
|
+ }
|
||||||
|
memcpy((void *)*image_addr, _text, kernel_size);
|
||||||
|
|
||||||
|
return EFI_SUCCESS;
|
||||||
|
--
|
||||||
|
2.33.2
|
||||||
|
|
|
@ -0,0 +1,45 @@
|
||||||
|
From b7bf519f24c2160c092ce8b6c9f72ead91dbe8cb Mon Sep 17 00:00:00 2001
|
||||||
|
From: Alexey Sheplyakov <asheplyakov@altlinux.org>
|
||||||
|
Date: Thu, 8 Oct 2020 18:31:28 +0400
|
||||||
|
Subject: [PATCH 624/634] efi-rtc: avoid calling efi.get_time on Baikal-M SoC
|
||||||
|
|
||||||
|
Old versions of Baikal-M UEFI (before SDK-M 4.4) do NOT provide
|
||||||
|
get_time at the runtime (not even as a stub), hence calling it
|
||||||
|
results in an Oops.
|
||||||
|
|
||||||
|
X-DONTUPSTREAM
|
||||||
|
X-legacy
|
||||||
|
---
|
||||||
|
drivers/rtc/rtc-efi.c | 9 +++++++++
|
||||||
|
1 file changed, 9 insertions(+)
|
||||||
|
|
||||||
|
diff --git a/drivers/rtc/rtc-efi.c b/drivers/rtc/rtc-efi.c
|
||||||
|
index 138c5e004..af2328022 100644
|
||||||
|
--- a/drivers/rtc/rtc-efi.c
|
||||||
|
+++ b/drivers/rtc/rtc-efi.c
|
||||||
|
@@ -17,6 +17,7 @@
|
||||||
|
#include <linux/platform_device.h>
|
||||||
|
#include <linux/rtc.h>
|
||||||
|
#include <linux/efi.h>
|
||||||
|
+#include <linux/of.h>
|
||||||
|
|
||||||
|
#define EFI_ISDST (EFI_TIME_ADJUST_DAYLIGHT|EFI_TIME_IN_DAYLIGHT)
|
||||||
|
|
||||||
|
@@ -257,6 +258,14 @@ static int __init efi_rtc_probe(struct platform_device *dev)
|
||||||
|
efi_time_t eft;
|
||||||
|
efi_time_cap_t cap;
|
||||||
|
|
||||||
|
+#ifdef CONFIG_OF
|
||||||
|
+ /* efi.get_time is not always safe to call since some UEFI
|
||||||
|
+ * implementations do not privde get_time at runtime. */
|
||||||
|
+ if (of_device_is_compatible(of_root, "baikal,baikal-m")) {
|
||||||
|
+ dev_err(&dev->dev, "Baikal-M UEFI has no get_time\n");
|
||||||
|
+ return -ENODEV;
|
||||||
|
+ }
|
||||||
|
+#endif
|
||||||
|
/* First check if the RTC is usable */
|
||||||
|
if (efi.get_time(&eft, &cap) != EFI_SUCCESS)
|
||||||
|
return -ENODEV;
|
||||||
|
--
|
||||||
|
2.33.2
|
||||||
|
|
|
@ -0,0 +1,92 @@
|
||||||
|
From 7d549f458db381baca4c38d27c984b57514b971f Mon Sep 17 00:00:00 2001
|
||||||
|
From: Alexey Sheplyakov <asheplyakov@altlinux.org>
|
||||||
|
Date: Mon, 16 Aug 2021 15:01:41 +0400
|
||||||
|
Subject: [PATCH 625/634] net: fwnode_get_phy_id: consider all compatible
|
||||||
|
strings
|
||||||
|
|
||||||
|
Commit cf99686072a1b7037a1d782b66037b2b722bf2c9 ("of: mdio:
|
||||||
|
Refactor of_get_phy_id()") has broken Ethernet on TF307 board
|
||||||
|
(and possibly other boards based on Baikal-M/T1 SoCs).
|
||||||
|
|
||||||
|
That commit replaces `of_get_phy_id` with `fwnode_get_phy_id`.
|
||||||
|
And `fwnode_get_phy_id` considers only the 1st compatible string
|
||||||
|
to find out phy_id. This works well for all schema compliant device
|
||||||
|
trees, since the `compatible` property of PHY nodes is supposed
|
||||||
|
to be "ethernet-phy-idNNNN.MMMM".
|
||||||
|
|
||||||
|
However DTB embedded in TF307 firmware describes PHY like this:
|
||||||
|
|
||||||
|
gmac0_phy: ethernet-phy@3 {
|
||||||
|
compatible = "micrel,ksz9031", "ethernet-phy-id0022.1620", "ethernet-phy-ieee802.3-c22";
|
||||||
|
reg = <0x3>;
|
||||||
|
};
|
||||||
|
|
||||||
|
That is, the 1st compatible string is "micrel,ksz9031". Thus
|
||||||
|
`fwnode_get_phy_id` is unable to parse phy_id, and
|
||||||
|
`stmmac_mdio_register` fails. As a result Ethernet driver is
|
||||||
|
unable to attach to PHY, and can't send/receive anything.
|
||||||
|
|
||||||
|
To avoid the problem this patch adjusts `fwnode_get_phy_id`
|
||||||
|
to consider *all* compatible strings.
|
||||||
|
|
||||||
|
X-DONTUPSTREAM
|
||||||
|
---
|
||||||
|
drivers/net/phy/phy_device.c | 41 ++++++++++++++++++++++++++----------
|
||||||
|
1 file changed, 30 insertions(+), 11 deletions(-)
|
||||||
|
|
||||||
|
diff --git a/drivers/net/phy/phy_device.c b/drivers/net/phy/phy_device.c
|
||||||
|
index 28f4a383a..68bd0dbc9 100644
|
||||||
|
--- a/drivers/net/phy/phy_device.c
|
||||||
|
+++ b/drivers/net/phy/phy_device.c
|
||||||
|
@@ -840,18 +840,37 @@ static int get_phy_c22_id(struct mii_bus *bus, int addr, u32 *phy_id)
|
||||||
|
int fwnode_get_phy_id(struct fwnode_handle *fwnode, u32 *phy_id)
|
||||||
|
{
|
||||||
|
unsigned int upper, lower;
|
||||||
|
- const char *cp;
|
||||||
|
- int ret;
|
||||||
|
-
|
||||||
|
- ret = fwnode_property_read_string(fwnode, "compatible", &cp);
|
||||||
|
- if (ret)
|
||||||
|
- return ret;
|
||||||
|
-
|
||||||
|
- if (sscanf(cp, "ethernet-phy-id%4x.%4x", &upper, &lower) != 2)
|
||||||
|
- return -EINVAL;
|
||||||
|
+ const char **compat;
|
||||||
|
+ int ret, count, i;
|
||||||
|
+
|
||||||
|
+ /* FIXME: where is fwnode_property_for_each_string? */
|
||||||
|
+ count = fwnode_property_read_string_array(fwnode, "compatible", NULL, 0);
|
||||||
|
+ if (count < 0)
|
||||||
|
+ return count;
|
||||||
|
+ else if (count == 0)
|
||||||
|
+ return -ENODATA;
|
||||||
|
+
|
||||||
|
+ compat = kcalloc(count, sizeof(*compat), GFP_KERNEL);
|
||||||
|
+ if (!compat)
|
||||||
|
+ return -ENOMEM;
|
||||||
|
+ ret = fwnode_property_read_string_array(fwnode, "compatible", compat, count);
|
||||||
|
+ if (ret < 0)
|
||||||
|
+ goto out;
|
||||||
|
|
||||||
|
- *phy_id = ((upper & GENMASK(15, 0)) << 16) | (lower & GENMASK(15, 0));
|
||||||
|
- return 0;
|
||||||
|
+ ret = -EINVAL;
|
||||||
|
+ for (i = 0; i < count; i++) {
|
||||||
|
+ pr_info("%s: considering '%s'\n", __func__, compat[i]);
|
||||||
|
+ if (sscanf(compat[i], "ethernet-phy-id%4x.%4x", &upper, &lower) != 2)
|
||||||
|
+ continue;
|
||||||
|
+ else {
|
||||||
|
+ *phy_id = ((upper & GENMASK(15, 0)) << 16) | (lower & GENMASK(15, 0));
|
||||||
|
+ ret = 0;
|
||||||
|
+ break;
|
||||||
|
+ }
|
||||||
|
+ }
|
||||||
|
+out:
|
||||||
|
+ kfree(compat);
|
||||||
|
+ return ret;
|
||||||
|
}
|
||||||
|
EXPORT_SYMBOL(fwnode_get_phy_id);
|
||||||
|
|
||||||
|
--
|
||||||
|
2.33.2
|
||||||
|
|
99
0626-BROKEN-dwc-i2s-support-Baikal-M-SoC.patch
Normal file
99
0626-BROKEN-dwc-i2s-support-Baikal-M-SoC.patch
Normal file
|
@ -0,0 +1,99 @@
|
||||||
|
From 0076b9479bf1b3ef5ae9b36c1516861a7c817c50 Mon Sep 17 00:00:00 2001
|
||||||
|
From: Alexey Sheplyakov <asheplyakov@altlinux.org>
|
||||||
|
Date: Mon, 29 Mar 2021 12:22:11 +0400
|
||||||
|
Subject: [PATCH 626/634] (BROKEN) dwc-i2s: support Baikal-M SoC
|
||||||
|
|
||||||
|
* dw_i2s_probe: request all IRQs specified in device tree
|
||||||
|
* i2s_irq_handler: avoid flooding system with RX overrun warnings
|
||||||
|
|
||||||
|
Note that the sound frequency is distorted (i.e. playing 440 Hz
|
||||||
|
sine wave results in 467 Hz)
|
||||||
|
---
|
||||||
|
sound/soc/dwc/dwc-i2s.c | 36 ++++++++++++++++++++++++++----------
|
||||||
|
sound/soc/dwc/local.h | 1 +
|
||||||
|
2 files changed, 27 insertions(+), 10 deletions(-)
|
||||||
|
|
||||||
|
diff --git a/sound/soc/dwc/dwc-i2s.c b/sound/soc/dwc/dwc-i2s.c
|
||||||
|
index 315ca5c4b..af98ec6fe 100644
|
||||||
|
--- a/sound/soc/dwc/dwc-i2s.c
|
||||||
|
+++ b/sound/soc/dwc/dwc-i2s.c
|
||||||
|
@@ -100,6 +100,7 @@ static inline void i2s_enable_irqs(struct dw_i2s_dev *dev, u32 stream,
|
||||||
|
|
||||||
|
static irqreturn_t i2s_irq_handler(int irq, void *dev_id)
|
||||||
|
{
|
||||||
|
+ unsigned int rxor_count;
|
||||||
|
struct dw_i2s_dev *dev = dev_id;
|
||||||
|
bool irq_valid = false;
|
||||||
|
u32 isr[4];
|
||||||
|
@@ -136,9 +137,13 @@ static irqreturn_t i2s_irq_handler(int irq, void *dev_id)
|
||||||
|
irq_valid = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
- /* Error Handling: TX */
|
||||||
|
+ /* Error Handling: RX */
|
||||||
|
if (isr[i] & ISR_RXFO) {
|
||||||
|
- dev_err(dev->dev, "RX overrun (ch_id=%d)\n", i);
|
||||||
|
+ rxor_count = READ_ONCE(dev->rx_overrun_count);
|
||||||
|
+ if (!(rxor_count & 0x3ff))
|
||||||
|
+ dev_dbg(dev->dev, "RX overrun (ch_id=%d)\n", i);
|
||||||
|
+ rxor_count++;
|
||||||
|
+ WRITE_ONCE(dev->rx_overrun_count, rxor_count);
|
||||||
|
irq_valid = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@@ -629,7 +634,8 @@ static int dw_i2s_probe(struct platform_device *pdev)
|
||||||
|
const struct i2s_platform_data *pdata = pdev->dev.platform_data;
|
||||||
|
struct dw_i2s_dev *dev;
|
||||||
|
struct resource *res;
|
||||||
|
- int ret, irq;
|
||||||
|
+ int ret, irq, irq_count;
|
||||||
|
+ unsigned idx;
|
||||||
|
struct snd_soc_dai_driver *dw_i2s_dai;
|
||||||
|
const char *clk_id;
|
||||||
|
|
||||||
|
@@ -649,13 +655,23 @@ static int dw_i2s_probe(struct platform_device *pdev)
|
||||||
|
|
||||||
|
dev->dev = &pdev->dev;
|
||||||
|
|
||||||
|
- irq = platform_get_irq_optional(pdev, 0);
|
||||||
|
- if (irq >= 0) {
|
||||||
|
- ret = devm_request_irq(&pdev->dev, irq, i2s_irq_handler, 0,
|
||||||
|
- pdev->name, dev);
|
||||||
|
- if (ret < 0) {
|
||||||
|
- dev_err(&pdev->dev, "failed to request irq\n");
|
||||||
|
- return ret;
|
||||||
|
+ irq_count = platform_irq_count(pdev);
|
||||||
|
+ if (irq_count < 0) /* - EPROBE_DEFER */
|
||||||
|
+ return irq_count;
|
||||||
|
+ else if (!irq_count) {
|
||||||
|
+ dev_err(&pdev->dev, "no IRQs found for device\n");
|
||||||
|
+ return -ENODEV;
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ for (idx = 0; idx < (unsigned)irq_count; idx++) {
|
||||||
|
+ irq = platform_get_irq_optional(pdev, idx);
|
||||||
|
+ if (irq >= 0) {
|
||||||
|
+ ret = devm_request_irq(&pdev->dev, irq, i2s_irq_handler, 0,
|
||||||
|
+ pdev->name, dev);
|
||||||
|
+ if (ret < 0) {
|
||||||
|
+ dev_err(&pdev->dev, "failed to request irq\n");
|
||||||
|
+ return ret;
|
||||||
|
+ }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
diff --git a/sound/soc/dwc/local.h b/sound/soc/dwc/local.h
|
||||||
|
index 1c361eb61..1d6b6fd87 100644
|
||||||
|
--- a/sound/soc/dwc/local.h
|
||||||
|
+++ b/sound/soc/dwc/local.h
|
||||||
|
@@ -117,6 +117,7 @@ struct dw_i2s_dev {
|
||||||
|
bool *period_elapsed);
|
||||||
|
unsigned int tx_ptr;
|
||||||
|
unsigned int rx_ptr;
|
||||||
|
+ unsigned int rx_overrun_count;
|
||||||
|
};
|
||||||
|
|
||||||
|
#if IS_ENABLED(CONFIG_SND_DESIGNWARE_PCM)
|
||||||
|
--
|
||||||
|
2.33.2
|
||||||
|
|
804
0627-input-added-TF307-serio-PS-2-emulator-driver.patch
Normal file
804
0627-input-added-TF307-serio-PS-2-emulator-driver.patch
Normal file
|
@ -0,0 +1,804 @@
|
||||||
|
From d2bbed3bea74154c6b94c1041e184f73f50f9857 Mon Sep 17 00:00:00 2001
|
||||||
|
From: "Vadim V. Vlasov" <vvv19xx@gmail.com>
|
||||||
|
Date: Fri, 2 Oct 2020 15:30:34 +0300
|
||||||
|
Subject: [PATCH 627/634] input: added TF307 serio PS/2 emulator driver
|
||||||
|
|
||||||
|
This provides support for PS/2 devices connected to the BMC (board
|
||||||
|
management controller) of TF307 board. I2C or SPI link is used as
|
||||||
|
a transport between BMC and kernel.
|
||||||
|
---
|
||||||
|
drivers/input/serio/Kconfig | 10 +
|
||||||
|
drivers/input/serio/Makefile | 1 +
|
||||||
|
drivers/input/serio/tp_serio.c | 749 +++++++++++++++++++++++++++++++++
|
||||||
|
3 files changed, 760 insertions(+)
|
||||||
|
create mode 100644 drivers/input/serio/tp_serio.c
|
||||||
|
|
||||||
|
diff --git a/drivers/input/serio/Kconfig b/drivers/input/serio/Kconfig
|
||||||
|
index f39b7b3f7..8e6b8b3ef 100644
|
||||||
|
--- a/drivers/input/serio/Kconfig
|
||||||
|
+++ b/drivers/input/serio/Kconfig
|
||||||
|
@@ -293,6 +293,16 @@ config SERIO_SUN4I_PS2
|
||||||
|
To compile this driver as a module, choose M here: the
|
||||||
|
module will be called sun4i-ps2.
|
||||||
|
|
||||||
|
+config SERIO_TPLATFORMS
|
||||||
|
+ tristate "T-Plaftorms serio port support"
|
||||||
|
+ depends on I2C || SPI
|
||||||
|
+ help
|
||||||
|
+ This selects support for PS/2 ports emulated by EC found on
|
||||||
|
+ Baikal-M-based Mini-ITX board.
|
||||||
|
+
|
||||||
|
+ To compile this driver as a module, choose M here: the
|
||||||
|
+ module will be called tp_serio.
|
||||||
|
+
|
||||||
|
config SERIO_GPIO_PS2
|
||||||
|
tristate "GPIO PS/2 bit banging driver"
|
||||||
|
depends on GPIOLIB
|
||||||
|
diff --git a/drivers/input/serio/Makefile b/drivers/input/serio/Makefile
|
||||||
|
index 6d97bad7b..a47319040 100644
|
||||||
|
--- a/drivers/input/serio/Makefile
|
||||||
|
+++ b/drivers/input/serio/Makefile
|
||||||
|
@@ -32,4 +32,5 @@ obj-$(CONFIG_SERIO_OLPC_APSP) += olpc_apsp.o
|
||||||
|
obj-$(CONFIG_HYPERV_KEYBOARD) += hyperv-keyboard.o
|
||||||
|
obj-$(CONFIG_SERIO_SUN4I_PS2) += sun4i-ps2.o
|
||||||
|
obj-$(CONFIG_SERIO_GPIO_PS2) += ps2-gpio.o
|
||||||
|
+obj-$(CONFIG_SERIO_TPLATFORMS) += tp_serio.o
|
||||||
|
obj-$(CONFIG_USERIO) += userio.o
|
||||||
|
diff --git a/drivers/input/serio/tp_serio.c b/drivers/input/serio/tp_serio.c
|
||||||
|
new file mode 100644
|
||||||
|
index 000000000..f2b0f78ff
|
||||||
|
--- /dev/null
|
||||||
|
+++ b/drivers/input/serio/tp_serio.c
|
||||||
|
@@ -0,0 +1,749 @@
|
||||||
|
+// SPDX-License-Identifier: GPL-2.0
|
||||||
|
+/*
|
||||||
|
+ * T-Platforms serio port driver
|
||||||
|
+ */
|
||||||
|
+
|
||||||
|
+#include <linux/kernel.h>
|
||||||
|
+#include <linux/slab.h>
|
||||||
|
+#include <linux/wait.h>
|
||||||
|
+#include <linux/kthread.h>
|
||||||
|
+#include <linux/module.h>
|
||||||
|
+#include <linux/delay.h>
|
||||||
|
+#include <linux/serio.h>
|
||||||
|
+#include <linux/i2c.h>
|
||||||
|
+#include <linux/spi/spi.h>
|
||||||
|
+#include <linux/of.h>
|
||||||
|
+#include <linux/of_irq.h>
|
||||||
|
+
|
||||||
|
+MODULE_DESCRIPTION("T-Platforms serio port driver");
|
||||||
|
+MODULE_LICENSE("GPL");
|
||||||
|
+
|
||||||
|
+#define TP_SERIO_CHUNK_SIZE 4
|
||||||
|
+#define TP_SERIO_SPI_SPEED_DEFAULT 500000
|
||||||
|
+#define TP_SERIO_TX_QUEUE_SIZE 64
|
||||||
|
+#define TP_SERIO_REQUEST_DELAY 2
|
||||||
|
+#define TP_SERIO_POLL_READ_DELAY_MIN 1
|
||||||
|
+#define TP_SERIO_POLL_READ_DELAY_MAX 2
|
||||||
|
+#define TP_SERIO_POLL_WRITE_DELAY 1
|
||||||
|
+#define TP_SERIO_POLL_ERROR_DELAY 100
|
||||||
|
+#define TP_SERIO_POLL_READ_TIMEOUT 8
|
||||||
|
+#define TP_SERIO_POLL_WAIT_TIMEOUT 100
|
||||||
|
+#define TP_SERIO_CMD_QUERY 0xFC
|
||||||
|
+#define TP_SERIO_CMD_RESET 0xFE
|
||||||
|
+
|
||||||
|
+static const unsigned char tp_serio_cmd_reset_response[] = {
|
||||||
|
+ TP_SERIO_CMD_RESET, 'P', 'S', '2'
|
||||||
|
+};
|
||||||
|
+
|
||||||
|
+struct tp_serio_tx {
|
||||||
|
+ bool has_data;
|
||||||
|
+ unsigned char data;
|
||||||
|
+};
|
||||||
|
+
|
||||||
|
+struct tp_serio_port {
|
||||||
|
+ struct serio *serio;
|
||||||
|
+ struct tp_serio_data *drv;
|
||||||
|
+ struct tp_serio_tx tx;
|
||||||
|
+ unsigned int id;
|
||||||
|
+ bool registered;
|
||||||
|
+};
|
||||||
|
+
|
||||||
|
+struct tp_serio_data {
|
||||||
|
+ struct i2c_client *dev_i2c;
|
||||||
|
+ struct spi_device *dev_spi;
|
||||||
|
+ struct task_struct *poll_task;
|
||||||
|
+ wait_queue_head_t poll_wq;
|
||||||
|
+ bool poll_ready;
|
||||||
|
+ int rx_irq;
|
||||||
|
+ unsigned int num_ports;
|
||||||
|
+ struct tp_serio_port *ports;
|
||||||
|
+};
|
||||||
|
+
|
||||||
|
+struct tp_serio_driver {
|
||||||
|
+#if defined(CONFIG_I2C)
|
||||||
|
+ struct i2c_driver i2c;
|
||||||
|
+#endif
|
||||||
|
+#if defined(CONFIG_SPI)
|
||||||
|
+ struct spi_driver spi;
|
||||||
|
+#endif
|
||||||
|
+};
|
||||||
|
+
|
||||||
|
+#if defined(CONFIG_I2C)
|
||||||
|
+static int tp_serio_i2c_write(struct tp_serio_data *drv,
|
||||||
|
+ size_t size, void *data)
|
||||||
|
+{
|
||||||
|
+ struct i2c_msg m;
|
||||||
|
+
|
||||||
|
+ m.addr = drv->dev_i2c->addr;
|
||||||
|
+ m.flags = 0;
|
||||||
|
+ m.len = size;
|
||||||
|
+ m.buf = data;
|
||||||
|
+ return i2c_transfer(drv->dev_i2c->adapter, &m, 1);
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
+static int tp_serio_i2c_read(struct tp_serio_data *drv,
|
||||||
|
+ size_t size, void *data)
|
||||||
|
+{
|
||||||
|
+ struct i2c_msg m;
|
||||||
|
+
|
||||||
|
+ m.addr = drv->dev_i2c->addr;
|
||||||
|
+ m.flags = I2C_M_RD;
|
||||||
|
+ m.len = size;
|
||||||
|
+ m.buf = data;
|
||||||
|
+ return i2c_transfer(drv->dev_i2c->adapter, &m, 1);
|
||||||
|
+}
|
||||||
|
+#endif
|
||||||
|
+
|
||||||
|
+#if defined(CONFIG_SPI)
|
||||||
|
+static int tp_serio_spi_write(struct tp_serio_data *drv,
|
||||||
|
+ size_t size, void *data)
|
||||||
|
+{
|
||||||
|
+ struct spi_transfer t = {
|
||||||
|
+ .speed_hz = TP_SERIO_SPI_SPEED_DEFAULT,
|
||||||
|
+ .tx_buf = data,
|
||||||
|
+ .len = size,
|
||||||
|
+ };
|
||||||
|
+ struct spi_message m;
|
||||||
|
+
|
||||||
|
+ spi_message_init(&m);
|
||||||
|
+ spi_message_add_tail(&t, &m);
|
||||||
|
+ return spi_sync(drv->dev_spi, &m);
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
+static int tp_serio_spi_read(struct tp_serio_data *drv,
|
||||||
|
+ size_t size, void *data)
|
||||||
|
+{
|
||||||
|
+ struct spi_transfer t = {
|
||||||
|
+ .speed_hz = TP_SERIO_SPI_SPEED_DEFAULT,
|
||||||
|
+ .rx_buf = data,
|
||||||
|
+ .len = size,
|
||||||
|
+ };
|
||||||
|
+ struct spi_message m;
|
||||||
|
+
|
||||||
|
+ spi_message_init(&m);
|
||||||
|
+ spi_message_add_tail(&t, &m);
|
||||||
|
+ return spi_sync(drv->dev_spi, &m);
|
||||||
|
+}
|
||||||
|
+#endif
|
||||||
|
+
|
||||||
|
+static int tp_serio_request(struct tp_serio_data *drv,
|
||||||
|
+ unsigned char cmd,
|
||||||
|
+ unsigned char *response)
|
||||||
|
+{
|
||||||
|
+ int result;
|
||||||
|
+ size_t size;
|
||||||
|
+ unsigned char message[TP_SERIO_CHUNK_SIZE];
|
||||||
|
+
|
||||||
|
+ result = -ENODEV;
|
||||||
|
+ memset(message, 0, sizeof(message));
|
||||||
|
+ message[0] = cmd;
|
||||||
|
+#if defined(CONFIG_SPI)
|
||||||
|
+ if (drv->dev_spi != NULL) {
|
||||||
|
+ size = sizeof(message);
|
||||||
|
+ result = tp_serio_spi_write(drv, size, message);
|
||||||
|
+ } else
|
||||||
|
+#endif
|
||||||
|
+#if defined(CONFIG_I2C)
|
||||||
|
+ if (drv->dev_i2c != NULL) {
|
||||||
|
+ size = 1;
|
||||||
|
+ result = tp_serio_i2c_write(drv, size, message);
|
||||||
|
+ }
|
||||||
|
+#endif
|
||||||
|
+ ;
|
||||||
|
+ if (result < 0)
|
||||||
|
+ return result;
|
||||||
|
+ usleep_range(TP_SERIO_REQUEST_DELAY * 1000,
|
||||||
|
+ TP_SERIO_REQUEST_DELAY * 1000);
|
||||||
|
+#if defined(CONFIG_I2C)
|
||||||
|
+ if (drv->dev_i2c != NULL)
|
||||||
|
+ result = tp_serio_i2c_read(drv, TP_SERIO_CHUNK_SIZE, response);
|
||||||
|
+ else
|
||||||
|
+#endif
|
||||||
|
+#if defined(CONFIG_SPI)
|
||||||
|
+ if (drv->dev_spi != NULL)
|
||||||
|
+ result = tp_serio_spi_read(drv, TP_SERIO_CHUNK_SIZE, response);
|
||||||
|
+#endif
|
||||||
|
+ ;
|
||||||
|
+ return result;
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
+static int tp_serio_data_read(struct tp_serio_data *drv)
|
||||||
|
+{
|
||||||
|
+ int result;
|
||||||
|
+ size_t size;
|
||||||
|
+ size_t index;
|
||||||
|
+ size_t dbg_len;
|
||||||
|
+ char dbg_line[256];
|
||||||
|
+ unsigned int port_id;
|
||||||
|
+ unsigned char message[TP_SERIO_CHUNK_SIZE];
|
||||||
|
+
|
||||||
|
+ memset(message, 0, sizeof(message));
|
||||||
|
+ result = -ENODEV;
|
||||||
|
+#if defined(CONFIG_I2C)
|
||||||
|
+ if (drv->dev_i2c != NULL)
|
||||||
|
+ result = tp_serio_i2c_read(drv, sizeof(message), message);
|
||||||
|
+ else
|
||||||
|
+#endif
|
||||||
|
+#if defined(CONFIG_SPI)
|
||||||
|
+ if (drv->dev_spi != NULL)
|
||||||
|
+ result = tp_serio_spi_read(drv, sizeof(message), message);
|
||||||
|
+#endif
|
||||||
|
+ ;
|
||||||
|
+ if (result < 0)
|
||||||
|
+ return result;
|
||||||
|
+
|
||||||
|
+#if 0
|
||||||
|
+ snprintf(dbg_line, ARRAY_SIZE(dbg_line) - 1, "raw read:");
|
||||||
|
+ for (index = 0; index < ARRAY_SIZE(message); index++) {
|
||||||
|
+ dbg_len = strlen(dbg_line);
|
||||||
|
+ snprintf(dbg_line + dbg_len,
|
||||||
|
+ ARRAY_SIZE(dbg_line) - 1 - dbg_len,
|
||||||
|
+ " %02x", message[index]);
|
||||||
|
+ }
|
||||||
|
+#if defined(CONFIG_I2C)
|
||||||
|
+ if (drv->dev_i2c != NULL)
|
||||||
|
+ dev_dbg(&drv->dev_i2c->dev, "%s\n", dbg_line);
|
||||||
|
+ else
|
||||||
|
+#endif
|
||||||
|
+#if defined(CONFIG_SPI)
|
||||||
|
+ if (drv->dev_spi != NULL)
|
||||||
|
+ dev_dbg(&drv->dev_spi->dev, "%s\n", dbg_line);
|
||||||
|
+#endif
|
||||||
|
+ ;
|
||||||
|
+#endif
|
||||||
|
+
|
||||||
|
+ result = 0;
|
||||||
|
+ size = message[0] & 0x0F;
|
||||||
|
+ port_id = (message[0] >> 4) & 0x0F;
|
||||||
|
+ if ((size > 0) && (port_id < drv->num_ports)) {
|
||||||
|
+ snprintf(dbg_line, ARRAY_SIZE(dbg_line) - 1,
|
||||||
|
+ "port %u read:", port_id);
|
||||||
|
+
|
||||||
|
+ if (size > (ARRAY_SIZE(message) - 1)) {
|
||||||
|
+ size = ARRAY_SIZE(message) - 1;
|
||||||
|
+ result = 1;
|
||||||
|
+ }
|
||||||
|
+ for (index = 0; index < size; index++) {
|
||||||
|
+ dbg_len = strlen(dbg_line);
|
||||||
|
+ snprintf(dbg_line + dbg_len,
|
||||||
|
+ ARRAY_SIZE(dbg_line) - 1 - dbg_len,
|
||||||
|
+ " %02x", message[index + 1]);
|
||||||
|
+ serio_interrupt(drv->ports[port_id].serio,
|
||||||
|
+ message[index + 1], 0);
|
||||||
|
+ }
|
||||||
|
+#if defined(CONFIG_I2C)
|
||||||
|
+ if (drv->dev_i2c != NULL)
|
||||||
|
+ dev_dbg(&drv->dev_i2c->dev, "%s\n", dbg_line);
|
||||||
|
+ else
|
||||||
|
+#endif
|
||||||
|
+#if defined(CONFIG_SPI)
|
||||||
|
+ if (drv->dev_spi != NULL)
|
||||||
|
+ dev_dbg(&drv->dev_spi->dev, "%s\n", dbg_line);
|
||||||
|
+#endif
|
||||||
|
+ ;
|
||||||
|
+ }
|
||||||
|
+ return result;
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
+static int tp_serio_data_write(struct tp_serio_data *drv,
|
||||||
|
+ u8 id, unsigned char data)
|
||||||
|
+{
|
||||||
|
+ int result;
|
||||||
|
+ size_t size;
|
||||||
|
+ unsigned char message[TP_SERIO_CHUNK_SIZE];
|
||||||
|
+ struct tp_serio_port *port = drv->ports + id;
|
||||||
|
+
|
||||||
|
+ result = -ENODEV;
|
||||||
|
+ memset(message, 0, sizeof(message));
|
||||||
|
+ message[0] = (port->id << 4) | 0x01;
|
||||||
|
+ message[1] = data;
|
||||||
|
+#if defined(CONFIG_SPI)
|
||||||
|
+ if (drv->dev_spi != NULL) {
|
||||||
|
+ size = sizeof(message);
|
||||||
|
+ dev_dbg(&drv->dev_spi->dev,
|
||||||
|
+ "port %u write: %02x\n", port->id, data);
|
||||||
|
+ result = tp_serio_spi_write(drv, size, message);
|
||||||
|
+ } else
|
||||||
|
+#endif
|
||||||
|
+#if defined(CONFIG_I2C)
|
||||||
|
+ if (drv->dev_i2c != NULL) {
|
||||||
|
+ size = 2;
|
||||||
|
+ dev_dbg(&drv->dev_i2c->dev,
|
||||||
|
+ "port %u write: %02x\n", port->id, data);
|
||||||
|
+ result = tp_serio_i2c_write(drv, size, message);
|
||||||
|
+ }
|
||||||
|
+#endif
|
||||||
|
+ ;
|
||||||
|
+ return result;
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
+static void tp_serio_trigger_tx(struct tp_serio_data *drv)
|
||||||
|
+{
|
||||||
|
+ drv->poll_ready = true;
|
||||||
|
+ wake_up(&drv->poll_wq);
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
+static int tp_serio_write(struct serio *serio, unsigned char data)
|
||||||
|
+{
|
||||||
|
+ int result = -EINVAL;
|
||||||
|
+ struct tp_serio_data *drv;
|
||||||
|
+ struct tp_serio_port *port = (struct tp_serio_port *)serio->port_data;
|
||||||
|
+
|
||||||
|
+ if (port != NULL) {
|
||||||
|
+ drv = port->drv;
|
||||||
|
+ if (port->tx.has_data) {
|
||||||
|
+ result = -ENOMEM;
|
||||||
|
+ } else {
|
||||||
|
+ port->tx.data = data;
|
||||||
|
+ port->tx.has_data = true;
|
||||||
|
+ result = 0;
|
||||||
|
+ }
|
||||||
|
+ tp_serio_trigger_tx(drv);
|
||||||
|
+ }
|
||||||
|
+ return result;
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
+static int tp_serio_start(struct serio *serio)
|
||||||
|
+{
|
||||||
|
+ struct tp_serio_port *port = (struct tp_serio_port *)serio->port_data;
|
||||||
|
+
|
||||||
|
+ if (port != NULL)
|
||||||
|
+ port->registered = true;
|
||||||
|
+ return 0;
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
+static void tp_serio_stop(struct serio *serio)
|
||||||
|
+{
|
||||||
|
+ struct tp_serio_port *port = (struct tp_serio_port *)serio->port_data;
|
||||||
|
+
|
||||||
|
+ if (port != NULL)
|
||||||
|
+ port->registered = false;
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
+static int tp_serio_create_port(struct tp_serio_data *drv, unsigned int id)
|
||||||
|
+{
|
||||||
|
+ struct serio *serio;
|
||||||
|
+ struct device *dev;
|
||||||
|
+
|
||||||
|
+#if defined(CONFIG_SPI)
|
||||||
|
+ if (drv->dev_spi != NULL) {
|
||||||
|
+ dev = &drv->dev_spi->dev;
|
||||||
|
+ } else
|
||||||
|
+#endif
|
||||||
|
+#if defined(CONFIG_I2C)
|
||||||
|
+ if (drv->dev_i2c != NULL) {
|
||||||
|
+ dev = &drv->dev_i2c->dev;
|
||||||
|
+ } else
|
||||||
|
+#endif
|
||||||
|
+ {
|
||||||
|
+ return -ENODEV;
|
||||||
|
+ }
|
||||||
|
+ serio = devm_kzalloc(dev, sizeof(struct serio), GFP_KERNEL);
|
||||||
|
+ if (!serio)
|
||||||
|
+ return -ENOMEM;
|
||||||
|
+ strlcpy(serio->name, "tp_serio", sizeof(serio->name));
|
||||||
|
+#if defined(CONFIG_SPI)
|
||||||
|
+ if (drv->dev_spi != NULL) {
|
||||||
|
+ snprintf(serio->phys, sizeof(serio->phys),
|
||||||
|
+ "%s/port%u", dev_name(&drv->dev_spi->dev), id);
|
||||||
|
+ } else
|
||||||
|
+#endif
|
||||||
|
+#if defined(CONFIG_I2C)
|
||||||
|
+ if (drv->dev_i2c != NULL) {
|
||||||
|
+ snprintf(serio->phys, sizeof(serio->phys),
|
||||||
|
+ "%s/port%u", dev_name(&drv->dev_i2c->dev), id);
|
||||||
|
+ }
|
||||||
|
+#endif
|
||||||
|
+ ;
|
||||||
|
+ serio->id.type = SERIO_8042;
|
||||||
|
+ serio->write = tp_serio_write;
|
||||||
|
+ serio->start = tp_serio_start;
|
||||||
|
+ serio->stop = tp_serio_stop;
|
||||||
|
+ serio->port_data = drv->ports + id;
|
||||||
|
+ drv->ports[id].serio = serio;
|
||||||
|
+ drv->ports[id].drv = drv;
|
||||||
|
+ drv->ports[id].id = id;
|
||||||
|
+ drv->ports[id].registered = false;
|
||||||
|
+ drv->ports[id].tx.has_data = false;
|
||||||
|
+ drv->ports[id].tx.data = 0x00;
|
||||||
|
+ return 0;
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
+static void tp_serio_destroy_port(struct tp_serio_data *drv, unsigned int id)
|
||||||
|
+{
|
||||||
|
+ if (drv->ports[id].registered)
|
||||||
|
+ serio_unregister_port(drv->ports[id].serio);
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
+static void tp_serio_read_error(struct tp_serio_data *drv, int error)
|
||||||
|
+{
|
||||||
|
+#if defined(CONFIG_I2C)
|
||||||
|
+ if (drv->dev_i2c != NULL)
|
||||||
|
+ dev_dbg(&drv->dev_i2c->dev,
|
||||||
|
+ "i2c read failed: %d\n", error);
|
||||||
|
+ else
|
||||||
|
+#endif
|
||||||
|
+#if defined(CONFIG_SPI)
|
||||||
|
+ if (drv->dev_spi != NULL)
|
||||||
|
+ dev_dbg(&drv->dev_spi->dev,
|
||||||
|
+ "spi read failed: %d\n", error);
|
||||||
|
+#endif
|
||||||
|
+ ;
|
||||||
|
+ msleep_interruptible(TP_SERIO_POLL_ERROR_DELAY);
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
+static void tp_serio_serio_process_tx(struct tp_serio_data *drv)
|
||||||
|
+{
|
||||||
|
+ unsigned int index;
|
||||||
|
+
|
||||||
|
+ for (index = 0; index < drv->num_ports; index++) {
|
||||||
|
+ if (drv->ports[index].tx.has_data) {
|
||||||
|
+ tp_serio_data_write(drv, index,
|
||||||
|
+ drv->ports[index].tx.data);
|
||||||
|
+ drv->ports[index].tx.has_data = false;
|
||||||
|
+ usleep_range(TP_SERIO_POLL_WRITE_DELAY * 1000,
|
||||||
|
+ TP_SERIO_POLL_WRITE_DELAY * 1000);
|
||||||
|
+ }
|
||||||
|
+ }
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
+static int tp_serio_serio_process_rx(struct tp_serio_data *drv)
|
||||||
|
+{
|
||||||
|
+ int ret;
|
||||||
|
+
|
||||||
|
+ do {
|
||||||
|
+ ret = tp_serio_data_read(drv);
|
||||||
|
+ usleep_range(TP_SERIO_POLL_READ_DELAY_MIN * 1000,
|
||||||
|
+ TP_SERIO_POLL_READ_DELAY_MAX * 1000);
|
||||||
|
+ } while (ret > 0);
|
||||||
|
+ if ((ret < 0) && (ret != -EAGAIN))
|
||||||
|
+ tp_serio_read_error(drv, ret);
|
||||||
|
+ return ret;
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
+static int tp_serio_poll(void *data)
|
||||||
|
+{
|
||||||
|
+ struct tp_serio_data *drv = (struct tp_serio_data *)data;
|
||||||
|
+ const unsigned int poll_timeout = (drv->rx_irq < 0) ?
|
||||||
|
+ TP_SERIO_POLL_READ_TIMEOUT :
|
||||||
|
+ TP_SERIO_POLL_WAIT_TIMEOUT;
|
||||||
|
+
|
||||||
|
+ while (!kthread_should_stop()) {
|
||||||
|
+ drv->poll_ready = false;
|
||||||
|
+ tp_serio_serio_process_tx(drv);
|
||||||
|
+
|
||||||
|
+ if (drv->rx_irq < 0)
|
||||||
|
+ while (tp_serio_serio_process_rx(drv))
|
||||||
|
+ ;
|
||||||
|
+
|
||||||
|
+ wait_event_interruptible_timeout(drv->poll_wq, drv->poll_ready,
|
||||||
|
+ msecs_to_jiffies(poll_timeout));
|
||||||
|
+ }
|
||||||
|
+ return 0;
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
+static irqreturn_t tp_serio_alert_handler(int irq, void *dev_id)
|
||||||
|
+{
|
||||||
|
+ struct tp_serio_data *drv = (struct tp_serio_data *)dev_id;
|
||||||
|
+
|
||||||
|
+ while (tp_serio_serio_process_rx(drv))
|
||||||
|
+ ;
|
||||||
|
+ return IRQ_HANDLED;
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
+static int tp_serio_device_reset(struct tp_serio_data *drv)
|
||||||
|
+{
|
||||||
|
+ int result;
|
||||||
|
+ unsigned char response[TP_SERIO_CHUNK_SIZE];
|
||||||
|
+
|
||||||
|
+ memset(response, 0, sizeof(response));
|
||||||
|
+ result = tp_serio_request(drv, TP_SERIO_CMD_RESET, response);
|
||||||
|
+ if (result < 0)
|
||||||
|
+ return result;
|
||||||
|
+ if (!memcmp(response, tp_serio_cmd_reset_response, sizeof(response)))
|
||||||
|
+ result = 0;
|
||||||
|
+ else
|
||||||
|
+ result = -EINVAL;
|
||||||
|
+ return result;
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
+static int tp_serio_device_query(struct tp_serio_data *drv)
|
||||||
|
+{
|
||||||
|
+ int result;
|
||||||
|
+ unsigned char response[TP_SERIO_CHUNK_SIZE];
|
||||||
|
+
|
||||||
|
+ memset(response, 0, sizeof(response));
|
||||||
|
+ result = tp_serio_request(drv, TP_SERIO_CMD_QUERY, response);
|
||||||
|
+ if (result < 0)
|
||||||
|
+ return result;
|
||||||
|
+ if (response[0] == TP_SERIO_CMD_QUERY) {
|
||||||
|
+ drv->num_ports = response[1];
|
||||||
|
+ result = 0;
|
||||||
|
+ } else {
|
||||||
|
+ result = -EINVAL;
|
||||||
|
+ }
|
||||||
|
+ return result;
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
+#if defined(CONFIG_I2C)
|
||||||
|
+static int tp_serio_probe_i2c(struct i2c_client *client,
|
||||||
|
+ const struct i2c_device_id *id)
|
||||||
|
+{
|
||||||
|
+ struct tp_serio_data *drv;
|
||||||
|
+ unsigned int index;
|
||||||
|
+ unsigned int free_index;
|
||||||
|
+ int error;
|
||||||
|
+ int irq;
|
||||||
|
+ struct serio *s;
|
||||||
|
+
|
||||||
|
+ drv = devm_kzalloc(&client->dev, sizeof(*drv), GFP_KERNEL);
|
||||||
|
+ if (drv == NULL)
|
||||||
|
+ return -ENOMEM;
|
||||||
|
+ drv->dev_i2c = client;
|
||||||
|
+#if defined(CONFIG_SPI)
|
||||||
|
+ drv->dev_spi = NULL;
|
||||||
|
+#endif
|
||||||
|
+ if (tp_serio_device_reset(drv) < 0) {
|
||||||
|
+ dev_err(&client->dev, "no compatible device found at %s\n",
|
||||||
|
+ dev_name(&client->dev));
|
||||||
|
+ return -ENODEV;
|
||||||
|
+ }
|
||||||
|
+ error = tp_serio_device_query(drv);
|
||||||
|
+ if (error || (drv->num_ports == 0)) {
|
||||||
|
+ dev_err(&client->dev, "no available ports found at %s\n",
|
||||||
|
+ dev_name(&client->dev));
|
||||||
|
+ return -ENODEV;
|
||||||
|
+ }
|
||||||
|
+ drv->ports = devm_kzalloc(&client->dev,
|
||||||
|
+ sizeof(struct tp_serio_port) * drv->num_ports,
|
||||||
|
+ GFP_KERNEL);
|
||||||
|
+ if (drv->ports == NULL)
|
||||||
|
+ return -ENOMEM;
|
||||||
|
+ for (index = 0; index < drv->num_ports; index++) {
|
||||||
|
+ error = tp_serio_create_port(drv, index);
|
||||||
|
+ if (error)
|
||||||
|
+ goto err_out;
|
||||||
|
+ }
|
||||||
|
+ init_waitqueue_head(&drv->poll_wq);
|
||||||
|
+ drv->poll_ready = false;
|
||||||
|
+ drv->rx_irq = -1;
|
||||||
|
+ dev_set_drvdata(&client->dev, drv);
|
||||||
|
+
|
||||||
|
+ for (index = 0; index < drv->num_ports; index++) {
|
||||||
|
+ s = drv->ports[index].serio;
|
||||||
|
+ dev_info(&client->dev, "%s port at %s\n", s->name, s->phys);
|
||||||
|
+ serio_register_port(s);
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ if (client->dev.of_node != NULL) {
|
||||||
|
+ irq = of_irq_get(client->dev.of_node, 0);
|
||||||
|
+ if (irq >= 0) {
|
||||||
|
+ drv->rx_irq = irq;
|
||||||
|
+ error = devm_request_threaded_irq(&client->dev, irq,
|
||||||
|
+ NULL, tp_serio_alert_handler,
|
||||||
|
+ IRQF_TRIGGER_LOW | IRQF_ONESHOT,
|
||||||
|
+ "tp_serio", drv);
|
||||||
|
+ if (error) {
|
||||||
|
+ dev_set_drvdata(&client->dev, NULL);
|
||||||
|
+ index = drv->num_ports;
|
||||||
|
+ goto err_out;
|
||||||
|
+ } else {
|
||||||
|
+ tp_serio_alert_handler(drv->rx_irq, drv);
|
||||||
|
+ }
|
||||||
|
+ }
|
||||||
|
+ }
|
||||||
|
+ drv->poll_task = kthread_run(tp_serio_poll, drv,
|
||||||
|
+ "tp_serio i2c");
|
||||||
|
+ return 0;
|
||||||
|
+err_out:
|
||||||
|
+ for (free_index = 0; free_index < index; free_index++)
|
||||||
|
+ tp_serio_destroy_port(drv, free_index);
|
||||||
|
+ return error;
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
+static int tp_serio_remove_i2c(struct i2c_client *client)
|
||||||
|
+{
|
||||||
|
+ struct tp_serio_data *drv =
|
||||||
|
+ (struct tp_serio_data *)dev_get_drvdata(&client->dev);
|
||||||
|
+ unsigned int index;
|
||||||
|
+
|
||||||
|
+ if (drv != NULL) {
|
||||||
|
+ kthread_stop(drv->poll_task);
|
||||||
|
+ for (index = 0; index < drv->num_ports; index++)
|
||||||
|
+ tp_serio_destroy_port(drv, index);
|
||||||
|
+ dev_set_drvdata(&client->dev, NULL);
|
||||||
|
+ }
|
||||||
|
+ return 0;
|
||||||
|
+}
|
||||||
|
+#endif
|
||||||
|
+
|
||||||
|
+#if defined(CONFIG_SPI)
|
||||||
|
+static int tp_serio_probe_spi(struct spi_device *spi)
|
||||||
|
+{
|
||||||
|
+ struct tp_serio_data *drv;
|
||||||
|
+ unsigned int index;
|
||||||
|
+ unsigned int free_index;
|
||||||
|
+ int error;
|
||||||
|
+ int irq;
|
||||||
|
+ struct serio *s;
|
||||||
|
+
|
||||||
|
+ drv = devm_kzalloc(&spi->dev, sizeof(*drv), GFP_KERNEL);
|
||||||
|
+ if (drv == NULL)
|
||||||
|
+ return -ENOMEM;
|
||||||
|
+#if defined(CONFIG_I2C)
|
||||||
|
+ drv->dev_i2c = NULL;
|
||||||
|
+#endif
|
||||||
|
+ drv->dev_spi = spi;
|
||||||
|
+ if (tp_serio_device_reset(drv) < 0) {
|
||||||
|
+ dev_err(&spi->dev, "no compatible device found at %s\n",
|
||||||
|
+ dev_name(&spi->dev));
|
||||||
|
+ return -ENODEV;
|
||||||
|
+ }
|
||||||
|
+ error = tp_serio_device_query(drv);
|
||||||
|
+ if (error || (drv->num_ports == 0)) {
|
||||||
|
+ dev_err(&spi->dev, "no available ports found at %s\n",
|
||||||
|
+ dev_name(&spi->dev));
|
||||||
|
+ return -ENODEV;
|
||||||
|
+ }
|
||||||
|
+ drv->ports = devm_kzalloc(&spi->dev,
|
||||||
|
+ sizeof(struct tp_serio_port) * drv->num_ports,
|
||||||
|
+ GFP_KERNEL);
|
||||||
|
+ if (drv->ports == NULL)
|
||||||
|
+ return -ENOMEM;
|
||||||
|
+ for (index = 0; index < drv->num_ports; index++) {
|
||||||
|
+ error = tp_serio_create_port(drv, index);
|
||||||
|
+ if (error)
|
||||||
|
+ goto err_out;
|
||||||
|
+ }
|
||||||
|
+ init_waitqueue_head(&drv->poll_wq);
|
||||||
|
+ drv->poll_ready = false;
|
||||||
|
+ drv->rx_irq = -1;
|
||||||
|
+ spi_set_drvdata(spi, drv);
|
||||||
|
+
|
||||||
|
+ for (index = 0; index < drv->num_ports; index++) {
|
||||||
|
+ s = drv->ports[index].serio;
|
||||||
|
+ dev_info(&spi->dev, "%s port at %s\n", s->name, s->phys);
|
||||||
|
+ serio_register_port(s);
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ if (spi->dev.of_node != NULL) {
|
||||||
|
+ irq = of_irq_get(spi->dev.of_node, 0);
|
||||||
|
+ if (irq >= 0) {
|
||||||
|
+ drv->rx_irq = irq;
|
||||||
|
+ error = devm_request_threaded_irq(&spi->dev, irq,
|
||||||
|
+ NULL, tp_serio_alert_handler,
|
||||||
|
+ IRQF_TRIGGER_LOW | IRQF_ONESHOT,
|
||||||
|
+ "tp_serio", drv);
|
||||||
|
+ if (error) {
|
||||||
|
+ spi_set_drvdata(spi, NULL);
|
||||||
|
+ index = drv->num_ports;
|
||||||
|
+ goto err_out;
|
||||||
|
+ } else {
|
||||||
|
+ tp_serio_alert_handler(drv->rx_irq, drv);
|
||||||
|
+ }
|
||||||
|
+ }
|
||||||
|
+ }
|
||||||
|
+ drv->poll_task = kthread_run(tp_serio_poll, drv,
|
||||||
|
+ "tp_serio spi");
|
||||||
|
+ return 0;
|
||||||
|
+err_out:
|
||||||
|
+ for (free_index = 0; free_index < index; free_index++)
|
||||||
|
+ tp_serio_destroy_port(drv, free_index);
|
||||||
|
+ return error;
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
+static int tp_serio_remove_spi(struct spi_device *spi)
|
||||||
|
+{
|
||||||
|
+ struct tp_serio_data *drv =
|
||||||
|
+ (struct tp_serio_data *)spi_get_drvdata(spi);
|
||||||
|
+ unsigned int index;
|
||||||
|
+
|
||||||
|
+ if (drv != NULL) {
|
||||||
|
+ kthread_stop(drv->poll_task);
|
||||||
|
+ for (index = 0; index < drv->num_ports; index++)
|
||||||
|
+ tp_serio_destroy_port(drv, index);
|
||||||
|
+ spi_set_drvdata(spi, NULL);
|
||||||
|
+ }
|
||||||
|
+ return 0;
|
||||||
|
+}
|
||||||
|
+#endif
|
||||||
|
+
|
||||||
|
+static int tp_serio_register(struct tp_serio_driver *driver)
|
||||||
|
+{
|
||||||
|
+ int res = 0;
|
||||||
|
+#if defined(CONFIG_I2C)
|
||||||
|
+ res = i2c_register_driver(THIS_MODULE, &driver->i2c);
|
||||||
|
+#endif
|
||||||
|
+#if defined(CONFIG_SPI)
|
||||||
|
+ if (res == 0)
|
||||||
|
+ res = spi_register_driver(&driver->spi);
|
||||||
|
+#endif
|
||||||
|
+ return res;
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
+static void tp_serio_unregister(struct tp_serio_driver *driver)
|
||||||
|
+{
|
||||||
|
+#if defined(CONFIG_SPI)
|
||||||
|
+ spi_unregister_driver(&driver->spi);
|
||||||
|
+#endif
|
||||||
|
+#if defined(CONFIG_I2C)
|
||||||
|
+ i2c_del_driver(&driver->i2c);
|
||||||
|
+#endif
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
+static const struct of_device_id tp_serio_of_ids[] = {
|
||||||
|
+ {
|
||||||
|
+ .compatible = "tp,tp_serio",
|
||||||
|
+ },
|
||||||
|
+ { }
|
||||||
|
+};
|
||||||
|
+MODULE_DEVICE_TABLE(of, tp_serio_of_ids);
|
||||||
|
+
|
||||||
|
+#if defined(CONFIG_I2C)
|
||||||
|
+static const struct i2c_device_id tp_serio_i2c_ids[] = {
|
||||||
|
+ {
|
||||||
|
+ .name = "tp_serio",
|
||||||
|
+ },
|
||||||
|
+ { }
|
||||||
|
+};
|
||||||
|
+MODULE_DEVICE_TABLE(i2c, tp_serio_i2c_ids);
|
||||||
|
+#endif
|
||||||
|
+
|
||||||
|
+#if defined(CONFIG_SPI)
|
||||||
|
+static const struct spi_device_id tp_serio_spi_ids[] = {
|
||||||
|
+ {
|
||||||
|
+ .name = "tp_serio",
|
||||||
|
+ },
|
||||||
|
+ { }
|
||||||
|
+};
|
||||||
|
+MODULE_DEVICE_TABLE(spi, tp_serio_spi_ids);
|
||||||
|
+#endif
|
||||||
|
+
|
||||||
|
+static struct tp_serio_driver tp_serio_drv = {
|
||||||
|
+#if defined(CONFIG_I2C)
|
||||||
|
+ {
|
||||||
|
+ .driver = {
|
||||||
|
+ .name = "tp_serio",
|
||||||
|
+ .owner = THIS_MODULE,
|
||||||
|
+ .of_match_table = of_match_ptr(tp_serio_of_ids)
|
||||||
|
+ },
|
||||||
|
+ .probe = tp_serio_probe_i2c,
|
||||||
|
+ .remove = tp_serio_remove_i2c,
|
||||||
|
+ .id_table = tp_serio_i2c_ids
|
||||||
|
+ },
|
||||||
|
+#endif
|
||||||
|
+#if defined(CONFIG_SPI)
|
||||||
|
+ {
|
||||||
|
+ .driver = {
|
||||||
|
+ .name = "tp_serio",
|
||||||
|
+ .owner = THIS_MODULE,
|
||||||
|
+ .of_match_table = of_match_ptr(tp_serio_of_ids)
|
||||||
|
+ },
|
||||||
|
+ .probe = tp_serio_probe_spi,
|
||||||
|
+ .remove = tp_serio_remove_spi,
|
||||||
|
+ .id_table = tp_serio_spi_ids
|
||||||
|
+ }
|
||||||
|
+#endif
|
||||||
|
+};
|
||||||
|
+
|
||||||
|
+module_driver(tp_serio_drv, tp_serio_register, tp_serio_unregister)
|
||||||
|
--
|
||||||
|
2.33.2
|
||||||
|
|
2679
0628-arm64-added-Baikal-M-SoC-and-TF307-board-device-tree.patch
Normal file
2679
0628-arm64-added-Baikal-M-SoC-and-TF307-board-device-tree.patch
Normal file
File diff suppressed because it is too large
Load diff
53
0629-arm64-device-tree-baikal-mark-GPU-as-dma-coherent.patch
Normal file
53
0629-arm64-device-tree-baikal-mark-GPU-as-dma-coherent.patch
Normal file
|
@ -0,0 +1,53 @@
|
||||||
|
From eadaa138ba55e1ec29f02dbd9ff7c9058da6c047 Mon Sep 17 00:00:00 2001
|
||||||
|
From: Alexey Sheplyakov <asheplyakov@altlinux.org>
|
||||||
|
Date: Fri, 24 Dec 2021 19:55:09 +0400
|
||||||
|
Subject: [PATCH 629/634] arm64: device tree: baikal: mark GPU as dma-coherent
|
||||||
|
|
||||||
|
With memattr 0x888d88 (set by arm_mali_lpae_alloc_pgtable) GPU
|
||||||
|
(Mali T628 r1p0) experiences a lot of DATA_INVALID faults,
|
||||||
|
unhandled page faults, and other errors. Also the screen goes
|
||||||
|
black almost immediately.
|
||||||
|
|
||||||
|
On the other hand with memattr 0x484d48 (as set by mali_kbase)
|
||||||
|
the GPU appears to work just fine.
|
||||||
|
|
||||||
|
Robin Murphy <robin.murphy@arm.com> explains:
|
||||||
|
|
||||||
|
> using the outer-cacheable attribute is deliberate because it is necessary
|
||||||
|
> for I/O-coherent GPUs to work properly (and should be irrelevant for
|
||||||
|
> non-coherent integrations)
|
||||||
|
|
||||||
|
> I'd note that panfrost has been working OK - to the extent that Mesa
|
||||||
|
> supports its older ISA - on the T624 (single core group) in Arm's
|
||||||
|
> Juno SoC for over a year now since commit 268af50f38b1.
|
||||||
|
|
||||||
|
> If you have to force outer non-cacheable to avoid getting translation
|
||||||
|
> faults and other errors that look like the GPU is inexplicably seeing
|
||||||
|
> the wrong data, I'd check whether you have the same thing where your
|
||||||
|
> integration is actually I/O-coherent and you're missing the "dma-coherent"
|
||||||
|
> property in your DT.
|
||||||
|
|
||||||
|
Indeed setting "gpu-coherent" property (and adjusting jobs affinity
|
||||||
|
for dual core group GPU) makes panfrost work just fine on Baikal-M.
|
||||||
|
|
||||||
|
X-DONTUPSTREAM
|
||||||
|
X-ALTLINUX-SKIP
|
||||||
|
---
|
||||||
|
arch/arm64/boot/dts/baikal/bm1000.dtsi | 1 +
|
||||||
|
1 file changed, 1 insertion(+)
|
||||||
|
|
||||||
|
diff --git a/arch/arm64/boot/dts/baikal/bm1000.dtsi b/arch/arm64/boot/dts/baikal/bm1000.dtsi
|
||||||
|
index bc69835c4..cc4e7d199 100644
|
||||||
|
--- a/arch/arm64/boot/dts/baikal/bm1000.dtsi
|
||||||
|
+++ b/arch/arm64/boot/dts/baikal/bm1000.dtsi
|
||||||
|
@@ -692,6 +692,7 @@ gpu: gpu@2a200000 {
|
||||||
|
interrupt-names = "job", "mmu", "gpu";
|
||||||
|
clocks = <&cmu_mali>;
|
||||||
|
clock-names = "gpuclk";
|
||||||
|
+ dma-coherent;
|
||||||
|
operating-points-v2 = <&gpu_opp_table>;
|
||||||
|
};
|
||||||
|
|
||||||
|
--
|
||||||
|
2.33.2
|
||||||
|
|
|
@ -0,0 +1,49 @@
|
||||||
|
From 870600a887f2ae718b1abbff174e4943bc8df1d6 Mon Sep 17 00:00:00 2001
|
||||||
|
From: Alexey Sheplyakov <asheplyakov@altlinux.org>
|
||||||
|
Date: Mon, 13 Dec 2021 18:20:25 +0400
|
||||||
|
Subject: [PATCH 630/634] arm64: device tree: Baikal-M: fixed PHY binding
|
||||||
|
description
|
||||||
|
|
||||||
|
According to the documentation [1] Ethernet PHY binding `compatible`
|
||||||
|
string should be one of ethernet-phy-ieee802.3-{c22,c45}, and
|
||||||
|
(optionally) PHY ID (if the PHY reports incorrect ID or none at all).
|
||||||
|
|
||||||
|
Since v5.14 the kernel rejects PHY description if the `compatible`
|
||||||
|
contains wrong entries. As a result Ethernet driver is unable to
|
||||||
|
attach the PHY (and Ethernet can't transmit/receive any packets).
|
||||||
|
|
||||||
|
This patch removes extraneous entries (`micrel,ksz9031`) from PHY
|
||||||
|
binding nodes to avoid the problem.
|
||||||
|
|
||||||
|
[1] Documentation/devicetree/bindings/net/ethernet-phy.yaml
|
||||||
|
|
||||||
|
X-ALTLINUX-SKIP
|
||||||
|
---
|
||||||
|
arch/arm64/boot/dts/baikal/bm1000.dtsi | 4 ++--
|
||||||
|
1 file changed, 2 insertions(+), 2 deletions(-)
|
||||||
|
|
||||||
|
diff --git a/arch/arm64/boot/dts/baikal/bm1000.dtsi b/arch/arm64/boot/dts/baikal/bm1000.dtsi
|
||||||
|
index cc4e7d199..390f0a0c6 100644
|
||||||
|
--- a/arch/arm64/boot/dts/baikal/bm1000.dtsi
|
||||||
|
+++ b/arch/arm64/boot/dts/baikal/bm1000.dtsi
|
||||||
|
@@ -850,7 +850,7 @@ gmdio0: gmac0_mdio {
|
||||||
|
#size-cells = <0>;
|
||||||
|
|
||||||
|
gmac0_phy: ethernet-phy@3 {
|
||||||
|
- compatible = "micrel,ksz9031", "ethernet-phy-id0022.1620", "ethernet-phy-ieee802.3-c22";
|
||||||
|
+ compatible = "ethernet-phy-id0022.1620", "ethernet-phy-ieee802.3-c22";
|
||||||
|
reg = <0x3>;
|
||||||
|
txd0-skew-ps = <0>;
|
||||||
|
txd1-skew-ps = <0>;
|
||||||
|
@@ -889,7 +889,7 @@ gmdio1: gmac1_mdio {
|
||||||
|
#size-cells = <0>;
|
||||||
|
|
||||||
|
gmac1_phy: ethernet-phy@3 {
|
||||||
|
- compatible = "micrel,ksz9031", "ethernet-phy-id0022.1620", "ethernet-phy-ieee802.3-c22";
|
||||||
|
+ compatible = "ethernet-phy-id0022.1620", "ethernet-phy-ieee802.3-c22";
|
||||||
|
reg = <0x3>;
|
||||||
|
txd0-skew-ps = <0>;
|
||||||
|
txd1-skew-ps = <0>;
|
||||||
|
--
|
||||||
|
2.33.2
|
||||||
|
|
31
0631-arm64-device-tree-Baikal-M-fixed-gpio-alias.patch
Normal file
31
0631-arm64-device-tree-Baikal-M-fixed-gpio-alias.patch
Normal file
|
@ -0,0 +1,31 @@
|
||||||
|
From 7d075b28d2990337e27a14718cc75f574b7c328d Mon Sep 17 00:00:00 2001
|
||||||
|
From: Alexey Sheplyakov <asheplyakov@altlinux.org>
|
||||||
|
Date: Fri, 28 Jan 2022 12:52:09 +0400
|
||||||
|
Subject: [PATCH 631/634] arm64: device tree: Baikal-M: fixed gpio alias
|
||||||
|
|
||||||
|
Fixes the following dtc warning:
|
||||||
|
|
||||||
|
arch/arm64/boot/dts/baikal/bm1000.dtsi:35.3-16: Warning (gpios_property): /aliases:gpio: property size (19) is invalid, expected multiple of 4
|
||||||
|
|
||||||
|
X-DONTUPSTREAM
|
||||||
|
X-ALTLINUX-SKIP
|
||||||
|
---
|
||||||
|
arch/arm64/boot/dts/baikal/bm1000.dtsi | 2 +-
|
||||||
|
1 file changed, 1 insertion(+), 1 deletion(-)
|
||||||
|
|
||||||
|
diff --git a/arch/arm64/boot/dts/baikal/bm1000.dtsi b/arch/arm64/boot/dts/baikal/bm1000.dtsi
|
||||||
|
index 390f0a0c6..ee68b7d1e 100644
|
||||||
|
--- a/arch/arm64/boot/dts/baikal/bm1000.dtsi
|
||||||
|
+++ b/arch/arm64/boot/dts/baikal/bm1000.dtsi
|
||||||
|
@@ -32,7 +32,7 @@ aliases {
|
||||||
|
ethernet3 = &xgmac0;
|
||||||
|
ethernet4 = &xgmac1;
|
||||||
|
gic = &gic;
|
||||||
|
- gpio = &gpio;
|
||||||
|
+ gpio0 = &gpio;
|
||||||
|
hda = &hda;
|
||||||
|
i2c0 = &i2c0;
|
||||||
|
i2c1 = &i2c1;
|
||||||
|
--
|
||||||
|
2.33.2
|
||||||
|
|
67
0632-arm64-device-tree-Baikal-M-fixed-GPU-opp_table.patch
Normal file
67
0632-arm64-device-tree-Baikal-M-fixed-GPU-opp_table.patch
Normal file
|
@ -0,0 +1,67 @@
|
||||||
|
From 8a3db51c872b4f20483fffc4cc16449c73d4a92a Mon Sep 17 00:00:00 2001
|
||||||
|
From: Alexey Sheplyakov <asheplyakov@altlinux.org>
|
||||||
|
Date: Tue, 1 Feb 2022 20:01:35 +0400
|
||||||
|
Subject: [PATCH 632/634] arm64: device tree: Baikal-M: fixed GPU opp_table
|
||||||
|
|
||||||
|
According to Documentation/devicetree/bindings/opp/opp-v2-base.yml
|
||||||
|
a valid name of opp node must not contain '@' character.
|
||||||
|
With this patch GPU frequency scaling can be enabled without
|
||||||
|
locking up the system.
|
||||||
|
|
||||||
|
X-DONTUPSTREAM
|
||||||
|
X-ALTLINUX-SKIP
|
||||||
|
---
|
||||||
|
arch/arm64/boot/dts/baikal/bm1000-clocks.dtsi | 16 ++++++++--------
|
||||||
|
1 file changed, 8 insertions(+), 8 deletions(-)
|
||||||
|
|
||||||
|
diff --git a/arch/arm64/boot/dts/baikal/bm1000-clocks.dtsi b/arch/arm64/boot/dts/baikal/bm1000-clocks.dtsi
|
||||||
|
index 0a5579d36..3c0d7de3c 100644
|
||||||
|
--- a/arch/arm64/boot/dts/baikal/bm1000-clocks.dtsi
|
||||||
|
+++ b/arch/arm64/boot/dts/baikal/bm1000-clocks.dtsi
|
||||||
|
@@ -280,35 +280,35 @@ gpu_clk: gpu_clk {
|
||||||
|
gpu_opp_table: opp_table_gpu {
|
||||||
|
compatible = "operating-points-v2", "operating-points-v2-mali";
|
||||||
|
|
||||||
|
- opp@400000000 {
|
||||||
|
+ opp-400000000 {
|
||||||
|
opp-hz = /bits/ 64 <400000000>;
|
||||||
|
clock-latency-ns = <10000000>;
|
||||||
|
};
|
||||||
|
- opp@450000000 {
|
||||||
|
+ opp-450000000 {
|
||||||
|
opp-hz = /bits/ 64 <450000000>;
|
||||||
|
clock-latency-ns = <10000000>;
|
||||||
|
};
|
||||||
|
- opp@500000000 {
|
||||||
|
+ opp-500000000 {
|
||||||
|
opp-hz = /bits/ 64 <500000000>;
|
||||||
|
clock-latency-ns = <10000000>;
|
||||||
|
};
|
||||||
|
- opp@550000000 {
|
||||||
|
+ opp-550000000 {
|
||||||
|
opp-hz = /bits/ 64 <550000000>;
|
||||||
|
clock-latency-ns = <10000000>;
|
||||||
|
};
|
||||||
|
- opp@600000000 {
|
||||||
|
+ opp-600000000 {
|
||||||
|
opp-hz = /bits/ 64 <600000000>;
|
||||||
|
clock-latency-ns = <10000000>;
|
||||||
|
};
|
||||||
|
- opp@650000000 {
|
||||||
|
+ opp-650000000 {
|
||||||
|
opp-hz = /bits/ 64 <650000000>;
|
||||||
|
clock-latency-ns = <10000000>;
|
||||||
|
};
|
||||||
|
- opp@700000000 {
|
||||||
|
+ opp-700000000 {
|
||||||
|
opp-hz = /bits/ 64 <700000000>;
|
||||||
|
clock-latency-ns = <10000000>;
|
||||||
|
};
|
||||||
|
- opp@750000000 {
|
||||||
|
+ opp-750000000 {
|
||||||
|
opp-hz = /bits/ 64 <750000000>;
|
||||||
|
clock-latency-ns = <10000000>;
|
||||||
|
};
|
||||||
|
--
|
||||||
|
2.33.2
|
||||||
|
|
297
0633-arm64-device-tree-Baikal-M-fixed-CPUs-opp_table.patch
Normal file
297
0633-arm64-device-tree-Baikal-M-fixed-CPUs-opp_table.patch
Normal file
|
@ -0,0 +1,297 @@
|
||||||
|
From 1296be60a09000823f3afe0e0eca05954560d2b1 Mon Sep 17 00:00:00 2001
|
||||||
|
From: Alexey Sheplyakov <asheplyakov@altlinux.org>
|
||||||
|
Date: Tue, 1 Feb 2022 20:01:35 +0400
|
||||||
|
Subject: [PATCH 633/634] arm64: device tree: Baikal-M: fixed CPUs opp_table
|
||||||
|
|
||||||
|
According to Documentation/devicetree/bindings/opp/opp-v2-base.yml
|
||||||
|
a valid name of opp node must not contain '@' character.
|
||||||
|
|
||||||
|
X-DONTUPSTREAM
|
||||||
|
X-ALTLINUX-SKIP
|
||||||
|
---
|
||||||
|
.../arm64/boot/dts/baikal/bm1000-cpufreq.dtsi | 88 +++++++++----------
|
||||||
|
1 file changed, 44 insertions(+), 44 deletions(-)
|
||||||
|
|
||||||
|
diff --git a/arch/arm64/boot/dts/baikal/bm1000-cpufreq.dtsi b/arch/arm64/boot/dts/baikal/bm1000-cpufreq.dtsi
|
||||||
|
index 76fee58d8..6303c6f00 100644
|
||||||
|
--- a/arch/arm64/boot/dts/baikal/bm1000-cpufreq.dtsi
|
||||||
|
+++ b/arch/arm64/boot/dts/baikal/bm1000-cpufreq.dtsi
|
||||||
|
@@ -10,57 +10,57 @@ cluster0_opp: opp_table0 {
|
||||||
|
compatible = "operating-points-v2";
|
||||||
|
opp-shared;
|
||||||
|
|
||||||
|
- opp@1500 {
|
||||||
|
+ opp-1500000000 {
|
||||||
|
opp-hz = /bits/ 64 <1500000000>;
|
||||||
|
clock-latency-ns = <10000000>;
|
||||||
|
};
|
||||||
|
|
||||||
|
- opp@1400 {
|
||||||
|
+ opp-1400000000 {
|
||||||
|
opp-hz = /bits/ 64 <1400000000>;
|
||||||
|
clock-latency-ns = <10000000>;
|
||||||
|
};
|
||||||
|
|
||||||
|
- opp@1300 {
|
||||||
|
+ opp-1300000000 {
|
||||||
|
opp-hz = /bits/ 64 <1300000000>;
|
||||||
|
clock-latency-ns = <10000000>;
|
||||||
|
};
|
||||||
|
|
||||||
|
- opp@1200 {
|
||||||
|
+ opp-1200000000 {
|
||||||
|
opp-hz = /bits/ 64 <1200000000>;
|
||||||
|
clock-latency-ns = <10000000>;
|
||||||
|
};
|
||||||
|
|
||||||
|
- opp@1100 {
|
||||||
|
+ opp-1100000000 {
|
||||||
|
opp-hz = /bits/ 64 <1100000000>;
|
||||||
|
clock-latency-ns = <10000000>;
|
||||||
|
};
|
||||||
|
|
||||||
|
- opp@1000 {
|
||||||
|
+ opp-1000000000 {
|
||||||
|
opp-hz = /bits/ 64 <1000000000>;
|
||||||
|
clock-latency-ns = <10000000>;
|
||||||
|
};
|
||||||
|
|
||||||
|
- opp@900 {
|
||||||
|
+ opp-900000000 {
|
||||||
|
opp-hz = /bits/ 64 <900000000>;
|
||||||
|
clock-latency-ns = <10000000>;
|
||||||
|
};
|
||||||
|
|
||||||
|
- opp@800 {
|
||||||
|
+ opp-800000000 {
|
||||||
|
opp-hz = /bits/ 64 <800000000>;
|
||||||
|
clock-latency-ns = <10000000>;
|
||||||
|
};
|
||||||
|
|
||||||
|
- opp@700 {
|
||||||
|
+ opp-700000000 {
|
||||||
|
opp-hz = /bits/ 64 <700000000>;
|
||||||
|
clock-latency-ns = <10000000>;
|
||||||
|
};
|
||||||
|
|
||||||
|
- opp@600 {
|
||||||
|
+ opp-600000000 {
|
||||||
|
opp-hz = /bits/ 64 <600000000>;
|
||||||
|
clock-latency-ns = <10000000>;
|
||||||
|
};
|
||||||
|
|
||||||
|
- opp@500 {
|
||||||
|
+ opp-500000000 {
|
||||||
|
opp-hz = /bits/ 64 <500000000>;
|
||||||
|
clock-latency-ns = <10000000>;
|
||||||
|
};
|
||||||
|
@@ -70,57 +70,57 @@ cluster1_opp: opp_table1 {
|
||||||
|
compatible = "operating-points-v2";
|
||||||
|
opp-shared;
|
||||||
|
|
||||||
|
- opp@1500 {
|
||||||
|
+ opp-1500000000 {
|
||||||
|
opp-hz = /bits/ 64 <1500000000>;
|
||||||
|
clock-latency-ns = <10000000>;
|
||||||
|
};
|
||||||
|
|
||||||
|
- opp@1400 {
|
||||||
|
+ opp-1400000000 {
|
||||||
|
opp-hz = /bits/ 64 <1400000000>;
|
||||||
|
clock-latency-ns = <10000000>;
|
||||||
|
};
|
||||||
|
|
||||||
|
- opp@1300 {
|
||||||
|
+ opp-1300000000 {
|
||||||
|
opp-hz = /bits/ 64 <1300000000>;
|
||||||
|
clock-latency-ns = <10000000>;
|
||||||
|
};
|
||||||
|
|
||||||
|
- opp@1200 {
|
||||||
|
+ opp-1200000000 {
|
||||||
|
opp-hz = /bits/ 64 <1200000000>;
|
||||||
|
clock-latency-ns = <10000000>;
|
||||||
|
};
|
||||||
|
|
||||||
|
- opp@1100 {
|
||||||
|
+ opp-1100000000 {
|
||||||
|
opp-hz = /bits/ 64 <1100000000>;
|
||||||
|
clock-latency-ns = <10000000>;
|
||||||
|
};
|
||||||
|
|
||||||
|
- opp@1000 {
|
||||||
|
+ opp-1000000000 {
|
||||||
|
opp-hz = /bits/ 64 <1000000000>;
|
||||||
|
clock-latency-ns = <10000000>;
|
||||||
|
};
|
||||||
|
|
||||||
|
- opp@900 {
|
||||||
|
+ opp-900000000 {
|
||||||
|
opp-hz = /bits/ 64 <900000000>;
|
||||||
|
clock-latency-ns = <10000000>;
|
||||||
|
};
|
||||||
|
|
||||||
|
- opp@800 {
|
||||||
|
+ opp-800000000 {
|
||||||
|
opp-hz = /bits/ 64 <800000000>;
|
||||||
|
clock-latency-ns = <10000000>;
|
||||||
|
};
|
||||||
|
|
||||||
|
- opp@700 {
|
||||||
|
+ opp-700000000 {
|
||||||
|
opp-hz = /bits/ 64 <700000000>;
|
||||||
|
clock-latency-ns = <10000000>;
|
||||||
|
};
|
||||||
|
|
||||||
|
- opp@600 {
|
||||||
|
+ opp-600000000 {
|
||||||
|
opp-hz = /bits/ 64 <600000000>;
|
||||||
|
clock-latency-ns = <10000000>;
|
||||||
|
};
|
||||||
|
|
||||||
|
- opp@500 {
|
||||||
|
+ opp-500000000 {
|
||||||
|
opp-hz = /bits/ 64 <500000000>;
|
||||||
|
clock-latency-ns = <10000000>;
|
||||||
|
};
|
||||||
|
@@ -130,57 +130,57 @@ cluster2_opp: opp_table2 {
|
||||||
|
compatible = "operating-points-v2";
|
||||||
|
opp-shared;
|
||||||
|
|
||||||
|
- opp@1500 {
|
||||||
|
+ opp-1500000000 {
|
||||||
|
opp-hz = /bits/ 64 <1500000000>;
|
||||||
|
clock-latency-ns = <10000000>;
|
||||||
|
};
|
||||||
|
|
||||||
|
- opp@1400 {
|
||||||
|
+ opp-1400000000 {
|
||||||
|
opp-hz = /bits/ 64 <1400000000>;
|
||||||
|
clock-latency-ns = <10000000>;
|
||||||
|
};
|
||||||
|
|
||||||
|
- opp@1300 {
|
||||||
|
+ opp-1300000000 {
|
||||||
|
opp-hz = /bits/ 64 <1300000000>;
|
||||||
|
clock-latency-ns = <10000000>;
|
||||||
|
};
|
||||||
|
|
||||||
|
- opp@1200 {
|
||||||
|
+ opp-1200000000 {
|
||||||
|
opp-hz = /bits/ 64 <1200000000>;
|
||||||
|
clock-latency-ns = <10000000>;
|
||||||
|
};
|
||||||
|
|
||||||
|
- opp@1100 {
|
||||||
|
+ opp-1100000000 {
|
||||||
|
opp-hz = /bits/ 64 <1100000000>;
|
||||||
|
clock-latency-ns = <10000000>;
|
||||||
|
};
|
||||||
|
|
||||||
|
- opp@1000 {
|
||||||
|
+ opp-1000000000 {
|
||||||
|
opp-hz = /bits/ 64 <1000000000>;
|
||||||
|
clock-latency-ns = <10000000>;
|
||||||
|
};
|
||||||
|
|
||||||
|
- opp@900 {
|
||||||
|
+ opp-900000000 {
|
||||||
|
opp-hz = /bits/ 64 <900000000>;
|
||||||
|
clock-latency-ns = <10000000>;
|
||||||
|
};
|
||||||
|
|
||||||
|
- opp@800 {
|
||||||
|
+ opp-800000000 {
|
||||||
|
opp-hz = /bits/ 64 <800000000>;
|
||||||
|
clock-latency-ns = <10000000>;
|
||||||
|
};
|
||||||
|
|
||||||
|
- opp@700 {
|
||||||
|
+ opp-700000000 {
|
||||||
|
opp-hz = /bits/ 64 <700000000>;
|
||||||
|
clock-latency-ns = <10000000>;
|
||||||
|
};
|
||||||
|
|
||||||
|
- opp@600 {
|
||||||
|
+ opp-600000000 {
|
||||||
|
opp-hz = /bits/ 64 <600000000>;
|
||||||
|
clock-latency-ns = <10000000>;
|
||||||
|
};
|
||||||
|
|
||||||
|
- opp@500 {
|
||||||
|
+ opp-500000000 {
|
||||||
|
opp-hz = /bits/ 64 <500000000>;
|
||||||
|
clock-latency-ns = <10000000>;
|
||||||
|
};
|
||||||
|
@@ -190,57 +190,57 @@ cluster3_opp: opp_table3 {
|
||||||
|
compatible = "operating-points-v2";
|
||||||
|
opp-shared;
|
||||||
|
|
||||||
|
- opp@1500 {
|
||||||
|
+ opp-1500000000 {
|
||||||
|
opp-hz = /bits/ 64 <1500000000>;
|
||||||
|
clock-latency-ns = <10000000>;
|
||||||
|
};
|
||||||
|
|
||||||
|
- opp@1400 {
|
||||||
|
+ opp-1400000000 {
|
||||||
|
opp-hz = /bits/ 64 <1400000000>;
|
||||||
|
clock-latency-ns = <10000000>;
|
||||||
|
};
|
||||||
|
|
||||||
|
- opp@1300 {
|
||||||
|
+ opp-1300000000 {
|
||||||
|
opp-hz = /bits/ 64 <1300000000>;
|
||||||
|
clock-latency-ns = <10000000>;
|
||||||
|
};
|
||||||
|
|
||||||
|
- opp@1200 {
|
||||||
|
+ opp-1200000000 {
|
||||||
|
opp-hz = /bits/ 64 <1200000000>;
|
||||||
|
clock-latency-ns = <10000000>;
|
||||||
|
};
|
||||||
|
|
||||||
|
- opp@1100 {
|
||||||
|
+ opp-1100000000 {
|
||||||
|
opp-hz = /bits/ 64 <1100000000>;
|
||||||
|
clock-latency-ns = <10000000>;
|
||||||
|
};
|
||||||
|
|
||||||
|
- opp@1000 {
|
||||||
|
+ opp-1000000000 {
|
||||||
|
opp-hz = /bits/ 64 <1000000000>;
|
||||||
|
clock-latency-ns = <10000000>;
|
||||||
|
};
|
||||||
|
|
||||||
|
- opp@900 {
|
||||||
|
+ opp-900000000 {
|
||||||
|
opp-hz = /bits/ 64 <900000000>;
|
||||||
|
clock-latency-ns = <10000000>;
|
||||||
|
};
|
||||||
|
|
||||||
|
- opp@800 {
|
||||||
|
+ opp-800000000 {
|
||||||
|
opp-hz = /bits/ 64 <800000000>;
|
||||||
|
clock-latency-ns = <10000000>;
|
||||||
|
};
|
||||||
|
|
||||||
|
- opp@700 {
|
||||||
|
+ opp-700000000 {
|
||||||
|
opp-hz = /bits/ 64 <700000000>;
|
||||||
|
clock-latency-ns = <10000000>;
|
||||||
|
};
|
||||||
|
|
||||||
|
- opp@600 {
|
||||||
|
+ opp-600000000 {
|
||||||
|
opp-hz = /bits/ 64 <600000000>;
|
||||||
|
clock-latency-ns = <10000000>;
|
||||||
|
};
|
||||||
|
|
||||||
|
- opp@500 {
|
||||||
|
+ opp-500000000 {
|
||||||
|
opp-hz = /bits/ 64 <500000000>;
|
||||||
|
clock-latency-ns = <10000000>;
|
||||||
|
};
|
||||||
|
--
|
||||||
|
2.33.2
|
||||||
|
|
5158
0634-arm64-defconfig-for-Baikal-M-support-testing.patch
Normal file
5158
0634-arm64-defconfig-for-Baikal-M-support-testing.patch
Normal file
File diff suppressed because it is too large
Load diff
51
kernel.spec
51
kernel.spec
|
@ -29,10 +29,10 @@
|
||||||
|
|
||||||
%define kernelversion 5
|
%define kernelversion 5
|
||||||
%define patchlevel 15
|
%define patchlevel 15
|
||||||
%define sublevel 43
|
%define sublevel 53
|
||||||
|
|
||||||
# Release number. Increase this before a rebuild.
|
# Release number. Increase this before a rebuild.
|
||||||
%define rpmrel 2
|
%define rpmrel 1
|
||||||
%define fullrpmrel %{rpmrel}
|
%define fullrpmrel %{rpmrel}
|
||||||
|
|
||||||
%define rpmtag %{disttag}
|
%define rpmtag %{disttag}
|
||||||
|
@ -353,16 +353,43 @@ Patch307: le9pf.diff
|
||||||
Patch308: 0001-Revert-kallsyms-unexport-kallsyms_lookup_name-and-ka.patch
|
Patch308: 0001-Revert-kallsyms-unexport-kallsyms_lookup_name-and-ka.patch
|
||||||
|
|
||||||
# Support SoC with Baikal-M (ARMv8) CPU
|
# Support SoC with Baikal-M (ARMv8) CPU
|
||||||
# From http://git.altlinux.org/gears/k/kernel-image-std-def.git (many thanks!)
|
# http://git.altlinux.org/gears/k/kernel-image-std-def.git
|
||||||
# They are based on sources from official SDK with patched kernel from Baikal Electronics
|
# https://github.com/asheplyakov/linux/commits/baikalm-5.15.y-next (many thanks!)
|
||||||
Patch0601: 0601-baikalm.patch
|
Patch0600: 0600-drm-panfrost-initial-dual-core-group-GPUs-support.patch
|
||||||
Patch0604: 0604-efi-arm-runtime-print-EFI-mapping.patch
|
Patch0601: 0601-net-stmmac-inital-support-of-Baikal-T1-M-SoCs-GMAC.patch
|
||||||
Patch0608: 0608-Baikal-M-USB-driver.patch
|
Patch0602: 0602-dt-bindings-dwmac-Add-bindings-for-Baikal-T1-M-SoCs.patch
|
||||||
# https://bugzilla.altlinux.org/show_bug.cgi?id=40269
|
Patch0603: 0603-net-stmmac-custom-mdio-reset-for-some-Baikal-M-board.patch
|
||||||
Patch0609: 0609-Baikal-M-video-unit-driver.patch
|
Patch0604: 0604-net-dwmac-baikal-added-compatible-strings.patch
|
||||||
Patch0616: 0616-Baikal-M-PCIe-driver-from-SDK-M-4.3.patch
|
Patch0605: 0605-hwmon-bt1-pvt-access-registers-via-pvt_-readl-writel.patch
|
||||||
Patch0617: 0617-Baikal-M-PCIe-driver-from-SDK-M-4.4.patch
|
Patch0606: 0606-hwmon-bt1-pvt-define-pvt_readl-pvt_writel-for-Baikal.patch
|
||||||
Patch0619: 0619-panfrost-compatibility-with-Baikal-M-firmware-from-S.patch
|
Patch0607: 0607-hwmon-bt1-pvt-adjusted-probing-for-Baikal-M-SoC.patch
|
||||||
|
Patch0608: 0608-hwmon-bt1-pvt-added-compatible-baikal-pvt.patch
|
||||||
|
Patch0609: 0609-clk-added-Baikal-M-clock-management-unit-driver.patch
|
||||||
|
Patch0610: 0610-cpufreq-dt-don-t-load-on-Baikal-M-SoC.patch
|
||||||
|
Patch0611: 0611-usb-dwc3-of-simple-added-compatible-string-for-Baika.patch
|
||||||
|
Patch0612: 0612-arm64-Enable-armv8-based-Baikal-M-SoC-support.patch
|
||||||
|
Patch0613: 0613-drm-bridge-New-bridge-driver-stdp4028.patch
|
||||||
|
Patch0614: 0614-drm-added-Baikal-M-SoC-video-display-unit-driver.patch
|
||||||
|
Patch0615: 0615-baikal_vdu-et101-display-port-support.patch
|
||||||
|
Patch0616: 0616-dw-hdmi-ahb-audio-support-Baikal-M-SoC.patch
|
||||||
|
Patch0617: 0617-ALSA-hda-Baikal-M-SoC-support.patch
|
||||||
|
Patch0618: 0618-Added-TF307-TF306-board-management-controller-driver.patch
|
||||||
|
Patch0619: 0619-rejected-serial-8250_dw-verify-clock-rate-in-dw8250_.patch
|
||||||
|
Patch0620: 0620-drm-panfrost-forcibly-set-dma-coherent-on-Baikal-M.patch
|
||||||
|
Patch0621: 0621-drm-panfrost-disable-devfreq-on-Baikal-M.patch
|
||||||
|
Patch0622: 0622-pm-disable-all-sleep-states-on-Baikal-M-based-boards.patch
|
||||||
|
Patch0623: 0623-arm64-stub-fixed-secondary-cores-boot-on-Baikal-M-So.patch
|
||||||
|
Patch0624: 0624-efi-rtc-avoid-calling-efi.get_time-on-Baikal-M-SoC.patch
|
||||||
|
Patch0625: 0625-net-fwnode_get_phy_id-consider-all-compatible-string.patch
|
||||||
|
Patch0626: 0626-BROKEN-dwc-i2s-support-Baikal-M-SoC.patch
|
||||||
|
Patch0627: 0627-input-added-TF307-serio-PS-2-emulator-driver.patch
|
||||||
|
Patch0628: 0628-arm64-added-Baikal-M-SoC-and-TF307-board-device-tree.patch
|
||||||
|
Patch0629: 0629-arm64-device-tree-baikal-mark-GPU-as-dma-coherent.patch
|
||||||
|
Patch0630: 0630-arm64-device-tree-Baikal-M-fixed-PHY-binding-descrip.patch
|
||||||
|
Patch0631: 0631-arm64-device-tree-Baikal-M-fixed-gpio-alias.patch
|
||||||
|
Patch0632: 0632-arm64-device-tree-Baikal-M-fixed-GPU-opp_table.patch
|
||||||
|
Patch0633: 0633-arm64-device-tree-Baikal-M-fixed-CPUs-opp_table.patch
|
||||||
|
Patch0634: 0634-arm64-defconfig-for-Baikal-M-support-testing.patch
|
||||||
|
|
||||||
# Disable AutoReq
|
# Disable AutoReq
|
||||||
AutoReq: 0
|
AutoReq: 0
|
||||||
|
|
Loading…
Add table
Reference in a new issue