1 /* 2 * Qualcomm Peripheral Image Loader helpers 3 * 4 * Copyright (C) 2016 Linaro Ltd 5 * Copyright (C) 2015 Sony Mobile Communications Inc 6 * Copyright (c) 2012-2013, The Linux Foundation. All rights reserved. 7 * 8 * This program is free software; you can redistribute it and/or 9 * modify it under the terms of the GNU General Public License 10 * version 2 as published by the Free Software Foundation. 11 * 12 * This program is distributed in the hope that it will be useful, 13 * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 * GNU General Public License for more details. 16 */ 17 18 #include <linux/firmware.h> 19 #include <linux/kernel.h> 20 #include <linux/module.h> 21 #include <linux/notifier.h> 22 #include <linux/remoteproc.h> 23 #include <linux/rpmsg/qcom_glink.h> 24 #include <linux/rpmsg/qcom_smd.h> 25 26 #include "remoteproc_internal.h" 27 #include "qcom_common.h" 28 29 #define to_glink_subdev(d) container_of(d, struct qcom_rproc_glink, subdev) 30 #define to_smd_subdev(d) container_of(d, struct qcom_rproc_subdev, subdev) 31 #define to_ssr_subdev(d) container_of(d, struct qcom_rproc_ssr, subdev) 32 33 static BLOCKING_NOTIFIER_HEAD(ssr_notifiers); 34 35 /** 36 * qcom_mdt_find_rsc_table() - provide dummy resource table for remoteproc 37 * @rproc: remoteproc handle 38 * @fw: firmware header 39 * @tablesz: outgoing size of the table 40 * 41 * Returns a dummy table. 42 */ 43 struct resource_table *qcom_mdt_find_rsc_table(struct rproc *rproc, 44 const struct firmware *fw, 45 int *tablesz) 46 { 47 static struct resource_table table = { .ver = 1, }; 48 49 *tablesz = sizeof(table); 50 return &table; 51 } 52 EXPORT_SYMBOL_GPL(qcom_mdt_find_rsc_table); 53 54 static int glink_subdev_probe(struct rproc_subdev *subdev) 55 { 56 struct qcom_rproc_glink *glink = to_glink_subdev(subdev); 57 58 glink->edge = qcom_glink_smem_register(glink->dev, glink->node); 59 60 return IS_ERR(glink->edge) ? PTR_ERR(glink->edge) : 0; 61 } 62 63 static void glink_subdev_remove(struct rproc_subdev *subdev) 64 { 65 struct qcom_rproc_glink *glink = to_glink_subdev(subdev); 66 67 qcom_glink_smem_unregister(glink->edge); 68 glink->edge = NULL; 69 } 70 71 /** 72 * qcom_add_glink_subdev() - try to add a GLINK subdevice to rproc 73 * @rproc: rproc handle to parent the subdevice 74 * @glink: reference to a GLINK subdev context 75 */ 76 void qcom_add_glink_subdev(struct rproc *rproc, struct qcom_rproc_glink *glink) 77 { 78 struct device *dev = &rproc->dev; 79 80 glink->node = of_get_child_by_name(dev->parent->of_node, "glink-edge"); 81 if (!glink->node) 82 return; 83 84 glink->dev = dev; 85 rproc_add_subdev(rproc, &glink->subdev, glink_subdev_probe, glink_subdev_remove); 86 } 87 EXPORT_SYMBOL_GPL(qcom_add_glink_subdev); 88 89 /** 90 * qcom_remove_glink_subdev() - remove a GLINK subdevice from rproc 91 * @rproc: rproc handle 92 * @glink: reference to a GLINK subdev context 93 */ 94 void qcom_remove_glink_subdev(struct rproc *rproc, struct qcom_rproc_glink *glink) 95 { 96 rproc_remove_subdev(rproc, &glink->subdev); 97 of_node_put(glink->node); 98 } 99 EXPORT_SYMBOL_GPL(qcom_remove_glink_subdev); 100 101 static int smd_subdev_probe(struct rproc_subdev *subdev) 102 { 103 struct qcom_rproc_subdev *smd = to_smd_subdev(subdev); 104 105 smd->edge = qcom_smd_register_edge(smd->dev, smd->node); 106 107 return PTR_ERR_OR_ZERO(smd->edge); 108 } 109 110 static void smd_subdev_remove(struct rproc_subdev *subdev) 111 { 112 struct qcom_rproc_subdev *smd = to_smd_subdev(subdev); 113 114 qcom_smd_unregister_edge(smd->edge); 115 smd->edge = NULL; 116 } 117 118 /** 119 * qcom_add_smd_subdev() - try to add a SMD subdevice to rproc 120 * @rproc: rproc handle to parent the subdevice 121 * @smd: reference to a Qualcomm subdev context 122 */ 123 void qcom_add_smd_subdev(struct rproc *rproc, struct qcom_rproc_subdev *smd) 124 { 125 struct device *dev = &rproc->dev; 126 127 smd->node = of_get_child_by_name(dev->parent->of_node, "smd-edge"); 128 if (!smd->node) 129 return; 130 131 smd->dev = dev; 132 rproc_add_subdev(rproc, &smd->subdev, smd_subdev_probe, smd_subdev_remove); 133 } 134 EXPORT_SYMBOL_GPL(qcom_add_smd_subdev); 135 136 /** 137 * qcom_remove_smd_subdev() - remove the smd subdevice from rproc 138 * @rproc: rproc handle 139 * @smd: the SMD subdevice to remove 140 */ 141 void qcom_remove_smd_subdev(struct rproc *rproc, struct qcom_rproc_subdev *smd) 142 { 143 rproc_remove_subdev(rproc, &smd->subdev); 144 of_node_put(smd->node); 145 } 146 EXPORT_SYMBOL_GPL(qcom_remove_smd_subdev); 147 148 /** 149 * qcom_register_ssr_notifier() - register SSR notification handler 150 * @nb: notifier_block to notify for restart notifications 151 * 152 * Returns 0 on success, negative errno on failure. 153 * 154 * This register the @notify function as handler for restart notifications. As 155 * remote processors are stopped this function will be called, with the SSR 156 * name passed as a parameter. 157 */ 158 int qcom_register_ssr_notifier(struct notifier_block *nb) 159 { 160 return blocking_notifier_chain_register(&ssr_notifiers, nb); 161 } 162 EXPORT_SYMBOL_GPL(qcom_register_ssr_notifier); 163 164 /** 165 * qcom_unregister_ssr_notifier() - unregister SSR notification handler 166 * @nb: notifier_block to unregister 167 */ 168 void qcom_unregister_ssr_notifier(struct notifier_block *nb) 169 { 170 blocking_notifier_chain_unregister(&ssr_notifiers, nb); 171 } 172 EXPORT_SYMBOL_GPL(qcom_unregister_ssr_notifier); 173 174 static int ssr_notify_start(struct rproc_subdev *subdev) 175 { 176 return 0; 177 } 178 179 static void ssr_notify_stop(struct rproc_subdev *subdev) 180 { 181 struct qcom_rproc_ssr *ssr = to_ssr_subdev(subdev); 182 183 blocking_notifier_call_chain(&ssr_notifiers, 0, (void *)ssr->name); 184 } 185 186 /** 187 * qcom_add_ssr_subdev() - register subdevice as restart notification source 188 * @rproc: rproc handle 189 * @ssr: SSR subdevice handle 190 * @ssr_name: identifier to use for notifications originating from @rproc 191 * 192 * As the @ssr is registered with the @rproc SSR events will be sent to all 193 * registered listeners in the system as the remoteproc is shut down. 194 */ 195 void qcom_add_ssr_subdev(struct rproc *rproc, struct qcom_rproc_ssr *ssr, 196 const char *ssr_name) 197 { 198 ssr->name = ssr_name; 199 200 rproc_add_subdev(rproc, &ssr->subdev, ssr_notify_start, ssr_notify_stop); 201 } 202 EXPORT_SYMBOL_GPL(qcom_add_ssr_subdev); 203 204 /** 205 * qcom_remove_ssr_subdev() - remove subdevice as restart notification source 206 * @rproc: rproc handle 207 * @ssr: SSR subdevice handle 208 */ 209 void qcom_remove_ssr_subdev(struct rproc *rproc, struct qcom_rproc_ssr *ssr) 210 { 211 rproc_remove_subdev(rproc, &ssr->subdev); 212 } 213 EXPORT_SYMBOL_GPL(qcom_remove_ssr_subdev); 214 215 MODULE_DESCRIPTION("Qualcomm Remoteproc helper driver"); 216 MODULE_LICENSE("GPL v2"); 217