Commit 0471b5fb authored by Russell King's avatar Russell King

[ARM] Add clock API

Since peripheral clocks are be derived in SoC or even platform
specific ways, we need a way to keep this information out of the
drivers.  AMBA Primecells are defined in terms of functional units
where clocks are derived from external sources, and it is up to the
SoC designer to determine where to derive those clocks from.

Therefore, we provide a very basic API which allows platforms to
provide this information in a generic manner to their peripherals.
This framework also allows peripherals to shut down unused clock
sources when they're not in use.
parent 292670c7
......@@ -4,7 +4,7 @@
# Object file lists.
obj-y := core.o lm.o time.o
obj-y := clock.o core.o lm.o time.o
obj-$(CONFIG_ARCH_INTEGRATOR_AP) += integrator_ap.o
obj-$(CONFIG_ARCH_INTEGRATOR_CP) += integrator_cp.o
......
/*
* linux/arch/arm/mach-integrator/clock.c
*
* Copyright (C) 2004 ARM Limited.
* Written by Deep Blue Solutions Limited.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/list.h>
#include <linux/errno.h>
#include <linux/err.h>
#include <asm/semaphore.h>
#include <asm/hardware/clock.h>
#include <asm/hardware/icst525.h>
#include "clock.h"
static LIST_HEAD(clocks);
static DECLARE_MUTEX(clocks_sem);
struct clk *clk_get(struct device *dev, const char *id)
{
struct clk *p, *clk = ERR_PTR(-ENOENT);
down(&clocks_sem);
list_for_each_entry(p, &clocks, node) {
if (strcmp(id, p->name) == 0 && try_module_get(p->owner)) {
clk = p;
break;
}
}
up(&clocks_sem);
return clk;
}
EXPORT_SYMBOL(clk_get);
void clk_put(struct clk *clk)
{
module_put(clk->owner);
}
EXPORT_SYMBOL(clk_put);
int clk_enable(struct clk *clk)
{
return 0;
}
EXPORT_SYMBOL(clk_enable);
void clk_disable(struct clk *clk)
{
}
EXPORT_SYMBOL(clk_disable);
int clk_use(struct clk *clk)
{
return 0;
}
EXPORT_SYMBOL(clk_use);
void clk_unuse(struct clk *clk)
{
}
EXPORT_SYMBOL(clk_unuse);
unsigned long clk_get_rate(struct clk *clk)
{
return clk->rate;
}
EXPORT_SYMBOL(clk_get_rate);
long clk_round_rate(struct clk *clk, unsigned long rate)
{
return rate;
}
EXPORT_SYMBOL(clk_round_rate);
int clk_set_rate(struct clk *clk, unsigned long rate)
{
int ret = -EIO;
if (clk->setvco) {
struct icst525_vco vco;
vco = icst525_khz_to_vco(clk->params, rate);
clk->rate = icst525_khz(clk->params, vco);
printk("Clock %s: setting VCO reg params: S=%d R=%d V=%d\n",
clk->name, vco.s, vco.r, vco.v);
clk->setvco(clk, vco);
ret = 0;
}
return 0;
}
EXPORT_SYMBOL(clk_set_rate);
/*
* These are fixed clocks.
*/
static struct clk kmi_clk = {
.name = "KMIREFCLK",
.rate = 24000000,
};
static struct clk uart_clk = {
.name = "UARTCLK",
.rate = 14745600,
};
int clk_register(struct clk *clk)
{
down(&clocks_sem);
list_add(&clk->node, &clocks);
up(&clocks_sem);
return 0;
}
EXPORT_SYMBOL(clk_register);
void clk_unregister(struct clk *clk)
{
down(&clocks_sem);
list_del(&clk->node);
up(&clocks_sem);
}
EXPORT_SYMBOL(clk_unregister);
static int __init clk_init(void)
{
clk_register(&kmi_clk);
clk_register(&uart_clk);
return 0;
}
arch_initcall(clk_init);
/*
* linux/arch/arm/mach-integrator/clock.h
*
* Copyright (C) 2004 ARM Limited.
* Written by Deep Blue Solutions Limited.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
struct module;
struct icst525_params;
struct clk {
struct list_head node;
unsigned long rate;
struct module *owner;
const char *name;
const struct icst525_params *params;
void *data;
void (*setvco)(struct clk *, struct icst525_vco vco);
};
int clk_register(struct clk *clk);
void clk_unregister(struct clk *clk);
......@@ -25,13 +25,16 @@
#include <asm/arch/impd1.h>
#include <asm/sizes.h>
#include "clock.h"
static int module_id;
module_param_named(lmid, module_id, int, 0444);
MODULE_PARM_DESC(lmid, "logic module stack position");
struct impd1_module {
void *base;
void *base;
struct clk vcos[2];
};
static const struct icst525_params impd1_vco_params = {
......@@ -43,25 +46,20 @@ static const struct icst525_params impd1_vco_params = {
.rd_max = 120,
};
void impd1_set_vco(struct device *dev, int vconr, unsigned long period)
static void impd1_setvco(struct clk *clk, struct icst525_vco vco)
{
struct impd1_module *impd1 = dev_get_drvdata(dev);
struct icst525_vco vco;
struct impd1_module *impd1 = clk->data;
int vconr = clk - impd1->vcos;
u32 val;
vco = icst525_ps_to_vco(&impd1_vco_params, period);
pr_debug("Guessed VCO reg params: S=%d R=%d V=%d\n",
vco.s, vco.r, vco.v);
val = vco.v | (vco.r << 9) | (vco.s << 16);
writel(0xa05f, impd1->base + IMPD1_LOCK);
switch (vconr) {
case 1:
case 0:
writel(val, impd1->base + IMPD1_OSC1);
break;
case 2:
case 1:
writel(val, impd1->base + IMPD1_OSC2);
break;
}
......@@ -77,8 +75,6 @@ void impd1_set_vco(struct device *dev, int vconr, unsigned long period)
#endif
}
EXPORT_SYMBOL(impd1_set_vco);
void impd1_tweak_control(struct device *dev, u32 mask, u32 val)
{
struct impd1_module *impd1 = dev_get_drvdata(dev);
......@@ -140,6 +136,11 @@ static struct impd1_device impd1_devs[] = {
}
};
static const char *impd1_vconames[2] = {
"CLCDCLK",
"AUXVCO2",
};
static int impd1_probe(struct lm_device *dev)
{
struct impd1_module *impd1;
......@@ -168,6 +169,16 @@ static int impd1_probe(struct lm_device *dev)
printk("IM-PD1 found at 0x%08lx\n", dev->resource.start);
for (i = 0; i < ARRAY_SIZE(impd1->vcos); i++) {
impd1->vcos[i].owner = THIS_MODULE,
impd1->vcos[i].name = impd1_vconames[i],
impd1->vcos[i].params = &impd1_vco_params,
impd1->vcos[i].data = impd1,
impd1->vcos[i].setvco = impd1_setvco;
clk_register(&impd1->vcos[i]);
}
for (i = 0; i < ARRAY_SIZE(impd1_devs); i++) {
struct impd1_device *idev = impd1_devs + i;
struct amba_device *d;
......@@ -216,6 +227,7 @@ static void impd1_remove(struct lm_device *dev)
{
struct impd1_module *impd1 = lm_get_drvdata(dev);
struct list_head *l, *n;
int i;
list_for_each_safe(l, n, &dev->dev.children) {
struct device *d = list_to_dev(l);
......@@ -223,6 +235,9 @@ static void impd1_remove(struct lm_device *dev)
device_unregister(d);
}
for (i = 0; i < ARRAY_SIZE(impd1->vcos); i++)
clk_unregister(&impd1->vcos[i]);
lm_set_drvdata(dev, NULL);
iounmap(impd1->base);
......
......@@ -23,6 +23,7 @@
#include <asm/mach-types.h>
#include <asm/hardware/amba.h>
#include <asm/hardware/amba_kmi.h>
#include <asm/hardware/icst525.h>
#include <asm/arch/lm.h>
......@@ -32,12 +33,16 @@
#include <asm/mach/mmc.h>
#include <asm/mach/map.h>
#include "clock.h"
#define INTCP_PA_MMC_BASE 0x1c000000
#define INTCP_PA_AACI_BASE 0x1d000000
#define INTCP_PA_FLASH_BASE 0x24000000
#define INTCP_FLASH_SIZE SZ_32M
#define INTCP_PA_CLCD_BASE 0xc0000000
#define INTCP_VA_CIC_BASE 0xf1000040
#define INTCP_VA_PIC_BASE 0xf1400000
#define INTCP_VA_SIC_BASE 0xfca00000
......@@ -209,6 +214,44 @@ static void __init intcp_init_irq(void)
pic_unmask_irq(IRQ_CP_CPPLDINT);
}
/*
* Clock handling
*/
#define CM_LOCK (IO_ADDRESS(INTEGRATOR_HDR_BASE)+INTEGRATOR_HDR_LOCK_OFFSET)
#define CM_AUXOSC (IO_ADDRESS(INTEGRATOR_HDR_BASE)+0x1c)
static const struct icst525_params cp_auxvco_params = {
.ref = 24000,
.vco_max = 320000,
.vd_min = 8,
.vd_max = 263,
.rd_min = 3,
.rd_max = 65,
};
static void cp_auxvco_set(struct clk *clk, struct icst525_vco vco)
{
u32 val;
val = readl(CM_AUXOSC) & ~0x7ffff;
val |= vco.v | (vco.r << 9) | (vco.s << 16);
writel(0xa05f, CM_LOCK);
writel(val, CM_AUXOSC);
writel(0, CM_LOCK);
}
static struct clk cp_clcd_clk = {
.name = "CLCDCLK",
.params = &cp_auxvco_params,
.setvco = cp_auxvco_set,
};
static struct clk cp_mmci_clk = {
.name = "MCLK",
.rate = 33000000,
};
/*
* Flash handling.
*/
......@@ -340,15 +383,34 @@ static struct amba_device aaci_device = {
.periphid = 0,
};
static struct amba_device clcd_device = {
.dev = {
.bus_id = "mb:c0",
.coherent_dma_mask = ~0,
},
.res = {
.start = INTCP_PA_CLCD_BASE,
.end = INTCP_PA_CLCD_BASE + SZ_4K - 1,
.flags = IORESOURCE_MEM,
},
.dma_mask = ~0,
.irq = { IRQ_CP_CLCDCINT, NO_IRQ },
.periphid = 0,
};
static struct amba_device *amba_devs[] __initdata = {
&mmc_device,
&aaci_device,
&clcd_device,
};
static void __init intcp_init(void)
{
int i;
clk_register(&cp_clcd_clk);
clk_register(&cp_mmci_clk);
platform_add_devices(intcp_devs, ARRAY_SIZE(intcp_devs));
for (i = 0; i < ARRAY_SIZE(amba_devs); i++) {
......
......@@ -2,4 +2,4 @@
# Makefile for the linux kernel.
#
obj-y := core.o
obj-y := core.o clock.o
/*
* linux/arch/arm/mach-versatile/clock.c
*
* Copyright (C) 2004 ARM Limited.
* Written by Deep Blue Solutions Limited.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/list.h>
#include <linux/errno.h>
#include <linux/err.h>
#include <asm/semaphore.h>
#include <asm/hardware/clock.h>
#include <asm/hardware/icst525.h>
#include "clock.h"
static LIST_HEAD(clocks);
static DECLARE_MUTEX(clocks_sem);
struct clk *clk_get(struct device *dev, const char *id)
{
struct clk *p, *clk = ERR_PTR(-ENOENT);
down(&clocks_sem);
list_for_each_entry(p, &clocks, node) {
if (strcmp(id, p->name) == 0 && try_module_get(p->owner)) {
clk = p;
break;
}
}
up(&clocks_sem);
return clk;
}
EXPORT_SYMBOL(clk_get);
void clk_put(struct clk *clk)
{
module_put(clk->owner);
}
EXPORT_SYMBOL(clk_put);
int clk_enable(struct clk *clk)
{
return 0;
}
EXPORT_SYMBOL(clk_enable);
void clk_disable(struct clk *clk)
{
}
EXPORT_SYMBOL(clk_disable);
int clk_use(struct clk *clk)
{
return 0;
}
EXPORT_SYMBOL(clk_use);
void clk_unuse(struct clk *clk)
{
}
EXPORT_SYMBOL(clk_unuse);
unsigned long clk_get_rate(struct clk *clk)
{
return clk->rate;
}
EXPORT_SYMBOL(clk_get_rate);
long clk_round_rate(struct clk *clk, unsigned long rate)
{
return rate;
}
EXPORT_SYMBOL(clk_round_rate);
int clk_set_rate(struct clk *clk, unsigned long rate)
{
int ret = -EIO;
#if 0 // Not yet
if (clk->setvco) {
struct icst525_vco vco;
vco = icst525_khz_to_vco(clk->params, rate);
clk->rate = icst525_khz(clk->params, vco);
printk("Clock %s: setting VCO reg params: S=%d R=%d V=%d\n",
clk->name, vco.s, vco.r, vco.v);
clk->setvco(clk, vco);
ret = 0;
}
#endif
return 0;
}
EXPORT_SYMBOL(clk_set_rate);
/*
* These are fixed clocks.
*/
static struct clk kmi_clk = {
.name = "KMIREFCLK",
.rate = 24000000,
};
static struct clk uart_clk = {
.name = "UARTCLK",
.rate = 24000000,
};
static struct clk mmci_clk = {
.name = "MCLK",
.rate = 33000000,
};
int clk_register(struct clk *clk)
{
down(&clocks_sem);
list_add(&clk->node, &clocks);
up(&clocks_sem);
return 0;
}
EXPORT_SYMBOL(clk_register);
void clk_unregister(struct clk *clk)
{
down(&clocks_sem);
list_del(&clk->node);
up(&clocks_sem);
}
EXPORT_SYMBOL(clk_unregister);
static int __init clk_init(void)
{
clk_register(&kmi_clk);
clk_register(&uart_clk);
clk_register(&mmci_clk);
return 0;
}
arch_initcall(clk_init);
/*
* linux/arch/arm/mach-versatile/clock.h
*
* Copyright (C) 2004 ARM Limited.
* Written by Deep Blue Solutions Limited.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
struct module;
struct icst525_params;
struct clk {
struct list_head node;
unsigned long rate;
struct module *owner;
const char *name;
const struct icst525_params *params;
void *data;
void (*setvco)(struct clk *, struct icst525_vco vco);
};
int clk_register(struct clk *clk);
void clk_unregister(struct clk *clk);
......@@ -18,17 +18,19 @@
#include <linux/device.h>
#include <linux/delay.h>
#include <linux/slab.h>
#include <linux/err.h>
#include <asm/io.h>
#include <asm/irq.h>
#include <asm/hardware/amba.h>
#include <asm/hardware/amba_kmi.h>
#include <asm/hardware/clock.h>
#define KMI_BASE (kmi->base)
struct amba_kmi_port {
struct serio io;
struct amba_kmi_port *next;
struct clk *clk;
unsigned char *base;
unsigned int irq;
unsigned int divisor;
......@@ -67,21 +69,38 @@ static int amba_kmi_write(struct serio *io, unsigned char val)
static int amba_kmi_open(struct serio *io)
{
struct amba_kmi_port *kmi = io->driver;
unsigned int divisor;
int ret;
writeb(kmi->divisor, KMICLKDIV);
ret = clk_use(kmi->clk);
if (ret)
goto out;
ret = clk_enable(kmi->clk);
if (ret)
goto clk_unuse;
divisor = clk_get_rate(kmi->clk) / 8000000 - 1;
writeb(divisor, KMICLKDIV);
writeb(KMICR_EN, KMICR);
ret = request_irq(kmi->irq, amba_kmi_int, 0, "kmi-pl050", kmi);
if (ret) {
printk(KERN_ERR "kmi: failed to claim IRQ%d\n", kmi->irq);
writeb(0, KMICR);
return ret;
goto clk_disable;
}
writeb(KMICR_EN | KMICR_RXINTREN, KMICR);
return 0;
clk_disable:
clk_disable(kmi->clk);
clk_unuse:
clk_unuse(kmi->clk);
out:
return ret;
}
static void amba_kmi_close(struct serio *io)
......@@ -91,6 +110,8 @@ static void amba_kmi_close(struct serio *io)
writeb(0, KMICR);
free_irq(kmi->irq, kmi);
clk_disable(kmi->clk);
clk_unuse(kmi->clk);
}
static int amba_kmi_probe(struct amba_device *dev, void *id)
......@@ -124,14 +145,20 @@ static int amba_kmi_probe(struct amba_device *dev, void *id)
goto out;
}
kmi->irq = dev->irq[0];
kmi->divisor = 24 / 8 - 1;
kmi->clk = clk_get(&dev->dev, "KMIREFCLK");
if (IS_ERR(kmi->clk)) {
ret = PTR_ERR(kmi->clk);
goto unmap;
}
kmi->irq = dev->irq[0];
amba_set_drvdata(dev, kmi);
serio_register_port(&kmi->io);
return 0;
unmap:
iounmap(kmi->base);
out:
kfree(kmi);
amba_release_regions(dev);
......@@ -145,6 +172,7 @@ static int amba_kmi_remove(struct amba_device *dev)
amba_set_drvdata(dev, NULL);
serio_unregister_port(&kmi->io);
clk_put(kmi->clk);
iounmap(kmi->base);
kfree(kmi);
amba_release_regions(dev);
......
......@@ -44,6 +44,7 @@
#include <asm/io.h>
#include <asm/irq.h>
#include <asm/hardware/amba.h>
#include <asm/hardware/clock.h>
#if defined(CONFIG_SERIAL_AMBA_PL011_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ)
#define SUPPORT_SYSRQ
......@@ -68,6 +69,7 @@
*/
struct uart_amba_port {
struct uart_port port;
struct clk *clk;
unsigned int im; /* interrupt mask */
unsigned int old_status;
};
......@@ -351,12 +353,21 @@ static int pl011_startup(struct uart_port *port)
unsigned int cr;
int retval;
/*
* Try to enable the clock producer.
*/
retval = clk_enable(uap->clk);
if (retval)
goto out;
uap->port.uartclk = clk_get_rate(uap->clk);
/*
* Allocate the IRQ
*/
retval = request_irq(uap->port.irq, pl011_int, 0, "uart-pl011", uap);
if (retval)
goto out;
goto clk_dis;
writew(UART011_IFLS_RX4_8|UART011_IFLS_TX4_8,
uap->port.membase + UART011_IFLS);
......@@ -391,6 +402,8 @@ static int pl011_startup(struct uart_port *port)
return 0;
clk_dis:
clk_disable(uap->clk);
out:
return retval;
}
......@@ -425,6 +438,11 @@ static void pl011_shutdown(struct uart_port *port)
val = readw(uap->port.membase + UART011_LCRH);
val &= ~(UART01x_LCRH_BRK | UART01x_LCRH_FEN);
writew(val, uap->port.membase + UART011_LCRH);
/*
* Shut down the clock producer
*/
clk_disable(uap->clk);
}
static void
......@@ -594,38 +612,40 @@ static struct uart_amba_port *amba_ports[UART_NR];
#ifdef CONFIG_SERIAL_AMBA_PL011_CONSOLE
static inline void
pl011_console_write_char(struct uart_port *port, char ch)
pl011_console_write_char(struct uart_amba_port *uap, char ch)
{
unsigned int status;
do {
status = readw(port->membase + UART01x_FR);
status = readw(uap->port.membase + UART01x_FR);
} while (status & UART01x_FR_TXFF);
writew(ch, port->membase + UART01x_DR);
writew(ch, uap->port.membase + UART01x_DR);
}
static void
pl011_console_write(struct console *co, const char *s, unsigned int count)
{
struct uart_port *port = &amba_ports[co->index]->port;
struct uart_amba_port *uap = amba_ports[co->index];
unsigned int status, old_cr, new_cr;
int i;
clk_enable(uap->clk);
/*
* First save the CR then disable the interrupts
*/
old_cr = readw(port->membase + UART011_CR);
old_cr = readw(uap->port.membase + UART011_CR);
new_cr = old_cr & ~UART011_CR_CTSEN;
new_cr |= UART01x_CR_UARTEN | UART011_CR_TXE;
writew(new_cr, port->membase + UART011_CR);
writew(new_cr, uap->port.membase + UART011_CR);
/*
* Now, do each character
*/
for (i = 0; i < count; i++) {
pl011_console_write_char(port, s[i]);
pl011_console_write_char(uap, s[i]);
if (s[i] == '\n')
pl011_console_write_char(port, '\r');
pl011_console_write_char(uap, '\r');
}
/*
......@@ -633,19 +653,21 @@ pl011_console_write(struct console *co, const char *s, unsigned int count)
* and restore the TCR
*/
do {
status = readw(port->membase + UART01x_FR);
status = readw(uap->port.membase + UART01x_FR);
} while (status & UART01x_FR_BUSY);
writew(old_cr, port->membase + UART011_CR);
writew(old_cr, uap->port.membase + UART011_CR);
clk_disable(uap->clk);
}
static void __init
pl011_console_get_options(struct uart_port *port, int *baud,
pl011_console_get_options(struct uart_amba_port *uap, int *baud,
int *parity, int *bits)
{
if (readw(port->membase + UART011_CR) & UART01x_CR_UARTEN) {
if (readw(uap->port.membase + UART011_CR) & UART01x_CR_UARTEN) {
unsigned int lcr_h, ibrd, fbrd;
lcr_h = readw(port->membase + UART011_LCRH);
lcr_h = readw(uap->port.membase + UART011_LCRH);
*parity = 'n';
if (lcr_h & UART01x_LCRH_PEN) {
......@@ -660,10 +682,10 @@ pl011_console_get_options(struct uart_port *port, int *baud,
else
*bits = 8;
ibrd = readw(port->membase + UART011_IBRD);
fbrd = readw(port->membase + UART011_FBRD);
ibrd = readw(uap->port.membase + UART011_IBRD);
fbrd = readw(uap->port.membase + UART011_FBRD);
*baud = port->uartclk * 4 / (64 * ibrd + fbrd);
*baud = uap->port.uartclk * 4 / (64 * ibrd + fbrd);
}
}
......@@ -685,10 +707,12 @@ static int __init pl011_console_setup(struct console *co, char *options)
co->index = 0;
uap = amba_ports[co->index];
uap->port.uartclk = clk_get_rate(uap->clk);
if (options)
uart_parse_options(options, &baud, &parity, &bits, &flow);
else
pl011_console_get_options(&uap->port, &baud, &parity, &bits);
pl011_console_get_options(uap, &baud, &parity, &bits);
return uart_set_options(&uap->port, co, baud, parity, bits, flow);
}
......@@ -747,16 +771,21 @@ static int pl011_probe(struct amba_device *dev, void *id)
}
memset(uap, 0, sizeof(struct uart_amba_port));
uap->clk = clk_get(&dev->dev, "UARTCLK");
if (IS_ERR(uap->clk)) {
ret = PTR_ERR(uap->clk);
goto unmap;
}
ret = clk_use(uap->clk);
if (ret)
goto putclk;
uap->port.dev = &dev->dev;
uap->port.mapbase = dev->res.start;
uap->port.membase = base;
uap->port.iotype = UPIO_MEM;
uap->port.irq = dev->irq[0];
#if 0 /* FIXME */
uap->port.uartclk = 14745600;
#else
uap->port.uartclk = 24000000;
#endif
uap->port.fifosize = 16;
uap->port.ops = &amba_pl011_pops;
uap->port.flags = UPF_BOOT_AUTOCONF;
......@@ -769,6 +798,10 @@ static int pl011_probe(struct amba_device *dev, void *id)
if (ret) {
amba_set_drvdata(dev, NULL);
amba_ports[i] = NULL;
clk_unuse(uap->clk);
putclk:
clk_put(uap->clk);
unmap:
iounmap(base);
free:
kfree(uap);
......@@ -791,6 +824,8 @@ static int pl011_remove(struct amba_device *dev)
amba_ports[i] = NULL;
iounmap(uap->port.membase);
clk_unuse(uap->clk);
clk_put(uap->clk);
kfree(uap);
return 0;
}
......
......@@ -14,6 +14,5 @@
struct device;
void impd1_set_vco(struct device *dev, int vconr, unsigned long period);
void impd1_tweak_control(struct device *dev, u32 mask, u32 val);
/*
* linux/include/asm-arm/hardware/clock.h
*
* Copyright (C) 2004 ARM Limited.
* Written by Deep Blue Solutions Limited.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#ifndef ASMARM_CLOCK_H
#define ASMARM_CLOCK_H
struct device;
/*
* The base API.
*/
/*
* struct clk - an machine class defined object / cookie.
*/
struct clk;
/**
* clk_get - lookup and obtain a reference to a clock producer.
* @dev: device for clock "consumer"
* @id: device ID
*
* Returns a struct clk corresponding to the clock producer, or
* valid IS_ERR() condition containing errno.
*/
struct clk *clk_get(struct device *dev, const char *id);
/**
* clk_enable - inform the system when the clock source should be running.
* @clk: clock source
*
* If the clock can not be enabled/disabled, this should return success.
*
* Returns success (0) or negative errno.
*/
int clk_enable(struct clk *clk);
/**
* clk_disable - inform the system when the clock source is no longer required.
* @clk: clock source
*/
void clk_disable(struct clk *clk);
/**
* clk_use - increment the use count
* @clk: clock source
*
* Returns success (0) or negative errno.
*/
int clk_use(struct clk *clk);
/**
* clk_unuse - decrement the use count
* @clk: clock source
*/
void clk_unuse(struct clk *clk);
/**
* clk_get_rate - obtain the current clock rate for a clock source.
* This is only valid once the clock source has been enabled.
* @clk: clock source
*/
unsigned long clk_get_rate(struct clk *clk);
/**
* clk_put - "free" the clock source
* @clk: clock source
*/
void clk_put(struct clk *clk);
/*
* The remaining APIs are optional for machine class support.
*/
/**
* clk_round_rate - adjust a rate to the exact rate a clock can provide
* @clk: clock source
* @rate: desired clock rate in kHz
*
* Returns rounded clock rate, or negative errno.
*/
long clk_round_rate(struct clk *clk, unsigned long rate);
/**
* clk_set_rate - set the clock rate for a clock source
* @clk: clock source
* @rate: desired clock rate in kHz
*
* Returns success (0) or negative errno.
*/
int clk_set_rate(struct clk *clk, unsigned long rate);
/**
* clk_set_parent - set the parent clock source for this clock
* @clk: clock source
* @parent: parent clock source
*
* Returns success (0) or negative errno.
*/
int clk_set_parent(struct clk *clk, struct clk *parent);
/**
* clk_get_parent - get the parent clock source for this clock
* @clk: clock source
*
* Returns struct clk corresponding to parent clock source, or
* valid IS_ERR() condition containing errno.
*/
struct clk *clk_get_parent(struct clk *clk);
#endif
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment