diff options
Diffstat (limited to 'drivers/clk/at91/pmc.c')
-rw-r--r-- | drivers/clk/at91/pmc.c | 193 |
1 files changed, 193 insertions, 0 deletions
diff --git a/drivers/clk/at91/pmc.c b/drivers/clk/at91/pmc.c new file mode 100644 index 0000000000..5aa9c1f1c8 --- /dev/null +++ b/drivers/clk/at91/pmc.c @@ -0,0 +1,193 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (C) 2013 Boris BREZILLON <b.brezillon@overkiz.com> + */ + +#include <linux/clk.h> +#include <linux/clk-provider.h> +#include <linux/clkdev.h> +#include <linux/clk/at91_pmc.h> +#include <linux/of.h> +#include <linux/of_address.h> +#include <linux/mfd/syscon.h> +#include <linux/platform_device.h> +#include <linux/regmap.h> +#include <linux/syscore_ops.h> + +#include <asm/proc-fns.h> + +#include "pmc.h" + +#define PMC_MAX_IDS 128 +#define PMC_MAX_PCKS 8 + +int of_at91_get_clk_range(struct device_node *np, const char *propname, + struct clk_range *range) +{ + u32 min, max; + int ret; + + ret = of_property_read_u32_index(np, propname, 0, &min); + if (ret) + return ret; + + ret = of_property_read_u32_index(np, propname, 1, &max); + if (ret) + return ret; + + if (range) { + range->min = min; + range->max = max; + } + + return 0; +} +EXPORT_SYMBOL_GPL(of_at91_get_clk_range); + +struct clk_hw *of_clk_hw_pmc_get(struct of_phandle_args *clkspec, void *data) +{ + unsigned int type = clkspec->args[0]; + unsigned int idx = clkspec->args[1]; + struct pmc_data *pmc_data = data; + + switch (type) { + case PMC_TYPE_CORE: + if (idx < pmc_data->ncore) + return pmc_data->chws[idx]; + break; + case PMC_TYPE_SYSTEM: + if (idx < pmc_data->nsystem) + return pmc_data->shws[idx]; + break; + case PMC_TYPE_PERIPHERAL: + if (idx < pmc_data->nperiph) + return pmc_data->phws[idx]; + break; + case PMC_TYPE_GCK: + if (idx < pmc_data->ngck) + return pmc_data->ghws[idx]; + break; + case PMC_TYPE_PROGRAMMABLE: + if (idx < pmc_data->npck) + return pmc_data->pchws[idx]; + break; + default: + break; + } + + pr_err("%s: invalid type (%u) or index (%u)\n", __func__, type, idx); + + return ERR_PTR(-EINVAL); +} + +struct pmc_data *pmc_data_allocate(unsigned int ncore, unsigned int nsystem, + unsigned int nperiph, unsigned int ngck, + unsigned int npck) +{ + unsigned int num_clks = ncore + nsystem + nperiph + ngck + npck; + struct pmc_data *pmc_data; + + pmc_data = kzalloc(struct_size(pmc_data, hwtable, num_clks), + GFP_KERNEL); + if (!pmc_data) + return NULL; + + pmc_data->ncore = ncore; + pmc_data->chws = pmc_data->hwtable; + + pmc_data->nsystem = nsystem; + pmc_data->shws = pmc_data->chws + ncore; + + pmc_data->nperiph = nperiph; + pmc_data->phws = pmc_data->shws + nsystem; + + pmc_data->ngck = ngck; + pmc_data->ghws = pmc_data->phws + nperiph; + + pmc_data->npck = npck; + pmc_data->pchws = pmc_data->ghws + ngck; + + return pmc_data; +} + +#ifdef CONFIG_PM + +/* Address in SECURAM that say if we suspend to backup mode. */ +static void __iomem *at91_pmc_backup_suspend; + +static int at91_pmc_suspend(void) +{ + unsigned int backup; + + if (!at91_pmc_backup_suspend) + return 0; + + backup = readl_relaxed(at91_pmc_backup_suspend); + if (!backup) + return 0; + + return clk_save_context(); +} + +static void at91_pmc_resume(void) +{ + unsigned int backup; + + if (!at91_pmc_backup_suspend) + return; + + backup = readl_relaxed(at91_pmc_backup_suspend); + if (!backup) + return; + + clk_restore_context(); +} + +static struct syscore_ops pmc_syscore_ops = { + .suspend = at91_pmc_suspend, + .resume = at91_pmc_resume, +}; + +static const struct of_device_id pmc_dt_ids[] = { + { .compatible = "atmel,sama5d2-pmc" }, + { .compatible = "microchip,sama7g5-pmc", }, + { /* sentinel */ } +}; + +static int __init pmc_register_ops(void) +{ + struct device_node *np; + + np = of_find_matching_node(NULL, pmc_dt_ids); + if (!np) + return -ENODEV; + + if (!of_device_is_available(np)) { + of_node_put(np); + return -ENODEV; + } + of_node_put(np); + + np = of_find_compatible_node(NULL, NULL, "atmel,sama5d2-securam"); + if (!np) + return -ENODEV; + + if (!of_device_is_available(np)) { + of_node_put(np); + return -ENODEV; + } + of_node_put(np); + + at91_pmc_backup_suspend = of_iomap(np, 0); + if (!at91_pmc_backup_suspend) { + pr_warn("%s(): unable to map securam\n", __func__); + return -ENOMEM; + } + + register_syscore_ops(&pmc_syscore_ops); + + return 0; +} +/* This has to happen before arch_initcall because of the tcb_clksrc driver */ +postcore_initcall(pmc_register_ops); +#endif |