mirror of https://github.com/Qortal/Brooklyn
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
141 lines
2.9 KiB
141 lines
2.9 KiB
// SPDX-License-Identifier: GPL-2.0-only |
|
/* |
|
* OMAP IOMMU quirks for various TI SoCs |
|
* |
|
* Copyright (C) 2015-2019 Texas Instruments Incorporated - https://www.ti.com/ |
|
* Suman Anna <[email protected]> |
|
*/ |
|
|
|
#include <linux/platform_device.h> |
|
#include <linux/err.h> |
|
#include <linux/clk.h> |
|
#include <linux/list.h> |
|
|
|
#include "clockdomain.h" |
|
#include "powerdomain.h" |
|
|
|
struct pwrdm_link { |
|
struct device *dev; |
|
struct powerdomain *pwrdm; |
|
struct list_head node; |
|
}; |
|
|
|
static DEFINE_SPINLOCK(iommu_lock); |
|
static struct clockdomain *emu_clkdm; |
|
static atomic_t emu_count; |
|
|
|
static void omap_iommu_dra7_emu_swsup_config(struct platform_device *pdev, |
|
bool enable) |
|
{ |
|
struct device_node *np = pdev->dev.of_node; |
|
unsigned long flags; |
|
|
|
if (!of_device_is_compatible(np, "ti,dra7-dsp-iommu")) |
|
return; |
|
|
|
if (!emu_clkdm) { |
|
emu_clkdm = clkdm_lookup("emu_clkdm"); |
|
if (WARN_ON_ONCE(!emu_clkdm)) |
|
return; |
|
} |
|
|
|
spin_lock_irqsave(&iommu_lock, flags); |
|
|
|
if (enable && (atomic_inc_return(&emu_count) == 1)) |
|
clkdm_deny_idle(emu_clkdm); |
|
else if (!enable && (atomic_dec_return(&emu_count) == 0)) |
|
clkdm_allow_idle(emu_clkdm); |
|
|
|
spin_unlock_irqrestore(&iommu_lock, flags); |
|
} |
|
|
|
static struct powerdomain *_get_pwrdm(struct device *dev) |
|
{ |
|
struct clk *clk; |
|
struct clk_hw_omap *hwclk; |
|
struct clockdomain *clkdm; |
|
struct powerdomain *pwrdm = NULL; |
|
struct pwrdm_link *entry; |
|
unsigned long flags; |
|
static LIST_HEAD(cache); |
|
|
|
spin_lock_irqsave(&iommu_lock, flags); |
|
|
|
list_for_each_entry(entry, &cache, node) { |
|
if (entry->dev == dev) { |
|
pwrdm = entry->pwrdm; |
|
break; |
|
} |
|
} |
|
|
|
spin_unlock_irqrestore(&iommu_lock, flags); |
|
|
|
if (pwrdm) |
|
return pwrdm; |
|
|
|
clk = of_clk_get(dev->of_node->parent, 0); |
|
if (IS_ERR(clk)) { |
|
dev_err(dev, "no fck found\n"); |
|
return NULL; |
|
} |
|
|
|
hwclk = to_clk_hw_omap(__clk_get_hw(clk)); |
|
clk_put(clk); |
|
if (!hwclk || !hwclk->clkdm_name) { |
|
dev_err(dev, "no hwclk data\n"); |
|
return NULL; |
|
} |
|
|
|
clkdm = clkdm_lookup(hwclk->clkdm_name); |
|
if (!clkdm) { |
|
dev_err(dev, "clkdm not found: %s\n", hwclk->clkdm_name); |
|
return NULL; |
|
} |
|
|
|
pwrdm = clkdm_get_pwrdm(clkdm); |
|
if (!pwrdm) { |
|
dev_err(dev, "pwrdm not found: %s\n", clkdm->name); |
|
return NULL; |
|
} |
|
|
|
entry = kmalloc(sizeof(*entry), GFP_KERNEL); |
|
if (entry) { |
|
entry->dev = dev; |
|
entry->pwrdm = pwrdm; |
|
spin_lock_irqsave(&iommu_lock, flags); |
|
list_add(&entry->node, &cache); |
|
spin_unlock_irqrestore(&iommu_lock, flags); |
|
} |
|
|
|
return pwrdm; |
|
} |
|
|
|
int omap_iommu_set_pwrdm_constraint(struct platform_device *pdev, bool request, |
|
u8 *pwrst) |
|
{ |
|
struct powerdomain *pwrdm; |
|
u8 next_pwrst; |
|
int ret = 0; |
|
|
|
pwrdm = _get_pwrdm(&pdev->dev); |
|
if (!pwrdm) |
|
return -ENODEV; |
|
|
|
if (request) { |
|
*pwrst = pwrdm_read_next_pwrst(pwrdm); |
|
omap_iommu_dra7_emu_swsup_config(pdev, true); |
|
} |
|
|
|
if (*pwrst > PWRDM_POWER_RET) |
|
goto out; |
|
|
|
next_pwrst = request ? PWRDM_POWER_ON : *pwrst; |
|
|
|
ret = pwrdm_set_next_pwrst(pwrdm, next_pwrst); |
|
|
|
out: |
|
if (!request) |
|
omap_iommu_dra7_emu_swsup_config(pdev, false); |
|
|
|
return ret; |
|
}
|
|
|