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_platform.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 27 static DEFINE_MUTEX(transaction_lock); 28 29 struct mpfs_sys_controller { 30 struct mbox_client client; 31 struct mbox_chan *chan; 32 struct completion c; 33 struct kref consumers; 34 }; 35 36 int 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 71 out: 72 mutex_unlock(&transaction_lock); 73 74 return ret; 75 } 76 EXPORT_SYMBOL(mpfs_blocking_transaction); 77 78 static 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 86 static 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 95 static 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 102 static 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 113 static 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 152 static 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 161 static const struct of_device_id mpfs_sys_controller_of_match[] = { 162 {.compatible = "microchip,mpfs-sys-controller", }, 163 {}, 164 }; 165 MODULE_DEVICE_TABLE(of, mpfs_sys_controller_of_match); 166 167 struct 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 194 err_no_device: 195 dev_dbg(dev, "Parent device was not an MPFS system controller\n"); 196 return ERR_PTR(-ENODEV); 197 198 err_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 } 202 EXPORT_SYMBOL(mpfs_sys_controller_get); 203 204 static 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 }; 212 module_platform_driver(mpfs_sys_controller_driver); 213 214 MODULE_LICENSE("GPL v2"); 215 MODULE_AUTHOR("Conor Dooley <conor.dooley@microchip.com>"); 216 MODULE_DESCRIPTION("MPFS system controller driver"); 217