| /* |
| * Samsung Exynos SoC series VIPx driver |
| * |
| * Copyright (c) 2018 Samsung Electronics Co., Ltd |
| * |
| * This program is free software; you can redistribute it and/or modify |
| * it under the terms of the GNU General Public License version 2 as |
| * published by the Free Software Foundation. |
| */ |
| |
| #include <linux/io.h> |
| #include <linux/exynos_iovmm.h> |
| |
| #include "vipx-log.h" |
| #include "vipx-device.h" |
| #include "vipx-system.h" |
| |
| int vipx_system_fw_bootup(struct vipx_system *sys) |
| { |
| int ret; |
| struct vipx_memory *mem; |
| struct vipx_binary *bin; |
| |
| struct { |
| unsigned int mbox_addr; |
| unsigned int mbox_size; |
| unsigned int heap_addr; |
| unsigned int heap_size; |
| unsigned int log_addr; |
| unsigned int log_size; |
| } *shared_mem; |
| |
| vipx_enter(); |
| mem = &sys->memory; |
| bin = &sys->binary; |
| |
| sys->ctrl_ops->reset(sys); |
| |
| ret = vipx_binary_firmware_load(bin, VIPX_FW_DRAM_NAME, mem->fw.kvaddr, |
| mem->fw.size); |
| if (ret) |
| goto p_err; |
| |
| ret = vipx_binary_firmware_load(bin, VIPX_FW_DTCM_NAME, sys->dtcm, |
| sys->dtcm_size); |
| if (ret) |
| goto p_err; |
| |
| shared_mem = sys->dtcm + 0x3FD0; |
| shared_mem->mbox_addr = mem->mbox.dvaddr; |
| shared_mem->mbox_size = mem->mbox.size; |
| shared_mem->heap_addr = mem->heap.dvaddr; |
| shared_mem->heap_size = mem->heap.size; |
| shared_mem->log_addr = mem->log.dvaddr; |
| shared_mem->log_size = mem->log.size; |
| |
| ret = vipx_binary_firmware_load(bin, VIPX_FW_ITCM_NAME, sys->itcm, |
| sys->itcm_size); |
| if (ret) |
| goto p_err; |
| |
| sys->ctrl_ops->start(sys); |
| |
| ret = vipx_hw_wait_bootup(&sys->interface); |
| if (ret) |
| goto p_err; |
| |
| vipx_pm_request_idle(&sys->pm); |
| vipx_leave(); |
| return 0; |
| p_err: |
| return ret; |
| } |
| |
| int vipx_system_start(struct vipx_system *sys) |
| { |
| int ret; |
| |
| vipx_enter(); |
| ret = vipx_interface_start(&sys->interface); |
| if (ret) |
| goto p_err; |
| |
| vipx_leave(); |
| p_err: |
| return ret; |
| } |
| |
| int vipx_system_stop(struct vipx_system *sys) |
| { |
| int ret; |
| |
| vipx_enter(); |
| ret = vipx_interface_stop(&sys->interface); |
| if (ret) |
| goto p_err; |
| |
| vipx_leave(); |
| p_err: |
| return ret; |
| } |
| |
| int vipx_system_resume(struct vipx_system *sys) |
| { |
| int ret; |
| |
| vipx_enter(); |
| if (vipx_pm_qos_active(&sys->pm)) { |
| vipx_pm_qos_resume(&sys->pm); |
| ret = vipx_system_fw_bootup(sys); |
| if (ret) |
| goto p_err; |
| } |
| |
| vipx_leave(); |
| return 0; |
| p_err: |
| return ret; |
| } |
| |
| int vipx_system_suspend(struct vipx_system *sys) |
| { |
| vipx_enter(); |
| if (vipx_pm_qos_active(&sys->pm)) { |
| sys->ctrl_ops->reset(sys); |
| vipx_pm_qos_suspend(&sys->pm); |
| } |
| |
| vipx_leave(); |
| return 0; |
| } |
| |
| int vipx_system_runtime_resume(struct vipx_system *sys) |
| { |
| int ret; |
| |
| vipx_enter(); |
| vipx_pm_open(&sys->pm); |
| |
| ret = sys->clk_ops->on(sys); |
| if (ret) |
| goto p_err_clk_on; |
| |
| sys->clk_ops->dump(sys); |
| |
| ret = iovmm_activate(sys->dev); |
| if (ret) { |
| vipx_err("Failed to activate iommu (%d)\n", ret); |
| goto p_err_iovmm; |
| } |
| |
| vipx_leave(); |
| return 0; |
| p_err_iovmm: |
| sys->clk_ops->off(sys); |
| p_err_clk_on: |
| vipx_pm_close(&sys->pm); |
| return ret; |
| } |
| |
| int vipx_system_runtime_suspend(struct vipx_system *sys) |
| { |
| vipx_enter(); |
| sys->ctrl_ops->hex_dump(sys); |
| sys->ctrl_ops->reset(sys); |
| sys->ctrl_ops->hex_dump(sys); |
| iovmm_deactivate(sys->dev); |
| sys->clk_ops->off(sys); |
| vipx_pm_close(&sys->pm); |
| |
| vipx_leave(); |
| return 0; |
| } |
| |
| int vipx_system_open(struct vipx_system *sys) |
| { |
| int ret; |
| |
| vipx_enter(); |
| ret = vipx_memory_open(&sys->memory); |
| if (ret) |
| goto p_err_memory; |
| |
| ret = vipx_interface_open(&sys->interface, |
| sys->memory.mbox.kvaddr); |
| if (ret) |
| goto p_err_interface; |
| |
| ret = vipx_graphmgr_open(&sys->graphmgr); |
| if (ret) |
| goto p_err_graphmgr; |
| |
| vipx_leave(); |
| return 0; |
| p_err_graphmgr: |
| vipx_interface_close(&sys->interface); |
| p_err_interface: |
| vipx_memory_close(&sys->memory); |
| p_err_memory: |
| return ret; |
| } |
| |
| int vipx_system_close(struct vipx_system *sys) |
| { |
| vipx_enter(); |
| vipx_graphmgr_close(&sys->graphmgr); |
| vipx_interface_close(&sys->interface); |
| vipx_memory_close(&sys->memory); |
| vipx_leave(); |
| return 0; |
| } |
| |
| int vipx_system_probe(struct vipx_device *device) |
| { |
| int ret; |
| struct vipx_system *sys; |
| struct platform_device *pdev; |
| struct device *dev; |
| struct resource *res; |
| void __iomem *iomem; |
| |
| vipx_enter(); |
| sys = &device->system; |
| sys->device = device; |
| |
| pdev = to_platform_device(device->dev); |
| dev = device->dev; |
| sys->dev = dev; |
| |
| /* VIPX_CPU_SS1 */ |
| res = platform_get_resource(pdev, IORESOURCE_MEM, 0); |
| if (!res) { |
| ret = -EINVAL; |
| vipx_err("platform_get_resource(0) is fail"); |
| goto p_err_res_ss1; |
| } |
| |
| iomem = devm_ioremap_resource(dev, res); |
| if (IS_ERR(iomem)) { |
| ret = PTR_ERR(iomem); |
| vipx_err("devm_ioremap_resource(0) is fail (%d)", ret); |
| goto p_err_remap_ss1; |
| } |
| |
| sys->reg_ss[REG_SS1] = iomem; |
| sys->reg_ss_size[REG_SS1] = resource_size(res); |
| |
| /* VIPX_CPU_SS2 */ |
| res = platform_get_resource(pdev, IORESOURCE_MEM, 1); |
| if (!res) { |
| ret = -EINVAL; |
| vipx_err("platform_get_resource(1) is fail"); |
| goto p_err_res_ss2; |
| } |
| |
| iomem = devm_ioremap_resource(dev, res); |
| if (IS_ERR(iomem)) { |
| ret = PTR_ERR(iomem); |
| vipx_err("devm_ioremap_resource(1) is fail (%d)", ret); |
| goto p_err_remap_ss2; |
| } |
| |
| sys->reg_ss[REG_SS2] = iomem; |
| sys->reg_ss_size[REG_SS2] = resource_size(res); |
| |
| /* VIPX ITCM */ |
| res = platform_get_resource(pdev, IORESOURCE_MEM, 2); |
| if (!res) { |
| ret = -EINVAL; |
| vipx_err("platform_get_resource(2) is fail"); |
| goto p_err_res_itcm; |
| } |
| |
| iomem = devm_ioremap_resource(dev, res); |
| if (IS_ERR(iomem)) { |
| ret = PTR_ERR(iomem); |
| vipx_err("devm_ioremap_resource(2) is fail (%d)", ret); |
| goto p_err_remap_itcm; |
| } |
| |
| sys->itcm = iomem; |
| sys->itcm_size = resource_size(res); |
| |
| /* VIPX DTCM */ |
| res = platform_get_resource(pdev, IORESOURCE_MEM, 3); |
| if (!res) { |
| ret = -EINVAL; |
| vipx_err("platform_get_resource(3) is fail"); |
| goto p_err_res_dtcm; |
| } |
| |
| iomem = devm_ioremap_resource(dev, res); |
| if (IS_ERR(iomem)) { |
| ret = PTR_ERR(iomem); |
| vipx_err("devm_ioremap_resource(3) is fail (%d)", ret); |
| goto p_err_remap_dtcm; |
| } |
| |
| sys->dtcm = iomem; |
| sys->dtcm_size = resource_size(res); |
| |
| /* SYSREG_VIPX1 */ |
| res = platform_get_resource(pdev, IORESOURCE_MEM, 4); |
| if (!res) { |
| ret = -EINVAL; |
| vipx_err("platform_get_resource(4) is fail"); |
| goto p_err_res_sysreg1; |
| } |
| |
| iomem = devm_ioremap_resource(dev, res); |
| if (IS_ERR(iomem)) { |
| ret = PTR_ERR(iomem); |
| vipx_err("devm_ioremap_resource(4) is fail (%d)", ret); |
| goto p_err_remap_sysreg1; |
| } |
| |
| sys->sysreg1 = iomem; |
| sys->sysreg1_size = resource_size(res); |
| |
| /* SYSREG_VIPX2 */ |
| res = platform_get_resource(pdev, IORESOURCE_MEM, 5); |
| if (!res) { |
| ret = -EINVAL; |
| vipx_err("platform_get_resource(5) is fail"); |
| goto p_err_res_sysreg2; |
| } |
| |
| iomem = devm_ioremap_resource(dev, res); |
| if (IS_ERR(iomem)) { |
| ret = PTR_ERR(iomem); |
| vipx_err("devm_ioremap_resource(5) is fail (%d)", ret); |
| goto p_err_remap_sysreg2; |
| } |
| |
| sys->sysreg2 = iomem; |
| sys->sysreg2_size = resource_size(res); |
| |
| sys->clk_ops = &vipx_clk_ops; |
| sys->ctrl_ops = &vipx_ctrl_ops; |
| sys->pinctrl = devm_pinctrl_get(dev); |
| if (IS_ERR(sys->pinctrl)) { |
| ret = PTR_ERR(sys->pinctrl); |
| vipx_err("Failed to get devm pinctrl (%d)\n", ret); |
| goto p_err_pinctrl; |
| } |
| |
| ret = vipx_pm_probe(sys); |
| if (ret) |
| goto p_err_pm; |
| |
| ret = sys->clk_ops->init(sys); |
| if (ret) |
| goto p_err_clk; |
| |
| ret = vipx_memory_probe(sys); |
| if (ret) |
| goto p_err_memory; |
| |
| ret = vipx_interface_probe(sys); |
| if (ret) |
| goto p_err_interface; |
| |
| ret = vipx_binary_init(sys); |
| if (ret) |
| goto p_err_binary; |
| |
| ret = vipx_graphmgr_probe(sys); |
| if (ret) |
| goto p_err_graphmgr; |
| |
| vipx_leave(); |
| return 0; |
| p_err_graphmgr: |
| vipx_binary_deinit(&sys->binary); |
| p_err_binary: |
| vipx_interface_remove(&sys->interface); |
| p_err_interface: |
| vipx_memory_remove(&sys->memory); |
| p_err_memory: |
| sys->clk_ops->deinit(sys); |
| p_err_clk: |
| vipx_pm_remove(&sys->pm); |
| p_err_pm: |
| devm_pinctrl_put(sys->pinctrl); |
| p_err_pinctrl: |
| devm_iounmap(dev, sys->sysreg2); |
| p_err_remap_sysreg2: |
| p_err_res_sysreg2: |
| devm_iounmap(dev, sys->sysreg1); |
| p_err_remap_sysreg1: |
| p_err_res_sysreg1: |
| devm_iounmap(dev, sys->dtcm); |
| p_err_remap_dtcm: |
| p_err_res_dtcm: |
| devm_iounmap(dev, sys->itcm); |
| p_err_remap_itcm: |
| p_err_res_itcm: |
| devm_iounmap(dev, sys->reg_ss[REG_SS2]); |
| p_err_remap_ss2: |
| p_err_res_ss2: |
| devm_iounmap(dev, sys->reg_ss[REG_SS1]); |
| p_err_remap_ss1: |
| p_err_res_ss1: |
| return ret; |
| } |
| |
| void vipx_system_remove(struct vipx_system *sys) |
| { |
| vipx_enter(); |
| vipx_graphmgr_remove(&sys->graphmgr); |
| vipx_binary_deinit(&sys->binary); |
| vipx_interface_remove(&sys->interface); |
| vipx_memory_remove(&sys->memory); |
| sys->clk_ops->deinit(sys); |
| vipx_pm_remove(&sys->pm); |
| devm_pinctrl_put(sys->pinctrl); |
| devm_iounmap(sys->dev, sys->sysreg2); |
| devm_iounmap(sys->dev, sys->sysreg1); |
| devm_iounmap(sys->dev, sys->dtcm); |
| devm_iounmap(sys->dev, sys->itcm); |
| devm_iounmap(sys->dev, sys->reg_ss[REG_SS2]); |
| devm_iounmap(sys->dev, sys->reg_ss[REG_SS1]); |
| vipx_leave(); |
| } |