linux/drivers/clk/at91/pmc.c
<<
>>
Prefs
   1/*
   2 *  Copyright (C) 2013 Boris BREZILLON <b.brezillon@overkiz.com>
   3 *
   4 * This program is free software; you can redistribute it and/or modify
   5 * it under the terms of the GNU General Public License as published by
   6 * the Free Software Foundation; either version 2 of the License, or
   7 * (at your option) any later version.
   8 *
   9 */
  10
  11#include <linux/clk-provider.h>
  12#include <linux/clkdev.h>
  13#include <linux/clk/at91_pmc.h>
  14#include <linux/of.h>
  15#include <linux/mfd/syscon.h>
  16#include <linux/platform_device.h>
  17#include <linux/regmap.h>
  18#include <linux/syscore_ops.h>
  19
  20#include <asm/proc-fns.h>
  21
  22#include "pmc.h"
  23
  24#define PMC_MAX_IDS 128
  25
  26int of_at91_get_clk_range(struct device_node *np, const char *propname,
  27                          struct clk_range *range)
  28{
  29        u32 min, max;
  30        int ret;
  31
  32        ret = of_property_read_u32_index(np, propname, 0, &min);
  33        if (ret)
  34                return ret;
  35
  36        ret = of_property_read_u32_index(np, propname, 1, &max);
  37        if (ret)
  38                return ret;
  39
  40        if (range) {
  41                range->min = min;
  42                range->max = max;
  43        }
  44
  45        return 0;
  46}
  47EXPORT_SYMBOL_GPL(of_at91_get_clk_range);
  48
  49#ifdef CONFIG_PM
  50static struct regmap *pmcreg;
  51
  52static u8 registered_ids[PMC_MAX_IDS];
  53
  54static struct
  55{
  56        u32 scsr;
  57        u32 pcsr0;
  58        u32 uckr;
  59        u32 mor;
  60        u32 mcfr;
  61        u32 pllar;
  62        u32 mckr;
  63        u32 usb;
  64        u32 imr;
  65        u32 pcsr1;
  66        u32 pcr[PMC_MAX_IDS];
  67        u32 audio_pll0;
  68        u32 audio_pll1;
  69} pmc_cache;
  70
  71void pmc_register_id(u8 id)
  72{
  73        int i;
  74
  75        for (i = 0; i < PMC_MAX_IDS; i++) {
  76                if (registered_ids[i] == 0) {
  77                        registered_ids[i] = id;
  78                        break;
  79                }
  80                if (registered_ids[i] == id)
  81                        break;
  82        }
  83}
  84
  85static int pmc_suspend(void)
  86{
  87        int i;
  88
  89        regmap_read(pmcreg, AT91_PMC_IMR, &pmc_cache.scsr);
  90        regmap_read(pmcreg, AT91_PMC_PCSR, &pmc_cache.pcsr0);
  91        regmap_read(pmcreg, AT91_CKGR_UCKR, &pmc_cache.uckr);
  92        regmap_read(pmcreg, AT91_CKGR_MOR, &pmc_cache.mor);
  93        regmap_read(pmcreg, AT91_CKGR_MCFR, &pmc_cache.mcfr);
  94        regmap_read(pmcreg, AT91_CKGR_PLLAR, &pmc_cache.pllar);
  95        regmap_read(pmcreg, AT91_PMC_MCKR, &pmc_cache.mckr);
  96        regmap_read(pmcreg, AT91_PMC_USB, &pmc_cache.usb);
  97        regmap_read(pmcreg, AT91_PMC_IMR, &pmc_cache.imr);
  98        regmap_read(pmcreg, AT91_PMC_PCSR1, &pmc_cache.pcsr1);
  99
 100        for (i = 0; registered_ids[i]; i++) {
 101                regmap_write(pmcreg, AT91_PMC_PCR,
 102                             (registered_ids[i] & AT91_PMC_PCR_PID_MASK));
 103                regmap_read(pmcreg, AT91_PMC_PCR,
 104                            &pmc_cache.pcr[registered_ids[i]]);
 105        }
 106
 107        return 0;
 108}
 109
 110static void pmc_resume(void)
 111{
 112        int i, ret = 0;
 113        u32 tmp;
 114
 115        regmap_read(pmcreg, AT91_PMC_MCKR, &tmp);
 116        if (pmc_cache.mckr != tmp)
 117                pr_warn("MCKR was not configured properly by the firmware\n");
 118        regmap_read(pmcreg, AT91_CKGR_PLLAR, &tmp);
 119        if (pmc_cache.pllar != tmp)
 120                pr_warn("PLLAR was not configured properly by the firmware\n");
 121
 122        regmap_write(pmcreg, AT91_PMC_IMR, pmc_cache.scsr);
 123        regmap_write(pmcreg, AT91_PMC_PCER, pmc_cache.pcsr0);
 124        regmap_write(pmcreg, AT91_CKGR_UCKR, pmc_cache.uckr);
 125        regmap_write(pmcreg, AT91_CKGR_MOR, pmc_cache.mor);
 126        regmap_write(pmcreg, AT91_CKGR_MCFR, pmc_cache.mcfr);
 127        regmap_write(pmcreg, AT91_PMC_USB, pmc_cache.usb);
 128        regmap_write(pmcreg, AT91_PMC_IMR, pmc_cache.imr);
 129        regmap_write(pmcreg, AT91_PMC_PCER1, pmc_cache.pcsr1);
 130
 131        for (i = 0; registered_ids[i]; i++) {
 132                regmap_write(pmcreg, AT91_PMC_PCR,
 133                             pmc_cache.pcr[registered_ids[i]] |
 134                             AT91_PMC_PCR_CMD);
 135        }
 136
 137        if (pmc_cache.uckr & AT91_PMC_UPLLEN) {
 138                ret = regmap_read_poll_timeout(pmcreg, AT91_PMC_SR, tmp,
 139                                               !(tmp & AT91_PMC_LOCKU),
 140                                               10, 5000);
 141                if (ret)
 142                        pr_crit("USB PLL didn't lock when resuming\n");
 143        }
 144}
 145
 146static struct syscore_ops pmc_syscore_ops = {
 147        .suspend = pmc_suspend,
 148        .resume = pmc_resume,
 149};
 150
 151static const struct of_device_id sama5d2_pmc_dt_ids[] = {
 152        { .compatible = "atmel,sama5d2-pmc" },
 153        { /* sentinel */ }
 154};
 155
 156static int __init pmc_register_ops(void)
 157{
 158        struct device_node *np;
 159
 160        np = of_find_matching_node(NULL, sama5d2_pmc_dt_ids);
 161
 162        pmcreg = syscon_node_to_regmap(np);
 163        if (IS_ERR(pmcreg))
 164                return PTR_ERR(pmcreg);
 165
 166        register_syscore_ops(&pmc_syscore_ops);
 167
 168        return 0;
 169}
 170/* This has to happen before arch_initcall because of the tcb_clksrc driver */
 171postcore_initcall(pmc_register_ops);
 172#endif
 173