| // SPDX-License-Identifier: GPL-2.0-only | |
| /* | |
| * Copyright (c) 2016-2018, 2020-2021 The Linux Foundation. All rights reserved. | |
| * Copyright (C) 2013 Red Hat | |
| * Author: Rob Clark <robdclark@gmail.com> | |
| */ | |
| #include <linux/interconnect.h> | |
| #include <linux/io.h> | |
| #include "msm_drv.h" | |
| /* | |
| * Util/helpers: | |
| */ | |
| struct clk *msm_clk_bulk_get_clock(struct clk_bulk_data *bulk, int count, | |
| const char *name) | |
| { | |
| int i; | |
| char n[32]; | |
| snprintf(n, sizeof(n), "%s_clk", name); | |
| for (i = 0; bulk && i < count; i++) { | |
| if (!strcmp(bulk[i].id, name) || !strcmp(bulk[i].id, n)) | |
| return bulk[i].clk; | |
| } | |
| return NULL; | |
| } | |
| struct clk *msm_clk_get(struct platform_device *pdev, const char *name) | |
| { | |
| struct clk *clk; | |
| char name2[32]; | |
| clk = devm_clk_get(&pdev->dev, name); | |
| if (!IS_ERR(clk) || PTR_ERR(clk) == -EPROBE_DEFER) | |
| return clk; | |
| snprintf(name2, sizeof(name2), "%s_clk", name); | |
| clk = devm_clk_get(&pdev->dev, name2); | |
| if (!IS_ERR(clk)) | |
| dev_warn(&pdev->dev, "Using legacy clk name binding. Use " | |
| "\"%s\" instead of \"%s\"\n", name, name2); | |
| return clk; | |
| } | |
| void __iomem *msm_ioremap_mdss(struct platform_device *mdss_pdev, | |
| struct platform_device *pdev, | |
| const char *name) | |
| { | |
| struct resource *res; | |
| res = platform_get_resource_byname(mdss_pdev, IORESOURCE_MEM, name); | |
| if (!res) | |
| return ERR_PTR(-EINVAL); | |
| return devm_ioremap_resource(&pdev->dev, res); | |
| } | |
| static void __iomem *_msm_ioremap(struct platform_device *pdev, const char *name, | |
| bool quiet, phys_addr_t *psize) | |
| { | |
| struct resource *res; | |
| unsigned long size; | |
| void __iomem *ptr; | |
| if (name) | |
| res = platform_get_resource_byname(pdev, IORESOURCE_MEM, name); | |
| else | |
| res = platform_get_resource(pdev, IORESOURCE_MEM, 0); | |
| if (!res) { | |
| if (!quiet) | |
| DRM_DEV_ERROR(&pdev->dev, "failed to get memory resource: %s\n", name); | |
| return ERR_PTR(-EINVAL); | |
| } | |
| size = resource_size(res); | |
| ptr = devm_ioremap(&pdev->dev, res->start, size); | |
| if (!ptr) { | |
| if (!quiet) | |
| DRM_DEV_ERROR(&pdev->dev, "failed to ioremap: %s\n", name); | |
| return ERR_PTR(-ENOMEM); | |
| } | |
| if (psize) | |
| *psize = size; | |
| return ptr; | |
| } | |
| void __iomem *msm_ioremap(struct platform_device *pdev, const char *name) | |
| { | |
| return _msm_ioremap(pdev, name, false, NULL); | |
| } | |
| void __iomem *msm_ioremap_quiet(struct platform_device *pdev, const char *name) | |
| { | |
| return _msm_ioremap(pdev, name, true, NULL); | |
| } | |
| void __iomem *msm_ioremap_size(struct platform_device *pdev, const char *name, | |
| phys_addr_t *psize) | |
| { | |
| return _msm_ioremap(pdev, name, false, psize); | |
| } | |
| static enum hrtimer_restart msm_hrtimer_worktimer(struct hrtimer *t) | |
| { | |
| struct msm_hrtimer_work *work = container_of(t, | |
| struct msm_hrtimer_work, timer); | |
| kthread_queue_work(work->worker, &work->work); | |
| return HRTIMER_NORESTART; | |
| } | |
| void msm_hrtimer_queue_work(struct msm_hrtimer_work *work, | |
| ktime_t wakeup_time, | |
| enum hrtimer_mode mode) | |
| { | |
| hrtimer_start(&work->timer, wakeup_time, mode); | |
| } | |
| void msm_hrtimer_work_init(struct msm_hrtimer_work *work, | |
| struct kthread_worker *worker, | |
| kthread_work_func_t fn, | |
| clockid_t clock_id, | |
| enum hrtimer_mode mode) | |
| { | |
| hrtimer_setup(&work->timer, msm_hrtimer_worktimer, clock_id, mode); | |
| work->worker = worker; | |
| kthread_init_work(&work->work, fn); | |
| } | |
| struct icc_path *msm_icc_get(struct device *dev, const char *name) | |
| { | |
| struct device *mdss_dev = dev->parent; | |
| struct icc_path *path; | |
| path = of_icc_get(dev, name); | |
| if (path) | |
| return path; | |
| /* | |
| * If there are no interconnects attached to the corresponding device | |
| * node, of_icc_get() will return NULL. | |
| * | |
| * If the MDP5/DPU device node doesn't have interconnects, lookup the | |
| * path in the parent (MDSS) device. | |
| */ | |
| return of_icc_get(mdss_dev, name); | |
| } |