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/mtd/mtd.h> |
16 | #include <linux/spi/spi.h> |
17 | #include <linux/interrupt.h> |
18 | #include <linux/of.h> |
19 | #include <linux/mailbox_client.h> |
20 | #include <linux/platform_device.h> |
21 | #include <soc/microchip/mpfs.h> |
22 | |
23 | /* |
24 | * This timeout must be long, as some services (example: image authentication) |
25 | * take significant time to complete |
26 | */ |
27 | #define MPFS_SYS_CTRL_TIMEOUT_MS 30000 |
28 | |
29 | static DEFINE_MUTEX(transaction_lock); |
30 | |
31 | struct mpfs_sys_controller { |
32 | struct mbox_client client; |
33 | struct mbox_chan *chan; |
34 | struct completion c; |
35 | struct mtd_info *flash; |
36 | struct kref consumers; |
37 | }; |
38 | |
39 | int mpfs_blocking_transaction(struct mpfs_sys_controller *sys_controller, struct mpfs_mss_msg *msg) |
40 | { |
41 | unsigned long timeout = msecs_to_jiffies(MPFS_SYS_CTRL_TIMEOUT_MS); |
42 | int ret; |
43 | |
44 | ret = mutex_lock_interruptible(&transaction_lock); |
45 | if (ret) |
46 | return ret; |
47 | |
48 | reinit_completion(x: &sys_controller->c); |
49 | |
50 | ret = mbox_send_message(chan: sys_controller->chan, mssg: msg); |
51 | if (ret < 0) { |
52 | dev_warn(sys_controller->client.dev, "MPFS sys controller service timeout\n" ); |
53 | goto out; |
54 | } |
55 | |
56 | /* |
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 |
61 | * failed. |
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. |
66 | */ |
67 | if (!wait_for_completion_timeout(x: &sys_controller->c, timeout)) { |
68 | ret = -EBADMSG; |
69 | dev_warn(sys_controller->client.dev, |
70 | "MPFS sys controller service failed with status: %d\n" , |
71 | msg->response->resp_status); |
72 | } else { |
73 | ret = 0; |
74 | } |
75 | |
76 | out: |
77 | mutex_unlock(lock: &transaction_lock); |
78 | |
79 | return ret; |
80 | } |
81 | EXPORT_SYMBOL(mpfs_blocking_transaction); |
82 | |
83 | static void mpfs_sys_controller_rx_callback(struct mbox_client *client, void *msg) |
84 | { |
85 | struct mpfs_sys_controller *sys_controller = |
86 | container_of(client, struct mpfs_sys_controller, client); |
87 | |
88 | complete(&sys_controller->c); |
89 | } |
90 | |
91 | static void mpfs_sys_controller_delete(struct kref *kref) |
92 | { |
93 | struct mpfs_sys_controller *sys_controller = |
94 | container_of(kref, struct mpfs_sys_controller, consumers); |
95 | |
96 | mbox_free_channel(chan: sys_controller->chan); |
97 | kfree(objp: sys_controller); |
98 | } |
99 | |
100 | static void mpfs_sys_controller_put(void *data) |
101 | { |
102 | struct mpfs_sys_controller *sys_controller = data; |
103 | |
104 | kref_put(kref: &sys_controller->consumers, release: mpfs_sys_controller_delete); |
105 | } |
106 | |
107 | struct mtd_info *mpfs_sys_controller_get_flash(struct mpfs_sys_controller *mpfs_client) |
108 | { |
109 | return mpfs_client->flash; |
110 | } |
111 | EXPORT_SYMBOL(mpfs_sys_controller_get_flash); |
112 | |
113 | static struct platform_device subdevs[] = { |
114 | { |
115 | .name = "mpfs-rng" , |
116 | .id = -1, |
117 | }, |
118 | { |
119 | .name = "mpfs-generic-service" , |
120 | .id = -1, |
121 | }, |
122 | { |
123 | .name = "mpfs-auto-update" , |
124 | .id = -1, |
125 | }, |
126 | }; |
127 | |
128 | static int mpfs_sys_controller_probe(struct platform_device *pdev) |
129 | { |
130 | struct device *dev = &pdev->dev; |
131 | struct mpfs_sys_controller *sys_controller; |
132 | struct device_node *np; |
133 | int i, ret; |
134 | |
135 | sys_controller = kzalloc(size: sizeof(*sys_controller), GFP_KERNEL); |
136 | if (!sys_controller) |
137 | return -ENOMEM; |
138 | |
139 | np = of_parse_phandle(np: dev->of_node, phandle_name: "microchip,bitstream-flash" , index: 0); |
140 | if (!np) |
141 | goto no_flash; |
142 | |
143 | sys_controller->flash = of_get_mtd_device_by_node(np); |
144 | of_node_put(node: np); |
145 | if (IS_ERR(ptr: sys_controller->flash)) |
146 | return dev_err_probe(dev, err: PTR_ERR(ptr: sys_controller->flash), fmt: "Failed to get flash\n" ); |
147 | |
148 | no_flash: |
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); |
153 | |
154 | sys_controller->chan = mbox_request_channel(cl: &sys_controller->client, index: 0); |
155 | if (IS_ERR(ptr: sys_controller->chan)) { |
156 | ret = dev_err_probe(dev, err: PTR_ERR(ptr: sys_controller->chan), |
157 | fmt: "Failed to get mbox channel\n" ); |
158 | kfree(objp: sys_controller); |
159 | return ret; |
160 | } |
161 | |
162 | init_completion(x: &sys_controller->c); |
163 | kref_init(kref: &sys_controller->consumers); |
164 | |
165 | platform_set_drvdata(pdev, data: sys_controller); |
166 | |
167 | |
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); |
172 | } |
173 | |
174 | dev_info(&pdev->dev, "Registered MPFS system controller\n" ); |
175 | |
176 | return 0; |
177 | } |
178 | |
179 | static void mpfs_sys_controller_remove(struct platform_device *pdev) |
180 | { |
181 | struct mpfs_sys_controller *sys_controller = platform_get_drvdata(pdev); |
182 | |
183 | mpfs_sys_controller_put(data: sys_controller); |
184 | } |
185 | |
186 | static const struct of_device_id mpfs_sys_controller_of_match[] = { |
187 | {.compatible = "microchip,mpfs-sys-controller" , }, |
188 | {}, |
189 | }; |
190 | MODULE_DEVICE_TABLE(of, mpfs_sys_controller_of_match); |
191 | |
192 | struct mpfs_sys_controller *mpfs_sys_controller_get(struct device *dev) |
193 | { |
194 | const struct of_device_id *match; |
195 | struct mpfs_sys_controller *sys_controller; |
196 | int ret; |
197 | |
198 | if (!dev->parent) |
199 | goto err_no_device; |
200 | |
201 | match = of_match_node(matches: mpfs_sys_controller_of_match, node: dev->parent->of_node); |
202 | of_node_put(node: dev->parent->of_node); |
203 | if (!match) |
204 | goto err_no_device; |
205 | |
206 | sys_controller = dev_get_drvdata(dev: dev->parent); |
207 | if (!sys_controller) |
208 | goto err_bad_device; |
209 | |
210 | if (!kref_get_unless_zero(kref: &sys_controller->consumers)) |
211 | goto err_bad_device; |
212 | |
213 | ret = devm_add_action_or_reset(dev, mpfs_sys_controller_put, sys_controller); |
214 | if (ret) |
215 | return ERR_PTR(error: ret); |
216 | |
217 | return sys_controller; |
218 | |
219 | err_no_device: |
220 | dev_dbg(dev, "Parent device was not an MPFS system controller\n" ); |
221 | return ERR_PTR(error: -ENODEV); |
222 | |
223 | err_bad_device: |
224 | dev_dbg(dev, "MPFS system controller found but could not register as a sub device\n" ); |
225 | return ERR_PTR(error: -EPROBE_DEFER); |
226 | } |
227 | EXPORT_SYMBOL(mpfs_sys_controller_get); |
228 | |
229 | static struct platform_driver mpfs_sys_controller_driver = { |
230 | .driver = { |
231 | .name = "mpfs-sys-controller" , |
232 | .of_match_table = mpfs_sys_controller_of_match, |
233 | }, |
234 | .probe = mpfs_sys_controller_probe, |
235 | .remove_new = mpfs_sys_controller_remove, |
236 | }; |
237 | module_platform_driver(mpfs_sys_controller_driver); |
238 | |
239 | MODULE_LICENSE("GPL v2" ); |
240 | MODULE_AUTHOR("Conor Dooley <conor.dooley@microchip.com>" ); |
241 | MODULE_DESCRIPTION("MPFS system controller driver" ); |
242 | |