1// SPDX-License-Identifier: GPL-2.0
2/*
3 * Microchip PolarFire SoC (MPFS) system controller driver
4 *
5 * Copyright (c) 2020-2021 Microchip Corporation. All rights reserved.
6 *
7 * Author: Conor Dooley <conor.dooley@microchip.com>
8 *
9 */
10
11#include <linux/slab.h>
12#include <linux/kref.h>
13#include <linux/module.h>
14#include <linux/jiffies.h>
15#include <linux/interrupt.h>
16#include <linux/of.h>
17#include <linux/mailbox_client.h>
18#include <linux/platform_device.h>
19#include <soc/microchip/mpfs.h>
20
21/*
22 * This timeout must be long, as some services (example: image authentication)
23 * take significant time to complete
24 */
25#define MPFS_SYS_CTRL_TIMEOUT_MS 30000
26
27static DEFINE_MUTEX(transaction_lock);
28
29struct mpfs_sys_controller {
30	struct mbox_client client;
31	struct mbox_chan *chan;
32	struct completion c;
33	struct kref consumers;
34};
35
36int mpfs_blocking_transaction(struct mpfs_sys_controller *sys_controller, struct mpfs_mss_msg *msg)
37{
38	unsigned long timeout = msecs_to_jiffies(MPFS_SYS_CTRL_TIMEOUT_MS);
39	int ret;
40
41	ret = mutex_lock_interruptible(&transaction_lock);
42	if (ret)
43		return ret;
44
45	reinit_completion(&sys_controller->c);
46
47	ret = mbox_send_message(sys_controller->chan, msg);
48	if (ret < 0) {
49		dev_warn(sys_controller->client.dev, "MPFS sys controller service timeout\n");
50		goto out;
51	}
52
53	/*
54	 * Unfortunately, the system controller will only deliver an interrupt
55	 * if a service succeeds. mbox_send_message() will block until the busy
56	 * flag is gone. If the busy flag is gone but no interrupt has arrived
57	 * to trigger the rx callback then the service can be deemed to have
58	 * failed.
59	 * The caller can then interrogate msg::response::resp_status to
60	 * determine the cause of the failure.
61	 * mbox_send_message() returns positive integers in the success path, so
62	 * ret needs to be cleared if we do get an interrupt.
63	 */
64	if (!wait_for_completion_timeout(&sys_controller->c, timeout)) {
65		ret = -EBADMSG;
66		dev_warn(sys_controller->client.dev, "MPFS sys controller service failed\n");
67	} else {
68		ret = 0;
69	}
70
71out:
72	mutex_unlock(&transaction_lock);
73
74	return ret;
75}
76EXPORT_SYMBOL(mpfs_blocking_transaction);
77
78static void mpfs_sys_controller_rx_callback(struct mbox_client *client, void *msg)
79{
80	struct mpfs_sys_controller *sys_controller =
81		container_of(client, struct mpfs_sys_controller, client);
82
83	complete(&sys_controller->c);
84}
85
86static void mpfs_sys_controller_delete(struct kref *kref)
87{
88	struct mpfs_sys_controller *sys_controller =
89		container_of(kref, struct mpfs_sys_controller, consumers);
90
91	mbox_free_channel(sys_controller->chan);
92	kfree(sys_controller);
93}
94
95static void mpfs_sys_controller_put(void *data)
96{
97	struct mpfs_sys_controller *sys_controller = data;
98
99	kref_put(&sys_controller->consumers, mpfs_sys_controller_delete);
100}
101
102static struct platform_device subdevs[] = {
103	{
104		.name		= "mpfs-rng",
105		.id		= -1,
106	},
107	{
108		.name		= "mpfs-generic-service",
109		.id		= -1,
110	}
111};
112
113static int mpfs_sys_controller_probe(struct platform_device *pdev)
114{
115	struct device *dev = &pdev->dev;
116	struct mpfs_sys_controller *sys_controller;
117	int i, ret;
118
119	sys_controller = kzalloc(sizeof(*sys_controller), GFP_KERNEL);
120	if (!sys_controller)
121		return -ENOMEM;
122
123	sys_controller->client.dev = dev;
124	sys_controller->client.rx_callback = mpfs_sys_controller_rx_callback;
125	sys_controller->client.tx_block = 1U;
126	sys_controller->client.tx_tout = msecs_to_jiffies(MPFS_SYS_CTRL_TIMEOUT_MS);
127
128	sys_controller->chan = mbox_request_channel(&sys_controller->client, 0);
129	if (IS_ERR(sys_controller->chan)) {
130		ret = dev_err_probe(dev, PTR_ERR(sys_controller->chan),
131				    "Failed to get mbox channel\n");
132		kfree(sys_controller);
133		return ret;
134	}
135
136	init_completion(&sys_controller->c);
137	kref_init(&sys_controller->consumers);
138
139	platform_set_drvdata(pdev, sys_controller);
140
141	dev_info(&pdev->dev, "Registered MPFS system controller\n");
142
143	for (i = 0; i < ARRAY_SIZE(subdevs); i++) {
144		subdevs[i].dev.parent = dev;
145		if (platform_device_register(&subdevs[i]))
146			dev_warn(dev, "Error registering sub device %s\n", subdevs[i].name);
147	}
148
149	return 0;
150}
151
152static int mpfs_sys_controller_remove(struct platform_device *pdev)
153{
154	struct mpfs_sys_controller *sys_controller = platform_get_drvdata(pdev);
155
156	mpfs_sys_controller_put(sys_controller);
157
158	return 0;
159}
160
161static const struct of_device_id mpfs_sys_controller_of_match[] = {
162	{.compatible = "microchip,mpfs-sys-controller", },
163	{},
164};
165MODULE_DEVICE_TABLE(of, mpfs_sys_controller_of_match);
166
167struct mpfs_sys_controller *mpfs_sys_controller_get(struct device *dev)
168{
169	const struct of_device_id *match;
170	struct mpfs_sys_controller *sys_controller;
171	int ret;
172
173	if (!dev->parent)
174		goto err_no_device;
175
176	match = of_match_node(mpfs_sys_controller_of_match,  dev->parent->of_node);
177	of_node_put(dev->parent->of_node);
178	if (!match)
179		goto err_no_device;
180
181	sys_controller = dev_get_drvdata(dev->parent);
182	if (!sys_controller)
183		goto err_bad_device;
184
185	if (!kref_get_unless_zero(&sys_controller->consumers))
186		goto err_bad_device;
187
188	ret = devm_add_action_or_reset(dev, mpfs_sys_controller_put, sys_controller);
189	if (ret)
190		return ERR_PTR(ret);
191
192	return sys_controller;
193
194err_no_device:
195	dev_dbg(dev, "Parent device was not an MPFS system controller\n");
196	return ERR_PTR(-ENODEV);
197
198err_bad_device:
199	dev_dbg(dev, "MPFS system controller found but could not register as a sub device\n");
200	return ERR_PTR(-EPROBE_DEFER);
201}
202EXPORT_SYMBOL(mpfs_sys_controller_get);
203
204static struct platform_driver mpfs_sys_controller_driver = {
205	.driver = {
206		.name = "mpfs-sys-controller",
207		.of_match_table = mpfs_sys_controller_of_match,
208	},
209	.probe = mpfs_sys_controller_probe,
210	.remove = mpfs_sys_controller_remove,
211};
212module_platform_driver(mpfs_sys_controller_driver);
213
214MODULE_LICENSE("GPL v2");
215MODULE_AUTHOR("Conor Dooley <conor.dooley@microchip.com>");
216MODULE_DESCRIPTION("MPFS system controller driver");
217