162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Copyright (C) 2013 Boris BREZILLON <b.brezillon@overkiz.com> 462306a36Sopenharmony_ci */ 562306a36Sopenharmony_ci 662306a36Sopenharmony_ci#include <linux/clk.h> 762306a36Sopenharmony_ci#include <linux/clk-provider.h> 862306a36Sopenharmony_ci#include <linux/clkdev.h> 962306a36Sopenharmony_ci#include <linux/clk/at91_pmc.h> 1062306a36Sopenharmony_ci#include <linux/of.h> 1162306a36Sopenharmony_ci#include <linux/of_address.h> 1262306a36Sopenharmony_ci#include <linux/mfd/syscon.h> 1362306a36Sopenharmony_ci#include <linux/platform_device.h> 1462306a36Sopenharmony_ci#include <linux/regmap.h> 1562306a36Sopenharmony_ci#include <linux/syscore_ops.h> 1662306a36Sopenharmony_ci 1762306a36Sopenharmony_ci#include <asm/proc-fns.h> 1862306a36Sopenharmony_ci 1962306a36Sopenharmony_ci#include "pmc.h" 2062306a36Sopenharmony_ci 2162306a36Sopenharmony_ci#define PMC_MAX_IDS 128 2262306a36Sopenharmony_ci#define PMC_MAX_PCKS 8 2362306a36Sopenharmony_ci 2462306a36Sopenharmony_ciint of_at91_get_clk_range(struct device_node *np, const char *propname, 2562306a36Sopenharmony_ci struct clk_range *range) 2662306a36Sopenharmony_ci{ 2762306a36Sopenharmony_ci u32 min, max; 2862306a36Sopenharmony_ci int ret; 2962306a36Sopenharmony_ci 3062306a36Sopenharmony_ci ret = of_property_read_u32_index(np, propname, 0, &min); 3162306a36Sopenharmony_ci if (ret) 3262306a36Sopenharmony_ci return ret; 3362306a36Sopenharmony_ci 3462306a36Sopenharmony_ci ret = of_property_read_u32_index(np, propname, 1, &max); 3562306a36Sopenharmony_ci if (ret) 3662306a36Sopenharmony_ci return ret; 3762306a36Sopenharmony_ci 3862306a36Sopenharmony_ci if (range) { 3962306a36Sopenharmony_ci range->min = min; 4062306a36Sopenharmony_ci range->max = max; 4162306a36Sopenharmony_ci } 4262306a36Sopenharmony_ci 4362306a36Sopenharmony_ci return 0; 4462306a36Sopenharmony_ci} 4562306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(of_at91_get_clk_range); 4662306a36Sopenharmony_ci 4762306a36Sopenharmony_cistruct clk_hw *of_clk_hw_pmc_get(struct of_phandle_args *clkspec, void *data) 4862306a36Sopenharmony_ci{ 4962306a36Sopenharmony_ci unsigned int type = clkspec->args[0]; 5062306a36Sopenharmony_ci unsigned int idx = clkspec->args[1]; 5162306a36Sopenharmony_ci struct pmc_data *pmc_data = data; 5262306a36Sopenharmony_ci 5362306a36Sopenharmony_ci switch (type) { 5462306a36Sopenharmony_ci case PMC_TYPE_CORE: 5562306a36Sopenharmony_ci if (idx < pmc_data->ncore) 5662306a36Sopenharmony_ci return pmc_data->chws[idx]; 5762306a36Sopenharmony_ci break; 5862306a36Sopenharmony_ci case PMC_TYPE_SYSTEM: 5962306a36Sopenharmony_ci if (idx < pmc_data->nsystem) 6062306a36Sopenharmony_ci return pmc_data->shws[idx]; 6162306a36Sopenharmony_ci break; 6262306a36Sopenharmony_ci case PMC_TYPE_PERIPHERAL: 6362306a36Sopenharmony_ci if (idx < pmc_data->nperiph) 6462306a36Sopenharmony_ci return pmc_data->phws[idx]; 6562306a36Sopenharmony_ci break; 6662306a36Sopenharmony_ci case PMC_TYPE_GCK: 6762306a36Sopenharmony_ci if (idx < pmc_data->ngck) 6862306a36Sopenharmony_ci return pmc_data->ghws[idx]; 6962306a36Sopenharmony_ci break; 7062306a36Sopenharmony_ci case PMC_TYPE_PROGRAMMABLE: 7162306a36Sopenharmony_ci if (idx < pmc_data->npck) 7262306a36Sopenharmony_ci return pmc_data->pchws[idx]; 7362306a36Sopenharmony_ci break; 7462306a36Sopenharmony_ci default: 7562306a36Sopenharmony_ci break; 7662306a36Sopenharmony_ci } 7762306a36Sopenharmony_ci 7862306a36Sopenharmony_ci pr_err("%s: invalid type (%u) or index (%u)\n", __func__, type, idx); 7962306a36Sopenharmony_ci 8062306a36Sopenharmony_ci return ERR_PTR(-EINVAL); 8162306a36Sopenharmony_ci} 8262306a36Sopenharmony_ci 8362306a36Sopenharmony_cistruct pmc_data *pmc_data_allocate(unsigned int ncore, unsigned int nsystem, 8462306a36Sopenharmony_ci unsigned int nperiph, unsigned int ngck, 8562306a36Sopenharmony_ci unsigned int npck) 8662306a36Sopenharmony_ci{ 8762306a36Sopenharmony_ci unsigned int num_clks = ncore + nsystem + nperiph + ngck + npck; 8862306a36Sopenharmony_ci struct pmc_data *pmc_data; 8962306a36Sopenharmony_ci 9062306a36Sopenharmony_ci pmc_data = kzalloc(struct_size(pmc_data, hwtable, num_clks), 9162306a36Sopenharmony_ci GFP_KERNEL); 9262306a36Sopenharmony_ci if (!pmc_data) 9362306a36Sopenharmony_ci return NULL; 9462306a36Sopenharmony_ci 9562306a36Sopenharmony_ci pmc_data->ncore = ncore; 9662306a36Sopenharmony_ci pmc_data->chws = pmc_data->hwtable; 9762306a36Sopenharmony_ci 9862306a36Sopenharmony_ci pmc_data->nsystem = nsystem; 9962306a36Sopenharmony_ci pmc_data->shws = pmc_data->chws + ncore; 10062306a36Sopenharmony_ci 10162306a36Sopenharmony_ci pmc_data->nperiph = nperiph; 10262306a36Sopenharmony_ci pmc_data->phws = pmc_data->shws + nsystem; 10362306a36Sopenharmony_ci 10462306a36Sopenharmony_ci pmc_data->ngck = ngck; 10562306a36Sopenharmony_ci pmc_data->ghws = pmc_data->phws + nperiph; 10662306a36Sopenharmony_ci 10762306a36Sopenharmony_ci pmc_data->npck = npck; 10862306a36Sopenharmony_ci pmc_data->pchws = pmc_data->ghws + ngck; 10962306a36Sopenharmony_ci 11062306a36Sopenharmony_ci return pmc_data; 11162306a36Sopenharmony_ci} 11262306a36Sopenharmony_ci 11362306a36Sopenharmony_ci#ifdef CONFIG_PM 11462306a36Sopenharmony_ci 11562306a36Sopenharmony_ci/* Address in SECURAM that say if we suspend to backup mode. */ 11662306a36Sopenharmony_cistatic void __iomem *at91_pmc_backup_suspend; 11762306a36Sopenharmony_ci 11862306a36Sopenharmony_cistatic int at91_pmc_suspend(void) 11962306a36Sopenharmony_ci{ 12062306a36Sopenharmony_ci unsigned int backup; 12162306a36Sopenharmony_ci 12262306a36Sopenharmony_ci if (!at91_pmc_backup_suspend) 12362306a36Sopenharmony_ci return 0; 12462306a36Sopenharmony_ci 12562306a36Sopenharmony_ci backup = readl_relaxed(at91_pmc_backup_suspend); 12662306a36Sopenharmony_ci if (!backup) 12762306a36Sopenharmony_ci return 0; 12862306a36Sopenharmony_ci 12962306a36Sopenharmony_ci return clk_save_context(); 13062306a36Sopenharmony_ci} 13162306a36Sopenharmony_ci 13262306a36Sopenharmony_cistatic void at91_pmc_resume(void) 13362306a36Sopenharmony_ci{ 13462306a36Sopenharmony_ci unsigned int backup; 13562306a36Sopenharmony_ci 13662306a36Sopenharmony_ci if (!at91_pmc_backup_suspend) 13762306a36Sopenharmony_ci return; 13862306a36Sopenharmony_ci 13962306a36Sopenharmony_ci backup = readl_relaxed(at91_pmc_backup_suspend); 14062306a36Sopenharmony_ci if (!backup) 14162306a36Sopenharmony_ci return; 14262306a36Sopenharmony_ci 14362306a36Sopenharmony_ci clk_restore_context(); 14462306a36Sopenharmony_ci} 14562306a36Sopenharmony_ci 14662306a36Sopenharmony_cistatic struct syscore_ops pmc_syscore_ops = { 14762306a36Sopenharmony_ci .suspend = at91_pmc_suspend, 14862306a36Sopenharmony_ci .resume = at91_pmc_resume, 14962306a36Sopenharmony_ci}; 15062306a36Sopenharmony_ci 15162306a36Sopenharmony_cistatic const struct of_device_id pmc_dt_ids[] = { 15262306a36Sopenharmony_ci { .compatible = "atmel,sama5d2-pmc" }, 15362306a36Sopenharmony_ci { .compatible = "microchip,sama7g5-pmc", }, 15462306a36Sopenharmony_ci { /* sentinel */ } 15562306a36Sopenharmony_ci}; 15662306a36Sopenharmony_ci 15762306a36Sopenharmony_cistatic int __init pmc_register_ops(void) 15862306a36Sopenharmony_ci{ 15962306a36Sopenharmony_ci struct device_node *np; 16062306a36Sopenharmony_ci 16162306a36Sopenharmony_ci np = of_find_matching_node(NULL, pmc_dt_ids); 16262306a36Sopenharmony_ci if (!np) 16362306a36Sopenharmony_ci return -ENODEV; 16462306a36Sopenharmony_ci 16562306a36Sopenharmony_ci if (!of_device_is_available(np)) { 16662306a36Sopenharmony_ci of_node_put(np); 16762306a36Sopenharmony_ci return -ENODEV; 16862306a36Sopenharmony_ci } 16962306a36Sopenharmony_ci of_node_put(np); 17062306a36Sopenharmony_ci 17162306a36Sopenharmony_ci np = of_find_compatible_node(NULL, NULL, "atmel,sama5d2-securam"); 17262306a36Sopenharmony_ci if (!np) 17362306a36Sopenharmony_ci return -ENODEV; 17462306a36Sopenharmony_ci 17562306a36Sopenharmony_ci if (!of_device_is_available(np)) { 17662306a36Sopenharmony_ci of_node_put(np); 17762306a36Sopenharmony_ci return -ENODEV; 17862306a36Sopenharmony_ci } 17962306a36Sopenharmony_ci of_node_put(np); 18062306a36Sopenharmony_ci 18162306a36Sopenharmony_ci at91_pmc_backup_suspend = of_iomap(np, 0); 18262306a36Sopenharmony_ci if (!at91_pmc_backup_suspend) { 18362306a36Sopenharmony_ci pr_warn("%s(): unable to map securam\n", __func__); 18462306a36Sopenharmony_ci return -ENOMEM; 18562306a36Sopenharmony_ci } 18662306a36Sopenharmony_ci 18762306a36Sopenharmony_ci register_syscore_ops(&pmc_syscore_ops); 18862306a36Sopenharmony_ci 18962306a36Sopenharmony_ci return 0; 19062306a36Sopenharmony_ci} 19162306a36Sopenharmony_ci/* This has to happen before arch_initcall because of the tcb_clksrc driver */ 19262306a36Sopenharmony_cipostcore_initcall(pmc_register_ops); 19362306a36Sopenharmony_ci#endif 194