| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581 |
- // SPDX-License-Identifier: GPL-2.0-only
- /*
- * Reset driver for the Mobileye EyeQ5, EyeQ6L and EyeQ6H 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.
- *
- * 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 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
- *
- * 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/mod_devicetable.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,
- };
- /*
- * 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)
- struct eqr_busy_wait_timings {
- unsigned long sleep_us;
- unsigned long timeout_us;
- };
- static const struct eqr_busy_wait_timings eqr_timings[] = {
- [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},
- };
- #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_EYEQ5_PCIE:
- ret = 0; /* No busy waiting. */
- 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;
- 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;
- 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;
- 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));
- 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 void eqr_of_node_put(void *_dev)
- {
- struct device *dev = _dev;
- of_node_put(dev->of_node);
- }
- 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;
- /*
- * We are an auxiliary device of clk-eyeq. We do not have an OF node by
- * default; let's reuse our parent's OF node.
- */
- WARN_ON(dev->of_node);
- device_set_of_node_from_dev(dev, dev->parent);
- if (!dev->of_node)
- return -ENODEV;
- ret = devm_add_action_or_reset(dev, eqr_of_node_put, dev);
- if (ret)
- return ret;
- /*
- * Using our newfound OF node, we can get match data. We cannot use
- * device_get_match_data() because it does not match reused OF nodes.
- */
- 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,
- };
- /* 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,
- };
- /*
- * 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,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 },
- {}
- };
- MODULE_DEVICE_TABLE(of, eqr_match_table);
- static const struct auxiliary_device_id eqr_id_table[] = {
- { .name = "clk_eyeq.reset" },
- { .name = "clk_eyeq.reset_west" },
- { .name = "clk_eyeq.reset_east" },
- { .name = "clk_eyeq.reset_acc" },
- {}
- };
- 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);
|