|
| 1 | +// SPDX-License-Identifier: GPL-2.0 |
| 2 | +/* |
| 3 | + * Tenstorrent Atlantis PRCM Reset Driver |
| 4 | + * |
| 5 | + * Copyright (c) 2026 Tenstorrent |
| 6 | + */ |
| 7 | + |
| 8 | +#include <dt-bindings/clock/tenstorrent,atlantis-prcm-rcpu.h> |
| 9 | +#include <linux/auxiliary_bus.h> |
| 10 | +#include <linux/reset-controller.h> |
| 11 | +#include <linux/regmap.h> |
| 12 | + |
| 13 | +/* RCPU Reset Register Offsets */ |
| 14 | +#define RCPU_BLK_RST_REG 0x001c |
| 15 | +#define LSIO_BLK_RST_REG 0x0020 |
| 16 | +#define HSIO_BLK_RST_REG 0x000c |
| 17 | +#define PCIE_SUBS_RST_REG 0x0000 |
| 18 | +#define MM_RSTN_REG 0x0014 |
| 19 | + |
| 20 | +struct atlantis_reset_data { |
| 21 | + u8 bit; |
| 22 | + u16 reg; |
| 23 | + bool active_low; |
| 24 | +}; |
| 25 | + |
| 26 | +struct atlantis_reset_controller_data { |
| 27 | + const struct atlantis_reset_data *reset_data; |
| 28 | + size_t count; |
| 29 | +}; |
| 30 | + |
| 31 | +struct atlantis_reset_controller { |
| 32 | + struct reset_controller_dev rcdev; |
| 33 | + const struct atlantis_reset_controller_data *data; |
| 34 | + struct regmap *regmap; |
| 35 | +}; |
| 36 | + |
| 37 | +static inline struct atlantis_reset_controller * |
| 38 | +to_atlantis_reset_controller(struct reset_controller_dev *rcdev) |
| 39 | +{ |
| 40 | + return container_of(rcdev, struct atlantis_reset_controller, rcdev); |
| 41 | +} |
| 42 | + |
| 43 | +#define RESET_DATA(_reg, _bit, _active_low) \ |
| 44 | + { \ |
| 45 | + .bit = _bit, .reg = _reg, .active_low = _active_low, \ |
| 46 | + } |
| 47 | + |
| 48 | +static const struct atlantis_reset_data atlantis_rcpu_resets[] = { |
| 49 | + [RST_SMNDMA0] = RESET_DATA(RCPU_BLK_RST_REG, 0, true), |
| 50 | + [RST_SMNDMA1] = RESET_DATA(RCPU_BLK_RST_REG, 1, true), |
| 51 | + [RST_WDT0] = RESET_DATA(RCPU_BLK_RST_REG, 2, true), |
| 52 | + [RST_WDT1] = RESET_DATA(RCPU_BLK_RST_REG, 3, true), |
| 53 | + [RST_TMR] = RESET_DATA(RCPU_BLK_RST_REG, 4, true), |
| 54 | + [RST_PVTC] = RESET_DATA(RCPU_BLK_RST_REG, 12, true), |
| 55 | + [RST_PMU] = RESET_DATA(RCPU_BLK_RST_REG, 13, true), |
| 56 | + [RST_MAILBOX] = RESET_DATA(RCPU_BLK_RST_REG, 14, true), |
| 57 | + [RST_SPACC] = RESET_DATA(RCPU_BLK_RST_REG, 26, true), |
| 58 | + [RST_OTP] = RESET_DATA(RCPU_BLK_RST_REG, 28, true), |
| 59 | + [RST_TRNG] = RESET_DATA(RCPU_BLK_RST_REG, 29, true), |
| 60 | + [RST_CRC] = RESET_DATA(RCPU_BLK_RST_REG, 30, true), |
| 61 | + [RST_QSPI] = RESET_DATA(LSIO_BLK_RST_REG, 0, true), |
| 62 | + [RST_I2C0] = RESET_DATA(LSIO_BLK_RST_REG, 1, true), |
| 63 | + [RST_I2C1] = RESET_DATA(LSIO_BLK_RST_REG, 2, true), |
| 64 | + [RST_I2C2] = RESET_DATA(LSIO_BLK_RST_REG, 3, true), |
| 65 | + [RST_I2C3] = RESET_DATA(LSIO_BLK_RST_REG, 4, true), |
| 66 | + [RST_I2C4] = RESET_DATA(LSIO_BLK_RST_REG, 5, true), |
| 67 | + [RST_UART0] = RESET_DATA(LSIO_BLK_RST_REG, 6, true), |
| 68 | + [RST_UART1] = RESET_DATA(LSIO_BLK_RST_REG, 7, true), |
| 69 | + [RST_UART2] = RESET_DATA(LSIO_BLK_RST_REG, 8, true), |
| 70 | + [RST_UART3] = RESET_DATA(LSIO_BLK_RST_REG, 9, true), |
| 71 | + [RST_UART4] = RESET_DATA(LSIO_BLK_RST_REG, 10, true), |
| 72 | + [RST_SPI0] = RESET_DATA(LSIO_BLK_RST_REG, 11, true), |
| 73 | + [RST_SPI1] = RESET_DATA(LSIO_BLK_RST_REG, 12, true), |
| 74 | + [RST_SPI2] = RESET_DATA(LSIO_BLK_RST_REG, 13, true), |
| 75 | + [RST_SPI3] = RESET_DATA(LSIO_BLK_RST_REG, 14, true), |
| 76 | + [RST_GPIO] = RESET_DATA(LSIO_BLK_RST_REG, 15, true), |
| 77 | + [RST_CAN0] = RESET_DATA(LSIO_BLK_RST_REG, 17, true), |
| 78 | + [RST_CAN1] = RESET_DATA(LSIO_BLK_RST_REG, 18, true), |
| 79 | + [RST_I2S0] = RESET_DATA(LSIO_BLK_RST_REG, 19, true), |
| 80 | + [RST_I2S1] = RESET_DATA(LSIO_BLK_RST_REG, 20, true), |
| 81 | + |
| 82 | +}; |
| 83 | + |
| 84 | +static const struct atlantis_reset_controller_data atlantis_rcpu_reset_data = { |
| 85 | + .reset_data = atlantis_rcpu_resets, |
| 86 | + .count = ARRAY_SIZE(atlantis_rcpu_resets), |
| 87 | +}; |
| 88 | + |
| 89 | +static int atlantis_reset_update(struct reset_controller_dev *rcdev, |
| 90 | + unsigned long id, bool assert) |
| 91 | +{ |
| 92 | + unsigned int val; |
| 93 | + struct atlantis_reset_controller *rst = |
| 94 | + to_atlantis_reset_controller(rcdev); |
| 95 | + const struct atlantis_reset_data *data = &rst->data->reset_data[id]; |
| 96 | + unsigned int mask = BIT(data->bit); |
| 97 | + struct regmap *regmap = rst->regmap; |
| 98 | + |
| 99 | + if (data->active_low ^ assert) |
| 100 | + val = mask; |
| 101 | + else |
| 102 | + val = 0; |
| 103 | + |
| 104 | + return regmap_update_bits(regmap, data->reg, mask, val); |
| 105 | +} |
| 106 | + |
| 107 | +static int atlantis_reset_assert(struct reset_controller_dev *rcdev, |
| 108 | + unsigned long id) |
| 109 | +{ |
| 110 | + return atlantis_reset_update(rcdev, id, true); |
| 111 | +} |
| 112 | + |
| 113 | +static int atlantis_reset_deassert(struct reset_controller_dev *rcdev, |
| 114 | + unsigned long id) |
| 115 | +{ |
| 116 | + return atlantis_reset_update(rcdev, id, false); |
| 117 | +} |
| 118 | + |
| 119 | +static const struct reset_control_ops atlantis_reset_control_ops = { |
| 120 | + .assert = atlantis_reset_assert, |
| 121 | + .deassert = atlantis_reset_deassert, |
| 122 | +}; |
| 123 | + |
| 124 | +static int |
| 125 | +atlantis_reset_controller_register(struct device *dev, |
| 126 | + struct atlantis_reset_controller *controller) |
| 127 | +{ |
| 128 | + struct reset_controller_dev *rcdev = &controller->rcdev; |
| 129 | + |
| 130 | + rcdev->ops = &atlantis_reset_control_ops; |
| 131 | + rcdev->owner = THIS_MODULE; |
| 132 | + rcdev->of_node = dev->of_node; |
| 133 | + rcdev->nr_resets = controller->data->count; |
| 134 | + |
| 135 | + return devm_reset_controller_register(dev, &controller->rcdev); |
| 136 | +} |
| 137 | +static int atlantis_reset_probe(struct auxiliary_device *adev, |
| 138 | + const struct auxiliary_device_id *id) |
| 139 | +{ |
| 140 | + struct atlantis_reset_controller *controller; |
| 141 | + struct device *dev = &adev->dev; |
| 142 | + struct regmap *regmap; |
| 143 | + |
| 144 | + regmap = dev_get_regmap(dev->parent, NULL); |
| 145 | + if (!regmap) |
| 146 | + return -ENODEV; |
| 147 | + |
| 148 | + controller = devm_kzalloc(dev, sizeof(*controller), GFP_KERNEL); |
| 149 | + if (!controller) |
| 150 | + return -ENOMEM; |
| 151 | + controller->data = |
| 152 | + (const struct atlantis_reset_controller_data *)id->driver_data; |
| 153 | + controller->regmap = regmap; |
| 154 | + |
| 155 | + return atlantis_reset_controller_register(dev, controller); |
| 156 | +} |
| 157 | + |
| 158 | +static const struct auxiliary_device_id atlantis_reset_ids[] = { |
| 159 | + { .name = "atlantis_prcm.rcpu-reset", |
| 160 | + .driver_data = (kernel_ulong_t)&atlantis_rcpu_reset_data }, |
| 161 | + {}, |
| 162 | +}; |
| 163 | +MODULE_DEVICE_TABLE(auxiliary, atlantis_reset_ids); |
| 164 | + |
| 165 | +static struct auxiliary_driver atlantis_reset_driver = { |
| 166 | + .probe = atlantis_reset_probe, |
| 167 | + .id_table = atlantis_reset_ids, |
| 168 | +}; |
| 169 | +module_auxiliary_driver(atlantis_reset_driver); |
| 170 | + |
| 171 | +MODULE_AUTHOR("Anirudh Srinivasan <asrinivasan@oss.tenstorrent.com>"); |
| 172 | +MODULE_DESCRIPTION("Atlantis PRCM reset controller driver"); |
| 173 | +MODULE_LICENSE("GPL"); |
0 commit comments