linux/arch/sh/kernel/cpu/hwblk.c
<<
>>
Prefs
   1#include <linux/clk.h>
   2#include <linux/compiler.h>
   3#include <linux/slab.h>
   4#include <linux/io.h>
   5#include <linux/spinlock.h>
   6#include <asm/suspend.h>
   7#include <asm/hwblk.h>
   8#include <asm/clock.h>
   9
  10static DEFINE_SPINLOCK(hwblk_lock);
  11
  12static void hwblk_area_mod_cnt(struct hwblk_info *info,
  13                               int area, int counter, int value, int goal)
  14{
  15        struct hwblk_area *hap = info->areas + area;
  16
  17        hap->cnt[counter] += value;
  18
  19        if (hap->cnt[counter] != goal)
  20                return;
  21
  22        if (hap->flags & HWBLK_AREA_FLAG_PARENT)
  23                hwblk_area_mod_cnt(info, hap->parent, counter, value, goal);
  24}
  25
  26
  27static int __hwblk_mod_cnt(struct hwblk_info *info, int hwblk,
  28                          int counter, int value, int goal)
  29{
  30        struct hwblk *hp = info->hwblks + hwblk;
  31
  32        hp->cnt[counter] += value;
  33        if (hp->cnt[counter] == goal)
  34                hwblk_area_mod_cnt(info, hp->area, counter, value, goal);
  35
  36        return hp->cnt[counter];
  37}
  38
  39static void hwblk_mod_cnt(struct hwblk_info *info, int hwblk,
  40                          int counter, int value, int goal)
  41{
  42        unsigned long flags;
  43
  44        spin_lock_irqsave(&hwblk_lock, flags);
  45        __hwblk_mod_cnt(info, hwblk, counter, value, goal);
  46        spin_unlock_irqrestore(&hwblk_lock, flags);
  47}
  48
  49void hwblk_cnt_inc(struct hwblk_info *info, int hwblk, int counter)
  50{
  51        hwblk_mod_cnt(info, hwblk, counter, 1, 1);
  52}
  53
  54void hwblk_cnt_dec(struct hwblk_info *info, int hwblk, int counter)
  55{
  56        hwblk_mod_cnt(info, hwblk, counter, -1, 0);
  57}
  58
  59void hwblk_enable(struct hwblk_info *info, int hwblk)
  60{
  61        struct hwblk *hp = info->hwblks + hwblk;
  62        unsigned long tmp;
  63        unsigned long flags;
  64        int ret;
  65
  66        spin_lock_irqsave(&hwblk_lock, flags);
  67
  68        ret = __hwblk_mod_cnt(info, hwblk, HWBLK_CNT_USAGE, 1, 1);
  69        if (ret == 1) {
  70                tmp = __raw_readl(hp->mstp);
  71                tmp &= ~(1 << hp->bit);
  72                __raw_writel(tmp, hp->mstp);
  73        }
  74
  75        spin_unlock_irqrestore(&hwblk_lock, flags);
  76}
  77
  78void hwblk_disable(struct hwblk_info *info, int hwblk)
  79{
  80        struct hwblk *hp = info->hwblks + hwblk;
  81        unsigned long tmp;
  82        unsigned long flags;
  83        int ret;
  84
  85        spin_lock_irqsave(&hwblk_lock, flags);
  86
  87        ret = __hwblk_mod_cnt(info, hwblk, HWBLK_CNT_USAGE, -1, 0);
  88        if (ret == 0) {
  89                tmp = __raw_readl(hp->mstp);
  90                tmp |= 1 << hp->bit;
  91                __raw_writel(tmp, hp->mstp);
  92        }
  93
  94        spin_unlock_irqrestore(&hwblk_lock, flags);
  95}
  96
  97struct hwblk_info *hwblk_info;
  98
  99int __init hwblk_register(struct hwblk_info *info)
 100{
 101        hwblk_info = info;
 102        return 0;
 103}
 104
 105int __init __weak arch_hwblk_init(void)
 106{
 107        return 0;
 108}
 109
 110int __weak arch_hwblk_sleep_mode(void)
 111{
 112        return SUSP_SH_SLEEP;
 113}
 114
 115int __init hwblk_init(void)
 116{
 117        return arch_hwblk_init();
 118}
 119
 120/* allow clocks to enable and disable hardware blocks */
 121static int sh_hwblk_clk_enable(struct clk *clk)
 122{
 123        if (!hwblk_info)
 124                return -ENOENT;
 125
 126        hwblk_enable(hwblk_info, clk->arch_flags);
 127        return 0;
 128}
 129
 130static void sh_hwblk_clk_disable(struct clk *clk)
 131{
 132        if (hwblk_info)
 133                hwblk_disable(hwblk_info, clk->arch_flags);
 134}
 135
 136static struct clk_ops sh_hwblk_clk_ops = {
 137        .enable         = sh_hwblk_clk_enable,
 138        .disable        = sh_hwblk_clk_disable,
 139        .recalc         = followparent_recalc,
 140};
 141
 142int __init sh_hwblk_clk_register(struct clk *clks, int nr)
 143{
 144        struct clk *clkp;
 145        int ret = 0;
 146        int k;
 147
 148        for (k = 0; !ret && (k < nr); k++) {
 149                clkp = clks + k;
 150                clkp->ops = &sh_hwblk_clk_ops;
 151                ret |= clk_register(clkp);
 152        }
 153
 154        return ret;
 155}
 156