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 #define MPFS_SYS_CTRL_TIMEOUT_MS 100 22 23 static DEFINE_MUTEX(transaction_lock); 24 25 struct mpfs_sys_controller { 26 struct mbox_client client; 27 struct mbox_chan *chan; 28 struct completion c; 29 struct kref consumers; 30 }; 31 32 int mpfs_blocking_transaction(struct mpfs_sys_controller *sys_controller, struct mpfs_mss_msg *msg) 33 { 34 unsigned long timeout = msecs_to_jiffies(MPFS_SYS_CTRL_TIMEOUT_MS); 35 int ret, err; 36 37 err = mutex_lock_interruptible(&transaction_lock); 38 if (err) 39 return err; 40 41 reinit_completion(&sys_controller->c); 42 43 ret = mbox_send_message(sys_controller->chan, msg); 44 if (ret >= 0) { 45 if (wait_for_completion_timeout(&sys_controller->c, timeout)) { 46 ret = 0; 47 } else { 48 ret = -ETIMEDOUT; 49 dev_warn(sys_controller->client.dev, 50 "MPFS sys controller transaction timeout\n"); 51 } 52 } else { 53 dev_err(sys_controller->client.dev, 54 "mpfs sys controller transaction returned %d\n", ret); 55 } 56 57 mutex_unlock(&transaction_lock); 58 59 return ret; 60 } 61 EXPORT_SYMBOL(mpfs_blocking_transaction); 62 63 static void rx_callback(struct mbox_client *client, void *msg) 64 { 65 struct mpfs_sys_controller *sys_controller = 66 container_of(client, struct mpfs_sys_controller, client); 67 68 complete(&sys_controller->c); 69 } 70 71 static void mpfs_sys_controller_delete(struct kref *kref) 72 { 73 struct mpfs_sys_controller *sys_controller = 74 container_of(kref, struct mpfs_sys_controller, consumers); 75 76 mbox_free_channel(sys_controller->chan); 77 kfree(sys_controller); 78 } 79 80 static void mpfs_sys_controller_put(void *data) 81 { 82 struct mpfs_sys_controller *sys_controller = data; 83 84 kref_put(&sys_controller->consumers, mpfs_sys_controller_delete); 85 } 86 87 static struct platform_device subdevs[] = { 88 { 89 .name = "mpfs-rng", 90 .id = -1, 91 }, 92 { 93 .name = "mpfs-generic-service", 94 .id = -1, 95 } 96 }; 97 98 static int mpfs_sys_controller_probe(struct platform_device *pdev) 99 { 100 struct device *dev = &pdev->dev; 101 struct mpfs_sys_controller *sys_controller; 102 int i, ret; 103 104 sys_controller = kzalloc(sizeof(*sys_controller), GFP_KERNEL); 105 if (!sys_controller) 106 return -ENOMEM; 107 108 sys_controller->client.dev = dev; 109 sys_controller->client.rx_callback = rx_callback; 110 sys_controller->client.tx_block = 1U; 111 112 sys_controller->chan = mbox_request_channel(&sys_controller->client, 0); 113 if (IS_ERR(sys_controller->chan)) { 114 ret = dev_err_probe(dev, PTR_ERR(sys_controller->chan), 115 "Failed to get mbox channel\n"); 116 kfree(sys_controller); 117 return ret; 118 } 119 120 init_completion(&sys_controller->c); 121 kref_init(&sys_controller->consumers); 122 123 platform_set_drvdata(pdev, sys_controller); 124 125 dev_info(&pdev->dev, "Registered MPFS system controller\n"); 126 127 for (i = 0; i < ARRAY_SIZE(subdevs); i++) { 128 subdevs[i].dev.parent = dev; 129 if (platform_device_register(&subdevs[i])) 130 dev_warn(dev, "Error registering sub device %s\n", subdevs[i].name); 131 } 132 133 return 0; 134 } 135 136 static int mpfs_sys_controller_remove(struct platform_device *pdev) 137 { 138 struct mpfs_sys_controller *sys_controller = platform_get_drvdata(pdev); 139 140 mpfs_sys_controller_put(sys_controller); 141 142 return 0; 143 } 144 145 static const struct of_device_id mpfs_sys_controller_of_match[] = { 146 {.compatible = "microchip,mpfs-sys-controller", }, 147 {}, 148 }; 149 MODULE_DEVICE_TABLE(of, mpfs_sys_controller_of_match); 150 151 struct mpfs_sys_controller *mpfs_sys_controller_get(struct device *dev) 152 { 153 const struct of_device_id *match; 154 struct mpfs_sys_controller *sys_controller; 155 int ret; 156 157 if (!dev->parent) 158 goto err_no_device; 159 160 match = of_match_node(mpfs_sys_controller_of_match, dev->parent->of_node); 161 of_node_put(dev->parent->of_node); 162 if (!match) 163 goto err_no_device; 164 165 sys_controller = dev_get_drvdata(dev->parent); 166 if (!sys_controller) 167 goto err_bad_device; 168 169 if (!kref_get_unless_zero(&sys_controller->consumers)) 170 goto err_bad_device; 171 172 ret = devm_add_action_or_reset(dev, mpfs_sys_controller_put, sys_controller); 173 if (ret) 174 return ERR_PTR(ret); 175 176 return sys_controller; 177 178 err_no_device: 179 dev_dbg(dev, "Parent device was not an MPFS system controller\n"); 180 return ERR_PTR(-ENODEV); 181 182 err_bad_device: 183 dev_dbg(dev, "MPFS system controller found but could not register as a sub device\n"); 184 return ERR_PTR(-EPROBE_DEFER); 185 } 186 EXPORT_SYMBOL(mpfs_sys_controller_get); 187 188 static struct platform_driver mpfs_sys_controller_driver = { 189 .driver = { 190 .name = "mpfs-sys-controller", 191 .of_match_table = mpfs_sys_controller_of_match, 192 }, 193 .probe = mpfs_sys_controller_probe, 194 .remove = mpfs_sys_controller_remove, 195 }; 196 module_platform_driver(mpfs_sys_controller_driver); 197 198 MODULE_LICENSE("GPL v2"); 199 MODULE_AUTHOR("Conor Dooley <conor.dooley@microchip.com>"); 200 MODULE_DESCRIPTION("MPFS system controller driver"); 201