1 // SPDX-License-Identifier: GPL-2.0-or-later 2 /* 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 128 22 #define PMC_MAX_PCKS 8 23 24 int 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 } 45 EXPORT_SYMBOL_GPL(of_at91_get_clk_range); 46 47 struct 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 83 struct 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_flex(*pmc_data, hwtable, num_clks); 91 if (!pmc_data) 92 return NULL; 93 94 pmc_data->ncore = ncore; 95 pmc_data->chws = pmc_data->hwtable; 96 97 pmc_data->nsystem = nsystem; 98 pmc_data->shws = pmc_data->chws + ncore; 99 100 pmc_data->nperiph = nperiph; 101 pmc_data->phws = pmc_data->shws + nsystem; 102 103 pmc_data->ngck = ngck; 104 pmc_data->ghws = pmc_data->phws + nperiph; 105 106 pmc_data->npck = npck; 107 pmc_data->pchws = pmc_data->ghws + ngck; 108 109 return pmc_data; 110 } 111 112 #ifdef CONFIG_PM 113 114 /* Address in SECURAM that say if we suspend to backup mode. */ 115 static void __iomem *at91_pmc_backup_suspend; 116 117 static int at91_pmc_suspend(void *data) 118 { 119 unsigned int backup; 120 121 if (!at91_pmc_backup_suspend) 122 return 0; 123 124 backup = readl_relaxed(at91_pmc_backup_suspend); 125 if (!backup) 126 return 0; 127 128 return clk_save_context(); 129 } 130 131 static void at91_pmc_resume(void *data) 132 { 133 unsigned int backup; 134 135 if (!at91_pmc_backup_suspend) 136 return; 137 138 backup = readl_relaxed(at91_pmc_backup_suspend); 139 if (!backup) 140 return; 141 142 clk_restore_context(); 143 } 144 145 static const struct syscore_ops pmc_syscore_ops = { 146 .suspend = at91_pmc_suspend, 147 .resume = at91_pmc_resume, 148 }; 149 150 static struct syscore pmc_syscore = { 151 .ops = &pmc_syscore_ops, 152 }; 153 154 static const struct of_device_id pmc_dt_ids[] = { 155 { .compatible = "atmel,sama5d2-pmc" }, 156 { .compatible = "microchip,sama7g5-pmc", }, 157 { .compatible = "microchip,sama7d65-pmc", }, 158 { /* sentinel */ } 159 }; 160 161 static int __init pmc_register_ops(void) 162 { 163 struct device_node *np; 164 165 np = of_find_matching_node(NULL, pmc_dt_ids); 166 if (!np) 167 return -ENODEV; 168 169 if (!of_device_is_available(np)) { 170 of_node_put(np); 171 return -ENODEV; 172 } 173 of_node_put(np); 174 175 np = of_find_compatible_node(NULL, NULL, "atmel,sama5d2-securam"); 176 if (!np) 177 return -ENODEV; 178 179 if (!of_device_is_available(np)) { 180 of_node_put(np); 181 return -ENODEV; 182 } 183 of_node_put(np); 184 185 at91_pmc_backup_suspend = of_iomap(np, 0); 186 if (!at91_pmc_backup_suspend) { 187 pr_warn("%s(): unable to map securam\n", __func__); 188 return -ENOMEM; 189 } 190 191 register_syscore(&pmc_syscore); 192 193 return 0; 194 } 195 /* This has to happen before arch_initcall because of the tcb_clksrc driver */ 196 postcore_initcall(pmc_register_ops); 197 #endif 198