1 // SPDX-License-Identifier: GPL-2.0-only 2 /* 3 * Copyright (C) 2017 Linaro Ltd. 4 */ 5 6 #include <linux/device.h> 7 #include <linux/firmware.h> 8 #include <linux/kernel.h> 9 #include <linux/iommu.h> 10 #include <linux/io.h> 11 #include <linux/of.h> 12 #include <linux/of_address.h> 13 #include <linux/platform_device.h> 14 #include <linux/of_device.h> 15 #include <linux/qcom_scm.h> 16 #include <linux/sizes.h> 17 #include <linux/soc/qcom/mdt_loader.h> 18 19 #include "core.h" 20 #include "firmware.h" 21 #include "hfi_venus_io.h" 22 23 #define VENUS_PAS_ID 9 24 #define VENUS_FW_MEM_SIZE (6 * SZ_1M) 25 #define VENUS_FW_START_ADDR 0x0 26 27 static void venus_reset_cpu(struct venus_core *core) 28 { 29 u32 fw_size = core->fw.mapped_mem_size; 30 void __iomem *base = core->base; 31 32 writel(0, base + WRAPPER_FW_START_ADDR); 33 writel(fw_size, base + WRAPPER_FW_END_ADDR); 34 writel(0, base + WRAPPER_CPA_START_ADDR); 35 writel(fw_size, base + WRAPPER_CPA_END_ADDR); 36 writel(fw_size, base + WRAPPER_NONPIX_START_ADDR); 37 writel(fw_size, base + WRAPPER_NONPIX_END_ADDR); 38 writel(0x0, base + WRAPPER_CPU_CGC_DIS); 39 writel(0x0, base + WRAPPER_CPU_CLOCK_CONFIG); 40 41 /* Bring ARM9 out of reset */ 42 writel(0, base + WRAPPER_A9SS_SW_RESET); 43 } 44 45 int venus_set_hw_state(struct venus_core *core, bool resume) 46 { 47 if (core->use_tz) 48 return qcom_scm_set_remote_state(resume, 0); 49 50 if (resume) 51 venus_reset_cpu(core); 52 else 53 writel(1, core->base + WRAPPER_A9SS_SW_RESET); 54 55 return 0; 56 } 57 58 static int venus_load_fw(struct venus_core *core, const char *fwname, 59 phys_addr_t *mem_phys, size_t *mem_size) 60 { 61 const struct firmware *mdt; 62 struct device_node *node; 63 struct device *dev; 64 struct resource r; 65 ssize_t fw_size; 66 void *mem_va; 67 int ret; 68 69 *mem_phys = 0; 70 *mem_size = 0; 71 72 dev = core->dev; 73 node = of_parse_phandle(dev->of_node, "memory-region", 0); 74 if (!node) { 75 dev_err(dev, "no memory-region specified\n"); 76 return -EINVAL; 77 } 78 79 ret = of_address_to_resource(node, 0, &r); 80 if (ret) 81 return ret; 82 83 ret = request_firmware(&mdt, fwname, dev); 84 if (ret < 0) 85 return ret; 86 87 fw_size = qcom_mdt_get_size(mdt); 88 if (fw_size < 0) { 89 ret = fw_size; 90 goto err_release_fw; 91 } 92 93 *mem_phys = r.start; 94 *mem_size = resource_size(&r); 95 96 if (*mem_size < fw_size || fw_size > VENUS_FW_MEM_SIZE) { 97 ret = -EINVAL; 98 goto err_release_fw; 99 } 100 101 mem_va = memremap(r.start, *mem_size, MEMREMAP_WC); 102 if (!mem_va) { 103 dev_err(dev, "unable to map memory region: %pa+%zx\n", 104 &r.start, *mem_size); 105 ret = -ENOMEM; 106 goto err_release_fw; 107 } 108 109 if (core->use_tz) 110 ret = qcom_mdt_load(dev, mdt, fwname, VENUS_PAS_ID, 111 mem_va, *mem_phys, *mem_size, NULL); 112 else 113 ret = qcom_mdt_load_no_init(dev, mdt, fwname, VENUS_PAS_ID, 114 mem_va, *mem_phys, *mem_size, NULL); 115 116 memunmap(mem_va); 117 err_release_fw: 118 release_firmware(mdt); 119 return ret; 120 } 121 122 static int venus_boot_no_tz(struct venus_core *core, phys_addr_t mem_phys, 123 size_t mem_size) 124 { 125 struct iommu_domain *iommu; 126 struct device *dev; 127 int ret; 128 129 dev = core->fw.dev; 130 if (!dev) 131 return -EPROBE_DEFER; 132 133 iommu = core->fw.iommu_domain; 134 core->fw.mapped_mem_size = mem_size; 135 136 ret = iommu_map(iommu, VENUS_FW_START_ADDR, mem_phys, mem_size, 137 IOMMU_READ | IOMMU_WRITE | IOMMU_PRIV); 138 if (ret) { 139 dev_err(dev, "could not map video firmware region\n"); 140 return ret; 141 } 142 143 venus_reset_cpu(core); 144 145 return 0; 146 } 147 148 static int venus_shutdown_no_tz(struct venus_core *core) 149 { 150 const size_t mapped = core->fw.mapped_mem_size; 151 struct iommu_domain *iommu; 152 size_t unmapped; 153 u32 reg; 154 struct device *dev = core->fw.dev; 155 void __iomem *base = core->base; 156 157 /* Assert the reset to ARM9 */ 158 reg = readl_relaxed(base + WRAPPER_A9SS_SW_RESET); 159 reg |= WRAPPER_A9SS_SW_RESET_BIT; 160 writel_relaxed(reg, base + WRAPPER_A9SS_SW_RESET); 161 162 /* Make sure reset is asserted before the mapping is removed */ 163 mb(); 164 165 iommu = core->fw.iommu_domain; 166 167 unmapped = iommu_unmap(iommu, VENUS_FW_START_ADDR, mapped); 168 if (unmapped != mapped) 169 dev_err(dev, "failed to unmap firmware\n"); 170 171 return 0; 172 } 173 174 int venus_boot(struct venus_core *core) 175 { 176 struct device *dev = core->dev; 177 phys_addr_t mem_phys; 178 size_t mem_size; 179 int ret; 180 181 if (!IS_ENABLED(CONFIG_QCOM_MDT_LOADER) || 182 (core->use_tz && !qcom_scm_is_available())) 183 return -EPROBE_DEFER; 184 185 ret = venus_load_fw(core, core->res->fwname, &mem_phys, &mem_size); 186 if (ret) { 187 dev_err(dev, "fail to load video firmware\n"); 188 return -EINVAL; 189 } 190 191 if (core->use_tz) 192 ret = qcom_scm_pas_auth_and_reset(VENUS_PAS_ID); 193 else 194 ret = venus_boot_no_tz(core, mem_phys, mem_size); 195 196 return ret; 197 } 198 199 int venus_shutdown(struct venus_core *core) 200 { 201 int ret; 202 203 if (core->use_tz) 204 ret = qcom_scm_pas_shutdown(VENUS_PAS_ID); 205 else 206 ret = venus_shutdown_no_tz(core); 207 208 return ret; 209 } 210 211 int venus_firmware_init(struct venus_core *core) 212 { 213 struct platform_device_info info; 214 struct iommu_domain *iommu_dom; 215 struct platform_device *pdev; 216 struct device_node *np; 217 int ret; 218 219 np = of_get_child_by_name(core->dev->of_node, "video-firmware"); 220 if (!np) { 221 core->use_tz = true; 222 return 0; 223 } 224 225 memset(&info, 0, sizeof(info)); 226 info.fwnode = &np->fwnode; 227 info.parent = core->dev; 228 info.name = np->name; 229 info.dma_mask = DMA_BIT_MASK(32); 230 231 pdev = platform_device_register_full(&info); 232 if (IS_ERR(pdev)) { 233 of_node_put(np); 234 return PTR_ERR(pdev); 235 } 236 237 pdev->dev.of_node = np; 238 239 ret = of_dma_configure(&pdev->dev, np, true); 240 if (ret) { 241 dev_err(core->dev, "dma configure fail\n"); 242 goto err_unregister; 243 } 244 245 core->fw.dev = &pdev->dev; 246 247 iommu_dom = iommu_domain_alloc(&platform_bus_type); 248 if (!iommu_dom) { 249 dev_err(core->fw.dev, "Failed to allocate iommu domain\n"); 250 ret = -ENOMEM; 251 goto err_unregister; 252 } 253 254 ret = iommu_attach_device(iommu_dom, core->fw.dev); 255 if (ret) { 256 dev_err(core->fw.dev, "could not attach device\n"); 257 goto err_iommu_free; 258 } 259 260 core->fw.iommu_domain = iommu_dom; 261 262 of_node_put(np); 263 264 return 0; 265 266 err_iommu_free: 267 iommu_domain_free(iommu_dom); 268 err_unregister: 269 platform_device_unregister(pdev); 270 of_node_put(np); 271 return ret; 272 } 273 274 void venus_firmware_deinit(struct venus_core *core) 275 { 276 struct iommu_domain *iommu; 277 278 if (!core->fw.dev) 279 return; 280 281 iommu = core->fw.iommu_domain; 282 283 iommu_detach_device(iommu, core->fw.dev); 284 iommu_domain_free(iommu); 285 286 platform_device_unregister(to_platform_device(core->fw.dev)); 287 } 288