summaryrefslogtreecommitdiffstats
path: root/drivers/reset/reset-eyeq.c
diff options
context:
space:
mode:
authorAdrian Betschart <adrian.betschart@cinemaone.ch>2026-09-11 17:13:14 +0200
committerAlex Deucher <alexander.deucher@amd.com>2026-10-01 17:18:05 -0400
commit106b4a2a78b2a2f14885604891957b84bba502db (patch)
tree5d40eef2428fb1a4791ab46a4b19091a5910eac2 /drivers/reset/reset-eyeq.c
downloadlinux-stable-106b4a2a78b2a2f14885604891957b84bba502db.tar.gz
linux-stable-106b4a2a78b2a2f14885604891957b84bba502db.zip
drm/amd/display: Try RGB before YCbCr 4:4:4 in stream validationgrafted
amdgpu_dm_create_validate_stream_for_sink() walks encoding_order[] and uses the first encoding that validates. YCbCr 4:4:4 is listed before RGB, so an HDMI sink that advertises 4:4:4 gets YCbCr 4:4:4 whenever the "color format" property is left at AUTO, even though RGB fits the same link. That contradicts the documented AUTO behaviour for HDMI in enum drm_connector_color_format (RGB, falling back to YCbCr 4:2:0 only when the bandwidth is not available or the mode is 4:2:0-only), which the amdgpu implementation of the property also describes. It also leaves the "Broadcast RGB" property without effect on such sinks, since the quantization range it selects only applies to RGB output. Try RGB first. The mask still holds every encoding the sink supports, so a mode that cannot carry RGB falls back exactly as before. For reference, v7.2 picked RGB here unless YCbCr 4:4:4 was forced through debugfs, while earlier kernels picked YCbCr 4:4:4 for any HDMI sink that advertised it. Fixes: 0b0ff65d3ca1 ("drm/amd/display: Refactor stream validation") Suggested-by: Adolfo Rodrigues <adolfotregosa@gmail.com> Assisted-by: Claude Code:claude-fable-5-1 Signed-off-by: Adrian Betschart <adrian.betschart@cinemaone.ch> Reviewed-by: Fangzhi Zuo <jerry.zuo@amd.com> Tested-by: Adolfo Rodrigues <adolfotregosa@gmail.com> Tested-by: Daniel Wheeler <daniel.wheeler@amd.com> Signed-off-by: Alex Deucher <alexander.deucher@amd.com> (cherry picked from commit 7de432bc753133764dc40d37f9388da56de251e7)
Diffstat (limited to 'drivers/reset/reset-eyeq.c')
-rw-r--r--drivers/reset/reset-eyeq.c844
1 files changed, 844 insertions, 0 deletions
diff --git a/drivers/reset/reset-eyeq.c b/drivers/reset/reset-eyeq.c
new file mode 100644
index 000000000..f2533086b
--- /dev/null
+++ b/drivers/reset/reset-eyeq.c
@@ -0,0 +1,844 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Reset driver for the Mobileye EyeQ5, EyeQ6L, EyeQ6H and EyeQ7H platforms.
+ *
+ * Controllers live in a shared register region called OLB. EyeQ5 and EyeQ6L
+ * have a single OLB instance for a single reset controller. EyeQ6H has seven
+ * OLB instances; three host reset controllers. EyeQ7H has fourteen OLB instances;
+ * eleven host reset controllers.
+ *
+ * Each reset controller has one or more domain. Domains are of a given type
+ * (see enum eqr_domain_type), with a valid offset mask (up to 32 resets per
+ * domain).
+ *
+ * Domain types define expected behavior: one-register-per-reset,
+ * one-bit-per-reset, status detection method, busywait duration, etc.
+ *
+ * We use eqr_ as prefix, as-in "EyeQ Reset", but way shorter.
+ *
+ * Known resets in EyeQ5 domain 0 (type EQR_EYEQ5_SARCR):
+ * 3. CAN0 4. CAN1 5. CAN2 6. SPI0
+ * 7. SPI1 8. SPI2 9. SPI3 10. UART0
+ * 11. UART1 12. UART2 13. I2C0 14. I2C1
+ * 15. I2C2 16. I2C3 17. I2C4 18. TIMER0
+ * 19. TIMER1 20. TIMER2 21. TIMER3 22. TIMER4
+ * 23. WD0 24. EXT0 25. EXT1 26. GPIO
+ * 27. WD1
+ *
+ * Known resets in EyeQ5 domain 1 (type EQR_EYEQ5_ACRP):
+ * 0. VMP0 1. VMP1 2. VMP2 3. VMP3
+ * 4. PMA0 5. PMA1 6. PMAC0 7. PMAC1
+ * 8. MPC0 9. MPC1 10. MPC2 11. MPC3
+ * 12. MPC4
+ *
+ * Known resets in EyeQ5 domain 2 (type EQR_EYEQ5_PCIE):
+ * 0. PCIE0_CORE 1. PCIE0_APB 2. PCIE0_LINK_AXI 3. PCIE0_LINK_MGMT
+ * 4. PCIE0_LINK_HOT 5. PCIE0_LINK_PIPE 6. PCIE1_CORE 7. PCIE1_APB
+ * 8. PCIE1_LINK_AXI 9. PCIE1_LINK_MGMT 10. PCIE1_LINK_HOT 11. PCIE1_LINK_PIPE
+ * 12. MULTIPHY 13. MULTIPHY_APB 15. PCIE0_LINK_MGMT 16. PCIE1_LINK_MGMT
+ * 17. PCIE0_LINK_PM 18. PCIE1_LINK_PM
+ *
+ * Known resets in EyeQ6L domain 0 (type EQR_EYEQ5_SARCR):
+ * 0. SPI0 1. SPI1 2. UART0 3. I2C0
+ * 4. I2C1 5. TIMER0 6. TIMER1 7. TIMER2
+ * 8. TIMER3 9. WD0 10. WD1 11. EXT0
+ * 12. EXT1 13. GPIO
+ *
+ * Known resets in EyeQ6L domain 1 (type EQR_EYEQ5_ACRP):
+ * 0. VMP0 1. VMP1 2. VMP2 3. VMP3
+ * 4. PMA0 5. PMA1 6. PMAC0 7. PMAC1
+ * 8. MPC0 9. MPC1 10. MPC2 11. MPC3
+ * 12. MPC4
+ *
+ * Known resets in EyeQ6Lplus domain 0 (type EQR_EYEQ5_PCIE):
+ * 0. SPI0 1. SPI1 2. UART0 3. I2C0
+ * 4. I2C1 5. TIMER0 6. TIMER1 7. TIMER2
+ * 8. TIMER3 9. WD0 10. WD1 11. EXT0
+ * 12. EXT1 13. GPIO
+ *
+ * Known resets in EyeQ6Lplus domain 1 (type EQR_EYEQ5_ACRP):
+ * 0. VMP0 1. VMP1 2. VMP2 3. VMP3
+ * 4. PMA0 5. PMA1 6. PMAC0 7. PMAC1
+ * 8. MPC0 9. MPC1 10. MPC2 11. MPC3
+ * 12. MPC4
+ *
+ * Known resets in EyeQ6H west/east (type EQR_EYEQ6H_SARCR):
+ * 0. CAN 1. SPI0 2. SPI1 3. UART0
+ * 4. UART1 5. I2C0 6. I2C1 7. -hole-
+ * 8. TIMER0 9. TIMER1 10. WD 11. EXT TIMER
+ * 12. GPIO
+ *
+ * Known resets in EyeQ6H acc (type EQR_EYEQ5_ACRP):
+ * 1. XNN0 2. XNN1 3. XNN2 4. XNN3
+ * 5. VMP0 6. VMP1 7. VMP2 8. VMP3
+ * 9. PMA0 10. PMA1 11. MPC0 12. MPC1
+ * 13. MPC2 14. MPC3 15. PERIPH
+ *
+ * Known resets in EyeQ7H acc domain 0 (type EQR_EYEQ7H_ACRP)
+ * 0. VMP0 1. XVMP1 2. VMP2 3. VMP3
+ * 4. MPC0 5. MPC1 6. PMA0 7. PMA1
+ *
+ * Known resets in EyeQ7H acc domain 1 (type EQR_EYEQ7H_ACC)
+ * 0. NCORE0 1. NCORE1 2. NCORE0_M 3. NCORE1_M
+ * 4. NCORE_NOC 5. VMP_NOC 6. MPC_NOC 7. PMA_NOC
+ *
+ * Known resets in EyeQ7H ddr (type EQR_EYEQ7H_CFG)
+ * 0. APB 1. DMI 2. DFI 3. PHY_SMS
+ * 4. CTL_SMS
+ *
+ * Known resets in EyeQ7H east (type EQR_EYEQ7H_CFG)
+ * 0. ISP 1. VEU 2. LBIST
+ *
+ * Known resets in EyeQ7H periph (type EQR_EYEQ6H_SARCR)
+ * 0. gpio 1.EXT TIMER 2.UART 3. SPI
+ * 4. I2C0 5. I2C1 6.I2C2 7. I2S
+ *
+ * Known resets in EyeQ7H south domain 0 (type EQR_EYEQ7H_CFG)
+ * 0. PCI_PHY 1. PCI_CTL 2. S_NOC 3. GBE_PHY
+ * 4. GBE_CTL
+ *
+ * Known resets in EyeQ7H south domain 1 (type EQR_EYEQ7H_CFG)
+ * 0. XSPI 1. UFS 2. VDIO
+ *
+ * Known resets in EyeQ7H west (type EQR_EYEQ7H_CFG)
+ * 0. GPU 1. CAU 2. LBIST 3. GPU_LBIST
+ *
+ * Known resets in EyeQ7H xnn domain 0 (type EQR_EYEQ7H_ACRP)
+ * 0. XNN0 1. XNN1 2.XNN2
+ *
+ * Known resets in EyeQ7H xnn domain 1 (type EQR_EYEQ7H_ACC)
+ * 0. XNN0 1. XNN1 2. XNN2 3. XNN3
+ * 4. NCORE 5. L2_0 6. L2_1 7. SMS_0
+ * 8. SMS_1
+ *
+ * Abbreviations:
+ * - PMA: Programmable Macro Array
+ * - MPC: Multi-threaded Processing Clusters
+ * - VMP: Vector Microcode Processors
+ *
+ * Copyright (C) 2024 Mobileye Vision Technologies Ltd.
+ */
+
+#include <linux/array_size.h>
+#include <linux/auxiliary_bus.h>
+#include <linux/bitfield.h>
+#include <linux/bits.h>
+#include <linux/bug.h>
+#include <linux/cleanup.h>
+#include <linux/container_of.h>
+#include <linux/device.h>
+#include <linux/err.h>
+#include <linux/errno.h>
+#include <linux/init.h>
+#include <linux/io.h>
+#include <linux/iopoll.h>
+#include <linux/lockdep.h>
+#include <linux/mutex.h>
+#include <linux/of.h>
+#include <linux/reset-controller.h>
+#include <linux/slab.h>
+#include <linux/types.h>
+
+/*
+ * A reset ID, as returned by eqr_of_xlate_*(), is a (domain, offset) pair.
+ * Low byte is domain, rest is offset.
+ */
+#define ID_DOMAIN_MASK GENMASK(7, 0)
+#define ID_OFFSET_MASK GENMASK(31, 8)
+
+enum eqr_domain_type {
+ EQR_EYEQ5_SARCR,
+ EQR_EYEQ5_ACRP,
+ EQR_EYEQ5_PCIE,
+ EQR_EYEQ6H_SARCR,
+ EQR_EYEQ7H_ACC,
+ EQR_EYEQ7H_ACRP,
+ EQR_EYEQ7H_CFG,
+
+ EQR_NB_DOM_TYPES /* number of domain types, keep at the end */
+};
+
+/*
+ * Domain type EQR_EYEQ5_SARCR register offsets.
+ */
+#define EQR_EYEQ5_SARCR_REQUEST (0x000)
+#define EQR_EYEQ5_SARCR_STATUS (0x004)
+
+/*
+ * Domain type EQR_EYEQ5_ACRP register masks.
+ * Registers are: base + 4 * offset.
+ */
+#define EQR_EYEQ5_ACRP_PD_REQ BIT(0)
+#define EQR_EYEQ5_ACRP_ST_POWER_DOWN BIT(27)
+#define EQR_EYEQ5_ACRP_ST_ACTIVE BIT(29)
+
+/*
+ * Domain type EQR_EYEQ6H_SARCR register offsets.
+ */
+#define EQR_EYEQ6H_SARCR_RST_REQUEST (0x000)
+#define EQR_EYEQ6H_SARCR_CLK_STATUS (0x004)
+#define EQR_EYEQ6H_SARCR_RST_STATUS (0x008)
+#define EQR_EYEQ6H_SARCR_CLK_REQUEST (0x00C)
+
+/*
+ * Domain type EQR_EYEQ7H_ACC register offsets.
+ */
+#define EQR_EYEQ7H_ACC_CLK_EN (0x000)
+#define EQR_EYEQ7H_ACC_RST_EN (0x004)
+
+/*
+ * Domain type EQR_EYEQ7H_ACRP register masks.
+ * Registers are: base + 4 * offset.
+ */
+#define EQR_EYEQ7H_ACRP_PD_REQ BIT(0)
+#define EQR_EYEQ7H_ACRP_MBIST_CFG GENMASK(3, 1)
+#define EQR_EYEQ7H_ACRP_ST_POWER_DOWN BIT(13)
+#define EQR_EYEQ7H_ACRP_ST_ACTIVE BIT(14)
+
+struct eqr_busy_wait_timings {
+ unsigned long sleep_us;
+ unsigned long timeout_us;
+};
+
+static const struct eqr_busy_wait_timings eqr_timings[EQR_NB_DOM_TYPES] = {
+ [EQR_EYEQ5_SARCR] = {1, 10},
+ [EQR_EYEQ5_ACRP] = {1, 40 * USEC_PER_MSEC}, /* LBIST implies long timeout. */
+ /* EQR_EYEQ5_PCIE does no busy waiting. */
+ [EQR_EYEQ6H_SARCR] = {1, 400},
+ /* EQR_EYEQ7H_ACC does no busy waiting. */
+ [EQR_EYEQ7H_ACRP] = {1, 40 * USEC_PER_MSEC},
+ /* EQR_EYEQ7H_CFG does no busy waiting. */
+};
+
+#define EQR_MAX_DOMAIN_COUNT 3
+
+struct eqr_domain_descriptor {
+ enum eqr_domain_type type;
+ u32 valid_mask;
+ unsigned int offset;
+};
+
+struct eqr_match_data {
+ unsigned int domain_count;
+ const struct eqr_domain_descriptor *domains;
+};
+
+struct eqr_private {
+ /*
+ * One mutex per domain for read-modify-write operations on registers.
+ * Some domains can be involved in LBIST which implies long critical
+ * sections; we wouldn't want other domains to be impacted by that.
+ */
+ struct mutex mutexes[EQR_MAX_DOMAIN_COUNT];
+ void __iomem *base;
+ const struct eqr_match_data *data;
+ struct reset_controller_dev rcdev;
+};
+
+static inline struct eqr_private *eqr_rcdev_to_priv(struct reset_controller_dev *x)
+{
+ return container_of(x, struct eqr_private, rcdev);
+}
+
+static u32 eqr_double_readl(void __iomem *addr_a, void __iomem *addr_b,
+ u32 *dest_a, u32 *dest_b)
+{
+ *dest_a = readl(addr_a);
+ *dest_b = readl(addr_b);
+ return 0; /* read_poll_timeout() op argument must return something. */
+}
+
+static int eqr_busy_wait_locked(struct eqr_private *priv, struct device *dev,
+ u32 domain, u32 offset, bool assert)
+{
+ void __iomem *base = priv->base + priv->data->domains[domain].offset;
+ enum eqr_domain_type domain_type = priv->data->domains[domain].type;
+ unsigned long timeout_us = eqr_timings[domain_type].timeout_us;
+ unsigned long sleep_us = eqr_timings[domain_type].sleep_us;
+ u32 val, mask, rst_status, clk_status;
+ void __iomem *reg;
+ int ret;
+
+ lockdep_assert_held(&priv->mutexes[domain]);
+
+ switch (domain_type) {
+ case EQR_EYEQ5_SARCR:
+ reg = base + EQR_EYEQ5_SARCR_STATUS;
+ mask = BIT(offset);
+
+ ret = readl_poll_timeout(reg, val, !(val & mask) == assert,
+ sleep_us, timeout_us);
+ break;
+
+ case EQR_EYEQ5_ACRP:
+ reg = base + 4 * offset;
+ if (assert)
+ mask = EQR_EYEQ5_ACRP_ST_POWER_DOWN;
+ else
+ mask = EQR_EYEQ5_ACRP_ST_ACTIVE;
+
+ ret = readl_poll_timeout(reg, val, !!(val & mask),
+ sleep_us, timeout_us);
+ break;
+
+ case EQR_EYEQ6H_SARCR:
+ /*
+ * Wait until both bits change:
+ * readl(base + EQR_EYEQ6H_SARCR_RST_STATUS) & BIT(offset)
+ * readl(base + EQR_EYEQ6H_SARCR_CLK_STATUS) & BIT(offset)
+ */
+ mask = BIT(offset);
+ ret = read_poll_timeout(eqr_double_readl, val,
+ (!(rst_status & mask) == assert) &&
+ (!(clk_status & mask) == assert),
+ sleep_us, timeout_us, false,
+ base + EQR_EYEQ6H_SARCR_RST_STATUS,
+ base + EQR_EYEQ6H_SARCR_CLK_STATUS,
+ &rst_status, &clk_status);
+ break;
+
+ case EQR_EYEQ7H_ACRP:
+ reg = base + 4 * offset;
+ if (assert)
+ mask = EQR_EYEQ7H_ACRP_ST_POWER_DOWN;
+ else
+ mask = EQR_EYEQ7H_ACRP_ST_ACTIVE;
+
+ ret = readl_poll_timeout(reg, val, !!(val & mask),
+ sleep_us, timeout_us);
+ break;
+
+ case EQR_EYEQ5_PCIE:
+ case EQR_EYEQ7H_ACC:
+ case EQR_EYEQ7H_CFG:
+ ret = 0; /* No busy waiting. */
+ break;
+
+ default:
+ WARN_ON(1);
+ ret = -EINVAL;
+ break;
+ }
+
+ if (ret == -ETIMEDOUT)
+ dev_dbg(dev, "%u-%u: timeout\n", domain, offset);
+ return ret;
+}
+
+static void eqr_assert_locked(struct eqr_private *priv, u32 domain, u32 offset)
+{
+ enum eqr_domain_type domain_type = priv->data->domains[domain].type;
+ void __iomem *base, *reg;
+ u32 val;
+
+ lockdep_assert_held(&priv->mutexes[domain]);
+
+ base = priv->base + priv->data->domains[domain].offset;
+
+ switch (domain_type) {
+ case EQR_EYEQ5_SARCR:
+ reg = base + EQR_EYEQ5_SARCR_REQUEST;
+ writel(readl(reg) & ~BIT(offset), reg);
+ break;
+
+ case EQR_EYEQ5_ACRP:
+ reg = base + 4 * offset;
+ writel(readl(reg) | EQR_EYEQ5_ACRP_PD_REQ, reg);
+ break;
+
+ case EQR_EYEQ5_PCIE:
+ writel(readl(base) & ~BIT(offset), base);
+ break;
+
+ case EQR_EYEQ6H_SARCR:
+ /* RST_REQUEST and CLK_REQUEST must be kept in sync. */
+ val = readl(base + EQR_EYEQ6H_SARCR_RST_REQUEST);
+ val &= ~BIT(offset);
+ writel(val, base + EQR_EYEQ6H_SARCR_RST_REQUEST);
+ writel(val, base + EQR_EYEQ6H_SARCR_CLK_REQUEST);
+ break;
+
+ case EQR_EYEQ7H_ACC:
+ /* RST_REQUEST and CLK_REQUEST must be kept in sync. */
+ val = readl(base + EQR_EYEQ7H_ACC_RST_EN);
+ val &= ~BIT(offset);
+ writel(val, base + EQR_EYEQ7H_ACC_RST_EN);
+ writel(val, base + EQR_EYEQ7H_ACC_CLK_EN);
+ break;
+
+ case EQR_EYEQ7H_ACRP:
+ /* set powerdown and leave MBIST bits at zero */
+ reg = base + 4 * offset;
+ val = readl(reg) & ~EQR_EYEQ7H_ACRP_MBIST_CFG;
+ writel(val | EQR_EYEQ7H_ACRP_PD_REQ, reg);
+ break;
+
+ case EQR_EYEQ7H_CFG:
+ /* clear clock enable and NRESET bits */
+ val = readl(base);
+ val &= ~GENMASK(2 * offset + 1, 2 * offset);
+ writel(val, base);
+ break;
+
+ default:
+ WARN_ON(1);
+ break;
+ }
+}
+
+static int eqr_assert(struct reset_controller_dev *rcdev, unsigned long id)
+{
+ struct eqr_private *priv = eqr_rcdev_to_priv(rcdev);
+ u32 domain = FIELD_GET(ID_DOMAIN_MASK, id);
+ u32 offset = FIELD_GET(ID_OFFSET_MASK, id);
+
+ dev_dbg(rcdev->dev, "%u-%u: assert request\n", domain, offset);
+
+ guard(mutex)(&priv->mutexes[domain]);
+
+ eqr_assert_locked(priv, domain, offset);
+ return eqr_busy_wait_locked(priv, rcdev->dev, domain, offset, true);
+}
+
+static void eqr_deassert_locked(struct eqr_private *priv, u32 domain,
+ u32 offset)
+{
+ enum eqr_domain_type domain_type = priv->data->domains[domain].type;
+ void __iomem *base, *reg;
+ u32 val;
+
+ lockdep_assert_held(&priv->mutexes[domain]);
+
+ base = priv->base + priv->data->domains[domain].offset;
+
+ switch (domain_type) {
+ case EQR_EYEQ5_SARCR:
+ reg = base + EQR_EYEQ5_SARCR_REQUEST;
+ writel(readl(reg) | BIT(offset), reg);
+ break;
+
+ case EQR_EYEQ5_ACRP:
+ reg = base + 4 * offset;
+ writel(readl(reg) & ~EQR_EYEQ5_ACRP_PD_REQ, reg);
+ break;
+
+ case EQR_EYEQ5_PCIE:
+ writel(readl(base) | BIT(offset), base);
+ break;
+
+ case EQR_EYEQ6H_SARCR:
+ /* RST_REQUEST and CLK_REQUEST must be kept in sync. */
+ val = readl(base + EQR_EYEQ6H_SARCR_RST_REQUEST);
+ val |= BIT(offset);
+ writel(val, base + EQR_EYEQ6H_SARCR_RST_REQUEST);
+ writel(val, base + EQR_EYEQ6H_SARCR_CLK_REQUEST);
+ break;
+
+ case EQR_EYEQ7H_ACC:
+ /* RST_REQUEST and CLK_REQUEST must be kept in sync. */
+ val = readl(base + EQR_EYEQ7H_ACC_RST_EN);
+ val |= BIT(offset);
+ writel(val, base + EQR_EYEQ7H_ACC_RST_EN);
+ writel(val, base + EQR_EYEQ7H_ACC_CLK_EN);
+ break;
+
+ case EQR_EYEQ7H_ACRP:
+ /* clear powerdown and leave MBIST bits at zero */
+ reg = base + 4 * offset;
+ val = readl(reg) & ~EQR_EYEQ7H_ACRP_MBIST_CFG;
+ writel(val & ~EQR_EYEQ7H_ACRP_PD_REQ, reg);
+ break;
+
+ case EQR_EYEQ7H_CFG:
+ /* set clock enable and NRESET bits */
+ val = readl(base);
+ val |= GENMASK(2 * offset + 1, 2 * offset);
+ writel(val, base);
+ break;
+
+ default:
+ WARN_ON(1);
+ break;
+ }
+}
+
+static int eqr_deassert(struct reset_controller_dev *rcdev, unsigned long id)
+{
+ struct eqr_private *priv = eqr_rcdev_to_priv(rcdev);
+ u32 domain = FIELD_GET(ID_DOMAIN_MASK, id);
+ u32 offset = FIELD_GET(ID_OFFSET_MASK, id);
+
+ dev_dbg(rcdev->dev, "%u-%u: deassert request\n", domain, offset);
+
+ guard(mutex)(&priv->mutexes[domain]);
+
+ eqr_deassert_locked(priv, domain, offset);
+ return eqr_busy_wait_locked(priv, rcdev->dev, domain, offset, false);
+}
+
+static int eqr_status(struct reset_controller_dev *rcdev, unsigned long id)
+{
+ u32 domain = FIELD_GET(ID_DOMAIN_MASK, id);
+ u32 offset = FIELD_GET(ID_OFFSET_MASK, id);
+ struct eqr_private *priv = eqr_rcdev_to_priv(rcdev);
+ enum eqr_domain_type domain_type = priv->data->domains[domain].type;
+ void __iomem *base, *reg;
+
+ dev_dbg(rcdev->dev, "%u-%u: status request\n", domain, offset);
+
+ guard(mutex)(&priv->mutexes[domain]);
+
+ base = priv->base + priv->data->domains[domain].offset;
+
+ switch (domain_type) {
+ case EQR_EYEQ5_SARCR:
+ reg = base + EQR_EYEQ5_SARCR_STATUS;
+ return !(readl(reg) & BIT(offset));
+ case EQR_EYEQ5_ACRP:
+ reg = base + 4 * offset;
+ return !(readl(reg) & EQR_EYEQ5_ACRP_ST_ACTIVE);
+ case EQR_EYEQ5_PCIE:
+ return !(readl(base) & BIT(offset));
+ case EQR_EYEQ6H_SARCR:
+ reg = base + EQR_EYEQ6H_SARCR_RST_STATUS;
+ return !(readl(reg) & BIT(offset));
+ case EQR_EYEQ7H_ACC:
+ reg = base + EQR_EYEQ7H_ACC_RST_EN;
+ return !(readl(reg) & BIT(offset));
+ case EQR_EYEQ7H_ACRP:
+ reg = base + 4 * offset;
+ return !(readl(reg) & EQR_EYEQ7H_ACRP_ST_ACTIVE);
+ case EQR_EYEQ7H_CFG:
+ return !(readl(base) & BIT(2 * offset));
+ default:
+ return -EINVAL;
+ }
+}
+
+static const struct reset_control_ops eqr_ops = {
+ .assert = eqr_assert,
+ .deassert = eqr_deassert,
+ .status = eqr_status,
+};
+
+static int eqr_of_xlate_internal(struct reset_controller_dev *rcdev,
+ u32 domain, u32 offset)
+{
+ struct eqr_private *priv = eqr_rcdev_to_priv(rcdev);
+
+ if (domain >= priv->data->domain_count || offset > 31 ||
+ !(priv->data->domains[domain].valid_mask & BIT(offset))) {
+ dev_err(rcdev->dev, "%u-%u: invalid reset\n", domain, offset);
+ return -EINVAL;
+ }
+
+ return FIELD_PREP(ID_DOMAIN_MASK, domain) | FIELD_PREP(ID_OFFSET_MASK, offset);
+}
+
+static int eqr_of_xlate_onecell(struct reset_controller_dev *rcdev,
+ const struct of_phandle_args *reset_spec)
+{
+ return eqr_of_xlate_internal(rcdev, 0, reset_spec->args[0]);
+}
+
+static int eqr_of_xlate_twocells(struct reset_controller_dev *rcdev,
+ const struct of_phandle_args *reset_spec)
+{
+ return eqr_of_xlate_internal(rcdev, reset_spec->args[0], reset_spec->args[1]);
+}
+
+static int eqr_probe(struct auxiliary_device *adev,
+ const struct auxiliary_device_id *id)
+{
+ const struct of_device_id *match;
+ struct device *dev = &adev->dev;
+ struct eqr_private *priv;
+ unsigned int i;
+ int ret;
+
+ /*
+ * Get match data. We cannot use device_get_match_data() because it does
+ * not accept reused OF nodes; see device_set_of_node_from_dev().
+ */
+ match = of_match_node(dev->driver->of_match_table, dev->of_node);
+ if (!match || !match->data)
+ return -ENODEV;
+
+ priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ priv->data = match->data;
+ priv->base = (void __iomem *)dev_get_platdata(dev);
+ priv->rcdev.ops = &eqr_ops;
+ priv->rcdev.owner = THIS_MODULE;
+ priv->rcdev.dev = dev;
+ priv->rcdev.of_node = dev->of_node;
+
+ if (priv->data->domain_count == 1) {
+ priv->rcdev.of_reset_n_cells = 1;
+ priv->rcdev.of_xlate = eqr_of_xlate_onecell;
+ } else {
+ priv->rcdev.of_reset_n_cells = 2;
+ priv->rcdev.of_xlate = eqr_of_xlate_twocells;
+ }
+
+ for (i = 0; i < priv->data->domain_count; i++)
+ mutex_init(&priv->mutexes[i]);
+
+ priv->rcdev.nr_resets = 0;
+ for (i = 0; i < priv->data->domain_count; i++)
+ priv->rcdev.nr_resets += hweight32(priv->data->domains[i].valid_mask);
+
+ ret = devm_reset_controller_register(dev, &priv->rcdev);
+ if (ret)
+ return dev_err_probe(dev, ret, "failed registering reset controller\n");
+
+ return 0;
+}
+
+static const struct eqr_domain_descriptor eqr_eyeq5_domains[] = {
+ {
+ .type = EQR_EYEQ5_SARCR,
+ .valid_mask = 0xFFFFFF8,
+ .offset = 0x004,
+ },
+ {
+ .type = EQR_EYEQ5_ACRP,
+ .valid_mask = 0x0001FFF,
+ .offset = 0x200,
+ },
+ {
+ .type = EQR_EYEQ5_PCIE,
+ .valid_mask = 0x007BFFF,
+ .offset = 0x120,
+ },
+};
+
+static const struct eqr_match_data eqr_eyeq5_data = {
+ .domain_count = ARRAY_SIZE(eqr_eyeq5_domains),
+ .domains = eqr_eyeq5_domains,
+};
+
+static const struct eqr_domain_descriptor eqr_eyeq6l_domains[] = {
+ {
+ .type = EQR_EYEQ5_SARCR,
+ .valid_mask = 0x3FFF,
+ .offset = 0x004,
+ },
+ {
+ .type = EQR_EYEQ5_ACRP,
+ .valid_mask = 0x00FF,
+ .offset = 0x200,
+ },
+};
+
+static const struct eqr_match_data eqr_eyeq6l_data = {
+ .domain_count = ARRAY_SIZE(eqr_eyeq6l_domains),
+ .domains = eqr_eyeq6l_domains,
+};
+
+static const struct eqr_domain_descriptor eqr_eyeq6lplus_domains[] = {
+ {
+ .type = EQR_EYEQ5_PCIE,
+ .valid_mask = 0x3FFF,
+ .offset = 0x004,
+ },
+ {
+ .type = EQR_EYEQ5_ACRP,
+ .valid_mask = 0x00FF,
+ .offset = 0x200,
+ },
+};
+
+static const struct eqr_match_data eqr_eyeq6lplus_data = {
+ .domain_count = ARRAY_SIZE(eqr_eyeq6lplus_domains),
+ .domains = eqr_eyeq6lplus_domains,
+};
+
+/* West and east OLBs each have an instance. */
+static const struct eqr_domain_descriptor eqr_eyeq6h_we_domains[] = {
+ {
+ .type = EQR_EYEQ6H_SARCR,
+ .valid_mask = 0x1F7F,
+ .offset = 0x004,
+ },
+};
+
+static const struct eqr_match_data eqr_eyeq6h_we_data = {
+ .domain_count = ARRAY_SIZE(eqr_eyeq6h_we_domains),
+ .domains = eqr_eyeq6h_we_domains,
+};
+
+static const struct eqr_domain_descriptor eqr_eyeq6h_acc_domains[] = {
+ {
+ .type = EQR_EYEQ5_ACRP,
+ .valid_mask = 0x7FFF,
+ .offset = 0x000,
+ },
+};
+
+static const struct eqr_match_data eqr_eyeq6h_acc_data = {
+ .domain_count = ARRAY_SIZE(eqr_eyeq6h_acc_domains),
+ .domains = eqr_eyeq6h_acc_domains,
+};
+
+static const struct eqr_domain_descriptor eqr_eyeq7h_acc_domains[] = {
+ {
+ .type = EQR_EYEQ7H_ACRP,
+ .valid_mask = 0xFF,
+ .offset = 0x000,
+ },
+ {
+ .type = EQR_EYEQ7H_ACC,
+ .valid_mask = 0xFF,
+ .offset = 0x060,
+ },
+};
+
+static const struct eqr_match_data eqr_eyeq7h_acc_data = {
+ .domain_count = ARRAY_SIZE(eqr_eyeq7h_acc_domains),
+ .domains = eqr_eyeq7h_acc_domains,
+};
+
+static const struct eqr_domain_descriptor eqr_eyeq7h_ddr_domains[] = {
+ {
+ .type = EQR_EYEQ7H_CFG,
+ .valid_mask = 0x1F,
+ .offset = 0x008,
+ },
+};
+
+static const struct eqr_match_data eqr_eyeq7h_ddr_data = {
+ .domain_count = ARRAY_SIZE(eqr_eyeq7h_ddr_domains),
+ .domains = eqr_eyeq7h_ddr_domains,
+};
+
+static const struct eqr_domain_descriptor eqr_eyeq7h_east_domains[] = {
+ {
+ .type = EQR_EYEQ7H_CFG,
+ .valid_mask = 0x7,
+ .offset = 0x060,
+ },
+};
+
+static const struct eqr_match_data eqr_eyeq7h_east_data = {
+ .domain_count = ARRAY_SIZE(eqr_eyeq7h_east_domains),
+ .domains = eqr_eyeq7h_east_domains,
+};
+
+/* Periph OLBs each have an instance. */
+static const struct eqr_domain_descriptor eqr_eyeq7h_per_domains[] = {
+ {
+ .type = EQR_EYEQ6H_SARCR,
+ .valid_mask = 0xFF,
+ .offset = 0x030,
+ },
+};
+
+static const struct eqr_match_data eqr_eyeq7h_per_data = {
+ .domain_count = ARRAY_SIZE(eqr_eyeq7h_per_domains),
+ .domains = eqr_eyeq7h_per_domains,
+};
+
+static const struct eqr_domain_descriptor eqr_eyeq7h_south_domains[] = {
+ {
+ .type = EQR_EYEQ7H_CFG,
+ .valid_mask = 0x1F,
+ .offset = 0x070,
+ },
+ {
+ .type = EQR_EYEQ7H_CFG,
+ .valid_mask = 0x7,
+ .offset = 0x074,
+ },
+};
+
+static const struct eqr_match_data eqr_eyeq7h_south_data = {
+ .domain_count = ARRAY_SIZE(eqr_eyeq7h_south_domains),
+ .domains = eqr_eyeq7h_south_domains,
+};
+
+static const struct eqr_domain_descriptor eqr_eyeq7h_west_domains[] = {
+ {
+ .type = EQR_EYEQ7H_CFG,
+ .valid_mask = 0xf,
+ .offset = 0x068,
+ },
+};
+
+static const struct eqr_match_data eqr_eyeq7h_west_data = {
+ .domain_count = ARRAY_SIZE(eqr_eyeq7h_west_domains),
+ .domains = eqr_eyeq7h_west_domains,
+};
+
+static const struct eqr_domain_descriptor eqr_eyeq7h_xnn_domains[] = {
+ {
+ .type = EQR_EYEQ7H_ACRP,
+ .valid_mask = 0x7,
+ .offset = 0x000,
+ },
+ {
+ .type = EQR_EYEQ7H_ACC,
+ .valid_mask = 0x1FF,
+ .offset = 0x060,
+ },
+};
+
+static const struct eqr_match_data eqr_eyeq7h_xnn_data = {
+ .domain_count = ARRAY_SIZE(eqr_eyeq7h_xnn_domains),
+ .domains = eqr_eyeq7h_xnn_domains,
+};
+
+/*
+ * Table describes OLB system-controller compatibles.
+ * It does not get used to match against devicetree node.
+ */
+static const struct of_device_id eqr_match_table[] = {
+ { .compatible = "mobileye,eyeq5-olb", .data = &eqr_eyeq5_data },
+ { .compatible = "mobileye,eyeq6l-olb", .data = &eqr_eyeq6l_data },
+ { .compatible = "mobileye,eyeq6lplus-olb", .data = &eqr_eyeq6lplus_data },
+ { .compatible = "mobileye,eyeq6h-west-olb", .data = &eqr_eyeq6h_we_data },
+ { .compatible = "mobileye,eyeq6h-east-olb", .data = &eqr_eyeq6h_we_data },
+ { .compatible = "mobileye,eyeq6h-acc-olb", .data = &eqr_eyeq6h_acc_data },
+ { .compatible = "mobileye,eyeq7h-acc0-olb", .data = &eqr_eyeq7h_acc_data },
+ { .compatible = "mobileye,eyeq7h-acc1-olb", .data = &eqr_eyeq7h_acc_data },
+ { .compatible = "mobileye,eyeq7h-ddr0-olb", .data = &eqr_eyeq7h_ddr_data },
+ { .compatible = "mobileye,eyeq7h-ddr1-olb", .data = &eqr_eyeq7h_ddr_data },
+ { .compatible = "mobileye,eyeq7h-east-olb", .data = &eqr_eyeq7h_east_data },
+ { .compatible = "mobileye,eyeq7h-periph-east-olb", .data = &eqr_eyeq7h_per_data },
+ { .compatible = "mobileye,eyeq7h-periph-west-olb", .data = &eqr_eyeq7h_per_data },
+ { .compatible = "mobileye,eyeq7h-south-olb", .data = &eqr_eyeq7h_south_data },
+ { .compatible = "mobileye,eyeq7h-west-olb", .data = &eqr_eyeq7h_west_data },
+ { .compatible = "mobileye,eyeq7h-xnn0-olb", .data = &eqr_eyeq7h_xnn_data },
+ { .compatible = "mobileye,eyeq7h-xnn1-olb", .data = &eqr_eyeq7h_xnn_data },
+ {}
+};
+MODULE_DEVICE_TABLE(of, eqr_match_table);
+
+static const struct auxiliary_device_id eqr_id_table[] = {
+ { .name = "clk_eyeq.reset" },
+ { .name = "clk_eyeq.reset_acc" },
+ { .name = "clk_eyeq.reset_acc0" },
+ { .name = "clk_eyeq.reset_acc1" },
+ { .name = "clk_eyeq.reset_ddr0" },
+ { .name = "clk_eyeq.reset_ddr1" },
+ { .name = "clk_eyeq.reset_east" },
+ { .name = "clk_eyeq.reset_periph_east" },
+ { .name = "clk_eyeq.reset_periph_west" },
+ { .name = "clk_eyeq.reset_south" },
+ { .name = "clk_eyeq.reset_west" },
+ { .name = "clk_eyeq.reset_xnn0" },
+ { .name = "clk_eyeq.reset_xnn1" },
+ {}
+};
+MODULE_DEVICE_TABLE(auxiliary, eqr_id_table);
+
+static struct auxiliary_driver eqr_driver = {
+ .probe = eqr_probe,
+ .id_table = eqr_id_table,
+ .driver = {
+ .of_match_table = eqr_match_table,
+ }
+};
+module_auxiliary_driver(eqr_driver);