1 // SPDX-License-Identifier: GPL-2.0
3 * Microchip PolarFire SoC (MPFS) system controller driver
5 * Copyright (c) 2020-2021 Microchip Corporation. All rights reserved.
7 * Author: Conor Dooley <conor.dooley@microchip.com>
11 #include <linux/slab.h>
12 #include <linux/kref.h>
13 #include <linux/module.h>
14 #include <linux/jiffies.h>
15 #include <linux/mtd/mtd.h>
16 #include <linux/spi/spi.h>
17 #include <linux/interrupt.h>
19 #include <linux/mailbox_client.h>
20 #include <linux/platform_device.h>
21 #include <soc/microchip/mpfs.h>
24 * This timeout must be long, as some services (example: image authentication)
25 * take significant time to complete
27 #define MPFS_SYS_CTRL_TIMEOUT_MS 30000
29 static DEFINE_MUTEX(transaction_lock
);
31 struct mpfs_sys_controller
{
32 struct mbox_client client
;
33 struct mbox_chan
*chan
;
35 struct mtd_info
*flash
;
36 struct kref consumers
;
39 int mpfs_blocking_transaction(struct mpfs_sys_controller
*sys_controller
, struct mpfs_mss_msg
*msg
)
41 unsigned long timeout
= msecs_to_jiffies(MPFS_SYS_CTRL_TIMEOUT_MS
);
44 ret
= mutex_lock_interruptible(&transaction_lock
);
48 reinit_completion(&sys_controller
->c
);
50 ret
= mbox_send_message(sys_controller
->chan
, msg
);
52 dev_warn(sys_controller
->client
.dev
, "MPFS sys controller service timeout\n");
57 * Unfortunately, the system controller will only deliver an interrupt
58 * if a service succeeds. mbox_send_message() will block until the busy
59 * flag is gone. If the busy flag is gone but no interrupt has arrived
60 * to trigger the rx callback then the service can be deemed to have
62 * The caller can then interrogate msg::response::resp_status to
63 * determine the cause of the failure.
64 * mbox_send_message() returns positive integers in the success path, so
65 * ret needs to be cleared if we do get an interrupt.
67 if (!wait_for_completion_timeout(&sys_controller
->c
, timeout
)) {
69 dev_warn(sys_controller
->client
.dev
,
70 "MPFS sys controller service failed with status: %d\n",
71 msg
->response
->resp_status
);
77 mutex_unlock(&transaction_lock
);
81 EXPORT_SYMBOL(mpfs_blocking_transaction
);
83 static void mpfs_sys_controller_rx_callback(struct mbox_client
*client
, void *msg
)
85 struct mpfs_sys_controller
*sys_controller
=
86 container_of(client
, struct mpfs_sys_controller
, client
);
88 complete(&sys_controller
->c
);
91 static void mpfs_sys_controller_delete(struct kref
*kref
)
93 struct mpfs_sys_controller
*sys_controller
=
94 container_of(kref
, struct mpfs_sys_controller
, consumers
);
96 mbox_free_channel(sys_controller
->chan
);
97 kfree(sys_controller
);
100 static void mpfs_sys_controller_put(void *data
)
102 struct mpfs_sys_controller
*sys_controller
= data
;
104 kref_put(&sys_controller
->consumers
, mpfs_sys_controller_delete
);
107 struct mtd_info
*mpfs_sys_controller_get_flash(struct mpfs_sys_controller
*mpfs_client
)
109 return mpfs_client
->flash
;
111 EXPORT_SYMBOL(mpfs_sys_controller_get_flash
);
113 static struct platform_device subdevs
[] = {
119 .name
= "mpfs-generic-service",
123 .name
= "mpfs-auto-update",
128 static int mpfs_sys_controller_probe(struct platform_device
*pdev
)
130 struct device
*dev
= &pdev
->dev
;
131 struct mpfs_sys_controller
*sys_controller
;
132 struct device_node
*np
;
135 sys_controller
= kzalloc(sizeof(*sys_controller
), GFP_KERNEL
);
139 np
= of_parse_phandle(dev
->of_node
, "microchip,bitstream-flash", 0);
143 sys_controller
->flash
= of_get_mtd_device_by_node(np
);
145 if (IS_ERR(sys_controller
->flash
))
146 return dev_err_probe(dev
, PTR_ERR(sys_controller
->flash
), "Failed to get flash\n");
149 sys_controller
->client
.dev
= dev
;
150 sys_controller
->client
.rx_callback
= mpfs_sys_controller_rx_callback
;
151 sys_controller
->client
.tx_block
= 1U;
152 sys_controller
->client
.tx_tout
= msecs_to_jiffies(MPFS_SYS_CTRL_TIMEOUT_MS
);
154 sys_controller
->chan
= mbox_request_channel(&sys_controller
->client
, 0);
155 if (IS_ERR(sys_controller
->chan
)) {
156 ret
= dev_err_probe(dev
, PTR_ERR(sys_controller
->chan
),
157 "Failed to get mbox channel\n");
158 kfree(sys_controller
);
162 init_completion(&sys_controller
->c
);
163 kref_init(&sys_controller
->consumers
);
165 platform_set_drvdata(pdev
, sys_controller
);
168 for (i
= 0; i
< ARRAY_SIZE(subdevs
); i
++) {
169 subdevs
[i
].dev
.parent
= dev
;
170 if (platform_device_register(&subdevs
[i
]))
171 dev_warn(dev
, "Error registering sub device %s\n", subdevs
[i
].name
);
174 dev_info(&pdev
->dev
, "Registered MPFS system controller\n");
179 static void mpfs_sys_controller_remove(struct platform_device
*pdev
)
181 struct mpfs_sys_controller
*sys_controller
= platform_get_drvdata(pdev
);
183 mpfs_sys_controller_put(sys_controller
);
186 static const struct of_device_id mpfs_sys_controller_of_match
[] = {
187 {.compatible
= "microchip,mpfs-sys-controller", },
190 MODULE_DEVICE_TABLE(of
, mpfs_sys_controller_of_match
);
192 struct mpfs_sys_controller
*mpfs_sys_controller_get(struct device
*dev
)
194 const struct of_device_id
*match
;
195 struct mpfs_sys_controller
*sys_controller
;
201 match
= of_match_node(mpfs_sys_controller_of_match
, dev
->parent
->of_node
);
202 of_node_put(dev
->parent
->of_node
);
206 sys_controller
= dev_get_drvdata(dev
->parent
);
210 if (!kref_get_unless_zero(&sys_controller
->consumers
))
213 ret
= devm_add_action_or_reset(dev
, mpfs_sys_controller_put
, sys_controller
);
217 return sys_controller
;
220 dev_dbg(dev
, "Parent device was not an MPFS system controller\n");
221 return ERR_PTR(-ENODEV
);
224 dev_dbg(dev
, "MPFS system controller found but could not register as a sub device\n");
225 return ERR_PTR(-EPROBE_DEFER
);
227 EXPORT_SYMBOL(mpfs_sys_controller_get
);
229 static struct platform_driver mpfs_sys_controller_driver
= {
231 .name
= "mpfs-sys-controller",
232 .of_match_table
= mpfs_sys_controller_of_match
,
234 .probe
= mpfs_sys_controller_probe
,
235 .remove
= mpfs_sys_controller_remove
,
237 module_platform_driver(mpfs_sys_controller_driver
);
239 MODULE_LICENSE("GPL v2");
240 MODULE_AUTHOR("Conor Dooley <conor.dooley@microchip.com>");
241 MODULE_DESCRIPTION("MPFS system controller driver");