1d0054a47SConor Dooley // SPDX-License-Identifier: GPL-2.0
2d0054a47SConor Dooley /*
3d0054a47SConor Dooley * Microchip PolarFire SoC (MPFS) system controller driver
4d0054a47SConor Dooley *
5d0054a47SConor Dooley * Copyright (c) 2020-2021 Microchip Corporation. All rights reserved.
6d0054a47SConor Dooley *
7d0054a47SConor Dooley * Author: Conor Dooley <[email protected]>
8d0054a47SConor Dooley *
9d0054a47SConor Dooley */
10d0054a47SConor Dooley
11d0054a47SConor Dooley #include <linux/slab.h>
12d0054a47SConor Dooley #include <linux/kref.h>
13d0054a47SConor Dooley #include <linux/module.h>
144f739af1SConor Dooley #include <linux/jiffies.h>
15742aa6c5SConor Dooley #include <linux/mtd/mtd.h>
16742aa6c5SConor Dooley #include <linux/spi/spi.h>
17d0054a47SConor Dooley #include <linux/interrupt.h>
1811795e02SRob Herring #include <linux/of.h>
19d0054a47SConor Dooley #include <linux/mailbox_client.h>
20d0054a47SConor Dooley #include <linux/platform_device.h>
21d0054a47SConor Dooley #include <soc/microchip/mpfs.h>
22d0054a47SConor Dooley
238f943dd1SConor Dooley /*
248f943dd1SConor Dooley * This timeout must be long, as some services (example: image authentication)
258f943dd1SConor Dooley * take significant time to complete
268f943dd1SConor Dooley */
278f943dd1SConor Dooley #define MPFS_SYS_CTRL_TIMEOUT_MS 30000
284f739af1SConor Dooley
29d0054a47SConor Dooley static DEFINE_MUTEX(transaction_lock);
30d0054a47SConor Dooley
31d0054a47SConor Dooley struct mpfs_sys_controller {
32d0054a47SConor Dooley struct mbox_client client;
33d0054a47SConor Dooley struct mbox_chan *chan;
34d0054a47SConor Dooley struct completion c;
35742aa6c5SConor Dooley struct mtd_info *flash;
36d0054a47SConor Dooley struct kref consumers;
37d0054a47SConor Dooley };
38d0054a47SConor Dooley
mpfs_blocking_transaction(struct mpfs_sys_controller * sys_controller,struct mpfs_mss_msg * msg)39d0054a47SConor Dooley int mpfs_blocking_transaction(struct mpfs_sys_controller *sys_controller, struct mpfs_mss_msg *msg)
40d0054a47SConor Dooley {
414f739af1SConor Dooley unsigned long timeout = msecs_to_jiffies(MPFS_SYS_CTRL_TIMEOUT_MS);
427606f4dfSConor Dooley int ret;
43d0054a47SConor Dooley
447606f4dfSConor Dooley ret = mutex_lock_interruptible(&transaction_lock);
457606f4dfSConor Dooley if (ret)
467606f4dfSConor Dooley return ret;
47d0054a47SConor Dooley
48d0054a47SConor Dooley reinit_completion(&sys_controller->c);
49d0054a47SConor Dooley
50d0054a47SConor Dooley ret = mbox_send_message(sys_controller->chan, msg);
518f943dd1SConor Dooley if (ret < 0) {
528f943dd1SConor Dooley dev_warn(sys_controller->client.dev, "MPFS sys controller service timeout\n");
537606f4dfSConor Dooley goto out;
548f943dd1SConor Dooley }
557606f4dfSConor Dooley
568f943dd1SConor Dooley /*
578f943dd1SConor Dooley * Unfortunately, the system controller will only deliver an interrupt
588f943dd1SConor Dooley * if a service succeeds. mbox_send_message() will block until the busy
598f943dd1SConor Dooley * flag is gone. If the busy flag is gone but no interrupt has arrived
608f943dd1SConor Dooley * to trigger the rx callback then the service can be deemed to have
618f943dd1SConor Dooley * failed.
628f943dd1SConor Dooley * The caller can then interrogate msg::response::resp_status to
638f943dd1SConor Dooley * determine the cause of the failure.
648f943dd1SConor Dooley * mbox_send_message() returns positive integers in the success path, so
658f943dd1SConor Dooley * ret needs to be cleared if we do get an interrupt.
668f943dd1SConor Dooley */
677606f4dfSConor Dooley if (!wait_for_completion_timeout(&sys_controller->c, timeout)) {
688f943dd1SConor Dooley ret = -EBADMSG;
69a8f00589SConor Dooley dev_warn(sys_controller->client.dev,
70a8f00589SConor Dooley "MPFS sys controller service failed with status: %d\n",
71a8f00589SConor Dooley msg->response->resp_status);
72d0054a47SConor Dooley } else {
737606f4dfSConor Dooley ret = 0;
74d0054a47SConor Dooley }
75d0054a47SConor Dooley
767606f4dfSConor Dooley out:
77d0054a47SConor Dooley mutex_unlock(&transaction_lock);
78d0054a47SConor Dooley
79d0054a47SConor Dooley return ret;
80d0054a47SConor Dooley }
81d0054a47SConor Dooley EXPORT_SYMBOL(mpfs_blocking_transaction);
82d0054a47SConor Dooley
mpfs_sys_controller_rx_callback(struct mbox_client * client,void * msg)834dd472bdSConor Dooley static void mpfs_sys_controller_rx_callback(struct mbox_client *client, void *msg)
84d0054a47SConor Dooley {
85d0054a47SConor Dooley struct mpfs_sys_controller *sys_controller =
86d0054a47SConor Dooley container_of(client, struct mpfs_sys_controller, client);
87d0054a47SConor Dooley
88d0054a47SConor Dooley complete(&sys_controller->c);
89d0054a47SConor Dooley }
90d0054a47SConor Dooley
mpfs_sys_controller_delete(struct kref * kref)91d0054a47SConor Dooley static void mpfs_sys_controller_delete(struct kref *kref)
92d0054a47SConor Dooley {
935ca631ecSConor Dooley struct mpfs_sys_controller *sys_controller =
945ca631ecSConor Dooley container_of(kref, struct mpfs_sys_controller, consumers);
95d0054a47SConor Dooley
96d0054a47SConor Dooley mbox_free_channel(sys_controller->chan);
97d0054a47SConor Dooley kfree(sys_controller);
98d0054a47SConor Dooley }
99d0054a47SConor Dooley
mpfs_sys_controller_put(void * data)100e6e6479cSConor Dooley static void mpfs_sys_controller_put(void *data)
101d0054a47SConor Dooley {
102d0054a47SConor Dooley struct mpfs_sys_controller *sys_controller = data;
103d0054a47SConor Dooley
104d0054a47SConor Dooley kref_put(&sys_controller->consumers, mpfs_sys_controller_delete);
105d0054a47SConor Dooley }
106d0054a47SConor Dooley
mpfs_sys_controller_get_flash(struct mpfs_sys_controller * mpfs_client)107742aa6c5SConor Dooley struct mtd_info *mpfs_sys_controller_get_flash(struct mpfs_sys_controller *mpfs_client)
108742aa6c5SConor Dooley {
109742aa6c5SConor Dooley return mpfs_client->flash;
110742aa6c5SConor Dooley }
111742aa6c5SConor Dooley EXPORT_SYMBOL(mpfs_sys_controller_get_flash);
112742aa6c5SConor Dooley
113d0054a47SConor Dooley static struct platform_device subdevs[] = {
114d0054a47SConor Dooley {
115d0054a47SConor Dooley .name = "mpfs-rng",
116d0054a47SConor Dooley .id = -1,
117d0054a47SConor Dooley },
118d0054a47SConor Dooley {
119d0054a47SConor Dooley .name = "mpfs-generic-service",
120d0054a47SConor Dooley .id = -1,
121fad13b5bSConor Dooley },
122fad13b5bSConor Dooley {
123fad13b5bSConor Dooley .name = "mpfs-auto-update",
124fad13b5bSConor Dooley .id = -1,
125fad13b5bSConor Dooley },
126d0054a47SConor Dooley };
127d0054a47SConor Dooley
mpfs_sys_controller_probe(struct platform_device * pdev)128d0054a47SConor Dooley static int mpfs_sys_controller_probe(struct platform_device *pdev)
129d0054a47SConor Dooley {
130d0054a47SConor Dooley struct device *dev = &pdev->dev;
131d0054a47SConor Dooley struct mpfs_sys_controller *sys_controller;
132742aa6c5SConor Dooley struct device_node *np;
1338e145bc7SConor Dooley int i, ret;
134d0054a47SConor Dooley
1358e145bc7SConor Dooley sys_controller = kzalloc(sizeof(*sys_controller), GFP_KERNEL);
136d0054a47SConor Dooley if (!sys_controller)
137d0054a47SConor Dooley return -ENOMEM;
138d0054a47SConor Dooley
139742aa6c5SConor Dooley np = of_parse_phandle(dev->of_node, "microchip,bitstream-flash", 0);
140742aa6c5SConor Dooley if (!np)
141742aa6c5SConor Dooley goto no_flash;
142742aa6c5SConor Dooley
143742aa6c5SConor Dooley sys_controller->flash = of_get_mtd_device_by_node(np);
144742aa6c5SConor Dooley of_node_put(np);
145742aa6c5SConor Dooley if (IS_ERR(sys_controller->flash))
146742aa6c5SConor Dooley return dev_err_probe(dev, PTR_ERR(sys_controller->flash), "Failed to get flash\n");
147742aa6c5SConor Dooley
148742aa6c5SConor Dooley no_flash:
149d0054a47SConor Dooley sys_controller->client.dev = dev;
1504dd472bdSConor Dooley sys_controller->client.rx_callback = mpfs_sys_controller_rx_callback;
151d0054a47SConor Dooley sys_controller->client.tx_block = 1U;
1528f943dd1SConor Dooley sys_controller->client.tx_tout = msecs_to_jiffies(MPFS_SYS_CTRL_TIMEOUT_MS);
153d0054a47SConor Dooley
154d0054a47SConor Dooley sys_controller->chan = mbox_request_channel(&sys_controller->client, 0);
1558e145bc7SConor Dooley if (IS_ERR(sys_controller->chan)) {
1568e145bc7SConor Dooley ret = dev_err_probe(dev, PTR_ERR(sys_controller->chan),
157d0054a47SConor Dooley "Failed to get mbox channel\n");
1588e145bc7SConor Dooley kfree(sys_controller);
1598e145bc7SConor Dooley return ret;
1608e145bc7SConor Dooley }
161d0054a47SConor Dooley
162d0054a47SConor Dooley init_completion(&sys_controller->c);
163d0054a47SConor Dooley kref_init(&sys_controller->consumers);
164d0054a47SConor Dooley
165d0054a47SConor Dooley platform_set_drvdata(pdev, sys_controller);
166d0054a47SConor Dooley
167d0054a47SConor Dooley
168d0054a47SConor Dooley for (i = 0; i < ARRAY_SIZE(subdevs); i++) {
169d0054a47SConor Dooley subdevs[i].dev.parent = dev;
170d0054a47SConor Dooley if (platform_device_register(&subdevs[i]))
171d0054a47SConor Dooley dev_warn(dev, "Error registering sub device %s\n", subdevs[i].name);
172d0054a47SConor Dooley }
173d0054a47SConor Dooley
174fad13b5bSConor Dooley dev_info(&pdev->dev, "Registered MPFS system controller\n");
175fad13b5bSConor Dooley
176d0054a47SConor Dooley return 0;
177d0054a47SConor Dooley }
178d0054a47SConor Dooley
mpfs_sys_controller_remove(struct platform_device * pdev)17922dedf8fSUwe Kleine-König static void mpfs_sys_controller_remove(struct platform_device *pdev)
180d0054a47SConor Dooley {
181d0054a47SConor Dooley struct mpfs_sys_controller *sys_controller = platform_get_drvdata(pdev);
182d0054a47SConor Dooley
183d0054a47SConor Dooley mpfs_sys_controller_put(sys_controller);
184d0054a47SConor Dooley }
185d0054a47SConor Dooley
186d0054a47SConor Dooley static const struct of_device_id mpfs_sys_controller_of_match[] = {
187d0054a47SConor Dooley {.compatible = "microchip,mpfs-sys-controller", },
188d0054a47SConor Dooley {},
189d0054a47SConor Dooley };
190d0054a47SConor Dooley MODULE_DEVICE_TABLE(of, mpfs_sys_controller_of_match);
191d0054a47SConor Dooley
mpfs_sys_controller_get(struct device * dev)192d0054a47SConor Dooley struct mpfs_sys_controller *mpfs_sys_controller_get(struct device *dev)
193d0054a47SConor Dooley {
194d0054a47SConor Dooley const struct of_device_id *match;
195d0054a47SConor Dooley struct mpfs_sys_controller *sys_controller;
196d0054a47SConor Dooley int ret;
197d0054a47SConor Dooley
198d0054a47SConor Dooley if (!dev->parent)
199d0054a47SConor Dooley goto err_no_device;
200d0054a47SConor Dooley
201d0054a47SConor Dooley match = of_match_node(mpfs_sys_controller_of_match, dev->parent->of_node);
202d0054a47SConor Dooley of_node_put(dev->parent->of_node);
203d0054a47SConor Dooley if (!match)
204d0054a47SConor Dooley goto err_no_device;
205d0054a47SConor Dooley
206d0054a47SConor Dooley sys_controller = dev_get_drvdata(dev->parent);
207d0054a47SConor Dooley if (!sys_controller)
208d0054a47SConor Dooley goto err_bad_device;
209d0054a47SConor Dooley
210d0054a47SConor Dooley if (!kref_get_unless_zero(&sys_controller->consumers))
211d0054a47SConor Dooley goto err_bad_device;
212d0054a47SConor Dooley
213d0054a47SConor Dooley ret = devm_add_action_or_reset(dev, mpfs_sys_controller_put, sys_controller);
214d0054a47SConor Dooley if (ret)
215d0054a47SConor Dooley return ERR_PTR(ret);
216d0054a47SConor Dooley
217d0054a47SConor Dooley return sys_controller;
218d0054a47SConor Dooley
219d0054a47SConor Dooley err_no_device:
220d0054a47SConor Dooley dev_dbg(dev, "Parent device was not an MPFS system controller\n");
221d0054a47SConor Dooley return ERR_PTR(-ENODEV);
222d0054a47SConor Dooley
223d0054a47SConor Dooley err_bad_device:
224d0054a47SConor Dooley dev_dbg(dev, "MPFS system controller found but could not register as a sub device\n");
225d0054a47SConor Dooley return ERR_PTR(-EPROBE_DEFER);
226d0054a47SConor Dooley }
227d0054a47SConor Dooley EXPORT_SYMBOL(mpfs_sys_controller_get);
228d0054a47SConor Dooley
229d0054a47SConor Dooley static struct platform_driver mpfs_sys_controller_driver = {
230d0054a47SConor Dooley .driver = {
231d0054a47SConor Dooley .name = "mpfs-sys-controller",
232d0054a47SConor Dooley .of_match_table = mpfs_sys_controller_of_match,
233d0054a47SConor Dooley },
234d0054a47SConor Dooley .probe = mpfs_sys_controller_probe,
235*511c06e3SUwe Kleine-König .remove = mpfs_sys_controller_remove,
236d0054a47SConor Dooley };
237d0054a47SConor Dooley module_platform_driver(mpfs_sys_controller_driver);
238d0054a47SConor Dooley
239d0054a47SConor Dooley MODULE_LICENSE("GPL v2");
240d0054a47SConor Dooley MODULE_AUTHOR("Conor Dooley <[email protected]>");
241d0054a47SConor Dooley MODULE_DESCRIPTION("MPFS system controller driver");
242