/* * Copyright (c) 2015-2021 NVIDIA CORPORATION. All rights reserved. * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU General Public License, * version 2, as published by the Free Software Foundation. * * This program is distributed in the hope it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "rtcpu/clk-group.h" #include "rtcpu/device-group.h" #include "rtcpu/reset-group.h" #include "soc/tegra/camrtc-commands.h" #include "soc/tegra/camrtc-ctrl-commands.h" #include #ifndef RTCPU_DRIVER_SM5_VERSION #define RTCPU_DRIVER_SM5_VERSION U32_C(5) #endif #if defined(LINUX_VERSION) && (LINUX_VERSION < 409) #define DISABLE_APE_RUNTIME_PM 1 #else #define DISABLE_APE_RUNTIME_PM 0 #endif enum tegra_cam_rtcpu_id { TEGRA_CAM_RTCPU_SCE, TEGRA_CAM_RTCPU_APE, TEGRA_CAM_RTCPU_RCE, }; #define CAMRTC_NUM_REGS 2 #define CAMRTC_NUM_RESETS 2 #define CAMRTC_NUM_IRQS 1 struct tegra_cam_rtcpu_pdata { const char *name; void (*assert_resets)(struct device *); int (*deassert_resets)(struct device *); int (*suspend_core)(struct device *); const char * const *reset_names; const char * const *reg_names; const char * const *irq_names; enum tegra_cam_rtcpu_id id; }; /* Register specifics */ #define TEGRA_APS_FRSC_SC_CTL_0 0x0 #define TEGRA_APS_FRSC_SC_MODEIN_0 0x14 #define TEGRA_PM_R5_CTRL_0 0x40 #define TEGRA_PM_PWR_STATUS_0 0x20 #define TEGRA_R5R_SC_DISABLE 0x5 #define TEGRA_FN_MODEIN 0x29527 #define TEGRA_PM_FWLOADDONE 0x2 #define TEGRA_PM_WFIPIPESTOPPED 0x200000 #define AMISC_ADSP_STATUS 0x14 #define AMISC_ADSP_L2_IDLE BIT(31) #define AMISC_ADSP_L2_CLKSTOPPED BIT(30) static int tegra_sce_cam_suspend_core(struct device *dev); static void tegra_sce_cam_assert_resets(struct device *dev); static int tegra_sce_cam_deassert_resets(struct device *dev); static int tegra_ape_cam_suspend_core(struct device *dev); static irqreturn_t tegra_camrtc_adsp_wfi_handler(int irq, void *data); static void tegra_ape_cam_assert_resets(struct device *dev); static int tegra_ape_cam_deassert_resets(struct device *dev); static void tegra_rce_cam_assert_resets(struct device *dev); static int tegra_rce_cam_deassert_resets(struct device *dev); static const char * const sce_reset_names[] = { "nvidia,reset-group-1", "nvidia,reset-group-2", NULL, }; static const char * const sce_reg_names[] = { "sce-pm", "sce-cfg", NULL }; static const struct tegra_cam_rtcpu_pdata sce_pdata = { .name = "sce", .suspend_core = tegra_sce_cam_suspend_core, .assert_resets = tegra_sce_cam_assert_resets, .deassert_resets = tegra_sce_cam_deassert_resets, .id = TEGRA_CAM_RTCPU_SCE, .reset_names = sce_reset_names, .reg_names = sce_reg_names, }; static const char * const ape_reg_names[] = { "ape-amisc", NULL, }; static const char * const ape_reset_names[] = { "reset-names", /* all named resets */ NULL, }; static const char * const ape_irq_names[] = { "adsp-wfi", NULL }; static const struct tegra_cam_rtcpu_pdata ape_pdata = { .name = "ape", .assert_resets = tegra_ape_cam_assert_resets, .deassert_resets = tegra_ape_cam_deassert_resets, .suspend_core = tegra_ape_cam_suspend_core, .id = TEGRA_CAM_RTCPU_APE, .reset_names = ape_reset_names, .reg_names = ape_reg_names, .irq_names = ape_irq_names, }; static const char * const rce_reset_names[] = { "reset-names", /* all named resets */ NULL, }; /* SCE and RCE share the PM regs */ static const char * const rce_reg_names[] = { "rce-pm", NULL, }; static const struct tegra_cam_rtcpu_pdata rce_pdata = { .name = "rce", .suspend_core = tegra_sce_cam_suspend_core, .assert_resets = tegra_rce_cam_assert_resets, .deassert_resets = tegra_rce_cam_deassert_resets, .id = TEGRA_CAM_RTCPU_RCE, .reset_names = rce_reset_names, .reg_names = rce_reg_names, }; #define NV(p) "nvidia," #p struct tegra_cam_rtcpu { const char *name; struct tegra_ivc_bus *ivc; struct device_dma_parameters dma_parms; struct device *hsp_device; struct tegra_hsp_sm_pair *sm_pair; struct tegra_rtcpu_trace *tracer; struct tegra_rtcpu_coverage *coverage; struct { struct mutex mutex; wait_queue_head_t response_waitq; wait_queue_head_t empty_waitq; atomic_t response; atomic_t emptied; u32 timeout; } cmd; u32 fw_version; u8 fw_hash[RTCPU_FW_HASH_SIZE]; struct { u64 reset_complete; u64 boot_handshake; } stats; union { void __iomem *regs[CAMRTC_NUM_REGS]; struct { void __iomem *pm_base; void __iomem *cfg_base; }; struct { void __iomem *amisc_base; }; }; struct camrtc_clk_group *clocks; struct camrtc_reset_group *resets[CAMRTC_NUM_RESETS]; union { int irqs[CAMRTC_NUM_IRQS]; struct { int adsp_wfi_irq; }; }; const struct tegra_cam_rtcpu_pdata *pdata; struct camrtc_device_group *camera_devices; struct tegra_bwmgr_client *bwmgr; struct tegra_camrtc_mon *monitor; unsigned long full_bw; u32 max_reboot_retry; bool powered; bool boot_sync_done; bool online; }; static int tegra_camrtc_mbox_exchange(struct device *dev, u32 command, long *timeout); static void __iomem *tegra_cam_ioremap(struct device *dev, int index) { struct resource mem; int err = of_address_to_resource(dev->of_node, index, &mem); if (err) return IOMEM_ERR_PTR(err); /* NOTE: assumes size is large enough for caller */ return devm_ioremap_resource(dev, &mem); } static void __iomem *tegra_cam_ioremap_byname(struct device *dev, const char *name) { int index = of_property_match_string(dev->of_node, "reg-names", name); if (index < 0) return IOMEM_ERR_PTR(-ENOENT); return tegra_cam_ioremap(dev, index); } static int tegra_camrtc_get_resources(struct device *dev) { struct tegra_cam_rtcpu *rtcpu = dev_get_drvdata(dev); const struct tegra_cam_rtcpu_pdata *pdata = rtcpu->pdata; struct camrtc_device_group *devgrp; int i, err; rtcpu->clocks = camrtc_clk_group_get(dev); if (IS_ERR(rtcpu->clocks)) { err = PTR_ERR(rtcpu->clocks); if (err == -EPROBE_DEFER) dev_info(dev, "defer %s probe because of %s\n", rtcpu->name, "clocks"); else dev_warn(dev, "clocks not available: %d\n", err); return err; } devgrp = camrtc_device_group_get(dev, "nvidia,camera-devices", "nvidia,camera-device-names"); if (!IS_ERR(devgrp)) { rtcpu->camera_devices = devgrp; } else { err = PTR_ERR(devgrp); if (err == -EPROBE_DEFER) return err; if (err != -ENODATA && err != -ENOENT) dev_warn(dev, "get %s: failed: %d\n", "nvidia,camera-devices", err); } #define GET_RESOURCES(_res_, _get_, _null_, _toerr) \ for (i = 0; i < ARRAY_SIZE(rtcpu->_res_##s); i++) { \ if (!pdata->_res_##_names[i]) \ break; \ rtcpu->_res_##s[i] = _get_(dev, pdata->_res_##_names[i]); \ err = _toerr(rtcpu->_res_##s[i]); \ if (err == 0) \ continue; \ rtcpu->_res_##s[i] = _null_; \ if (err == -EPROBE_DEFER) { \ dev_info(dev, "defer %s probe because %s %s\n", \ rtcpu->name, #_res_, pdata->_res_##_names[i]); \ return err; \ } \ if (err != -ENODATA && err != -ENOENT) \ dev_warn(dev, "%s %s not available: %d\n", #_res_, \ pdata->_res_##_names[i], err); \ } #define _PTR2ERR(x) (IS_ERR(x) ? PTR_ERR(x) : 0) GET_RESOURCES(reset, camrtc_reset_group_get, NULL, _PTR2ERR); GET_RESOURCES(reg, tegra_cam_ioremap_byname, NULL, _PTR2ERR); #undef _PTR2ERR if (rtcpu->resets[0] == NULL) { struct camrtc_reset_group *resets; resets = camrtc_reset_group_get(dev, NULL); if (!IS_ERR(resets)) rtcpu->resets[0] = resets; else if (PTR_ERR(resets) == -EPROBE_DEFER) { dev_info(dev, "defer %s probe because of %s\n", rtcpu->name, "resets"); return -EPROBE_DEFER; } } return 0; } static int tegra_camrtc_get_irqs(struct device *dev) { struct tegra_cam_rtcpu *rtcpu = dev_get_drvdata(dev); const struct tegra_cam_rtcpu_pdata *pdata = rtcpu->pdata; int i, err; /* * AGIC can be touched only after APE is fully powered on. * * This can be called only after runtime resume. */ if (pdata->irq_names == NULL) return 0; #define _get_irq(_dev, _name) of_irq_get_byname(_dev->of_node, _name) #define _int2err(x) ((x) < 0 ? (x) : 0) GET_RESOURCES(irq, _get_irq, 0, _int2err); #undef _get_irq #undef _int2err return 0; } static int tegra_camrtc_enable_clks(struct device *dev) { struct tegra_cam_rtcpu *rtcpu = dev_get_drvdata(dev); return camrtc_clk_group_enable(rtcpu->clocks); } static void tegra_camrtc_disable_clks(struct device *dev) { struct tegra_cam_rtcpu *rtcpu = dev_get_drvdata(dev); return camrtc_clk_group_disable(rtcpu->clocks); } static void tegra_camrtc_assert_resets(struct device *dev) { struct tegra_cam_rtcpu *rtcpu = dev_get_drvdata(dev); if (rtcpu->pdata->assert_resets) rtcpu->pdata->assert_resets(dev); } static int tegra_camrtc_deassert_resets(struct device *dev) { struct tegra_cam_rtcpu *rtcpu = dev_get_drvdata(dev); int ret = 0; if (rtcpu->pdata->deassert_resets) { ret = rtcpu->pdata->deassert_resets(dev); rtcpu->stats.reset_complete = ktime_get_ns(); rtcpu->stats.boot_handshake = 0; } return ret; } static void tegra_camrtc_init_bwmgr(struct device *dev) { struct tegra_cam_rtcpu *rtcpu = dev_get_drvdata(dev); u32 bw; if (of_property_read_u32(dev->of_node, NV(memory-bw), &bw) != 0) return; if (bw == 0xFFFFFFFFU) rtcpu->full_bw = tegra_bwmgr_get_max_emc_rate(); else rtcpu->full_bw = tegra_bwmgr_round_rate(bw); rtcpu->bwmgr = tegra_bwmgr_register(TEGRA_BWMGR_CLIENT_CAMRTC); if (IS_ERR_OR_NULL(rtcpu->bwmgr)) { dev_warn(dev, "no memory bw manager\n"); rtcpu->bwmgr = NULL; return; } dev_dbg(dev, "using emc rate %lu for power-on\n", rtcpu->full_bw); } static void tegra_camrtc_full_mem_bw(struct device *dev) { struct tegra_cam_rtcpu *rtcpu = dev_get_drvdata(dev); if (rtcpu->bwmgr != NULL) { int ret = tegra_bwmgr_set_emc(rtcpu->bwmgr, rtcpu->full_bw, TEGRA_BWMGR_SET_EMC_FLOOR); if (ret < 0) dev_info(dev, "emc request rate %lu failed, %d\n", rtcpu->full_bw, ret); else dev_dbg(dev, "requested emc rate %lu\n", rtcpu->full_bw); } } static void tegra_camrtc_slow_mem_bw(struct device *dev) { struct tegra_cam_rtcpu *rtcpu = dev_get_drvdata(dev); if (rtcpu->bwmgr != NULL) (void)tegra_bwmgr_set_emc(rtcpu->bwmgr, 0, TEGRA_BWMGR_SET_EMC_FLOOR); } /* * Send the PM_SUSPEND command to remote core FW. */ static int tegra_camrtc_cmd_pm_suspend(struct device *dev, long *timeout) { struct tegra_cam_rtcpu *rtcpu = dev_get_drvdata(dev); u32 command = RTCPU_COMMAND(PM_SUSPEND, 0); int expect = RTCPU_COMMAND(PM_SUSPEND, (CAMRTC_PM_CTRL_STATE_SUSPEND << 8) | CAMRTC_PM_CTRL_STATUS_OK); int err; mutex_lock(&rtcpu->cmd.mutex); err = tegra_camrtc_mbox_exchange(dev, command, timeout); mutex_unlock(&rtcpu->cmd.mutex); if (err == expect) return 0; dev_WARN(dev, "PM_SUSPEND failed: 0x%08x\n", (unsigned)err); if (err >= 0) err = -EIO; return err; } static int tegra_sce_cam_deassert_resets(struct device *dev) { struct tegra_cam_rtcpu *rtcpu = dev_get_drvdata(dev); int err; err = camrtc_reset_group_deassert(rtcpu->resets[0]); if (err) return err; /* Configure R5 core */ if (rtcpu->cfg_base != NULL) { u32 val = readl(rtcpu->cfg_base + TEGRA_APS_FRSC_SC_CTL_0); if (val != TEGRA_R5R_SC_DISABLE) { /* Disable R5R and smartcomp in camera mode */ writel(TEGRA_R5R_SC_DISABLE, rtcpu->cfg_base + TEGRA_APS_FRSC_SC_CTL_0); /* Enable JTAG/Coresight */ writel(TEGRA_FN_MODEIN, rtcpu->cfg_base + TEGRA_APS_FRSC_SC_MODEIN_0); } } /* Group 2 */ err = camrtc_reset_group_deassert(rtcpu->resets[1]); if (err) return err; /* Group 3: nCPUHALT controlled by PM, not by CAR. */ if (rtcpu->pm_base != NULL) { u32 val = readl(rtcpu->pm_base + TEGRA_PM_R5_CTRL_0); writel(val | TEGRA_PM_FWLOADDONE, rtcpu->pm_base + TEGRA_PM_R5_CTRL_0); } return 0; } static void tegra_sce_cam_assert_resets(struct device *dev) { struct tegra_cam_rtcpu *rtcpu = dev_get_drvdata(dev); camrtc_reset_group_assert(rtcpu->resets[1]); camrtc_reset_group_assert(rtcpu->resets[0]); } static int tegra_sce_cam_wait_for_wfi(struct device *dev, long *timeout) { struct tegra_cam_rtcpu *rtcpu = dev_get_drvdata(dev); long delay_stride = HZ / 50; /* Poll for WFI assert.*/ for (;;) { u32 val = readl(rtcpu->pm_base + TEGRA_PM_PWR_STATUS_0); if ((val & TEGRA_PM_WFIPIPESTOPPED) == 0) break; if (*timeout < 0) { dev_WARN(dev, "timeout waiting for WFI\n"); return -EBUSY; } msleep(delay_stride); *timeout -= delay_stride; } return 0; } static int tegra_sce_cam_suspend_core(struct device *dev) { struct tegra_cam_rtcpu *rtcpu = dev_get_drvdata(dev); long timeout = 2 * rtcpu->cmd.timeout; u32 val; int err; err = tegra_camrtc_cmd_pm_suspend(dev, &timeout); if (rtcpu->pm_base != NULL) { /* Don't bother to check for WFI if core is unresponsive */ if (err == 0) err = tegra_sce_cam_wait_for_wfi(dev, &timeout); val = readl(rtcpu->pm_base + TEGRA_PM_R5_CTRL_0); writel(val & ~TEGRA_PM_FWLOADDONE, rtcpu->pm_base + TEGRA_PM_R5_CTRL_0); } return err; } static void tegra_ape_cam_assert_resets(struct device *dev) { struct tegra_cam_rtcpu *rtcpu = dev_get_drvdata(dev); camrtc_reset_group_assert(rtcpu->resets[0]); } static int tegra_ape_cam_deassert_resets(struct device *dev) { struct tegra_cam_rtcpu *rtcpu = dev_get_drvdata(dev); return camrtc_reset_group_deassert(rtcpu->resets[0]); } static irqreturn_t tegra_camrtc_adsp_wfi_handler(int irq, void *data) { struct completion *entered_wfi = data; disable_irq_nosync(irq); complete(entered_wfi); return IRQ_HANDLED; } static int tegra_ape_cam_wait_for_l2_idle(struct device *dev, long *timeout) { struct tegra_cam_rtcpu *rtcpu = dev_get_drvdata(dev); long delay_stride = HZ / 50; if (rtcpu->amisc_base == NULL) { dev_WARN(dev, "iobase \"ape-amisc\" missing\n"); return 0; } /* Poll for L2 idle.*/ for (;;) { u32 val = readl(rtcpu->amisc_base + AMISC_ADSP_STATUS); u32 mask = AMISC_ADSP_L2_IDLE; if ((val & mask) == mask) break; if (*timeout <= 0) { dev_WARN(dev, "timeout waiting for L2 idle\n"); return -EBUSY; } msleep(delay_stride); *timeout -= delay_stride; } return 0; } static int tegra_ape_cam_wait_for_wfi(struct device *dev, long *timeout) { struct tegra_cam_rtcpu *rtcpu = dev_get_drvdata(dev); DECLARE_COMPLETION_ONSTACK(entered_wfi); unsigned int irq = rtcpu->adsp_wfi_irq; int err; if (irq <= 0) { dev_WARN(dev, "irq \"adsp-wfi\" missing\n"); return 0; } err = request_threaded_irq(irq, tegra_camrtc_adsp_wfi_handler, NULL, IRQF_TRIGGER_HIGH, "adsp-wfi", &entered_wfi); if (err) { dev_WARN(dev, "cannot request for %s interrupt: %d\n", "adsp-wfi", err); return err; } *timeout = wait_for_completion_timeout(&entered_wfi, *timeout); free_irq(irq, &entered_wfi); if (*timeout == 0) { dev_WARN(dev, "timeout waiting for WFI\n"); return -EBUSY; } return 0; } static int tegra_ape_cam_suspend_core(struct device *dev) { struct tegra_cam_rtcpu *rtcpu = dev_get_drvdata(dev); long timeout = 2 * rtcpu->cmd.timeout; int err; err = tegra_camrtc_cmd_pm_suspend(dev, &timeout); if (err) return err; err = tegra_ape_cam_wait_for_wfi(dev, &timeout); if (err) return err; return tegra_ape_cam_wait_for_l2_idle(dev, &timeout); } static int tegra_rce_cam_deassert_resets(struct device *dev) { struct tegra_cam_rtcpu *rtcpu = dev_get_drvdata(dev); int err; err = camrtc_reset_group_deassert(rtcpu->resets[0]); if (err) return err; /* nCPUHALT is a reset controlled by PM, not by CAR. */ if (rtcpu->pm_base != NULL) { u32 val = readl(rtcpu->pm_base + TEGRA_PM_R5_CTRL_0); writel(val | TEGRA_PM_FWLOADDONE, rtcpu->pm_base + TEGRA_PM_R5_CTRL_0); } return 0; } static void tegra_rce_cam_assert_resets(struct device *dev) { struct tegra_cam_rtcpu *rtcpu = dev_get_drvdata(dev); camrtc_reset_group_assert(rtcpu->resets[0]); } static int tegra_camrtc_suspend_core(struct device *dev) { struct tegra_cam_rtcpu *rtcpu = dev_get_drvdata(dev); if (!rtcpu->boot_sync_done || !rtcpu->sm_pair) return 0; rtcpu->boot_sync_done = false; return rtcpu->pdata->suspend_core(dev); } static void tegra_camrtc_set_online(struct device *dev, bool online) { struct tegra_cam_rtcpu *rtcpu = dev_get_drvdata(dev); /* No need to report if there is no bus yet */ if (IS_ERR_OR_NULL(rtcpu->ivc)) return; if (online == rtcpu->online) return; if (online) { /* Activate the IVC services in firmware */ int ret = tegra_ivc_bus_boot_sync(rtcpu->ivc); if (ret < 0) { dev_err(dev, "ivc-bus boot sync failed: %d\n", ret); return; } } rtcpu->online = online; tegra_ivc_bus_ready(rtcpu->ivc, online); } static u32 tegra_camrtc_full_notify(void *data, u32 response) { struct tegra_cam_rtcpu *rtcpu = data; atomic_set(&rtcpu->cmd.response, response); wake_up(&rtcpu->cmd.response_waitq); return 0; } static void tegra_camrtc_empty_notify(void *data, u32 empty_value) { struct tegra_cam_rtcpu *rtcpu = data; atomic_set(&rtcpu->cmd.emptied, 1); wake_up(&rtcpu->cmd.empty_waitq); } static long tegra_camrtc_wait_for_empty(struct device *dev, long timeout) { struct tegra_cam_rtcpu *rtcpu = dev_get_drvdata(dev); if (timeout == 0) timeout = 2 * HZ; timeout = wait_event_timeout( rtcpu->cmd.empty_waitq, /* Make sure IRQ has been handled */ atomic_read(&rtcpu->cmd.emptied) != 0, timeout); return timeout; } static int tegra_camrtc_mbox_exchange(struct device *dev, u32 command, long *timeout) { struct tegra_cam_rtcpu *rtcpu = dev_get_drvdata(dev); #define INVALID_RESPONSE (0x80000000U) *timeout = tegra_camrtc_wait_for_empty(dev, *timeout); if (*timeout <= 0) { dev_err(dev, "command: 0x%08x: empty mailbox%s timeout\n", command, tegra_hsp_sm_pair_is_empty(rtcpu->sm_pair) ? " interrupt" : ""); return -ETIMEDOUT; } atomic_set(&rtcpu->cmd.response, INVALID_RESPONSE); /* Clear cmd.emptied before the write */ atomic_set(&rtcpu->cmd.emptied, 0); tegra_hsp_sm_pair_write(rtcpu->sm_pair, command); *timeout = wait_event_timeout( rtcpu->cmd.response_waitq, atomic_read(&rtcpu->cmd.response) != INVALID_RESPONSE, *timeout); if (*timeout <= 0) { dev_err(dev, "command: 0x%08x: response timeout\n", command); return -ETIMEDOUT; } return (int)atomic_read(&rtcpu->cmd.response); } int tegra_camrtc_command(struct device *dev, u32 command, long timeout) { struct tegra_cam_rtcpu *rtcpu = dev_get_drvdata(dev); int response; if (timeout == 0) timeout = rtcpu->cmd.timeout; mutex_lock(&rtcpu->cmd.mutex); response = tegra_camrtc_mbox_exchange(dev, command, &timeout); mutex_unlock(&rtcpu->cmd.mutex); return response; } EXPORT_SYMBOL(tegra_camrtc_command); int tegra_camrtc_prefix_command(struct device *dev, u32 prefix, u32 command, long timeout) { struct tegra_cam_rtcpu *rtcpu = dev_get_drvdata(dev); int response; if (timeout == 0) timeout = rtcpu->cmd.timeout; mutex_lock(&rtcpu->cmd.mutex); prefix = RTCPU_COMMAND(PREFIX, prefix); response = tegra_camrtc_mbox_exchange(dev, prefix, &timeout); if (RTCPU_GET_COMMAND_ID(response) == RTCPU_CMD_PREFIX) response = tegra_camrtc_mbox_exchange(dev, command, &timeout); mutex_unlock(&rtcpu->cmd.mutex); return response; } EXPORT_SYMBOL(tegra_camrtc_prefix_command); static int tegra_camrtc_poweron(struct device *dev, bool full_speed) { struct tegra_cam_rtcpu *rtcpu = dev_get_drvdata(dev); int ret; if (rtcpu->powered) { if (full_speed) camrtc_clk_group_adjust_fast(rtcpu->clocks); return 0; } /* APE power domain may misbehave and try to resume while probing */ if (rtcpu->sm_pair == NULL) { dev_info(dev, "poweron while probing"); return 0; } /* Power on and let core run */ ret = tegra_camrtc_enable_clks(dev); if (ret) { dev_err(dev, "failed to turn on %s clocks: %d\n", rtcpu->name, ret); return ret; } if (full_speed) camrtc_clk_group_adjust_fast(rtcpu->clocks); camrtc_device_group_reset(rtcpu->camera_devices); ret = tegra_camrtc_deassert_resets(dev); if (ret) return ret; rtcpu->powered = true; return 0; } static void tegra_camrtc_poweroff(struct device *dev) { struct tegra_cam_rtcpu *rtcpu = dev_get_drvdata(dev); if (!rtcpu->powered) return; rtcpu->powered = false; tegra_camrtc_assert_resets(dev); tegra_camrtc_disable_clks(dev); } static int tegra_camrtc_boot_sync(struct device *dev) { struct tegra_cam_rtcpu *rtcpu = dev_get_drvdata(dev); int ret; u32 command; if (rtcpu->boot_sync_done) return 0; /* * Handshake FW version before continuing with the boot */ command = RTCPU_COMMAND(INIT, 0); ret = tegra_camrtc_command(dev, command, 0); if (ret < 0) return ret; if (ret != command) { dev_err(dev, "RTCPU sync problem (response=0x%08x)\n", ret); return -EIO; } if (rtcpu->stats.boot_handshake == 0) { u64 bt; rtcpu->stats.boot_handshake = ktime_get_ns(); bt = rtcpu->stats.boot_handshake - rtcpu->stats.reset_complete + 500U; dev_dbg(dev, "boot time %llu.%03u ms\n", bt / 1000000U, (unsigned int)(bt % 10000000U) / 1000U); } command = RTCPU_COMMAND(FW_VERSION, RTCPU_DRIVER_SM5_VERSION); ret = tegra_camrtc_command(dev, command, 0); if (ret < 0) return ret; if (RTCPU_GET_COMMAND_ID(ret) != RTCPU_CMD_FW_VERSION || RTCPU_GET_COMMAND_VALUE(ret) < RTCPU_FW_SM4_VERSION) { dev_err(dev, "RTCPU version mismatch (response=0x%08x)\n", ret); return -EIO; } rtcpu->fw_version = RTCPU_GET_COMMAND_VALUE(ret); rtcpu->boot_sync_done = true; /* * Enable trace */ if (rtcpu->tracer) { ret = tegra_rtcpu_trace_boot_sync(rtcpu->tracer); if (ret < 0) { dev_err(dev, "trace boot sync failed: %d\n", ret); return ret; } } if (rtcpu->coverage != NULL) { ret = tegra_rtcpu_coverage_boot_sync(rtcpu->coverage); if (ret < 0) { dev_dbg(dev, "coverage boot sync status: %d\n", ret); /* * Not a fatal error, don't stop the sync. * But go ahead and remove the coverage debug FS * entries and release the memory. */ tegra_rtcpu_coverage_destroy(rtcpu->coverage); rtcpu->coverage = NULL; } } return 0; } /* * RTCPU boot sequence */ static int tegra_camrtc_boot(struct device *dev) { struct tegra_cam_rtcpu *rtcpu = dev_get_drvdata(dev); int retry = 0, max_retries = rtcpu->max_reboot_retry; int ret; ret = tegra_camrtc_poweron(dev, true); if (ret) return ret; tegra_camrtc_full_mem_bw(dev); for (;;) { ret = tegra_camrtc_boot_sync(dev); if (ret == 0) break; if (retry++ == max_retries) break; dev_warn(dev, "%s full reset, retry %u/%u\n", rtcpu->name, retry, max_retries); tegra_camrtc_assert_resets(dev); usleep_range(10, 30); tegra_camrtc_deassert_resets(dev); } if (ret == 0) { tegra_camrtc_set_online(dev, true); } tegra_camrtc_slow_mem_bw(dev); return 0; } int tegra_camrtc_iovm_setup(struct device *dev, dma_addr_t iova) { u32 command = RTCPU_COMMAND(CH_SETUP, iova >> 8); int ret = tegra_camrtc_command(dev, command, 0); if (ret < 0) return -ret; if (RTCPU_GET_COMMAND_ID(ret) == RTCPU_CMD_ERROR) { u32 error = RTCPU_GET_COMMAND_VALUE(ret); dev_dbg(dev, "IOVM setup error: %u\n", error); return (int)error; } return 0; } EXPORT_SYMBOL(tegra_camrtc_iovm_setup); static int tegra_camrtc_get_fw_hash(struct device *dev, u8 hash[RTCPU_FW_HASH_SIZE]) { struct tegra_cam_rtcpu *rtcpu = dev_get_drvdata(dev); int ret, i; u32 value; if (rtcpu->fw_version < RTCPU_FW_SM2_VERSION) { dev_info(dev, "fw version %u has no sha1\n", rtcpu->fw_version); return -EIO; } for (i = 0; i < RTCPU_FW_HASH_SIZE; i++) { ret = tegra_camrtc_command(dev, RTCPU_COMMAND(FW_HASH, i), 0); value = RTCPU_GET_COMMAND_VALUE(ret); if (ret < 0 || RTCPU_GET_COMMAND_ID(ret) != RTCPU_CMD_FW_HASH || value > (u8)~0) { dev_warn(dev, "FW_HASH problem (0x%08x)\n", ret); return -EIO; } hash[i] = value; } return 0; } ssize_t tegra_camrtc_print_version(struct device *dev, char *buf, size_t size) { struct tegra_cam_rtcpu *rtcpu = dev_get_drvdata(dev); struct seq_buf s; int i; seq_buf_init(&s, buf, size); seq_buf_printf(&s, "version cpu=%s cmd=%u sha1=", rtcpu->name, rtcpu->fw_version); for (i = 0; i < RTCPU_FW_HASH_SIZE; i++) seq_buf_printf(&s, "%02x", rtcpu->fw_hash[i]); return seq_buf_used(&s); } EXPORT_SYMBOL(tegra_camrtc_print_version); static void tegra_camrtc_log_fw_version(struct device *dev) { char version[TEGRA_CAMRTC_VERSION_LEN]; tegra_camrtc_print_version(dev, version, sizeof(version)); dev_info(dev, "firmware %s\n", version); } static int tegra_cam_rtcpu_runtime_suspend(struct device *dev) { struct tegra_cam_rtcpu *rtcpu = dev_get_drvdata(dev); int err; err = tegra_camrtc_suspend_core(dev); /* Try full reset if an error occurred while suspending core. */ if (WARN(err < 0, "RTCPU suspend failed, resetting it")) { /* runtime_resume() powers RTCPU back on */ tegra_camrtc_poweroff(dev); tegra_camrtc_set_online(dev, false); } camrtc_clk_group_adjust_slow(rtcpu->clocks); camrtc_device_group_idle(rtcpu->camera_devices); return 0; } static int tegra_cam_rtcpu_runtime_resume(struct device *dev) { struct tegra_cam_rtcpu *rtcpu = dev_get_drvdata(dev); int ret; ret = camrtc_device_group_busy(rtcpu->camera_devices); if (ret < 0) return ret; ret = tegra_camrtc_boot(dev); if (ret < 0) camrtc_device_group_idle(rtcpu->camera_devices); return ret; } static struct device *tegra_camrtc_get_hsp_device(struct device_node *hsp_node) { struct device_node *of_node; struct platform_device *pdev; of_node = of_parse_phandle(hsp_node, "device", 0); if (of_node == NULL) return NULL; pdev = of_find_device_by_node(of_node); of_node_put(of_node); if (pdev == NULL) return ERR_PTR(-EPROBE_DEFER); if (&pdev->dev.driver == NULL) { platform_device_put(pdev); return ERR_PTR(-EPROBE_DEFER); } return &pdev->dev; } static int tegra_camrtc_mbox_init(struct device *dev) { struct tegra_cam_rtcpu *rtcpu = dev_get_drvdata(dev); struct device_node *hsp_node; if (!IS_ERR_OR_NULL(rtcpu->sm_pair)) return 0; mutex_init(&rtcpu->cmd.mutex); init_waitqueue_head(&rtcpu->cmd.response_waitq); init_waitqueue_head(&rtcpu->cmd.empty_waitq); hsp_node = of_get_child_by_name(dev->of_node, "hsp"); rtcpu->hsp_device = tegra_camrtc_get_hsp_device(hsp_node); if (IS_ERR(rtcpu->hsp_device)) { of_node_put(hsp_node); return PTR_ERR(rtcpu->hsp_device); } if (rtcpu->hsp_device != NULL) { int ret = pm_runtime_get_sync(rtcpu->hsp_device); if (ret < 0) { dev_warn(rtcpu->hsp_device, "power on failure: %d\n", ret); put_device(rtcpu->hsp_device); rtcpu->hsp_device = NULL; return ret; } } rtcpu->sm_pair = of_tegra_hsp_sm_pair_by_name(hsp_node, "cmd-pair", tegra_camrtc_full_notify, tegra_camrtc_empty_notify, rtcpu); of_node_put(hsp_node); if (IS_ERR(rtcpu->sm_pair)) { int ret = PTR_ERR(rtcpu->sm_pair); if (ret != -EPROBE_DEFER) dev_err(dev, "failed to obtain %s mbox pair: %d\n", rtcpu->name, ret); rtcpu->sm_pair = NULL; return ret; } return 0; } static int tegra_cam_rtcpu_remove(struct platform_device *pdev) { struct tegra_cam_rtcpu *rtcpu = platform_get_drvdata(pdev); bool pm_is_active = pm_runtime_active(&pdev->dev); pm_runtime_disable(&pdev->dev); pm_runtime_set_suspended(&pdev->dev); tegra_camrtc_set_online(&pdev->dev, false); if (rtcpu->sm_pair) { if (pm_is_active) tegra_cam_rtcpu_runtime_suspend(&pdev->dev); tegra_hsp_sm_pair_free(rtcpu->sm_pair); rtcpu->sm_pair = NULL; } if (!IS_ERR_OR_NULL(rtcpu->hsp_device)) { pm_runtime_put(rtcpu->hsp_device); put_device(rtcpu->hsp_device); } tegra_rtcpu_trace_destroy(rtcpu->tracer); rtcpu->tracer = NULL; tegra_rtcpu_coverage_destroy(rtcpu->coverage); rtcpu->coverage = NULL; tegra_camrtc_poweroff(&pdev->dev); if (rtcpu->bwmgr != NULL) tegra_bwmgr_unregister(rtcpu->bwmgr); rtcpu->bwmgr = NULL; tegra_pd_remove_device(&pdev->dev); tegra_cam_rtcpu_mon_destroy(rtcpu->monitor); tegra_ivc_bus_destroy(rtcpu->ivc); pdev->dev.dma_parms = NULL; return 0; } static struct device *s_dev; static int tegra_cam_rtcpu_probe(struct platform_device *pdev) { struct tegra_cam_rtcpu *rtcpu; const struct tegra_cam_rtcpu_pdata *pdata; struct device *dev = &pdev->dev; int ret; const char *name; uint32_t timeout; pdata = of_device_get_match_data(dev); if (pdata == NULL) { dev_err(dev, "no device match\n"); return -ENODEV; } name = pdata->name; of_property_read_string(dev->of_node, NV(cpu-name), &name); dev_dbg(dev, "probing RTCPU on %s\n", name); rtcpu = devm_kzalloc(dev, sizeof(*rtcpu), GFP_KERNEL); if (rtcpu == NULL) return -ENOMEM; rtcpu->pdata = pdata; rtcpu->name = name; platform_set_drvdata(pdev, rtcpu); /* Enable runtime power management */ pm_runtime_enable(dev); ret = tegra_camrtc_get_resources(dev); if (ret) goto fail; rtcpu->max_reboot_retry = 2; (void)of_property_read_u32(dev->of_node, NV(max-reboot), &rtcpu->max_reboot_retry); timeout = 2000; (void)of_property_read_u32(dev->of_node, NV(cmd-timeout), &timeout); rtcpu->cmd.timeout = msecs_to_jiffies(timeout); timeout = 60000; ret = of_property_read_u32(dev->of_node, NV(autosuspend-delay-ms), &timeout); if (ret == 0) { pm_runtime_use_autosuspend(dev); pm_runtime_set_autosuspend_delay(&pdev->dev, timeout); } tegra_camrtc_init_bwmgr(dev); dev->dma_parms = &rtcpu->dma_parms; dma_set_max_seg_size(dev, UINT_MAX); rtcpu->tracer = tegra_rtcpu_trace_create(dev, rtcpu->camera_devices); rtcpu->coverage = tegra_rtcpu_coverage_create(dev); ret = tegra_camrtc_mbox_init(dev); if (ret) goto fail; /* Power on device */ ret = pm_runtime_get_sync(dev); if (ret < 0) goto fail; /* Clocks are on, resets are deasserted, we can touch the hardware */ /* Tegra-agic driver routes IRQs when probing, do it when powered */ ret = tegra_camrtc_get_irqs(dev); if (ret) goto put_and_fail; rtcpu->ivc = tegra_ivc_bus_create(dev); if (IS_ERR(rtcpu->ivc)) { ret = PTR_ERR(rtcpu->ivc); goto put_and_fail; } rtcpu->monitor = tegra_camrtc_mon_create(dev); if (IS_ERR(rtcpu->monitor)) { ret = PTR_ERR(rtcpu->monitor); goto put_and_fail; } if (of_property_read_bool(dev->of_node, NV(disable-runtime-pm)) || /* APE power domain powergates APE block when suspending */ /* This won't do */ (DISABLE_APE_RUNTIME_PM && pdata->id == TEGRA_CAM_RTCPU_APE)) { pm_runtime_get(dev); } ret = tegra_camrtc_get_fw_hash(dev, rtcpu->fw_hash); if (ret == 0) devm_tegrafw_register(dev, name != pdata->name ? name : "camrtc", TFW_NORMAL, tegra_camrtc_print_version, NULL); tegra_camrtc_set_online(dev, true); pm_runtime_put(dev); /* Print firmware version */ tegra_camrtc_log_fw_version(dev); s_dev = dev; dev_dbg(dev, "successfully probed RTCPU on %s\n", name); return 0; put_and_fail: pm_runtime_dont_use_autosuspend(dev); pm_runtime_put_sync_suspend(dev); fail: tegra_cam_rtcpu_remove(pdev); return ret; } int tegra_camrtc_reboot(struct device *dev) { struct tegra_cam_rtcpu *rtcpu = dev_get_drvdata(dev); if (pm_runtime_suspended(dev)) { dev_info(dev, "cannot reboot while suspended\n"); return -EIO; } if (!rtcpu->powered) return -EIO; rtcpu->boot_sync_done = false; pm_runtime_mark_last_busy(dev); tegra_camrtc_set_online(dev, false); tegra_camrtc_assert_resets(dev); rtcpu->powered = false; return tegra_camrtc_boot(dev); } EXPORT_SYMBOL(tegra_camrtc_reboot); int tegra_camrtc_restore(struct device *dev) { struct tegra_cam_rtcpu *rtcpu = dev_get_drvdata(dev); if (rtcpu->monitor) return tegra_camrtc_mon_restore_rtcpu(rtcpu->monitor); else return tegra_camrtc_reboot(dev); } EXPORT_SYMBOL(tegra_camrtc_restore); bool tegra_camrtc_is_rtcpu_alive(struct device *dev) { struct tegra_cam_rtcpu *rtcpu = dev_get_drvdata(dev); return rtcpu->online; } EXPORT_SYMBOL(tegra_camrtc_is_rtcpu_alive); bool tegra_camrtc_is_rtcpu_powered(void) { struct tegra_cam_rtcpu *rtcpu; if (s_dev) { rtcpu = dev_get_drvdata(s_dev); return rtcpu->powered; } return false; } EXPORT_SYMBOL(tegra_camrtc_is_rtcpu_powered); void tegra_camrtc_flush_trace(struct device *dev) { struct tegra_cam_rtcpu *rtcpu = dev_get_drvdata(dev); tegra_rtcpu_trace_flush(rtcpu->tracer); } EXPORT_SYMBOL(tegra_camrtc_flush_trace); static int tegra_camrtc_halt(struct device *dev) { struct tegra_cam_rtcpu *rtcpu = dev_get_drvdata(dev); tegra_camrtc_set_online(dev, false); if (!rtcpu->powered) return 0; if (!pm_runtime_suspended(dev)) tegra_camrtc_suspend_core(dev); tegra_camrtc_poweroff(dev); return 0; } static int tegra_camrtc_resume(struct device *dev) { return tegra_camrtc_poweron(dev, false); } static void tegra_cam_rtcpu_shutdown(struct platform_device *pdev) { tegra_camrtc_halt(&pdev->dev); } static const struct of_device_id tegra_cam_rtcpu_of_match[] = { { .compatible = NV(tegra186-sce-ivc), .data = &sce_pdata }, { .compatible = NV(tegra186-ape-ivc), .data = &ape_pdata }, { .compatible = NV(tegra194-rce), .data = &rce_pdata }, { }, }; MODULE_DEVICE_TABLE(of, tegra_cam_rtcpu_of_match); static const struct dev_pm_ops tegra_cam_rtcpu_pm_ops = { .suspend = tegra_camrtc_halt, .resume = tegra_camrtc_resume, .runtime_suspend = tegra_cam_rtcpu_runtime_suspend, .runtime_resume = tegra_cam_rtcpu_runtime_resume, }; static struct platform_driver tegra_cam_rtcpu_driver = { .driver = { .name = "tegra186-cam-rtcpu", .owner = THIS_MODULE, .of_match_table = of_match_ptr(tegra_cam_rtcpu_of_match), #ifdef CONFIG_PM .pm = &tegra_cam_rtcpu_pm_ops, #endif }, .probe = tegra_cam_rtcpu_probe, .remove = tegra_cam_rtcpu_remove, .shutdown = tegra_cam_rtcpu_shutdown, }; module_platform_driver(tegra_cam_rtcpu_driver); MODULE_DESCRIPTION("CAMERA RTCPU driver"); MODULE_AUTHOR("NVIDIA"); MODULE_LICENSE("GPL v2");