Contributors: 1
Author Tokens Token Proportion Commits Commit Proportion
Anirudh Srinivasan 981 100.00% 1 100.00%
Total 981 1


// SPDX-License-Identifier: GPL-2.0
/*
 * Tenstorrent Atlantis PRCM Reset Driver
 *
 * Copyright (c) 2026 Tenstorrent
 */

#include <dt-bindings/clock/tenstorrent,atlantis-prcm-rcpu.h>
#include <linux/auxiliary_bus.h>
#include <linux/reset-controller.h>
#include <linux/regmap.h>

/* RCPU Reset Register Offsets */
#define RCPU_BLK_RST_REG 0x001c
#define LSIO_BLK_RST_REG 0x0020
#define HSIO_BLK_RST_REG 0x000c
#define PCIE_SUBS_RST_REG 0x0000
#define MM_RSTN_REG 0x0014

struct atlantis_reset_data {
	u8 bit;
	u16 reg;
	bool active_low;
};

struct atlantis_reset_controller_data {
	const struct atlantis_reset_data *reset_data;
	size_t count;
};

struct atlantis_reset_controller {
	struct reset_controller_dev rcdev;
	const struct atlantis_reset_controller_data *data;
	struct regmap *regmap;
};

static inline struct atlantis_reset_controller *
to_atlantis_reset_controller(struct reset_controller_dev *rcdev)
{
	return container_of(rcdev, struct atlantis_reset_controller, rcdev);
}

#define RESET_DATA(_reg, _bit, _active_low)                          \
	{                                                            \
		.bit = _bit, .reg = _reg, .active_low = _active_low, \
	}

static const struct atlantis_reset_data atlantis_rcpu_resets[] = {
	[RST_SMNDMA0]	= RESET_DATA(RCPU_BLK_RST_REG, 0, true),
	[RST_SMNDMA1]	= RESET_DATA(RCPU_BLK_RST_REG, 1, true),
	[RST_WDT0]	= RESET_DATA(RCPU_BLK_RST_REG, 2, true),
	[RST_WDT1]	= RESET_DATA(RCPU_BLK_RST_REG, 3, true),
	[RST_TMR]	= RESET_DATA(RCPU_BLK_RST_REG, 4, true),
	[RST_PVTC]	= RESET_DATA(RCPU_BLK_RST_REG, 12, true),
	[RST_PMU]	= RESET_DATA(RCPU_BLK_RST_REG, 13, true),
	[RST_MAILBOX]	= RESET_DATA(RCPU_BLK_RST_REG, 14, true),
	[RST_SPACC]	= RESET_DATA(RCPU_BLK_RST_REG, 26, true),
	[RST_OTP]	= RESET_DATA(RCPU_BLK_RST_REG, 28, true),
	[RST_TRNG]	= RESET_DATA(RCPU_BLK_RST_REG, 29, true),
	[RST_CRC]	= RESET_DATA(RCPU_BLK_RST_REG, 30, true),
	[RST_QSPI]	= RESET_DATA(LSIO_BLK_RST_REG, 0, true),
	[RST_I2C0]	= RESET_DATA(LSIO_BLK_RST_REG, 1, true),
	[RST_I2C1]	= RESET_DATA(LSIO_BLK_RST_REG, 2, true),
	[RST_I2C2]	= RESET_DATA(LSIO_BLK_RST_REG, 3, true),
	[RST_I2C3]	= RESET_DATA(LSIO_BLK_RST_REG, 4, true),
	[RST_I2C4]	= RESET_DATA(LSIO_BLK_RST_REG, 5, true),
	[RST_UART0]	= RESET_DATA(LSIO_BLK_RST_REG, 6, true),
	[RST_UART1]	= RESET_DATA(LSIO_BLK_RST_REG, 7, true),
	[RST_UART2]	= RESET_DATA(LSIO_BLK_RST_REG, 8, true),
	[RST_UART3]	= RESET_DATA(LSIO_BLK_RST_REG, 9, true),
	[RST_UART4]	= RESET_DATA(LSIO_BLK_RST_REG, 10, true),
	[RST_SPI0]	= RESET_DATA(LSIO_BLK_RST_REG, 11, true),
	[RST_SPI1]	= RESET_DATA(LSIO_BLK_RST_REG, 12, true),
	[RST_SPI2]	= RESET_DATA(LSIO_BLK_RST_REG, 13, true),
	[RST_SPI3]	= RESET_DATA(LSIO_BLK_RST_REG, 14, true),
	[RST_GPIO]	= RESET_DATA(LSIO_BLK_RST_REG, 15, true),
	[RST_CAN0]	= RESET_DATA(LSIO_BLK_RST_REG, 17, true),
	[RST_CAN1]	= RESET_DATA(LSIO_BLK_RST_REG, 18, true),
	[RST_I2S0]	= RESET_DATA(LSIO_BLK_RST_REG, 19, true),
	[RST_I2S1]	= RESET_DATA(LSIO_BLK_RST_REG, 20, true),

};

