162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * ARM System Control and Management Interface (ARM SCMI) reset driver
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * Copyright (C) 2019-2021 ARM Ltd.
662306a36Sopenharmony_ci */
762306a36Sopenharmony_ci
862306a36Sopenharmony_ci#include <linux/module.h>
962306a36Sopenharmony_ci#include <linux/of.h>
1062306a36Sopenharmony_ci#include <linux/device.h>
1162306a36Sopenharmony_ci#include <linux/reset-controller.h>
1262306a36Sopenharmony_ci#include <linux/scmi_protocol.h>
1362306a36Sopenharmony_ci
1462306a36Sopenharmony_cistatic const struct scmi_reset_proto_ops *reset_ops;
1562306a36Sopenharmony_ci
1662306a36Sopenharmony_ci/**
1762306a36Sopenharmony_ci * struct scmi_reset_data - reset controller information structure
1862306a36Sopenharmony_ci * @rcdev: reset controller entity
1962306a36Sopenharmony_ci * @ph: ARM SCMI protocol handle used for communication with system controller
2062306a36Sopenharmony_ci */
2162306a36Sopenharmony_cistruct scmi_reset_data {
2262306a36Sopenharmony_ci	struct reset_controller_dev rcdev;
2362306a36Sopenharmony_ci	const struct scmi_protocol_handle *ph;
2462306a36Sopenharmony_ci};
2562306a36Sopenharmony_ci
2662306a36Sopenharmony_ci#define to_scmi_reset_data(p)	container_of((p), struct scmi_reset_data, rcdev)
2762306a36Sopenharmony_ci#define to_scmi_handle(p)	(to_scmi_reset_data(p)->ph)
2862306a36Sopenharmony_ci
2962306a36Sopenharmony_ci/**
3062306a36Sopenharmony_ci * scmi_reset_assert() - assert device reset
3162306a36Sopenharmony_ci * @rcdev: reset controller entity
3262306a36Sopenharmony_ci * @id: ID of the reset to be asserted
3362306a36Sopenharmony_ci *
3462306a36Sopenharmony_ci * This function implements the reset driver op to assert a device's reset
3562306a36Sopenharmony_ci * using the ARM SCMI protocol.
3662306a36Sopenharmony_ci *
3762306a36Sopenharmony_ci * Return: 0 for successful request, else a corresponding error value
3862306a36Sopenharmony_ci */
3962306a36Sopenharmony_cistatic int
4062306a36Sopenharmony_ciscmi_reset_assert(struct reset_controller_dev *rcdev, unsigned long id)
4162306a36Sopenharmony_ci{
4262306a36Sopenharmony_ci	const struct scmi_protocol_handle *ph = to_scmi_handle(rcdev);
4362306a36Sopenharmony_ci
4462306a36Sopenharmony_ci	return reset_ops->assert(ph, id);
4562306a36Sopenharmony_ci}
4662306a36Sopenharmony_ci
4762306a36Sopenharmony_ci/**
4862306a36Sopenharmony_ci * scmi_reset_deassert() - deassert device reset
4962306a36Sopenharmony_ci * @rcdev: reset controller entity
5062306a36Sopenharmony_ci * @id: ID of the reset to be deasserted
5162306a36Sopenharmony_ci *
5262306a36Sopenharmony_ci * This function implements the reset driver op to deassert a device's reset
5362306a36Sopenharmony_ci * using the ARM SCMI protocol.
5462306a36Sopenharmony_ci *
5562306a36Sopenharmony_ci * Return: 0 for successful request, else a corresponding error value
5662306a36Sopenharmony_ci */
5762306a36Sopenharmony_cistatic int
5862306a36Sopenharmony_ciscmi_reset_deassert(struct reset_controller_dev *rcdev, unsigned long id)
5962306a36Sopenharmony_ci{
6062306a36Sopenharmony_ci	const struct scmi_protocol_handle *ph = to_scmi_handle(rcdev);
6162306a36Sopenharmony_ci
6262306a36Sopenharmony_ci	return reset_ops->deassert(ph, id);
6362306a36Sopenharmony_ci}
6462306a36Sopenharmony_ci
6562306a36Sopenharmony_ci/**
6662306a36Sopenharmony_ci * scmi_reset_reset() - reset the device
6762306a36Sopenharmony_ci * @rcdev: reset controller entity
6862306a36Sopenharmony_ci * @id: ID of the reset signal to be reset(assert + deassert)
6962306a36Sopenharmony_ci *
7062306a36Sopenharmony_ci * This function implements the reset driver op to trigger a device's
7162306a36Sopenharmony_ci * reset signal using the ARM SCMI protocol.
7262306a36Sopenharmony_ci *
7362306a36Sopenharmony_ci * Return: 0 for successful request, else a corresponding error value
7462306a36Sopenharmony_ci */
7562306a36Sopenharmony_cistatic int
7662306a36Sopenharmony_ciscmi_reset_reset(struct reset_controller_dev *rcdev, unsigned long id)
7762306a36Sopenharmony_ci{
7862306a36Sopenharmony_ci	const struct scmi_protocol_handle *ph = to_scmi_handle(rcdev);
7962306a36Sopenharmony_ci
8062306a36Sopenharmony_ci	return reset_ops->reset(ph, id);
8162306a36Sopenharmony_ci}
8262306a36Sopenharmony_ci
8362306a36Sopenharmony_cistatic const struct reset_control_ops scmi_reset_ops = {
8462306a36Sopenharmony_ci	.assert		= scmi_reset_assert,
8562306a36Sopenharmony_ci	.deassert	= scmi_reset_deassert,
8662306a36Sopenharmony_ci	.reset		= scmi_reset_reset,
8762306a36Sopenharmony_ci};
8862306a36Sopenharmony_ci
8962306a36Sopenharmony_cistatic int scmi_reset_probe(struct scmi_device *sdev)
9062306a36Sopenharmony_ci{
9162306a36Sopenharmony_ci	struct scmi_reset_data *data;
9262306a36Sopenharmony_ci	struct device *dev = &sdev->dev;
9362306a36Sopenharmony_ci	struct device_node *np = dev->of_node;
9462306a36Sopenharmony_ci	const struct scmi_handle *handle = sdev->handle;
9562306a36Sopenharmony_ci	struct scmi_protocol_handle *ph;
9662306a36Sopenharmony_ci
9762306a36Sopenharmony_ci	if (!handle)
9862306a36Sopenharmony_ci		return -ENODEV;
9962306a36Sopenharmony_ci
10062306a36Sopenharmony_ci	reset_ops = handle->devm_protocol_get(sdev, SCMI_PROTOCOL_RESET, &ph);
10162306a36Sopenharmony_ci	if (IS_ERR(reset_ops))
10262306a36Sopenharmony_ci		return PTR_ERR(reset_ops);
10362306a36Sopenharmony_ci
10462306a36Sopenharmony_ci	data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);
10562306a36Sopenharmony_ci	if (!data)
10662306a36Sopenharmony_ci		return -ENOMEM;
10762306a36Sopenharmony_ci
10862306a36Sopenharmony_ci	data->rcdev.ops = &scmi_reset_ops;
10962306a36Sopenharmony_ci	data->rcdev.owner = THIS_MODULE;
11062306a36Sopenharmony_ci	data->rcdev.of_node = np;
11162306a36Sopenharmony_ci	data->rcdev.nr_resets = reset_ops->num_domains_get(ph);
11262306a36Sopenharmony_ci	data->ph = ph;
11362306a36Sopenharmony_ci
11462306a36Sopenharmony_ci	return devm_reset_controller_register(dev, &data->rcdev);
11562306a36Sopenharmony_ci}
11662306a36Sopenharmony_ci
11762306a36Sopenharmony_cistatic const struct scmi_device_id scmi_id_table[] = {
11862306a36Sopenharmony_ci	{ SCMI_PROTOCOL_RESET, "reset" },
11962306a36Sopenharmony_ci	{ },
12062306a36Sopenharmony_ci};
12162306a36Sopenharmony_ciMODULE_DEVICE_TABLE(scmi, scmi_id_table);
12262306a36Sopenharmony_ci
12362306a36Sopenharmony_cistatic struct scmi_driver scmi_reset_driver = {
12462306a36Sopenharmony_ci	.name = "scmi-reset",
12562306a36Sopenharmony_ci	.probe = scmi_reset_probe,
12662306a36Sopenharmony_ci	.id_table = scmi_id_table,
12762306a36Sopenharmony_ci};
12862306a36Sopenharmony_cimodule_scmi_driver(scmi_reset_driver);
12962306a36Sopenharmony_ci
13062306a36Sopenharmony_ciMODULE_AUTHOR("Sudeep Holla <sudeep.holla@arm.com>");
13162306a36Sopenharmony_ciMODULE_DESCRIPTION("ARM SCMI reset controller driver");
13262306a36Sopenharmony_ciMODULE_LICENSE("GPL v2");
133