Update to 5.15.53, format Baikal patches more clearly

This commit is contained in:
Mikhail Novosyolov 2022-07-11 14:38:59 +03:00
parent 9388a6c209
commit 8f19ac0825
44 changed files with 15593 additions and 25418 deletions

View file

@ -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

View 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

File diff suppressed because it is too large Load diff

View 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

View file

@ -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

View file

@ -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

View file

@ -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

View 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

View 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

View file

@ -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

View 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

View file

@ -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");

View 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

View file

@ -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);
+}

View 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

View 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

View file

@ -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

View 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

View 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

File diff suppressed because it is too large Load diff

View 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

View 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

View 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

View file

@ -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, &reg);
+ 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");

View 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

View file

@ -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);

View file

@ -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

View file

@ -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

View 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

View file

@ -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

View 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

View file

@ -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

View file

@ -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

View 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

View 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

File diff suppressed because it is too large Load diff

View 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

View file

@ -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

View 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

View 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

View 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

File diff suppressed because it is too large Load diff

View file

@ -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