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