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