Commit 1d81eedb8f6c13c262a70b8167530ec24b09c0ff

Authored by Lennert Buytenhek
Committed by Russell King
1 parent d384ea691f

[ARM] 3634/1: ep93xx: initial implementation of the clk_* API

Patch from Lennert Buytenhek

Add an initial implementation of the clk_* API for the cirrus ep93xx
to the tree.  The initial implementation is somewhat minimal, with the
intention of extending it as we go along.

Signed-off-by: Lennert Buytenhek <buytenh@wantstofly.org>
Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>

Showing 5 changed files with 162 additions and 1 deletions Side-by-side Diff

arch/arm/mach-ep93xx/Makefile
1 1 #
2 2 # Makefile for the linux kernel.
3 3 #
4   -obj-y := core.o
  4 +obj-y := core.o clock.o
5 5 obj-m :=
6 6 obj-n :=
7 7 obj- :=
arch/arm/mach-ep93xx/clock.c
  1 +/*
  2 + * arch/arm/mach-ep93xx/clock.c
  3 + * Clock control for Cirrus EP93xx chips.
  4 + *
  5 + * Copyright (C) 2006 Lennert Buytenhek <buytenh@wantstofly.org>
  6 + *
  7 + * This program is free software; you can redistribute it and/or modify
  8 + * it under the terms of the GNU General Public License as published by
  9 + * the Free Software Foundation; either version 2 of the License, or (at
  10 + * your option) any later version.
  11 + */
  12 +
  13 +#include <linux/kernel.h>
  14 +#include <linux/clk.h>
  15 +#include <linux/err.h>
  16 +#include <linux/string.h>
  17 +#include <asm/div64.h>
  18 +#include <asm/hardware.h>
  19 +#include <asm/io.h>
  20 +
  21 +struct clk {
  22 + char *name;
  23 + unsigned long rate;
  24 + int users;
  25 + u32 enable_reg;
  26 + u32 enable_mask;
  27 +};
  28 +
  29 +static struct clk clk_pll1 = {
  30 + .name = "pll1",
  31 +};
  32 +static struct clk clk_f = {
  33 + .name = "fclk",
  34 +};
  35 +static struct clk clk_h = {
  36 + .name = "hclk",
  37 +};
  38 +static struct clk clk_p = {
  39 + .name = "pclk",
  40 +};
  41 +static struct clk clk_pll2 = {
  42 + .name = "pll2",
  43 +};
  44 +static struct clk clk_usb_host = {
  45 + .name = "usb_host",
  46 + .enable_reg = EP93XX_SYSCON_CLOCK_CONTROL,
  47 + .enable_mask = EP93XX_SYSCON_CLOCK_USH_EN,
  48 +};
  49 +
  50 +
  51 +static struct clk *clocks[] = {
  52 + &clk_pll1,
  53 + &clk_f,
  54 + &clk_h,
  55 + &clk_p,
  56 + &clk_pll2,
  57 + &clk_usb_host,
  58 +};
  59 +
  60 +struct clk *clk_get(struct device *dev, const char *id)
  61 +{
  62 + int i;
  63 +
  64 + for (i = 0; i < ARRAY_SIZE(clocks); i++) {
  65 + if (!strcmp(clocks[i]->name, id))
  66 + return clocks[i];
  67 + }
  68 +
  69 + return ERR_PTR(-ENOENT);
  70 +}
  71 +
  72 +int clk_enable(struct clk *clk)
  73 +{
  74 + if (!clk->users++ && clk->enable_reg) {
  75 + u32 value;
  76 +
  77 + value = __raw_readl(clk->enable_reg);
  78 + __raw_writel(value | clk->enable_mask, clk->enable_reg);
  79 + }
  80 +
  81 + return 0;
  82 +}
  83 +
  84 +void clk_disable(struct clk *clk)
  85 +{
  86 + if (!--clk->users && clk->enable_reg) {
  87 + u32 value;
  88 +
  89 + value = __raw_readl(clk->enable_reg);
  90 + __raw_writel(value & ~clk->enable_mask, clk->enable_reg);
  91 + }
  92 +}
  93 +
  94 +unsigned long clk_get_rate(struct clk *clk)
  95 +{
  96 + return clk->rate;
  97 +}
  98 +
  99 +void clk_put(struct clk *clk)
  100 +{
  101 +}
  102 +
  103 +
  104 +
  105 +static char fclk_divisors[] = { 1, 2, 4, 8, 16, 1, 1, 1 };
  106 +static char hclk_divisors[] = { 1, 2, 4, 5, 6, 8, 16, 32 };
  107 +static char pclk_divisors[] = { 1, 2, 4, 8 };
  108 +
  109 +/*
  110 + * PLL rate = 14.7456 MHz * (X1FBD + 1) * (X2FBD + 1) / (X2IPD + 1) / 2^PS
  111 + */
  112 +static unsigned long calc_pll_rate(u32 config_word)
  113 +{
  114 + unsigned long long rate;
  115 + int i;
  116 +
  117 + rate = 14745600;
  118 + rate *= ((config_word >> 11) & 0x1f) + 1; /* X1FBD */
  119 + rate *= ((config_word >> 5) & 0x3f) + 1; /* X2FBD */
  120 + do_div(rate, (config_word & 0x1f) + 1); /* X2IPD */
  121 + for (i = 0; i < ((config_word >> 16) & 3); i++) /* PS */
  122 + rate >>= 1;
  123 +
  124 + return (unsigned long)rate;
  125 +}
  126 +
  127 +void ep93xx_clock_init(void)
  128 +{
  129 + u32 value;
  130 +
  131 + value = __raw_readl(EP93XX_SYSCON_CLOCK_SET1);
  132 + if (!(value & 0x00800000)) { /* PLL1 bypassed? */
  133 + clk_pll1.rate = 14745600;
  134 + } else {
  135 + clk_pll1.rate = calc_pll_rate(value);
  136 + }
  137 + clk_f.rate = clk_pll1.rate / fclk_divisors[(value >> 25) & 0x7];
  138 + clk_h.rate = clk_pll1.rate / hclk_divisors[(value >> 20) & 0x7];
  139 + clk_p.rate = clk_h.rate / pclk_divisors[(value >> 18) & 0x3];
  140 +
  141 + value = __raw_readl(EP93XX_SYSCON_CLOCK_SET2);
  142 + if (!(value & 0x00080000)) { /* PLL2 bypassed? */
  143 + clk_pll2.rate = 14745600;
  144 + } else if (value & 0x00040000) { /* PLL2 enabled? */
  145 + clk_pll2.rate = calc_pll_rate(value);
  146 + } else {
  147 + clk_pll2.rate = 0;
  148 + }
  149 + clk_usb_host.rate = clk_pll2.rate / (((value >> 28) & 0xf) + 1);
  150 +
  151 + printk(KERN_INFO "ep93xx: PLL1 running at %ld MHz, PLL2 at %ld MHz\n",
  152 + clk_pll1.rate / 1000000, clk_pll2.rate / 1000000);
  153 + printk(KERN_INFO "ep93xx: FCLK %ld MHz, HCLK %ld MHz, PCLK %ld MHz\n",
  154 + clk_f.rate / 1000000, clk_h.rate / 1000000,
  155 + clk_p.rate / 1000000);
  156 +}
