diff options
| -rw-r--r-- | arch/arm/mach-ep93xx/Makefile | 2 | ||||
| -rw-r--r-- | arch/arm/mach-ep93xx/clock.c | 156 | ||||
| -rw-r--r-- | arch/arm/mach-ep93xx/core.c | 2 | ||||
| -rw-r--r-- | include/asm-arm/arch-ep93xx/ep93xx-regs.h | 2 | ||||
| -rw-r--r-- | include/asm-arm/arch-ep93xx/platform.h | 1 |
5 files changed, 162 insertions, 1 deletions
diff --git a/arch/arm/mach-ep93xx/Makefile b/arch/arm/mach-ep93xx/Makefile index 5393af989e94..05a48a21038e 100644 --- a/arch/arm/mach-ep93xx/Makefile +++ b/arch/arm/mach-ep93xx/Makefile | |||
| @@ -1,7 +1,7 @@ | |||
| 1 | # | 1 | # |
| 2 | # Makefile for the linux kernel. | 2 | # Makefile for the linux kernel. |
| 3 | # | 3 | # |
| 4 | obj-y := core.o | 4 | obj-y := core.o clock.o |
| 5 | obj-m := | 5 | obj-m := |
| 6 | obj-n := | 6 | obj-n := |
| 7 | obj- := | 7 | obj- := |
diff --git a/arch/arm/mach-ep93xx/clock.c b/arch/arm/mach-ep93xx/clock.c new file mode 100644 index 000000000000..08ad782c1649 --- /dev/null +++ b/arch/arm/mach-ep93xx/clock.c | |||
| @@ -0,0 +1,156 @@ | |||
| 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 | } | ||
diff --git a/arch/arm/mach-ep93xx/core.c b/arch/arm/mach-ep93xx/core.c index bf6bd71bdd08..6fd6aa74a1ff 100644 --- a/arch/arm/mach-ep93xx/core.c +++ b/arch/arm/mach-ep93xx/core.c | |||
| @@ -437,6 +437,8 @@ void __init ep93xx_init_devices(void) | |||
| 437 | { | 437 | { |
| 438 | unsigned int v; | 438 | unsigned int v; |
| 439 | 439 | ||
| 440 | ep93xx_clock_init(); | ||
| 441 | |||
| 440 | /* | 442 | /* |
| 441 | * Disallow access to MaverickCrunch initially. | 443 | * Disallow access to MaverickCrunch initially. |
| 442 | */ | 444 | */ |
diff --git a/include/asm-arm/arch-ep93xx/ep93xx-regs.h b/include/asm-arm/arch-ep93xx/ep93xx-regs.h index 71cea0b5841b..8c322975f96e 100644 --- a/include/asm-arm/arch-ep93xx/ep93xx-regs.h +++ b/include/asm-arm/arch-ep93xx/ep93xx-regs.h | |||
| @@ -115,6 +115,8 @@ | |||
| 115 | #define EP93XX_SYSCON_CLOCK_USH_EN 0x10000000 | 115 | #define EP93XX_SYSCON_CLOCK_USH_EN 0x10000000 |
| 116 | #define EP93XX_SYSCON_HALT EP93XX_SYSCON_REG(0x08) | 116 | #define EP93XX_SYSCON_HALT EP93XX_SYSCON_REG(0x08) |
| 117 | #define EP93XX_SYSCON_STANDBY EP93XX_SYSCON_REG(0x0c) | 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 | #define EP93XX_SYSCON_DEVICE_CONFIG EP93XX_SYSCON_REG(0x80) | 120 | #define EP93XX_SYSCON_DEVICE_CONFIG EP93XX_SYSCON_REG(0x80) |
| 119 | #define EP93XX_SYSCON_DEVICE_CONFIG_CRUNCH_ENABLE 0x00800000 | 121 | #define EP93XX_SYSCON_DEVICE_CONFIG_CRUNCH_ENABLE 0x00800000 |
| 120 | #define EP93XX_SYSCON_SWLOCK EP93XX_SYSCON_REG(0xc0) | 122 | #define EP93XX_SYSCON_SWLOCK EP93XX_SYSCON_REG(0xc0) |
diff --git a/include/asm-arm/arch-ep93xx/platform.h b/include/asm-arm/arch-ep93xx/platform.h index df9cbb6ef660..d7a34ce20293 100644 --- a/include/asm-arm/arch-ep93xx/platform.h +++ b/include/asm-arm/arch-ep93xx/platform.h | |||
| @@ -8,6 +8,7 @@ void ep93xx_map_io(void); | |||
| 8 | void ep93xx_init_irq(void); | 8 | void ep93xx_init_irq(void); |
| 9 | void ep93xx_init_time(unsigned long); | 9 | void ep93xx_init_time(unsigned long); |
| 10 | void ep93xx_init_devices(void); | 10 | void ep93xx_init_devices(void); |
| 11 | void ep93xx_clock_init(void); | ||
| 11 | extern struct sys_timer ep93xx_timer; | 12 | extern struct sys_timer ep93xx_timer; |
| 12 | 13 | ||
| 13 | 14 | ||
