162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Microchip PolarFire SoC (MPFS) system controller driver
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * Copyright (c) 2020-2021 Microchip Corporation. All rights reserved.
662306a36Sopenharmony_ci *
762306a36Sopenharmony_ci * Author: Conor Dooley <conor.dooley@microchip.com>
862306a36Sopenharmony_ci *
962306a36Sopenharmony_ci */
1062306a36Sopenharmony_ci
1162306a36Sopenharmony_ci#include <linux/slab.h>
1262306a36Sopenharmony_ci#include <linux/kref.h>
1362306a36Sopenharmony_ci#include <linux/module.h>
1462306a36Sopenharmony_ci#include <linux/jiffies.h>
1562306a36Sopenharmony_ci#include <linux/interrupt.h>
1662306a36Sopenharmony_ci#include <linux/of.h>
1762306a36Sopenharmony_ci#include <linux/mailbox_client.h>
1862306a36Sopenharmony_ci#include <linux/platform_device.h>
1962306a36Sopenharmony_ci#include <soc/microchip/mpfs.h>
2062306a36Sopenharmony_ci
2162306a36Sopenharmony_ci/*
2262306a36Sopenharmony_ci * This timeout must be long, as some services (example: image authentication)
2362306a36Sopenharmony_ci * take significant time to complete
2462306a36Sopenharmony_ci */
2562306a36Sopenharmony_ci#define MPFS_SYS_CTRL_TIMEOUT_MS 30000
2662306a36Sopenharmony_ci
2762306a36Sopenharmony_cistatic DEFINE_MUTEX(transaction_lock);
2862306a36Sopenharmony_ci
2962306a36Sopenharmony_cistruct mpfs_sys_controller {
3062306a36Sopenharmony_ci	struct mbox_client client;
3162306a36Sopenharmony_ci	struct mbox_chan *chan;
3262306a36Sopenharmony_ci	struct completion c;
3362306a36Sopenharmony_ci	struct kref consumers;
3462306a36Sopenharmony_ci};
3562306a36Sopenharmony_ci
3662306a36Sopenharmony_ciint mpfs_blocking_transaction(struct mpfs_sys_controller *sys_controller, struct mpfs_mss_msg *msg)
3762306a36Sopenharmony_ci{
3862306a36Sopenharmony_ci	unsigned long timeout = msecs_to_jiffies(MPFS_SYS_CTRL_TIMEOUT_MS);
3962306a36Sopenharmony_ci	int ret;
4062306a36Sopenharmony_ci
4162306a36Sopenharmony_ci	ret = mutex_lock_interruptible(&transaction_lock);
4262306a36Sopenharmony_ci	if (ret)
4362306a36Sopenharmony_ci		return ret;
4462306a36Sopenharmony_ci
4562306a36Sopenharmony_ci	reinit_completion(&sys_controller->c);
4662306a36Sopenharmony_ci
4762306a36Sopenharmony_ci	ret = mbox_send_message(sys_controller->chan, msg);
4862306a36Sopenharmony_ci	if (ret < 0) {
4962306a36Sopenharmony_ci		dev_warn(sys_controller->client.dev, "MPFS sys controller service timeout\n");
5062306a36Sopenharmony_ci		goto out;
5162306a36Sopenharmony_ci	}
5262306a36Sopenharmony_ci
5362306a36Sopenharmony_ci	/*
5462306a36Sopenharmony_ci	 * Unfortunately, the system controller will only deliver an interrupt
5562306a36Sopenharmony_ci	 * if a service succeeds. mbox_send_message() will block until the busy
5662306a36Sopenharmony_ci	 * flag is gone. If the busy flag is gone but no interrupt has arrived
5762306a36Sopenharmony_ci	 * to trigger the rx callback then the service can be deemed to have
5862306a36Sopenharmony_ci	 * failed.
5962306a36Sopenharmony_ci	 * The caller can then interrogate msg::response::resp_status to
6062306a36Sopenharmony_ci	 * determine the cause of the failure.
6162306a36Sopenharmony_ci	 * mbox_send_message() returns positive integers in the success path, so
6262306a36Sopenharmony_ci	 * ret needs to be cleared if we do get an interrupt.
6362306a36Sopenharmony_ci	 */
6462306a36Sopenharmony_ci	if (!wait_for_completion_timeout(&sys_controller->c, timeout)) {
6562306a36Sopenharmony_ci		ret = -EBADMSG;
6662306a36Sopenharmony_ci		dev_warn(sys_controller->client.dev, "MPFS sys controller service failed\n");
6762306a36Sopenharmony_ci	} else {
6862306a36Sopenharmony_ci		ret = 0;
6962306a36Sopenharmony_ci	}
7062306a36Sopenharmony_ci
7162306a36Sopenharmony_ciout:
7262306a36Sopenharmony_ci	mutex_unlock(&transaction_lock);
7362306a36Sopenharmony_ci
7462306a36Sopenharmony_ci	return ret;
7562306a36Sopenharmony_ci}
7662306a36Sopenharmony_ciEXPORT_SYMBOL(mpfs_blocking_transaction);
7762306a36Sopenharmony_ci
7862306a36Sopenharmony_cistatic void mpfs_sys_controller_rx_callback(struct mbox_client *client, void *msg)
7962306a36Sopenharmony_ci{
8062306a36Sopenharmony_ci	struct mpfs_sys_controller *sys_controller =
8162306a36Sopenharmony_ci		container_of(client, struct mpfs_sys_controller, client);
8262306a36Sopenharmony_ci
8362306a36Sopenharmony_ci	complete(&sys_controller->c);
8462306a36Sopenharmony_ci}
8562306a36Sopenharmony_ci
8662306a36Sopenharmony_cistatic void mpfs_sys_controller_delete(struct kref *kref)
8762306a36Sopenharmony_ci{
8862306a36Sopenharmony_ci	struct mpfs_sys_controller *sys_controller =
8962306a36Sopenharmony_ci		container_of(kref, struct mpfs_sys_controller, consumers);
9062306a36Sopenharmony_ci
9162306a36Sopenharmony_ci	mbox_free_channel(sys_controller->chan);
9262306a36Sopenharmony_ci	kfree(sys_controller);
9362306a36Sopenharmony_ci}
9462306a36Sopenharmony_ci
9562306a36Sopenharmony_cistatic void mpfs_sys_controller_put(void *data)
9662306a36Sopenharmony_ci{
9762306a36Sopenharmony_ci	struct mpfs_sys_controller *sys_controller = data;
9862306a36Sopenharmony_ci
9962306a36Sopenharmony_ci	kref_put(&sys_controller->consumers, mpfs_sys_controller_delete);
10062306a36Sopenharmony_ci}
10162306a36Sopenharmony_ci
10262306a36Sopenharmony_cistatic struct platform_device subdevs[] = {
10362306a36Sopenharmony_ci	{
10462306a36Sopenharmony_ci		.name		= "mpfs-rng",
10562306a36Sopenharmony_ci		.id		= -1,
10662306a36Sopenharmony_ci	},
10762306a36Sopenharmony_ci	{
10862306a36Sopenharmony_ci		.name		= "mpfs-generic-service",
10962306a36Sopenharmony_ci		.id		= -1,
11062306a36Sopenharmony_ci	}
11162306a36Sopenharmony_ci};
11262306a36Sopenharmony_ci
11362306a36Sopenharmony_cistatic int mpfs_sys_controller_probe(struct platform_device *pdev)
11462306a36Sopenharmony_ci{
11562306a36Sopenharmony_ci	struct device *dev = &pdev->dev;
11662306a36Sopenharmony_ci	struct mpfs_sys_controller *sys_controller;
11762306a36Sopenharmony_ci	int i, ret;
11862306a36Sopenharmony_ci
11962306a36Sopenharmony_ci	sys_controller = kzalloc(sizeof(*sys_controller), GFP_KERNEL);
12062306a36Sopenharmony_ci	if (!sys_controller)
12162306a36Sopenharmony_ci		return -ENOMEM;
12262306a36Sopenharmony_ci
12362306a36Sopenharmony_ci	sys_controller->client.dev = dev;
12462306a36Sopenharmony_ci	sys_controller->client.rx_callback = mpfs_sys_controller_rx_callback;
12562306a36Sopenharmony_ci	sys_controller->client.tx_block = 1U;
12662306a36Sopenharmony_ci	sys_controller->client.tx_tout = msecs_to_jiffies(MPFS_SYS_CTRL_TIMEOUT_MS);
12762306a36Sopenharmony_ci
12862306a36Sopenharmony_ci	sys_controller->chan = mbox_request_channel(&sys_controller->client, 0);
12962306a36Sopenharmony_ci	if (IS_ERR(sys_controller->chan)) {
13062306a36Sopenharmony_ci		ret = dev_err_probe(dev, PTR_ERR(sys_controller->chan),
13162306a36Sopenharmony_ci				    "Failed to get mbox channel\n");
13262306a36Sopenharmony_ci		kfree(sys_controller);
13362306a36Sopenharmony_ci		return ret;
13462306a36Sopenharmony_ci	}
13562306a36Sopenharmony_ci
13662306a36Sopenharmony_ci	init_completion(&sys_controller->c);
13762306a36Sopenharmony_ci	kref_init(&sys_controller->consumers);
13862306a36Sopenharmony_ci
13962306a36Sopenharmony_ci	platform_set_drvdata(pdev, sys_controller);
14062306a36Sopenharmony_ci
14162306a36Sopenharmony_ci	dev_info(&pdev->dev, "Registered MPFS system controller\n");
14262306a36Sopenharmony_ci
14362306a36Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(subdevs); i++) {
14462306a36Sopenharmony_ci		subdevs[i].dev.parent = dev;
14562306a36Sopenharmony_ci		if (platform_device_register(&subdevs[i]))
14662306a36Sopenharmony_ci			dev_warn(dev, "Error registering sub device %s\n", subdevs[i].name);
14762306a36Sopenharmony_ci	}
14862306a36Sopenharmony_ci
14962306a36Sopenharmony_ci	return 0;
15062306a36Sopenharmony_ci}
15162306a36Sopenharmony_ci
15262306a36Sopenharmony_cistatic int mpfs_sys_controller_remove(struct platform_device *pdev)
15362306a36Sopenharmony_ci{
15462306a36Sopenharmony_ci	struct mpfs_sys_controller *sys_controller = platform_get_drvdata(pdev);
15562306a36Sopenharmony_ci
15662306a36Sopenharmony_ci	mpfs_sys_controller_put(sys_controller);
15762306a36Sopenharmony_ci
15862306a36Sopenharmony_ci	return 0;
15962306a36Sopenharmony_ci}
16062306a36Sopenharmony_ci
16162306a36Sopenharmony_cistatic const struct of_device_id mpfs_sys_controller_of_match[] = {
16262306a36Sopenharmony_ci	{.compatible = "microchip,mpfs-sys-controller", },
16362306a36Sopenharmony_ci	{},
16462306a36Sopenharmony_ci};
16562306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, mpfs_sys_controller_of_match);
16662306a36Sopenharmony_ci
16762306a36Sopenharmony_cistruct mpfs_sys_controller *mpfs_sys_controller_get(struct device *dev)
16862306a36Sopenharmony_ci{
16962306a36Sopenharmony_ci	const struct of_device_id *match;
17062306a36Sopenharmony_ci	struct mpfs_sys_controller *sys_controller;
17162306a36Sopenharmony_ci	int ret;
17262306a36Sopenharmony_ci
17362306a36Sopenharmony_ci	if (!dev->parent)
17462306a36Sopenharmony_ci		goto err_no_device;
17562306a36Sopenharmony_ci
17662306a36Sopenharmony_ci	match = of_match_node(mpfs_sys_controller_of_match,  dev->parent->of_node);
17762306a36Sopenharmony_ci	of_node_put(dev->parent->of_node);
17862306a36Sopenharmony_ci	if (!match)
17962306a36Sopenharmony_ci		goto err_no_device;
18062306a36Sopenharmony_ci
18162306a36Sopenharmony_ci	sys_controller = dev_get_drvdata(dev->parent);
18262306a36Sopenharmony_ci	if (!sys_controller)
18362306a36Sopenharmony_ci		goto err_bad_device;
18462306a36Sopenharmony_ci
18562306a36Sopenharmony_ci	if (!kref_get_unless_zero(&sys_controller->consumers))
18662306a36Sopenharmony_ci		goto err_bad_device;
18762306a36Sopenharmony_ci
18862306a36Sopenharmony_ci	ret = devm_add_action_or_reset(dev, mpfs_sys_controller_put, sys_controller);
18962306a36Sopenharmony_ci	if (ret)
19062306a36Sopenharmony_ci		return ERR_PTR(ret);
19162306a36Sopenharmony_ci
19262306a36Sopenharmony_ci	return sys_controller;
19362306a36Sopenharmony_ci
19462306a36Sopenharmony_cierr_no_device:
19562306a36Sopenharmony_ci	dev_dbg(dev, "Parent device was not an MPFS system controller\n");
19662306a36Sopenharmony_ci	return ERR_PTR(-ENODEV);
19762306a36Sopenharmony_ci
19862306a36Sopenharmony_cierr_bad_device:
19962306a36Sopenharmony_ci	dev_dbg(dev, "MPFS system controller found but could not register as a sub device\n");
20062306a36Sopenharmony_ci	return ERR_PTR(-EPROBE_DEFER);
20162306a36Sopenharmony_ci}
20262306a36Sopenharmony_ciEXPORT_SYMBOL(mpfs_sys_controller_get);
20362306a36Sopenharmony_ci
20462306a36Sopenharmony_cistatic struct platform_driver mpfs_sys_controller_driver = {
20562306a36Sopenharmony_ci	.driver = {
20662306a36Sopenharmony_ci		.name = "mpfs-sys-controller",
20762306a36Sopenharmony_ci		.of_match_table = mpfs_sys_controller_of_match,
20862306a36Sopenharmony_ci	},
20962306a36Sopenharmony_ci	.probe = mpfs_sys_controller_probe,
21062306a36Sopenharmony_ci	.remove = mpfs_sys_controller_remove,
21162306a36Sopenharmony_ci};
21262306a36Sopenharmony_cimodule_platform_driver(mpfs_sys_controller_driver);
21362306a36Sopenharmony_ci
21462306a36Sopenharmony_ciMODULE_LICENSE("GPL v2");
21562306a36Sopenharmony_ciMODULE_AUTHOR("Conor Dooley <conor.dooley@microchip.com>");
21662306a36Sopenharmony_ciMODULE_DESCRIPTION("MPFS system controller driver");
217