arch/arm/mach-ep93xx/core.c
... ... @@ -437,6 +437,8 @@
437 437 {
438 438 unsigned int v;
439 439  
  440 + ep93xx_clock_init();
  441 +
440 442 /*
441 443 * Disallow access to MaverickCrunch initially.
442 444 */
include/asm-arm/arch-ep93xx/ep93xx-regs.h
... ... @@ -115,6 +115,8 @@
115 115 #define EP93XX_SYSCON_CLOCK_USH_EN 0x10000000
116 116 #define EP93XX_SYSCON_HALT EP93XX_SYSCON_REG(0x08)
117 117 #define EP93XX_SYSCON_STANDBY EP93XX_SYSCON_REG(0x0c)
  118 +#define EP93XX_SYSCON_CLOCK_SET1 EP93XX_SYSCON_REG(0x20)
  119 +#define EP93XX_SYSCON_CLOCK_SET2 EP93XX_SYSCON_REG(0x24)
118 120 #define EP93XX_SYSCON_DEVICE_CONFIG EP93XX_SYSCON_REG(0x80)
119 121 #define EP93XX_SYSCON_DEVICE_CONFIG_CRUNCH_ENABLE 0x00800000
120 122 #define EP93XX_SYSCON_SWLOCK EP93XX_SYSCON_REG(0xc0)
include/asm-arm/arch-ep93xx/platform.h
... ... @@ -8,6 +8,7 @@
8 8 void ep93xx_init_irq(void);
9 9 void ep93xx_init_time(unsigned long);
10 10 void ep93xx_init_devices(void);
  11 +void ep93xx_clock_init(void);
11 12 extern struct sys_timer ep93xx_timer;
12 13  
13 14