linux/arch/arm/mach-s3c2410/clock.c
<<
>>
Prefs
   1/* linux/arch/arm/mach-s3c2410/clock.c
   2 *
   3 * Copyright (c) 2004 Simtec Electronics
   4 * Ben Dooks <ben@simtec.co.uk>
   5 *
   6 * S3C2410 Clock control support
   7 *
   8 * Based on, and code from linux/arch/arm/mach-versatile/clock.c
   9 **
  10 **  Copyright (C) 2004 ARM Limited.
  11 **  Written by Deep Blue Solutions Limited.
  12 *
  13 *
  14 * This program is free software; you can redistribute it and/or modify
  15 * it under the terms of the GNU General Public License as published by
  16 * the Free Software Foundation; either version 2 of the License, or
  17 * (at your option) any later version.
  18 *
  19 * This program is distributed in the hope that it will be useful,
  20 * but WITHOUT ANY WARRANTY; without even the implied warranty of
  21 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  22 * GNU General Public License for more details.
  23 *
  24 * You should have received a copy of the GNU General Public License
  25 * along with this program; if not, write to the Free Software
  26 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
  27*/
  28
  29#include <linux/init.h>
  30#include <linux/module.h>
  31#include <linux/kernel.h>
  32#include <linux/list.h>
  33#include <linux/errno.h>
  34#include <linux/err.h>
  35#include <linux/device.h>
  36
  37#include <linux/interrupt.h>
  38#include <linux/ioport.h>
  39
  40#include <asm/hardware.h>
  41#include <asm/atomic.h>
  42#include <asm/irq.h>
  43#include <asm/io.h>
  44
  45#include <asm/hardware/clock.h>
  46#include <asm/arch/regs-clock.h>
  47
  48#include "clock.h"
  49
  50/* clock information */
  51
  52unsigned long s3c24xx_xtal = 12*1000*1000;      /* default 12MHz */
  53unsigned long s3c24xx_fclk;
  54unsigned long s3c24xx_hclk;
  55unsigned long s3c24xx_pclk;
  56
  57static LIST_HEAD(clocks);
  58static DECLARE_MUTEX(clocks_sem);
  59
  60
  61/* old functions */
  62
  63void inline s3c24xx_clk_enable(unsigned int clocks, unsigned int enable)
  64{
  65        unsigned long clkcon;
  66        unsigned long flags;
  67
  68        local_irq_save(flags);
  69
  70        clkcon = __raw_readl(S3C2410_CLKCON);
  71        clkcon &= ~clocks;
  72
  73        if (enable)
  74                clkcon |= clocks;
  75
  76        /* ensure none of the special function bits set */
  77        clkcon &= ~(S3C2410_CLKCON_IDLE|S3C2410_CLKCON_POWER);
  78
  79        __raw_writel(clkcon, S3C2410_CLKCON);
  80
  81        local_irq_restore(flags);
  82}
  83
  84/* enable and disable calls for use with the clk struct */
  85
  86static int clk_null_enable(struct clk *clk, int enable)
  87{
  88        return 0;
  89}
  90
  91int s3c24xx_clkcon_enable(struct clk *clk, int enable)
  92{
  93        s3c24xx_clk_enable(clk->ctrlbit, enable);
  94        return 0;
  95}
  96
  97/* Clock API calls */
  98
  99struct clk *clk_get(struct device *dev, const char *id)
 100{
 101        struct clk *p;
 102        struct clk *clk = ERR_PTR(-ENOENT);
 103        int idno;
 104
 105        idno = (dev == NULL) ? -1 : to_platform_device(dev)->id;
 106
 107        down(&clocks_sem);
 108
 109        list_for_each_entry(p, &clocks, list) {
 110                if (p->id == idno &&
 111                    strcmp(id, p->name) == 0 &&
 112                    try_module_get(p->owner)) {
 113                        clk = p;
 114                        break;
 115                }
 116        }
 117
 118        /* check for the case where a device was supplied, but the
 119         * clock that was being searched for is not device specific */
 120
 121        if (IS_ERR(clk)) {
 122                list_for_each_entry(p, &clocks, list) {
 123                        if (p->id == -1 && strcmp(id, p->name) == 0 &&
 124                            try_module_get(p->owner)) {
 125                                clk = p;
 126                                break;
 127                        }
 128                }
 129        }
 130
 131        up(&clocks_sem);
 132        return clk;
 133}
 134
 135void clk_put(struct clk *clk)
 136{
 137        module_put(clk->owner);
 138}
 139
 140int clk_enable(struct clk *clk)
 141{
 142        if (IS_ERR(clk))
 143                return -EINVAL;
 144
 145        return (clk->enable)(clk, 1);
 146}
 147
 148void clk_disable(struct clk *clk)
 149{
 150        if (!IS_ERR(clk))
 151                (clk->enable)(clk, 0);
 152}
 153
 154
 155int clk_use(struct clk *clk)
 156{
 157        atomic_inc(&clk->used);
 158        return 0;
 159}
 160
 161
 162void clk_unuse(struct clk *clk)
 163{
 164        atomic_dec(&clk->used);
 165}
 166
 167unsigned long clk_get_rate(struct clk *clk)
 168{
 169        if (IS_ERR(clk))
 170                return 0;
 171
 172        if (clk->rate != 0)
 173                return clk->rate;
 174
 175        while (clk->parent != NULL && clk->rate == 0)
 176                clk = clk->parent;
 177
 178        return clk->rate;
 179}
 180
 181long clk_round_rate(struct clk *clk, unsigned long rate)
 182{
 183        return rate;
 184}
 185
 186int clk_set_rate(struct clk *clk, unsigned long rate)
 187{
 188        return -EINVAL;
 189}
 190
 191struct clk *clk_get_parent(struct clk *clk)
 192{
 193        return clk->parent;
 194}
 195
 196EXPORT_SYMBOL(clk_get);
 197EXPORT_SYMBOL(clk_put);
 198EXPORT_SYMBOL(clk_enable);
 199EXPORT_SYMBOL(clk_disable);
 200EXPORT_SYMBOL(clk_use);
 201EXPORT_SYMBOL(clk_unuse);
 202EXPORT_SYMBOL(clk_get_rate);
 203EXPORT_SYMBOL(clk_round_rate);
 204EXPORT_SYMBOL(clk_set_rate);
 205EXPORT_SYMBOL(clk_get_parent);
 206
 207/* base clocks */
 208
 209static struct clk clk_f = {
 210        .name           = "fclk",
 211        .id             = -1,
 212        .rate           = 0,
 213        .parent         = NULL,
 214        .ctrlbit        = 0,
 215};
 216
 217static struct clk clk_h = {
 218        .name           = "hclk",
 219        .id             = -1,
 220        .rate           = 0,
 221        .parent         = NULL,
 222        .ctrlbit        = 0,
 223};
 224
 225static struct clk clk_p = {
 226        .name           = "pclk",
 227        .id             = -1,
 228        .rate           = 0,
 229        .parent         = NULL,
 230        .ctrlbit        = 0,
 231};
 232
 233/* clocks that could be registered by external code */
 234
 235struct clk s3c24xx_dclk0 = {
 236        .name           = "dclk0",
 237        .id             = -1,
 238};
 239
 240struct clk s3c24xx_dclk1 = {
 241        .name           = "dclk1",
 242        .id             = -1,
 243};
 244
 245struct clk s3c24xx_clkout0 = {
 246        .name           = "clkout0",
 247        .id             = -1,
 248};
 249
 250struct clk s3c24xx_clkout1 = {
 251        .name           = "clkout1",
 252        .id             = -1,
 253};
 254
 255struct clk s3c24xx_uclk = {
 256        .name           = "uclk",
 257        .id             = -1,
 258};
 259
 260
 261/* clock definitions */
 262
 263static struct clk init_clocks[] = {
 264        { .name    = "nand",
 265          .id      = -1,
 266          .parent  = &clk_h,
 267          .enable  = s3c24xx_clkcon_enable,
 268          .ctrlbit = S3C2410_CLKCON_NAND
 269        },
 270        { .name    = "lcd",
 271          .id      = -1,
 272          .parent  = &clk_h,
 273          .enable  = s3c24xx_clkcon_enable,
 274          .ctrlbit = S3C2410_CLKCON_LCDC
 275        },
 276        { .name    = "usb-host",
 277          .id      = -1,
 278          .parent  = &clk_h,
 279          .enable  = s3c24xx_clkcon_enable,
 280          .ctrlbit = S3C2410_CLKCON_USBH
 281        },
 282        { .name    = "usb-device",
 283          .id      = -1,
 284          .parent  = &clk_h,
 285          .enable  = s3c24xx_clkcon_enable,
 286          .ctrlbit = S3C2410_CLKCON_USBD
 287        },
 288        { .name    = "timers",
 289          .parent  = &clk_p,
 290          .enable  = s3c24xx_clkcon_enable,
 291          .ctrlbit = S3C2410_CLKCON_PWMT
 292        },
 293        { .name    = "sdi",
 294          .id      = -1,
 295          .parent  = &clk_p,
 296          .enable  = s3c24xx_clkcon_enable,
 297          .ctrlbit = S3C2410_CLKCON_SDI
 298        },
 299        { .name    = "uart",
 300          .id      = 0,
 301          .parent  = &clk_p,
 302          .enable  = s3c24xx_clkcon_enable,
 303          .ctrlbit = S3C2410_CLKCON_UART0
 304        },
 305        { .name    = "uart",
 306          .id      = 1,
 307          .parent  = &clk_p,
 308          .enable  = s3c24xx_clkcon_enable,
 309          .ctrlbit = S3C2410_CLKCON_UART1
 310        },
 311        { .name    = "uart",
 312          .id      = 2,
 313          .parent  = &clk_p,
 314          .enable  = s3c24xx_clkcon_enable,
 315          .ctrlbit = S3C2410_CLKCON_UART2
 316        },
 317        { .name    = "gpio",
 318          .id      = -1,
 319          .parent  = &clk_p,
 320          .enable  = s3c24xx_clkcon_enable,
 321          .ctrlbit = S3C2410_CLKCON_GPIO
 322        },
 323        { .name    = "rtc",
 324          .id      = -1,
 325          .parent  = &clk_p,
 326          .enable  = s3c24xx_clkcon_enable,
 327          .ctrlbit = S3C2410_CLKCON_RTC
 328        },
 329        { .name    = "adc",
 330          .id      = -1,
 331          .parent  = &clk_p,
 332          .enable  = s3c24xx_clkcon_enable,
 333          .ctrlbit = S3C2410_CLKCON_ADC
 334        },
 335        { .name    = "i2c",
 336          .id      = -1,
 337          .parent  = &clk_p,
 338          .enable  = s3c24xx_clkcon_enable,
 339          .ctrlbit = S3C2410_CLKCON_IIC
 340        },
 341        { .name    = "iis",
 342          .id      = -1,
 343          .parent  = &clk_p,
 344          .enable  = s3c24xx_clkcon_enable,
 345          .ctrlbit = S3C2410_CLKCON_IIS
 346        },
 347        { .name    = "spi",
 348          .id      = -1,
 349          .parent  = &clk_p,
 350          .enable  = s3c24xx_clkcon_enable,
 351          .ctrlbit = S3C2410_CLKCON_SPI
 352        },
 353        { .name    = "watchdog",
 354          .id      = -1,
 355          .parent  = &clk_p,
 356          .ctrlbit = 0
 357        }
 358};
 359
 360/* initialise the clock system */
 361
 362int s3c24xx_register_clock(struct clk *clk)
 363{
 364        clk->owner = THIS_MODULE;
 365        atomic_set(&clk->used, 0);
 366
 367        if (clk->enable == NULL)
 368                clk->enable = clk_null_enable;
 369
 370        /* add to the list of available clocks */
 371
 372        down(&clocks_sem);
 373        list_add(&clk->list, &clocks);
 374        up(&clocks_sem);
 375
 376        return 0;
 377}
 378
 379/* initalise all the clocks */
 380
 381int __init s3c24xx_setup_clocks(void)
 382{
 383        struct clk *clkp = init_clocks;
 384        int ptr;
 385        int ret;
 386
 387        printk(KERN_INFO "S3C2410 Clock control, (c) 2004 Simtec Electronics\n");
 388
 389        /* initialise the main system clocks */
 390
 391        clk_h.rate = s3c24xx_hclk;
 392        clk_p.rate = s3c24xx_pclk;
 393        clk_f.rate = s3c24xx_fclk;
 394
 395        /* it looks like just setting the register here is not good
 396         * enough, and causes the odd hang at initial boot time, so
 397         * do all of them indivdually.
 398         *
 399         * I think disabling the LCD clock if the LCD is active is
 400         * very dangerous, and therefore the bootloader should be
 401         * careful to not enable the LCD clock if it is not needed.
 402         *
 403         * and of course, this looks neater
 404         */
 405
 406        s3c24xx_clk_enable(S3C2410_CLKCON_NAND, 0);
 407        s3c24xx_clk_enable(S3C2410_CLKCON_USBH, 0);
 408        s3c24xx_clk_enable(S3C2410_CLKCON_USBD, 0);
 409        s3c24xx_clk_enable(S3C2410_CLKCON_ADC, 0);
 410        s3c24xx_clk_enable(S3C2410_CLKCON_IIC, 0);
 411        s3c24xx_clk_enable(S3C2410_CLKCON_SPI, 0);
 412
 413        /* assume uart clocks are correctly setup */
 414
 415        /* register our clocks */
 416
 417        if (s3c24xx_register_clock(&clk_f) < 0)
 418                printk(KERN_ERR "failed to register cpu fclk\n");
 419
 420        if (s3c24xx_register_clock(&clk_h) < 0)
 421                printk(KERN_ERR "failed to register cpu hclk\n");
 422
 423        if (s3c24xx_register_clock(&clk_p) < 0)
 424                printk(KERN_ERR "failed to register cpu pclk\n");
 425
 426        for (ptr = 0; ptr < ARRAY_SIZE(init_clocks); ptr++, clkp++) {
 427                ret = s3c24xx_register_clock(clkp);
 428                if (ret < 0) {
 429                        printk(KERN_ERR "Failed to register clock %s (%d)\n",
 430                               clkp->name, ret);
 431                }
 432        }
 433
 434        return 0;
 435}
 436
 437
 438
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.