18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * OMAP IOMMU quirks for various TI SoCs 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (C) 2015-2019 Texas Instruments Incorporated - https://www.ti.com/ 68c2ecf20Sopenharmony_ci * Suman Anna <s-anna@ti.com> 78c2ecf20Sopenharmony_ci */ 88c2ecf20Sopenharmony_ci 98c2ecf20Sopenharmony_ci#include <linux/platform_device.h> 108c2ecf20Sopenharmony_ci#include <linux/err.h> 118c2ecf20Sopenharmony_ci#include <linux/clk.h> 128c2ecf20Sopenharmony_ci#include <linux/list.h> 138c2ecf20Sopenharmony_ci 148c2ecf20Sopenharmony_ci#include "clockdomain.h" 158c2ecf20Sopenharmony_ci#include "powerdomain.h" 168c2ecf20Sopenharmony_ci 178c2ecf20Sopenharmony_cistruct pwrdm_link { 188c2ecf20Sopenharmony_ci struct device *dev; 198c2ecf20Sopenharmony_ci struct powerdomain *pwrdm; 208c2ecf20Sopenharmony_ci struct list_head node; 218c2ecf20Sopenharmony_ci}; 228c2ecf20Sopenharmony_ci 238c2ecf20Sopenharmony_cistatic DEFINE_SPINLOCK(iommu_lock); 248c2ecf20Sopenharmony_cistatic struct clockdomain *emu_clkdm; 258c2ecf20Sopenharmony_cistatic atomic_t emu_count; 268c2ecf20Sopenharmony_ci 278c2ecf20Sopenharmony_cistatic void omap_iommu_dra7_emu_swsup_config(struct platform_device *pdev, 288c2ecf20Sopenharmony_ci bool enable) 298c2ecf20Sopenharmony_ci{ 308c2ecf20Sopenharmony_ci struct device_node *np = pdev->dev.of_node; 318c2ecf20Sopenharmony_ci unsigned long flags; 328c2ecf20Sopenharmony_ci 338c2ecf20Sopenharmony_ci if (!of_device_is_compatible(np, "ti,dra7-dsp-iommu")) 348c2ecf20Sopenharmony_ci return; 358c2ecf20Sopenharmony_ci 368c2ecf20Sopenharmony_ci if (!emu_clkdm) { 378c2ecf20Sopenharmony_ci emu_clkdm = clkdm_lookup("emu_clkdm"); 388c2ecf20Sopenharmony_ci if (WARN_ON_ONCE(!emu_clkdm)) 398c2ecf20Sopenharmony_ci return; 408c2ecf20Sopenharmony_ci } 418c2ecf20Sopenharmony_ci 428c2ecf20Sopenharmony_ci spin_lock_irqsave(&iommu_lock, flags); 438c2ecf20Sopenharmony_ci 448c2ecf20Sopenharmony_ci if (enable && (atomic_inc_return(&emu_count) == 1)) 458c2ecf20Sopenharmony_ci clkdm_deny_idle(emu_clkdm); 468c2ecf20Sopenharmony_ci else if (!enable && (atomic_dec_return(&emu_count) == 0)) 478c2ecf20Sopenharmony_ci clkdm_allow_idle(emu_clkdm); 488c2ecf20Sopenharmony_ci 498c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&iommu_lock, flags); 508c2ecf20Sopenharmony_ci} 518c2ecf20Sopenharmony_ci 528c2ecf20Sopenharmony_cistatic struct powerdomain *_get_pwrdm(struct device *dev) 538c2ecf20Sopenharmony_ci{ 548c2ecf20Sopenharmony_ci struct clk *clk; 558c2ecf20Sopenharmony_ci struct clk_hw_omap *hwclk; 568c2ecf20Sopenharmony_ci struct clockdomain *clkdm; 578c2ecf20Sopenharmony_ci struct powerdomain *pwrdm = NULL; 588c2ecf20Sopenharmony_ci struct pwrdm_link *entry; 598c2ecf20Sopenharmony_ci unsigned long flags; 608c2ecf20Sopenharmony_ci static LIST_HEAD(cache); 618c2ecf20Sopenharmony_ci 628c2ecf20Sopenharmony_ci spin_lock_irqsave(&iommu_lock, flags); 638c2ecf20Sopenharmony_ci 648c2ecf20Sopenharmony_ci list_for_each_entry(entry, &cache, node) { 658c2ecf20Sopenharmony_ci if (entry->dev == dev) { 668c2ecf20Sopenharmony_ci pwrdm = entry->pwrdm; 678c2ecf20Sopenharmony_ci break; 688c2ecf20Sopenharmony_ci } 698c2ecf20Sopenharmony_ci } 708c2ecf20Sopenharmony_ci 718c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&iommu_lock, flags); 728c2ecf20Sopenharmony_ci 738c2ecf20Sopenharmony_ci if (pwrdm) 748c2ecf20Sopenharmony_ci return pwrdm; 758c2ecf20Sopenharmony_ci 768c2ecf20Sopenharmony_ci clk = of_clk_get(dev->of_node->parent, 0); 778c2ecf20Sopenharmony_ci if (IS_ERR(clk)) { 788c2ecf20Sopenharmony_ci dev_err(dev, "no fck found\n"); 798c2ecf20Sopenharmony_ci return NULL; 808c2ecf20Sopenharmony_ci } 818c2ecf20Sopenharmony_ci 828c2ecf20Sopenharmony_ci hwclk = to_clk_hw_omap(__clk_get_hw(clk)); 838c2ecf20Sopenharmony_ci clk_put(clk); 848c2ecf20Sopenharmony_ci if (!hwclk || !hwclk->clkdm_name) { 858c2ecf20Sopenharmony_ci dev_err(dev, "no hwclk data\n"); 868c2ecf20Sopenharmony_ci return NULL; 878c2ecf20Sopenharmony_ci } 888c2ecf20Sopenharmony_ci 898c2ecf20Sopenharmony_ci clkdm = clkdm_lookup(hwclk->clkdm_name); 908c2ecf20Sopenharmony_ci if (!clkdm) { 918c2ecf20Sopenharmony_ci dev_err(dev, "clkdm not found: %s\n", hwclk->clkdm_name); 928c2ecf20Sopenharmony_ci return NULL; 938c2ecf20Sopenharmony_ci } 948c2ecf20Sopenharmony_ci 958c2ecf20Sopenharmony_ci pwrdm = clkdm_get_pwrdm(clkdm); 968c2ecf20Sopenharmony_ci if (!pwrdm) { 978c2ecf20Sopenharmony_ci dev_err(dev, "pwrdm not found: %s\n", clkdm->name); 988c2ecf20Sopenharmony_ci return NULL; 998c2ecf20Sopenharmony_ci } 1008c2ecf20Sopenharmony_ci 1018c2ecf20Sopenharmony_ci entry = kmalloc(sizeof(*entry), GFP_KERNEL); 1028c2ecf20Sopenharmony_ci if (entry) { 1038c2ecf20Sopenharmony_ci entry->dev = dev; 1048c2ecf20Sopenharmony_ci entry->pwrdm = pwrdm; 1058c2ecf20Sopenharmony_ci spin_lock_irqsave(&iommu_lock, flags); 1068c2ecf20Sopenharmony_ci list_add(&entry->node, &cache); 1078c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&iommu_lock, flags); 1088c2ecf20Sopenharmony_ci } 1098c2ecf20Sopenharmony_ci 1108c2ecf20Sopenharmony_ci return pwrdm; 1118c2ecf20Sopenharmony_ci} 1128c2ecf20Sopenharmony_ci 1138c2ecf20Sopenharmony_ciint omap_iommu_set_pwrdm_constraint(struct platform_device *pdev, bool request, 1148c2ecf20Sopenharmony_ci u8 *pwrst) 1158c2ecf20Sopenharmony_ci{ 1168c2ecf20Sopenharmony_ci struct powerdomain *pwrdm; 1178c2ecf20Sopenharmony_ci u8 next_pwrst; 1188c2ecf20Sopenharmony_ci int ret = 0; 1198c2ecf20Sopenharmony_ci 1208c2ecf20Sopenharmony_ci pwrdm = _get_pwrdm(&pdev->dev); 1218c2ecf20Sopenharmony_ci if (!pwrdm) 1228c2ecf20Sopenharmony_ci return -ENODEV; 1238c2ecf20Sopenharmony_ci 1248c2ecf20Sopenharmony_ci if (request) { 1258c2ecf20Sopenharmony_ci *pwrst = pwrdm_read_next_pwrst(pwrdm); 1268c2ecf20Sopenharmony_ci omap_iommu_dra7_emu_swsup_config(pdev, true); 1278c2ecf20Sopenharmony_ci } 1288c2ecf20Sopenharmony_ci 1298c2ecf20Sopenharmony_ci if (*pwrst > PWRDM_POWER_RET) 1308c2ecf20Sopenharmony_ci goto out; 1318c2ecf20Sopenharmony_ci 1328c2ecf20Sopenharmony_ci next_pwrst = request ? PWRDM_POWER_ON : *pwrst; 1338c2ecf20Sopenharmony_ci 1348c2ecf20Sopenharmony_ci ret = pwrdm_set_next_pwrst(pwrdm, next_pwrst); 1358c2ecf20Sopenharmony_ci 1368c2ecf20Sopenharmony_ciout: 1378c2ecf20Sopenharmony_ci if (!request) 1388c2ecf20Sopenharmony_ci omap_iommu_dra7_emu_swsup_config(pdev, false); 1398c2ecf20Sopenharmony_ci 1408c2ecf20Sopenharmony_ci return ret; 1418c2ecf20Sopenharmony_ci} 142