linux/arch/arm/mach-s3c24xx/clock-s3c2410.c
<<
>>
Prefs
   1/*
   2 * Copyright (c) 2006 Simtec Electronics
   3 *      Ben Dooks <ben@simtec.co.uk>
   4 *
   5 * S3C2410,S3C2440,S3C2442 Clock control support
   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
  10 * (at your option) any later version.
  11 *
  12 * This program is distributed in the hope that it will be useful,
  13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
  14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  15 * GNU General Public License for more details.
  16 *
  17 * You should have received a copy of the GNU General Public License
  18 * along with this program; if not, write to the Free Software
  19 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
  20*/
  21
  22#include <linux/init.h>
  23#include <linux/module.h>
  24#include <linux/kernel.h>
  25#include <linux/list.h>
  26#include <linux/errno.h>
  27#include <linux/err.h>
  28#include <linux/device.h>
  29#include <linux/clk.h>
  30#include <linux/mutex.h>
  31#include <linux/delay.h>
  32#include <linux/serial_core.h>
  33#include <linux/io.h>
  34
  35#include <asm/mach/map.h>
  36
  37#include <mach/hardware.h>
  38
  39#include <plat/regs-serial.h>
  40#include <mach/regs-clock.h>
  41#include <mach/regs-gpio.h>
  42
  43#include <plat/clock.h>
  44#include <plat/cpu.h>
  45
  46int s3c2410_clkcon_enable(struct clk *clk, int enable)
  47{
  48        unsigned int clocks = clk->ctrlbit;
  49        unsigned long clkcon;
  50
  51        clkcon = __raw_readl(S3C2410_CLKCON);
  52
  53        if (enable)
  54                clkcon |= clocks;
  55        else
  56                clkcon &= ~clocks;
  57
  58        /* ensure none of the special function bits set */
  59        clkcon &= ~(S3C2410_CLKCON_IDLE|S3C2410_CLKCON_POWER);
  60
  61        __raw_writel(clkcon, S3C2410_CLKCON);
  62
  63        return 0;
  64}
  65
  66static int s3c2410_upll_enable(struct clk *clk, int enable)
  67{
  68        unsigned long clkslow = __raw_readl(S3C2410_CLKSLOW);
  69        unsigned long orig = clkslow;
  70
  71        if (enable)
  72                clkslow &= ~S3C2410_CLKSLOW_UCLK_OFF;
  73        else
  74                clkslow |= S3C2410_CLKSLOW_UCLK_OFF;
  75
  76        __raw_writel(clkslow, S3C2410_CLKSLOW);
  77
  78        /* if we started the UPLL, then allow to settle */
  79
  80        if (enable && (orig & S3C2410_CLKSLOW_UCLK_OFF))
  81                udelay(200);
  82
  83        return 0;
  84}
  85
  86/* standard clock definitions */
  87
  88static struct clk init_clocks_off[] = {
  89        {
  90                .name           = "nand",
  91                .parent         = &clk_h,
  92                .enable         = s3c2410_clkcon_enable,
  93                .ctrlbit        = S3C2410_CLKCON_NAND,
  94        }, {
  95                .name           = "sdi",
  96                .parent         = &clk_p,
  97                .enable         = s3c2410_clkcon_enable,
  98                .ctrlbit        = S3C2410_CLKCON_SDI,
  99        }, {
 100                .name           = "adc",
 101                .parent         = &clk_p,
 102                .enable         = s3c2410_clkcon_enable,
 103                .ctrlbit        = S3C2410_CLKCON_ADC,
 104        }, {
 105                .name           = "i2c",
 106                .parent         = &clk_p,
 107                .enable         = s3c2410_clkcon_enable,
 108                .ctrlbit        = S3C2410_CLKCON_IIC,
 109        }, {
 110                .name           = "iis",
 111                .parent         = &clk_p,
 112                .enable         = s3c2410_clkcon_enable,
 113                .ctrlbit        = S3C2410_CLKCON_IIS,
 114        }, {
 115                .name           = "spi",
 116                .parent         = &clk_p,
 117                .enable         = s3c2410_clkcon_enable,
 118                .ctrlbit        = S3C2410_CLKCON_SPI,
 119        }
 120};
 121
 122static struct clk clk_lcd = {
 123        .name           = "lcd",
 124        .parent         = &clk_h,
 125        .enable         = s3c2410_clkcon_enable,
 126        .ctrlbit        = S3C2410_CLKCON_LCDC,
 127};
 128
 129static struct clk clk_gpio = {
 130        .name           = "gpio",
 131        .parent         = &clk_p,
 132        .enable         = s3c2410_clkcon_enable,
 133        .ctrlbit        = S3C2410_CLKCON_GPIO,
 134};
 135
 136static struct clk clk_usb_host = {
 137        .name           = "usb-host",
 138        .parent         = &clk_h,
 139        .enable         = s3c2410_clkcon_enable,
 140        .ctrlbit        = S3C2410_CLKCON_USBH,
 141};
 142
 143static struct clk clk_usb_device = {
 144        .name           = "usb-device",
 145        .parent         = &clk_h,
 146        .enable         = s3c2410_clkcon_enable,
 147        .ctrlbit        = S3C2410_CLKCON_USBD,
 148};
 149
 150static struct clk clk_timers = {
 151        .name           = "timers",
 152        .parent         = &clk_p,
 153        .enable         = s3c2410_clkcon_enable,
 154        .ctrlbit        = S3C2410_CLKCON_PWMT,
 155};
 156
 157struct clk s3c24xx_clk_uart0 = {
 158        .name           = "uart",
 159        .devname        = "s3c2410-uart.0",
 160        .parent         = &clk_p,
 161        .enable         = s3c2410_clkcon_enable,
 162        .ctrlbit        = S3C2410_CLKCON_UART0,
 163};
 164
 165struct clk s3c24xx_clk_uart1 = {
 166        .name           = "uart",
 167        .devname        = "s3c2410-uart.1",
 168        .parent         = &clk_p,
 169        .enable         = s3c2410_clkcon_enable,
 170        .ctrlbit        = S3C2410_CLKCON_UART1,
 171};
 172
 173struct clk s3c24xx_clk_uart2 = {
 174        .name           = "uart",
 175        .devname        = "s3c2410-uart.2",
 176        .parent         = &clk_p,
 177        .enable         = s3c2410_clkcon_enable,
 178        .ctrlbit        = S3C2410_CLKCON_UART2,
 179};
 180
 181static struct clk clk_rtc = {
 182        .name           = "rtc",
 183        .parent         = &clk_p,
 184        .enable         = s3c2410_clkcon_enable,
 185        .ctrlbit        = S3C2410_CLKCON_RTC,
 186};
 187
 188static struct clk clk_watchdog = {
 189        .name           = "watchdog",
 190        .parent         = &clk_p,
 191        .ctrlbit        = 0,
 192};
 193
 194static struct clk clk_usb_bus_host = {
 195        .name           = "usb-bus-host",
 196        .parent         = &clk_usb_bus,
 197};
 198
 199static struct clk clk_usb_bus_gadget = {
 200        .name           = "usb-bus-gadget",
 201        .parent         = &clk_usb_bus,
 202};
 203
 204static struct clk *init_clocks[] = {
 205        &clk_lcd,
 206        &clk_gpio,
 207        &clk_usb_host,
 208        &clk_usb_device,
 209        &clk_timers,
 210        &s3c24xx_clk_uart0,
 211        &s3c24xx_clk_uart1,
 212        &s3c24xx_clk_uart2,
 213        &clk_rtc,
 214        &clk_watchdog,
 215        &clk_usb_bus_host,
 216        &clk_usb_bus_gadget,
 217};
 218
 219/* s3c2410_baseclk_add()
 220 *
 221 * Add all the clocks used by the s3c2410 or compatible CPUs
 222 * such as the S3C2440 and S3C2442.
 223 *
 224 * We cannot use a system device as we are needed before any
 225 * of the init-calls that initialise the devices are actually
 226 * done.
 227*/
 228
 229int __init s3c2410_baseclk_add(void)
 230{
 231        unsigned long clkslow = __raw_readl(S3C2410_CLKSLOW);
 232        unsigned long clkcon  = __raw_readl(S3C2410_CLKCON);
 233        struct clk *xtal;
 234        int ret;
 235        int ptr;
 236
 237        clk_upll.enable = s3c2410_upll_enable;
 238
 239        if (s3c24xx_register_clock(&clk_usb_bus) < 0)
 240                printk(KERN_ERR "failed to register usb bus clock\n");
 241
 242        /* register clocks from clock array */
 243
 244        for (ptr = 0; ptr < ARRAY_SIZE(init_clocks); ptr++) {
 245                struct clk *clkp = init_clocks[ptr];
 246
 247                /* ensure that we note the clock state */
 248
 249                clkp->usage = clkcon & clkp->ctrlbit ? 1 : 0;
 250
 251                ret = s3c24xx_register_clock(clkp);
 252                if (ret < 0) {
 253                        printk(KERN_ERR "Failed to register clock %s (%d)\n",
 254                               clkp->name, ret);
 255                }
 256        }
 257
 258        /* We must be careful disabling the clocks we are not intending to
 259         * be using at boot time, as subsystems such as the LCD which do
 260         * their own DMA requests to the bus can cause the system to lockup
 261         * if they where in the middle of requesting bus access.
 262         *
 263         * Disabling the LCD clock if the LCD is active is very dangerous,
 264         * and therefore the bootloader should be careful to not enable
 265         * the LCD clock if it is not needed.
 266        */
 267
 268        /* install (and disable) the clocks we do not need immediately */
 269
 270        s3c_register_clocks(init_clocks_off, ARRAY_SIZE(init_clocks_off));
 271        s3c_disable_clocks(init_clocks_off, ARRAY_SIZE(init_clocks_off));
 272
 273        /* show the clock-slow value */
 274
 275        xtal = clk_get(NULL, "xtal");
 276
 277        printk("CLOCK: Slow mode (%ld.%ld MHz), %s, MPLL %s, UPLL %s\n",
 278               print_mhz(clk_get_rate(xtal) /
 279                         ( 2 * S3C2410_CLKSLOW_GET_SLOWVAL(clkslow))),
 280               (clkslow & S3C2410_CLKSLOW_SLOW) ? "slow" : "fast",
 281               (clkslow & S3C2410_CLKSLOW_MPLL_OFF) ? "off" : "on",
 282               (clkslow & S3C2410_CLKSLOW_UCLK_OFF) ? "off" : "on");
 283
 284        return 0;
 285}
 286