Per Åstrand | 2cd5397 | 2021-04-12 13:46:04 +0200 | [diff] [blame] | 1 | /* |
| 2 | * Copyright (c) 2021 Arm Limited. All rights reserved. |
| 3 | * |
| 4 | * This program is free software and is provided to you under the terms of the |
| 5 | * GNU General Public License version 2 as published by the Free Software |
| 6 | * Foundation, and any use by you of this program is subject to the terms |
| 7 | * of such GNU licence. |
| 8 | * |
| 9 | * This program is distributed in the hope that it will be useful, |
| 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| 12 | * GNU General Public License for more details. |
| 13 | * |
| 14 | * You should have received a copy of the GNU General Public License |
| 15 | * along with this program; if not, you can access it online at |
| 16 | * http://www.gnu.org/licenses/gpl-2.0.html. |
| 17 | * |
| 18 | * SPDX-License-Identifier: GPL-2.0-only |
| 19 | */ |
| 20 | |
| 21 | #include <linux/firmware.h> |
| 22 | #include <linux/io.h> |
| 23 | #include <linux/kernel.h> |
| 24 | #include <linux/module.h> |
| 25 | #include <linux/of.h> |
| 26 | #include <linux/of_address.h> |
| 27 | #include <linux/of_device.h> |
| 28 | #include <linux/platform_device.h> |
| 29 | #include <linux/remoteproc.h> |
| 30 | #include <linux/reset.h> |
Nir Ekhauz | 4823921 | 2021-11-23 10:28:34 +0200 | [diff] [blame] | 31 | #include <linux/version.h> |
Per Åstrand | 2cd5397 | 2021-04-12 13:46:04 +0200 | [diff] [blame] | 32 | |
| 33 | #define ETHOSU_RPROC_DRIVER_VERSION "0.0.1" |
| 34 | |
| 35 | #define DEFAULT_FW_FILE "arm-ethos-u65.fw" |
| 36 | #define DEFAULT_AUTO_BOOT (false) |
| 37 | |
| 38 | /* firmware naming module parameter */ |
| 39 | static char fw_filename_param[256] = DEFAULT_FW_FILE; |
| 40 | /* As the remoteproc is setup at probe, just allow the filename readonly */ |
| 41 | module_param_string(filename, fw_filename_param, sizeof(fw_filename_param), |
| 42 | 0444); |
| 43 | MODULE_PARM_DESC(filename, |
| 44 | "Filename for firmware image for Ethos-U remoteproc"); |
| 45 | |
| 46 | static bool auto_boot = DEFAULT_AUTO_BOOT; |
| 47 | module_param(auto_boot, bool, DEFAULT_AUTO_BOOT); |
| 48 | MODULE_PARM_DESC(auto_boot, "Set to one to auto boot at load."); |
| 49 | |
| 50 | struct ethosu_rproc { |
| 51 | struct device *dev; |
| 52 | struct reset_control *rstc; |
| 53 | struct rproc_mem_mapping *map; |
| 54 | size_t map_size; |
| 55 | }; |
| 56 | |
| 57 | struct rproc_mem_mapping { |
| 58 | const char *name; |
| 59 | phys_addr_t rproc_addr; |
| 60 | void __iomem *vaddr; |
| 61 | size_t size; |
| 62 | }; |
| 63 | |
| 64 | struct ethosu_rproc_config { |
| 65 | struct fw_config *fw; |
| 66 | }; |
| 67 | |
| 68 | /*****************************************************************************/ |
| 69 | |
| 70 | static int ethosu_rproc_start(struct rproc *rproc) |
| 71 | { |
| 72 | struct ethosu_rproc *ethosu = (struct ethosu_rproc *)rproc->priv; |
| 73 | struct device *dev = ethosu->dev; |
| 74 | |
| 75 | dev_info(dev, "Starting up Ethos-U subsystem CPU!"); |
| 76 | |
| 77 | return reset_control_deassert(ethosu->rstc); |
| 78 | } |
| 79 | |
| 80 | static int ethosu_rproc_stop(struct rproc *rproc) |
| 81 | { |
| 82 | struct ethosu_rproc *ethosu = (struct ethosu_rproc *)rproc->priv; |
| 83 | struct device *dev = ethosu->dev; |
| 84 | |
| 85 | dev_info(dev, "Stopping Ethos-U subsystem CPU!"); |
| 86 | |
| 87 | return reset_control_assert(ethosu->rstc); |
| 88 | } |
| 89 | |
| 90 | static void ethosu_rproc_kick(struct rproc *rproc, |
| 91 | int vqid) |
| 92 | { |
| 93 | return; |
| 94 | } |
| 95 | |
Nir Ekhauz | 4823921 | 2021-11-23 10:28:34 +0200 | [diff] [blame] | 96 | #if LINUX_VERSION_CODE < KERNEL_VERSION(5, 13, 0) |
Per Åstrand | 2cd5397 | 2021-04-12 13:46:04 +0200 | [diff] [blame] | 97 | static void *ethosu_da_to_va(struct rproc *rproc, |
| 98 | u64 da, |
| 99 | int len) |
Nir Ekhauz | 4823921 | 2021-11-23 10:28:34 +0200 | [diff] [blame] | 100 | #else |
| 101 | static void *ethosu_da_to_va(struct rproc *rproc, |
| 102 | u64 da, |
| 103 | size_t len, |
| 104 | bool *is_iomem) |
| 105 | #endif |
Per Åstrand | 2cd5397 | 2021-04-12 13:46:04 +0200 | [diff] [blame] | 106 | { |
| 107 | struct ethosu_rproc *ethosu = (struct ethosu_rproc *)rproc->priv; |
| 108 | int offset; |
| 109 | int i; |
| 110 | |
| 111 | for (i = 0; i < ethosu->map_size; i++) |
| 112 | if (da >= ethosu->map[i].rproc_addr && |
| 113 | da < (ethosu->map[i].rproc_addr + ethosu->map[i].size)) { |
| 114 | offset = da - ethosu->map[i].rproc_addr; |
| 115 | dev_info(ethosu->dev, |
| 116 | "mapping %llx to %p (offset: 0x%x)", da, |
| 117 | (void *)(ethosu->map[i].vaddr + offset), |
| 118 | offset); |
| 119 | |
| 120 | return (void *)(ethosu->map[i].vaddr + offset); |
| 121 | } |
| 122 | |
| 123 | return NULL; |
| 124 | } |
| 125 | |
| 126 | static const struct rproc_ops ethosu_rproc_ops = { |
| 127 | .start = ðosu_rproc_start, |
| 128 | .stop = ðosu_rproc_stop, |
| 129 | .kick = ðosu_rproc_kick, |
| 130 | .da_to_va = ðosu_da_to_va, |
| 131 | }; |
| 132 | |
| 133 | /** |
| 134 | * Since the remote side doesn't yet support rpmsg just return an |
| 135 | * empty resource table when asked about it. |
| 136 | */ |
| 137 | struct resource_table *ethosu_rproc_find_rsc_table(struct rproc *rproc, |
| 138 | const struct firmware *fw, |
| 139 | int *tablesz) |
| 140 | { |
| 141 | static struct resource_table table = { .ver = 1, }; |
| 142 | struct ethosu_rproc *ethosu = (struct ethosu_rproc *)rproc->priv; |
| 143 | |
| 144 | dev_info(ethosu->dev, "Sizeof struct resource_table : %zu", |
| 145 | sizeof(table)); |
| 146 | *tablesz = sizeof(table); |
| 147 | |
| 148 | return &table; |
| 149 | } |
| 150 | |
| 151 | /*****************************************************************************/ |
| 152 | |
| 153 | static int ethosu_rproc_of_memory_translations(struct platform_device *pdev, |
| 154 | struct ethosu_rproc *ethosu_rproc) |
| 155 | { |
| 156 | const char *const of_rproc_address_cells = |
| 157 | "#ethosu,rproc-address-cells"; |
| 158 | const char *const of_rproc_ranges = "ethosu,rproc-ranges"; |
| 159 | const char *const of_rproc_ranges_names = "ethosu,rproc-names"; |
| 160 | |
| 161 | struct device *dev = &pdev->dev; |
| 162 | struct device_node *np = dev->of_node; |
| 163 | struct rproc_mem_mapping *mem_map; |
| 164 | const __be32 *rproc_ranges; |
| 165 | |
| 166 | int addr_cells, rproc_addr_cells, size_cells, cells_for_array_element; |
| 167 | int i, len, cnt, name_cnt, ret = 0; |
| 168 | |
| 169 | if (of_property_read_u32(np, of_rproc_address_cells, |
| 170 | &rproc_addr_cells)) { |
| 171 | dev_info(dev, "%s not defined in dtb", of_rproc_address_cells); |
| 172 | |
| 173 | return -ENODEV; |
| 174 | } |
| 175 | |
| 176 | addr_cells = of_n_addr_cells(np); |
| 177 | size_cells = of_n_size_cells(np); |
| 178 | |
| 179 | dev_dbg(dev, "Using %d remote proc address cells for parsing mapping", |
| 180 | rproc_addr_cells); |
| 181 | dev_dbg(dev, |
| 182 | "Using %d of size %d parent address cells for parsing mapping", |
| 183 | addr_cells, size_cells); |
| 184 | |
| 185 | cells_for_array_element = addr_cells + rproc_addr_cells + size_cells; |
| 186 | |
| 187 | cnt = of_property_count_elems_of_size(np, of_rproc_ranges, |
| 188 | cells_for_array_element); |
| 189 | cnt /= sizeof(u32); |
| 190 | |
| 191 | if (cnt <= 0) { |
| 192 | dev_info(dev, "No remoteproc memory mapping ranges found."); |
| 193 | |
| 194 | return 0; |
| 195 | } |
| 196 | |
| 197 | name_cnt = of_property_count_strings(np, of_rproc_ranges_names); |
| 198 | if (name_cnt > 0 && name_cnt != cnt) { |
| 199 | dev_err(dev, "Mismatch length for %s and %s", of_rproc_ranges, |
| 200 | of_rproc_ranges_names); |
| 201 | |
| 202 | return -EINVAL; |
| 203 | } |
| 204 | |
| 205 | mem_map = devm_kcalloc(dev, cnt, sizeof(*mem_map), GFP_KERNEL); |
| 206 | if (!mem_map) |
| 207 | return -ENOMEM; |
| 208 | |
| 209 | rproc_ranges = of_get_property(np, of_rproc_ranges, &len); |
| 210 | |
| 211 | for (i = 0; i < cnt; i++) { |
| 212 | struct resource *r; |
| 213 | const char *name = NULL; |
| 214 | int n; |
| 215 | |
| 216 | of_property_read_string_index(np, of_rproc_ranges_names, i, |
| 217 | &name); |
| 218 | mem_map[i].name = name; |
| 219 | n = i * cells_for_array_element; |
| 220 | mem_map[i].rproc_addr = |
| 221 | of_read_number(&rproc_ranges[n + addr_cells], |
| 222 | rproc_addr_cells); |
| 223 | mem_map[i].size = |
| 224 | of_read_number(&rproc_ranges[n + addr_cells + |
| 225 | rproc_addr_cells], |
| 226 | size_cells); |
| 227 | |
| 228 | r = platform_get_resource_byname(pdev, IORESOURCE_MEM, name); |
| 229 | if (!r) { |
| 230 | dev_err(&pdev->dev, "Failed to get '%s' resource.\n", |
| 231 | name); |
| 232 | |
| 233 | return -EINVAL; |
| 234 | } |
| 235 | |
| 236 | mem_map[i].vaddr = devm_ioremap_wc(dev, r->start, |
| 237 | mem_map[i].size); |
| 238 | if (IS_ERR(mem_map[i].vaddr)) { |
| 239 | dev_err(dev, "Failed to remap '%s'", name); |
| 240 | |
| 241 | return PTR_ERR(mem_map[i].vaddr); |
| 242 | } |
| 243 | |
| 244 | dev_dbg(dev, |
| 245 | "rproc memory mapping[%i]=%s: da %llx, va, %pa, size %zx:\n", |
| 246 | i, name, mem_map[i].rproc_addr, &mem_map[i].vaddr, |
| 247 | mem_map[i].size); |
| 248 | } |
| 249 | |
| 250 | ethosu_rproc->map = mem_map; |
| 251 | ethosu_rproc->map_size = cnt; |
| 252 | dev_dbg(dev, "rproc memory mapped %zx regions", ethosu_rproc->map_size); |
| 253 | |
| 254 | return ret; |
| 255 | } |
| 256 | |
| 257 | static const struct of_device_id ethosu_rproc_match[] = { |
Per Åstrand | 9f36f2e | 2021-09-30 09:57:34 +0200 | [diff] [blame] | 258 | { .compatible = "arm,ethosu-rproc" }, |
Per Åstrand | 2cd5397 | 2021-04-12 13:46:04 +0200 | [diff] [blame] | 259 | }; |
| 260 | |
| 261 | static int ethosu_rproc_probe(struct platform_device *pdev) |
| 262 | { |
| 263 | struct device *dev = &pdev->dev; |
| 264 | struct device_node *np = dev->of_node; |
| 265 | struct ethosu_rproc *ethosu_rproc; |
| 266 | struct rproc *rproc; |
| 267 | int ret = -ENODEV; |
| 268 | |
| 269 | rproc = rproc_alloc(dev, np->name, ðosu_rproc_ops, |
| 270 | fw_filename_param, |
| 271 | sizeof(*ethosu_rproc)); |
| 272 | if (!rproc) { |
| 273 | ret = -ENOMEM; |
| 274 | goto out; |
| 275 | } |
| 276 | |
| 277 | /* Configure rproc */ |
| 278 | rproc->has_iommu = false; |
| 279 | rproc->auto_boot = auto_boot; |
| 280 | |
| 281 | platform_set_drvdata(pdev, rproc); |
| 282 | |
| 283 | ethosu_rproc = rproc->priv; |
| 284 | ethosu_rproc->dev = dev; |
| 285 | |
| 286 | /* Get the reset handler for the subsystem */ |
| 287 | ethosu_rproc->rstc = devm_reset_control_get_exclusive_by_index(dev, 0); |
| 288 | if (IS_ERR(ethosu_rproc->rstc)) { |
| 289 | dev_err(&pdev->dev, "Failed to get reset controller.\n"); |
| 290 | ret = PTR_ERR(ethosu_rproc->rstc); |
| 291 | goto free_rproc; |
| 292 | } |
| 293 | |
| 294 | /* Get the translation from device memory to kernel space */ |
| 295 | ret = ethosu_rproc_of_memory_translations(pdev, ethosu_rproc); |
| 296 | if (ret) |
| 297 | goto free_rproc; |
| 298 | |
| 299 | ret = rproc_add(rproc); |
| 300 | |
| 301 | free_rproc: |
| 302 | if (ret) |
| 303 | rproc_free(rproc); |
| 304 | |
| 305 | out: |
| 306 | |
| 307 | return ret; |
| 308 | } |
| 309 | |
| 310 | static int ethosu_rproc_remove(struct platform_device *pdev) |
| 311 | { |
| 312 | struct rproc *rproc = platform_get_drvdata(pdev); |
| 313 | |
| 314 | rproc_del(rproc); |
| 315 | rproc_free(rproc); |
| 316 | |
| 317 | return 0; |
| 318 | } |
| 319 | |
| 320 | static struct platform_driver ethosu_rproc_driver = { |
| 321 | .probe = ethosu_rproc_probe, |
| 322 | .remove = ethosu_rproc_remove, |
| 323 | .driver = { |
| 324 | .name = "ethosu-rproc", |
| 325 | .of_match_table = of_match_ptr(ethosu_rproc_match), |
| 326 | }, |
| 327 | }; |
| 328 | |
| 329 | module_platform_driver(ethosu_rproc_driver); |
| 330 | |
| 331 | MODULE_LICENSE("GPL v2"); |
| 332 | MODULE_AUTHOR("Arm Ltd"); |
| 333 | MODULE_DESCRIPTION("Arm Ethos-U NPU RemoteProc Driver"); |
| 334 | MODULE_VERSION(ETHOSU_RPROC_DRIVER_VERSION); |