static const struct atlantis_reset_controller_data atlantis_rcpu_reset_data = {
	.reset_data = atlantis_rcpu_resets,
	.count = ARRAY_SIZE(atlantis_rcpu_resets),
};

static int atlantis_reset_update(struct reset_controller_dev *rcdev,
				 unsigned long id, bool assert)
{
	unsigned int val;
	struct atlantis_reset_controller *rst =
		to_atlantis_reset_controller(rcdev);
	const struct atlantis_reset_data *data = &rst->data->reset_data[id];
	unsigned int mask = BIT(data->bit);
	struct regmap *regmap = rst->regmap;

	if (data->active_low ^ assert)
		val = mask;
	else
		val = 0;

	return regmap_update_bits(regmap, data->reg, mask, val);
}

static int atlantis_reset_assert(struct reset_controller_dev *rcdev,
				 unsigned long id)
{
	return atlantis_reset_update(rcdev, id, true);
}

static int atlantis_reset_deassert(struct reset_controller_dev *rcdev,
				   unsigned long id)
{
	return atlantis_reset_update(rcdev, id, false);
}

static const struct reset_control_ops atlantis_reset_control_ops = {
	.assert = atlantis_reset_assert,
	.deassert = atlantis_reset_deassert,
};

static int
atlantis_reset_controller_register(struct device *dev,
				   struct atlantis_reset_controller *controller)
{
	struct reset_controller_dev *rcdev = &controller->rcdev;

	rcdev->ops = &atlantis_reset_control_ops;
	rcdev->owner = THIS_MODULE;
	rcdev->of_node = dev->of_node;
	rcdev->nr_resets = controller->data->count;

	return devm_reset_controller_register(dev, &controller->rcdev);
}
static int atlantis_reset_probe(struct auxiliary_device *adev,
				const struct auxiliary_device_id *id)
{
	struct atlantis_reset_controller *controller;
	struct device *dev = &adev->dev;
	struct regmap *regmap;

	regmap = dev_get_regmap(dev->parent, NULL);
	if (!regmap)
		return -ENODEV;

	controller = devm_kzalloc(dev, sizeof(*controller), GFP_KERNEL);
	if (!controller)
		return -ENOMEM;
	controller->data =
		(const struct atlantis_reset_controller_data *)id->driver_data;
	controller->regmap = regmap;

	return atlantis_reset_controller_register(dev, controller);
}

static const struct auxiliary_device_id atlantis_reset_ids[] = {
	{ .name = "atlantis_prcm.rcpu-reset",
	  .driver_data = (kernel_ulong_t)&atlantis_rcpu_reset_data },
	{},
};
MODULE_DEVICE_TABLE(auxiliary, atlantis_reset_ids);

static struct auxiliary_driver atlantis_reset_driver = {
	.probe = atlantis_reset_probe,
	.id_table = atlantis_reset_ids,
};
module_auxiliary_driver(atlantis_reset_driver);

MODULE_AUTHOR("Anirudh Srinivasan <asrinivasan@oss.tenstorrent.com>");
MODULE_DESCRIPTION("Atlantis PRCM reset controller driver");
MODULE_LICENSE("GPL");