194 lines · c
1// SPDX-License-Identifier: GPL-2.0-or-later2/*3 * Copyright (C) 2013 Boris BREZILLON <b.brezillon@overkiz.com>4 */5 6#include <linux/clk.h>7#include <linux/clk-provider.h>8#include <linux/clkdev.h>9#include <linux/clk/at91_pmc.h>10#include <linux/of.h>11#include <linux/of_address.h>12#include <linux/mfd/syscon.h>13#include <linux/platform_device.h>14#include <linux/regmap.h>15#include <linux/syscore_ops.h>16 17#include <asm/proc-fns.h>18 19#include "pmc.h"20 21#define PMC_MAX_IDS 12822#define PMC_MAX_PCKS 823 24int of_at91_get_clk_range(struct device_node *np, const char *propname,25 struct clk_range *range)26{27 u32 min, max;28 int ret;29 30 ret = of_property_read_u32_index(np, propname, 0, &min);31 if (ret)32 return ret;33 34 ret = of_property_read_u32_index(np, propname, 1, &max);35 if (ret)36 return ret;37 38 if (range) {39 range->min = min;40 range->max = max;41 }42 43 return 0;44}45EXPORT_SYMBOL_GPL(of_at91_get_clk_range);46 47struct clk_hw *of_clk_hw_pmc_get(struct of_phandle_args *clkspec, void *data)48{49 unsigned int type = clkspec->args[0];50 unsigned int idx = clkspec->args[1];51 struct pmc_data *pmc_data = data;52 53 switch (type) {54 case PMC_TYPE_CORE:55 if (idx < pmc_data->ncore)56 return pmc_data->chws[idx];57 break;58 case PMC_TYPE_SYSTEM:59 if (idx < pmc_data->nsystem)60 return pmc_data->shws[idx];61 break;62 case PMC_TYPE_PERIPHERAL:63 if (idx < pmc_data->nperiph)64 return pmc_data->phws[idx];65 break;66 case PMC_TYPE_GCK:67 if (idx < pmc_data->ngck)68 return pmc_data->ghws[idx];69 break;70 case PMC_TYPE_PROGRAMMABLE:71 if (idx < pmc_data->npck)72 return pmc_data->pchws[idx];73 break;74 default:75 break;76 }77 78 pr_err("%s: invalid type (%u) or index (%u)\n", __func__, type, idx);79 80 return ERR_PTR(-EINVAL);81}82 83struct pmc_data *pmc_data_allocate(unsigned int ncore, unsigned int nsystem,84 unsigned int nperiph, unsigned int ngck,85 unsigned int npck)86{87 unsigned int num_clks = ncore + nsystem + nperiph + ngck + npck;88 struct pmc_data *pmc_data;89 90 pmc_data = kzalloc(struct_size(pmc_data, hwtable, num_clks),91 GFP_KERNEL);92 if (!pmc_data)93 return NULL;94 95 pmc_data->ncore = ncore;96 pmc_data->chws = pmc_data->hwtable;97 98 pmc_data->nsystem = nsystem;99 pmc_data->shws = pmc_data->chws + ncore;100 101 pmc_data->nperiph = nperiph;102 pmc_data->phws = pmc_data->shws + nsystem;103 104 pmc_data->ngck = ngck;105 pmc_data->ghws = pmc_data->phws + nperiph;106 107 pmc_data->npck = npck;108 pmc_data->pchws = pmc_data->ghws + ngck;109 110 return pmc_data;111}112 113#ifdef CONFIG_PM114 115/* Address in SECURAM that say if we suspend to backup mode. */116static void __iomem *at91_pmc_backup_suspend;117 118static int at91_pmc_suspend(void)119{120 unsigned int backup;121 122 if (!at91_pmc_backup_suspend)123 return 0;124 125 backup = readl_relaxed(at91_pmc_backup_suspend);126 if (!backup)127 return 0;128 129 return clk_save_context();130}131 132static void at91_pmc_resume(void)133{134 unsigned int backup;135 136 if (!at91_pmc_backup_suspend)137 return;138 139 backup = readl_relaxed(at91_pmc_backup_suspend);140 if (!backup)141 return;142 143 clk_restore_context();144}145 146static struct syscore_ops pmc_syscore_ops = {147 .suspend = at91_pmc_suspend,148 .resume = at91_pmc_resume,149};150 151static const struct of_device_id pmc_dt_ids[] = {152 { .compatible = "atmel,sama5d2-pmc" },153 { .compatible = "microchip,sama7g5-pmc", },154 { /* sentinel */ }155};156 157static int __init pmc_register_ops(void)158{159 struct device_node *np;160 161 np = of_find_matching_node(NULL, pmc_dt_ids);162 if (!np)163 return -ENODEV;164 165 if (!of_device_is_available(np)) {166 of_node_put(np);167 return -ENODEV;168 }169 of_node_put(np);170 171 np = of_find_compatible_node(NULL, NULL, "atmel,sama5d2-securam");172 if (!np)173 return -ENODEV;174 175 if (!of_device_is_available(np)) {176 of_node_put(np);177 return -ENODEV;178 }179 of_node_put(np);180 181 at91_pmc_backup_suspend = of_iomap(np, 0);182 if (!at91_pmc_backup_suspend) {183 pr_warn("%s(): unable to map securam\n", __func__);184 return -ENOMEM;185 }186 187 register_syscore_ops(&pmc_syscore_ops);188 189 return 0;190}191/* This has to happen before arch_initcall because of the tcb_clksrc driver */192postcore_initcall(pmc_register_ops);193#endif194