Commit b1f174b1 authored by Shawn Guo's avatar Shawn Guo

Merge remote-tracking branch 'mturquette/clk-3.7' into imx/dt-for-3.7-2

parents 8f71fb87 494bfec9
......@@ -273,7 +273,7 @@ config ARCH_INTEGRATOR
select ARM_AMBA
select ARCH_HAS_CPUFREQ
select COMMON_CLK
select CLK_VERSATILE
select COMMON_CLK_VERSATILE
select HAVE_TCM
select ICST
select GENERIC_CLOCKEVENTS
......@@ -289,13 +289,12 @@ config ARCH_INTEGRATOR
config ARCH_REALVIEW
bool "ARM Ltd. RealView family"
select ARM_AMBA
select CLKDEV_LOOKUP
select HAVE_MACH_CLKDEV
select COMMON_CLK
select COMMON_CLK_VERSATILE
select ICST
select GENERIC_CLOCKEVENTS
select ARCH_WANT_OPTIONAL_GPIOLIB
select PLAT_VERSATILE
select PLAT_VERSATILE_CLOCK
select PLAT_VERSATILE_CLCD
select ARM_TIMER_SP804
select GPIO_PL061 if GPIOLIB
......@@ -413,7 +412,7 @@ config ARCH_PRIMA2
select NO_IOPORT
select ARCH_REQUIRE_GPIOLIB
select GENERIC_CLOCKEVENTS
select CLKDEV_LOOKUP
select COMMON_CLK
select GENERIC_IRQ_CHIP
select MIGHT_HAVE_CACHE_L2X0
select PINCTRL
......
......@@ -108,18 +108,21 @@ endmenu
config CPU_PXA168
bool
select CPU_MOHAWK
select COMMON_CLK
help
Select code specific to PXA168
config CPU_PXA910
bool
select CPU_MOHAWK
select COMMON_CLK
help
Select code specific to PXA910
config CPU_MMP2
bool
select CPU_PJ4
select COMMON_CLK
help
Select code specific to MMP2. MMP2 is ARMv7 compatible.
......
obj-y := timer.o
obj-y += irq.o
obj-y += clock.o
obj-y += rstc.o
obj-y += prima2.o
obj-y += rtciobrg.o
......
This diff is collapsed.
......@@ -38,7 +38,6 @@ static const char *prima2cb_dt_match[] __initdata = {
MACHINE_START(PRIMA2_EVB, "prima2cb")
/* Maintainer: Barry Song <baohua.song@csr.com> */
.atag_offset = 0x100,
.init_early = sirfsoc_of_clk_init,
.map_io = sirfsoc_map_lluart,
.init_irq = sirfsoc_of_irq_init,
.timer = &sirfsoc_timer,
......
......@@ -21,6 +21,8 @@
#include <asm/sched_clock.h>
#include <asm/mach/time.h>
#include "common.h"
#define SIRFSOC_TIMER_COUNTER_LO 0x0000
#define SIRFSOC_TIMER_COUNTER_HI 0x0004
#define SIRFSOC_TIMER_MATCH_0 0x0008
......@@ -188,9 +190,13 @@ static void __init sirfsoc_clockevent_init(void)
static void __init sirfsoc_timer_init(void)
{
unsigned long rate;
struct clk *clk;
/* initialize clocking early, we want to set the OS timer */
sirfsoc_of_clk_init();
/* timer's input clock is io clock */
struct clk *clk = clk_get_sys("io", NULL);
clk = clk_get_sys("io", NULL);
BUG_ON(IS_ERR(clk));
......
......@@ -30,7 +30,6 @@
#include <linux/ata_platform.h>
#include <linux/amba/mmci.h>
#include <linux/gfp.h>
#include <linux/clkdev.h>
#include <linux/mtd/physmap.h>
#include <mach/hardware.h>
......@@ -226,115 +225,10 @@ struct mmci_platform_data realview_mmc1_plat_data = {
.cd_invert = true,
};
/*
* Clock handling
*/
static const struct icst_params realview_oscvco_params = {
.ref = 24000000,
.vco_max = ICST307_VCO_MAX,
.vco_min = ICST307_VCO_MIN,
.vd_min = 4 + 8,
.vd_max = 511 + 8,
.rd_min = 1 + 2,
.rd_max = 127 + 2,
.s2div = icst307_s2div,
.idx2s = icst307_idx2s,
};
static void realview_oscvco_set(struct clk *clk, struct icst_vco vco)
{
void __iomem *sys_lock = __io_address(REALVIEW_SYS_BASE) + REALVIEW_SYS_LOCK_OFFSET;
u32 val;
val = readl(clk->vcoreg) & ~0x7ffff;
val |= vco.v | (vco.r << 9) | (vco.s << 16);
writel(0xa05f, sys_lock);
writel(val, clk->vcoreg);
writel(0, sys_lock);
}
static const struct clk_ops oscvco_clk_ops = {
.round = icst_clk_round,
.set = icst_clk_set,
.setvco = realview_oscvco_set,
};
static struct clk oscvco_clk = {
.ops = &oscvco_clk_ops,
.params = &realview_oscvco_params,
};
/*
* These are fixed clocks.
*/
static struct clk ref24_clk = {
.rate = 24000000,
};
static struct clk sp804_clk = {
.rate = 1000000,
};
static struct clk dummy_apb_pclk;
static struct clk_lookup lookups[] = {
{ /* Bus clock */
.con_id = "apb_pclk",
.clk = &dummy_apb_pclk,
}, { /* UART0 */
.dev_id = "dev:uart0",
.clk = &ref24_clk,
}, { /* UART1 */
.dev_id = "dev:uart1",
.clk = &ref24_clk,
}, { /* UART2 */
.dev_id = "dev:uart2",
.clk = &ref24_clk,
}, { /* UART3 */
.dev_id = "fpga:uart3",
.clk = &ref24_clk,
}, { /* UART3 is on the dev chip in PB1176 */
.dev_id = "dev:uart3",
.clk = &ref24_clk,
}, { /* UART4 only exists in PB1176 */
.dev_id = "fpga:uart4",
.clk = &ref24_clk,
}, { /* KMI0 */
.dev_id = "fpga:kmi0",
.clk = &ref24_clk,
}, { /* KMI1 */
.dev_id = "fpga:kmi1",
.clk = &ref24_clk,
}, { /* MMC0 */
.dev_id = "fpga:mmc0",
.clk = &ref24_clk,
}, { /* CLCD is in the PB1176 and EB DevChip */
.dev_id = "dev:clcd",
.clk = &oscvco_clk,
}, { /* PB:CLCD */
.dev_id = "issp:clcd",
.clk = &oscvco_clk,
}, { /* SSP */
.dev_id = "dev:ssp0",
.clk = &ref24_clk,
}, { /* SP804 timers */
.dev_id = "sp804",
.clk = &sp804_clk,
},
};
void __init realview_init_early(void)
{
void __iomem *sys = __io_address(REALVIEW_SYS_BASE);
if (machine_is_realview_pb1176())
oscvco_clk.vcoreg = sys + REALVIEW_SYS_OSC0_OFFSET;
else
oscvco_clk.vcoreg = sys + REALVIEW_SYS_OSC4_OFFSET;
clkdev_add_table(lookups, ARRAY_SIZE(lookups));
versatile_sched_clock_init(sys + REALVIEW_SYS_24MHz_OFFSET, 24000000);
}
......
#ifndef __ASM_MACH_CLKDEV_H
#define __ASM_MACH_CLKDEV_H
#include <plat/clock.h>
struct clk {
unsigned long rate;
const struct clk_ops *ops;
const struct icst_params *params;
void __iomem *vcoreg;
};
#define __clk_get(clk) ({ 1; })
#define __clk_put(clk) do { } while (0)
#endif
......@@ -27,6 +27,7 @@
#include <linux/amba/mmci.h>
#include <linux/amba/pl022.h>
#include <linux/io.h>
#include <linux/platform_data/clk-realview.h>
#include <mach/hardware.h>
#include <asm/irq.h>
......@@ -414,6 +415,7 @@ static void __init realview_eb_timer_init(void)
else
timer_irq = IRQ_EB_TIMER0_1;
realview_clk_init(__io_address(REALVIEW_SYS_BASE), false);
realview_timer_init(timer_irq);
realview_eb_twd_init();
}
......
......@@ -29,6 +29,7 @@
#include <linux/mtd/physmap.h>
#include <linux/mtd/partitions.h>
#include <linux/io.h>
#include <linux/platform_data/clk-realview.h>
#include <mach/hardware.h>
#include <asm/irq.h>
......@@ -326,6 +327,7 @@ static void __init realview_pb1176_timer_init(void)
timer2_va_base = __io_address(REALVIEW_PB1176_TIMER2_3_BASE);
timer3_va_base = __io_address(REALVIEW_PB1176_TIMER2_3_BASE) + 0x20;
realview_clk_init(__io_address(REALVIEW_SYS_BASE), true);
realview_timer_init(IRQ_DC1176_TIMER0);
}
......
......@@ -27,6 +27,7 @@
#include <linux/amba/mmci.h>
#include <linux/amba/pl022.h>
#include <linux/io.h>
#include <linux/platform_data/clk-realview.h>
#include <mach/hardware.h>
#include <asm/irq.h>
......@@ -312,6 +313,7 @@ static void __init realview_pb11mp_timer_init(void)
timer2_va_base = __io_address(REALVIEW_PB11MP_TIMER2_3_BASE);
timer3_va_base = __io_address(REALVIEW_PB11MP_TIMER2_3_BASE) + 0x20;
realview_clk_init(__io_address(REALVIEW_SYS_BASE), false);
realview_timer_init(IRQ_TC11MP_TIMER0_1);
realview_pb11mp_twd_init();
}
......
......@@ -27,6 +27,7 @@
#include <linux/amba/mmci.h>
#include <linux/amba/pl022.h>
#include <linux/io.h>
#include <linux/platform_data/clk-realview.h>
#include <asm/irq.h>
#include <asm/leds.h>
......@@ -261,6 +262,7 @@ static void __init realview_pba8_timer_init(void)
timer2_va_base = __io_address(REALVIEW_PBA8_TIMER2_3_BASE);
timer3_va_base = __io_address(REALVIEW_PBA8_TIMER2_3_BASE) + 0x20;
realview_clk_init(__io_address(REALVIEW_SYS_BASE), false);
realview_timer_init(IRQ_PBA8_TIMER0_1);
}
......
......@@ -26,6 +26,7 @@
#include <linux/amba/mmci.h>
#include <linux/amba/pl022.h>
#include <linux/io.h>
#include <linux/platform_data/clk-realview.h>
#include <asm/irq.h>
#include <asm/leds.h>
......@@ -320,6 +321,7 @@ static void __init realview_pbx_timer_init(void)
timer2_va_base = __io_address(REALVIEW_PBX_TIMER2_3_BASE);
timer3_va_base = __io_address(REALVIEW_PBX_TIMER2_3_BASE) + 0x20;
realview_clk_init(__io_address(REALVIEW_SYS_BASE), false);
realview_timer_init(IRQ_PBX_TIMER0_1);
realview_pbx_twd_init();
}
......
......@@ -11,6 +11,7 @@ config UX500_SOC_COMMON
select CACHE_L2X0
select PINCTRL
select PINCTRL_NOMADIK
select COMMON_CLK
config UX500_SOC_DB8500
bool
......
......@@ -2,7 +2,7 @@
# Makefile for the linux kernel, U8500 machine.
#
obj-y := clock.o cpu.o devices.o devices-common.o \
obj-y := cpu.o devices.o devices-common.o \
id.o usb.o timer.o
obj-$(CONFIG_CPU_IDLE) += cpuidle.o
obj-$(CONFIG_CACHE_L2X0) += cache-l2x0.o
......
This diff is collapsed.
/*
* Copyright (C) 2010 ST-Ericsson
* Copyright (C) 2009 STMicroelectronics
*
* 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 clkops - ux500 clock operations
* @enable: function to enable the clock
* @disable: function to disable the clock
* @get_rate: function to get the current clock rate
*
* This structure contains function pointers to functions that will be used to
* control the clock. All of these functions are optional. If get_rate is
* NULL, the rate in the struct clk will be used.
*/
struct clkops {
void (*enable) (struct clk *);
void (*disable) (struct clk *);
unsigned long (*get_rate) (struct clk *);
int (*set_parent)(struct clk *, struct clk *);
};
/**
* struct clk - ux500 clock structure
* @ops: pointer to clkops struct used to control this clock
* @name: name, for debugging
* @enabled: refcount. positive if enabled, zero if disabled
* @get_rate: custom callback for getting the clock rate
* @data: custom per-clock data for example for the get_rate
* callback
* @rate: fixed rate for clocks which don't implement
* ops->getrate
* @prcmu_cg_off: address offset of the combined enable/disable register
* (used on u8500v1)
* @prcmu_cg_bit: bit in the combined enable/disable register (used on
* u8500v1)
* @prcmu_cg_mgt: address of the enable/disable register (used on
* u8500ed)
* @cluster: peripheral cluster number
* @prcc_bus: bit for the bus clock in the peripheral's CLKRST
* @prcc_kernel: bit for the kernel clock in the peripheral's CLKRST.
* -1 if no kernel clock exists.
* @parent_cluster: pointer to parent's cluster clk struct
* @parent_periph: pointer to parent's peripheral clk struct
*
* Peripherals are organised into clusters, and each cluster has an associated
* bus clock. Some peripherals also have a parent peripheral clock.
*
* In order to enable a clock for a peripheral, we need to enable:
* (1) the parent cluster (bus) clock at the PRCMU level
* (2) the parent peripheral clock (if any) at the PRCMU level
* (3) the peripheral's bus & kernel clock at the PRCC level
*
* (1) and (2) are handled by defining clk structs (DEFINE_PRCMU_CLK) for each
* of the cluster and peripheral clocks, and hooking these as the parents of
* the individual peripheral clocks.
*
* (3) is handled by specifying the bits in the PRCC control registers required
* to enable these clocks and modifying them in the ->enable and
* ->disable callbacks of the peripheral clocks (DEFINE_PRCC_CLK).
*
* This structure describes both the PRCMU-level clocks and PRCC-level clocks.
* The prcmu_* fields are only used for the PRCMU clocks, and the cluster,
* prcc, and parent pointers are only used for the PRCC-level clocks.
*/
struct clk {
const struct clkops *ops;
const char *name;
unsigned int enabled;
unsigned long (*get_rate)(struct clk *);
void *data;
unsigned long rate;
struct list_head list;
/* These three are only for PRCMU clks */
unsigned int prcmu_cg_off;
unsigned int prcmu_cg_bit;
unsigned int prcmu_cg_mgt;
/* The rest are only for PRCC clks */
int cluster;
unsigned int prcc_bus;
unsigned int prcc_kernel;
struct clk *parent_cluster;
struct clk *parent_periph;
#if defined(CONFIG_DEBUG_FS)
struct dentry *dent; /* For visible tree hierarchy */
struct dentry *dent_bus; /* For visible tree hierarchy */
#endif
};
#define DEFINE_PRCMU_CLK(_name, _cg_off, _cg_bit, _reg) \
struct clk clk_##_name = { \
.name = #_name, \
.ops = &clk_prcmu_ops, \
.prcmu_cg_off = _cg_off, \
.prcmu_cg_bit = _cg_bit, \
.prcmu_cg_mgt = PRCM_##_reg##_MGT \
}
#define DEFINE_PRCMU_CLK_RATE(_name, _cg_off, _cg_bit, _reg, _rate) \
struct clk clk_##_name = { \
.name = #_name, \
.ops = &clk_prcmu_ops, \
.prcmu_cg_off = _cg_off, \
.prcmu_cg_bit = _cg_bit, \
.rate = _rate, \
.prcmu_cg_mgt = PRCM_##_reg##_MGT \
}
#define DEFINE_PRCC_CLK(_pclust, _name, _bus_en, _kernel_en, _kernclk) \
struct clk clk_##_name = { \
.name = #_name, \
.ops = &clk_prcc_ops, \
.cluster = _pclust, \
.prcc_bus = _bus_en, \
.prcc_kernel = _kernel_en, \
.parent_cluster = &clk_per##_pclust##clk, \
.parent_periph = _kernclk \
}
#define DEFINE_PRCC_CLK_CUSTOM(_pclust, _name, _bus_en, _kernel_en, _kernclk, _callback, _data) \
struct clk clk_##_name = { \
.name = #_name, \
.ops = &clk_prcc_ops, \
.cluster = _pclust, \
.prcc_bus = _bus_en, \
.prcc_kernel = _kernel_en, \
.parent_cluster = &clk_per##_pclust##clk, \
.parent_periph = _kernclk, \
.get_rate = _callback, \
.data = (void *) _data \
}
#define CLK(_clk, _devname, _conname) \
{ \
.clk = &clk_##_clk, \
.dev_id = _devname, \
.con_id = _conname, \
}
int __init clk_db8500_ed_fixup(void);
int __init clk_init(void);
#ifdef CONFIG_DEBUG_FS
int clk_debugfs_init(void);
#else
static inline int clk_debugfs_init(void) { return 0; }
#endif
#ifdef CONFIG_CPU_FREQ
int clk_init_smp_twd_cpufreq(void);
#else
static inline int clk_init_smp_twd_cpufreq(void) { return 0; }
#endif
......@@ -8,7 +8,6 @@
#include <linux/platform_device.h>
#include <linux/io.h>
#include <linux/clk.h>
#include <linux/mfd/db8500-prcmu.h>
#include <linux/clksrc-dbx500-prcmu.h>
#include <linux/sys_soc.h>
......@@ -17,6 +16,7 @@
#include <linux/stat.h>
#include <linux/of.h>
#include <linux/of_irq.h>
#include <linux/platform_data/clk-ux500.h>
#include <asm/hardware/gic.h>
#include <asm/mach/map.h>
......@@ -25,8 +25,6 @@
#include <mach/setup.h>
#include <mach/devices.h>
#include "clock.h"
void __iomem *_PRCMU_BASE;
/*
......@@ -70,13 +68,17 @@ void __init ux500_init_irq(void)
*/
if (cpu_is_u8500_family())
db8500_prcmu_early_init();
clk_init();
if (cpu_is_u8500_family())
u8500_clk_init();
else if (cpu_is_u9540())
u9540_clk_init();
else if (cpu_is_u8540())
u8540_clk_init();
}
void __init ux500_init_late(void)
{
clk_debugfs_init();
clk_init_smp_twd_cpufreq();
}
static const char * __init ux500_get_machine(void)
......
......@@ -40,4 +40,17 @@ config COMMON_CLK_WM831X
Supports the clocking subsystem of the WM831x/2x series of
PMICs from Wolfson Microlectronics.
config COMMON_CLK_VERSATILE
bool "Clock driver for ARM Reference designs"
depends on ARCH_INTEGRATOR || ARCH_REALVIEW
---help---
Supports clocking on ARM Reference designs Integrator/AP,
Integrator/CP, RealView PB1176, EB, PB11MP and PBX.
config COMMON_CLK_MAX77686
tristate "Clock driver for Maxim 77686 MFD"
depends on MFD_MAX77686
---help---
This driver supports Maxim 77686 crystal oscillator clock.
endmenu
......@@ -9,7 +9,14 @@ obj-$(CONFIG_ARCH_MXS) += mxs/
obj-$(CONFIG_ARCH_SOCFPGA) += socfpga/
obj-$(CONFIG_PLAT_SPEAR) += spear/
obj-$(CONFIG_ARCH_U300) += clk-u300.o
obj-$(CONFIG_ARCH_INTEGRATOR) += versatile/
obj-$(CONFIG_COMMON_CLK_VERSATILE) += versatile/
obj-$(CONFIG_ARCH_PRIMA2) += clk-prima2.o
ifeq ($(CONFIG_COMMON_CLK), y)
obj-$(CONFIG_ARCH_MMP) += mmp/
endif
obj-$(CONFIG_MACH_LOONGSON1) += clk-ls1x.o
obj-$(CONFIG_ARCH_U8500) += ux500/
# Chip specific
obj-$(CONFIG_COMMON_CLK_WM831X) += clk-wm831x.o
obj-$(CONFIG_COMMON_CLK_MAX77686) += clk-max77686.o
/*
* Copyright (c) 2012 Zhang, Keguang <keguang.zhang@gmail.com>
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation; either version 2 of the License, or (at your
* option) any later version.
*/
#include <linux/clkdev.h>
#include <linux/clk-provider.h>
#include <linux/io.h>
#include <linux/slab.h>
#include <linux/err.h>
#include <loongson1.h>
#define OSC 33
static DEFINE_SPINLOCK(_lock);
static int ls1x_pll_clk_enable(struct clk_hw *hw)
{
return 0;
}
static void ls1x_pll_clk_disable(struct clk_hw *hw)
{
}
static unsigned long ls1x_pll_recalc_rate(struct clk_hw *hw,
unsigned long parent_rate)
{
u32 pll, rate;
pll = __raw_readl(LS1X_CLK_PLL_FREQ);
rate = ((12 + (pll & 0x3f)) * 1000000) +
((((pll >> 8) & 0x3ff) * 1000000) >> 10);
rate *= OSC;
rate >>= 1;
return rate;
}
static const struct clk_ops ls1x_pll_clk_ops = {
.enable = ls1x_pll_clk_enable,
.disable = ls1x_pll_clk_disable,
.recalc_rate = ls1x_pll_recalc_rate,
};
static struct clk * __init clk_register_pll(struct device *dev,
const char *name, const char *parent_name, unsigned long flags)
{
struct clk_hw *hw;
struct clk *clk;
struct clk_init_data init;
/* allocate the divider */
hw = kzalloc(sizeof(struct clk_hw), GFP_KERNEL);
if (!hw) {
pr_err("%s: could not allocate clk_hw\n", __func__);
return ERR_PTR(-ENOMEM);
}
init.name = name;
init.ops = &ls1x_pll_clk_ops;
init.flags = flags | CLK_IS_BASIC;
init.parent_names = (parent_name ? &parent_name : NULL);
init.num_parents = (parent_name ? 1 : 0);
hw->init = &init;
/* register the clock */
clk = clk_register(dev, hw);
if (IS_ERR(clk))
kfree(hw);
return clk;
}
void __init ls1x_clk_init(void)
{
struct clk *clk;
clk = clk_register_pll(NULL, "pll_clk", NULL, CLK_IS_ROOT);
clk_prepare_enable(clk);
clk = clk_register_divider(NULL, "cpu_clk", "pll_clk",
CLK_SET_RATE_PARENT, LS1X_CLK_PLL_DIV, DIV_CPU_SHIFT,
DIV_CPU_WIDTH, CLK_DIVIDER_ONE_BASED, &_lock);
clk_prepare_enable(clk);
clk_register_clkdev(clk, "cpu", NULL);
clk = clk_register_divider(NULL, "dc_clk", "pll_clk",
CLK_SET_RATE_PARENT, LS1X_CLK_PLL_DIV, DIV_DC_SHIFT,
DIV_DC_WIDTH, CLK_DIVIDER_ONE_BASED, &_lock);
clk_prepare_enable(clk);
clk_register_clkdev(clk, "dc", NULL);
clk = clk_register_divider(NULL, "ahb_clk", "pll_clk",
CLK_SET_RATE_PARENT, LS1X_CLK_PLL_DIV, DIV_DDR_SHIFT,
DIV_DDR_WIDTH, CLK_DIVIDER_ONE_BASED, &_lock);
clk_prepare_enable(clk);
clk_register_clkdev(clk, "ahb", NULL);
clk_register_clkdev(clk, "stmmaceth", NULL);
clk = clk_register_fixed_factor(NULL, "apb_clk", "ahb_clk", 0, 1, 2);
clk_prepare_enable(clk);
clk_register_clkdev(clk, "apb", NULL);
clk_register_clkdev(clk, "serial8250", NULL);
}
/*
* clk-max77686.c - Clock driver for Maxim 77686
*
* Copyright (C) 2012 Samsung Electornics
* Jonghwa Lee <jonghwa3.lee@samsung.com>
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation; either version 2 of the License, or (at your
* option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
*/
#include <linux/kernel.h>
#include <linux/slab.h>
#include <linux/err.h>
#include <linux/platform_device.h>
#include <linux/mfd/max77686.h>
#include <linux/mfd/max77686-private.h>
#include <linux/clk-provider.h>
#include <linux/mutex.h>
#include <linux/clkdev.h>
enum {
MAX77686_CLK_AP = 0,
MAX77686_CLK_CP,
MAX77686_CLK_PMIC,
MAX77686_CLKS_NUM,
};
struct max77686_clk {
struct max77686_dev *iodev;
u32 mask;
struct clk_hw hw;
struct clk_lookup *lookup;
};
static struct max77686_clk *get_max77686_clk(struct clk_hw *hw)
{
return container_of(hw, struct max77686_clk, hw);
}
static int max77686_clk_prepare(struct clk_hw *hw)
{
struct max77686_clk *max77686;
int ret;
max77686 = get_max77686_clk(hw);
if (!max77686)
return -ENOMEM;
ret = regmap_update_bits(max77686->iodev->regmap,
MAX77686_REG_32KHZ, max77686->mask, max77686->mask);
return ret;
}
static void max77686_clk_unprepare(struct clk_hw *hw)
{
struct max77686_clk *max77686;
max77686 = get_max77686_clk(hw);
if (!max77686)
return;
regmap_update_bits(max77686->iodev->regmap,
MAX77686_REG_32KHZ, max77686->mask, ~max77686->mask);
}
static int max77686_clk_is_enabled(struct clk_hw *hw)
{
struct max77686_clk *max77686;
int ret;
u32 val;
max77686 = get_max77686_clk(hw);
if (!max77686)
return -ENOMEM;
ret = regmap_read(max77686->iodev->regmap,
MAX77686_REG_32KHZ, &val);
if (ret < 0)
return -EINVAL;
return val & max77686->mask;
}
static struct clk_ops max77686_clk_ops = {
.prepare = max77686_clk_prepare,
.unprepare = max77686_clk_unprepare,
.is_enabled = max77686_clk_is_enabled,
};
static struct clk_init_data max77686_clks_init[MAX77686_CLKS_NUM] = {
[MAX77686_CLK_AP] = {
.name = "32khz_ap",
.ops = &max77686_clk_ops,
.flags = CLK_IS_ROOT,
},
[MAX77686_CLK_CP] = {
.name = "32khz_cp",
.ops = &max77686_clk_ops,
.flags = CLK_IS_ROOT,
},
[MAX77686_CLK_PMIC] = {
.name = "32khz_pmic",
.ops = &max77686_clk_ops,
.flags = CLK_IS_ROOT,
},
};
static int max77686_clk_register(struct device *dev,
struct max77686_clk *max77686)
{
struct clk *clk;
struct clk_hw *hw = &max77686->hw;
clk = clk_register(dev, hw);
if (IS_ERR(clk))
return -ENOMEM;
max77686->lookup = devm_kzalloc(dev, sizeof(struct clk_lookup),
GFP_KERNEL);
if (IS_ERR(max77686->lookup))
return -ENOMEM;
max77686->lookup->con_id = hw->init->name;
max77686->lookup->clk = clk;
clkdev_add(max77686->lookup);
return 0;
}
static __devinit int max77686_clk_probe(struct platform_device *pdev)
{
struct max77686_dev *iodev = dev_get_drvdata(pdev->dev.parent);
struct max77686_clk **max77686_clks;
int i, ret;
max77686_clks = devm_kzalloc(&pdev->dev, sizeof(struct max77686_clk *)
* MAX77686_CLKS_NUM, GFP_KERNEL);
if (IS_ERR(max77686_clks))
return -ENOMEM;
for (i = 0; i < MAX77686_CLKS_NUM; i++) {
max77686_clks[i] = devm_kzalloc(&pdev->dev,
sizeof(struct max77686_clk), GFP_KERNEL);
if (IS_ERR(max77686_clks[i]))
return -ENOMEM;
}
for (i = 0; i < MAX77686_CLKS_NUM; i++) {
max77686_clks[i]->iodev = iodev;
max77686_clks[i]->mask = 1 << i;
max77686_clks[i]->hw.init = &max77686_clks_init[i];
ret = max77686_clk_register(&pdev->dev, max77686_clks[i]);
if (ret) {
switch (i) {
case MAX77686_CLK_AP:
dev_err(&pdev->dev, "Fail to register CLK_AP\n");
goto err_clk_ap;
break;
case MAX77686_CLK_CP:
dev_err(&pdev->dev, "Fail to register CLK_CP\n");
goto err_clk_cp;
break;
case MAX77686_CLK_PMIC:
dev_err(&pdev->dev, "Fail to register CLK_PMIC\n");
goto err_clk_pmic;
}
}
}
platform_set_drvdata(pdev, max77686_clks);
goto out;
err_clk_pmic:
clkdev_drop(max77686_clks[MAX77686_CLK_CP]->lookup);
kfree(max77686_clks[MAX77686_CLK_CP]->hw.clk);
err_clk_cp:
clkdev_drop(max77686_clks[MAX77686_CLK_AP]->lookup);
kfree(max77686_clks[MAX77686_CLK_AP]->hw.clk);
err_clk_ap:
out:
return ret;
}
static int __devexit max77686_clk_remove(struct platform_device *pdev)
{
struct max77686_clk **max77686_clks = platform_get_drvdata(pdev);
int i;
for (i = 0; i < MAX77686_CLKS_NUM; i++) {
clkdev_drop(max77686_clks[i]->lookup);
kfree(max77686_clks[i]->hw.clk);
}
return 0;
}
static const struct platform_device_id max77686_clk_id[] = {
{ "max77686-clk", 0},
{ },
};
MODULE_DEVICE_TABLE(platform, max77686_clk_id);
static struct platform_driver max77686_clk_driver = {
.driver = {
.name = "max77686-clk",
.owner = THIS_MODULE,
},
.probe = max77686_clk_probe,
.remove = __devexit_p(max77686_clk_remove),
.id_table = max77686_clk_id,
};
static int __init max77686_clk_init(void)
{
return platform_driver_register(&max77686_clk_driver);
}
subsys_initcall(max77686_clk_init);
static void __init max77686_clk_cleanup(void)
{
platform_driver_unregister(&max77686_clk_driver);
}
module_exit(max77686_clk_cleanup);
MODULE_DESCRIPTION("MAXIM 77686 Clock Driver");
MODULE_AUTHOR("Jonghwa Lee <jonghwa3.lee@samsung.com>");
MODULE_LICENSE("GPL");
This diff is collapsed.
......@@ -557,25 +557,6 @@ int clk_enable(struct clk *clk)
}
EXPORT_SYMBOL_GPL(clk_enable);
/**
* clk_get_rate - return the rate of clk
* @clk: the clk whose rate is being returned
*
* Simply returns the cached rate of the clk. Does not query the hardware. If
* clk is NULL then returns 0.
*/
unsigned long clk_get_rate(struct clk *clk)
{
unsigned long rate;
mutex_lock(&prepare_lock);
rate = __clk_get_rate(clk);
mutex_unlock(&prepare_lock);
return rate;
}
EXPORT_SYMBOL_GPL(clk_get_rate);
/**
* __clk_round_rate - round the given rate for a clk
* @clk: round the rate of this clock
......@@ -701,6 +682,30 @@ static void __clk_recalc_rates(struct clk *clk, unsigned long msg)
__clk_recalc_rates(child, msg);
}
/**
* clk_get_rate - return the rate of clk
* @clk: the clk whose rate is being returned
*
* Simply returns the cached rate of the clk, unless CLK_GET_RATE_NOCACHE flag
* is set, which means a recalc_rate will be issued.
* If clk is NULL then returns 0.
*/
unsigned long clk_get_rate(struct clk *clk)
{
unsigned long rate;
mutex_lock(&prepare_lock);
if (clk && (clk->flags & CLK_GET_RATE_NOCACHE))
__clk_recalc_rates(clk, 0);
rate = __clk_get_rate(clk);
mutex_unlock(&prepare_lock);
return rate;
}
EXPORT_SYMBOL_GPL(clk_get_rate);
/**
* __clk_speculate_rates
* @clk: first clk in the subtree
......@@ -1582,6 +1587,20 @@ struct clk *of_clk_src_simple_get(struct of_phandle_args *clkspec,
}
EXPORT_SYMBOL_GPL(of_clk_src_simple_get);
struct clk *of_clk_src_onecell_get(struct of_phandle_args *clkspec, void *data)
{
struct clk_onecell_data *clk_data = data;
unsigned int idx = clkspec->args[0];
if (idx >= clk_data->clk_num) {
pr_err("%s: invalid clock index %d\n", __func__, idx);
return ERR_PTR(-EINVAL);
}
return clk_data->clks[idx];
}
EXPORT_SYMBOL_GPL(of_clk_src_onecell_get);
/**
* of_clk_add_provider() - Register a clock provider for a node
* @np: Device node pointer associated with clock provider
......
#
# Makefile for mmp specific clk
#
obj-y += clk-apbc.o clk-apmu.o clk-frac.o
obj-$(CONFIG_CPU_PXA168) += clk-pxa168.o
obj-$(CONFIG_CPU_PXA910) += clk-pxa910.o
obj-$(CONFIG_CPU_MMP2) += clk-mmp2.o
/*
* mmp APB clock operation source file
*
* Copyright (C) 2012 Marvell
* Chao Xie <xiechao.mail@gmail.com>
*
* This file is licensed under the terms of the GNU General Public
* License version 2. This program is licensed "as is" without any
* warranty of any kind, whether express or implied.
*/
#include <linux/kernel.h>
#include <linux/clk.h>
#include <linux/io.h>
#include <linux/err.h>
#include <linux/delay.h>
#include <linux/slab.h>
#include "clk.h"
/* Common APB clock register bit definitions */
#define APBC_APBCLK (1 << 0) /* APB Bus Clock Enable */
#define APBC_FNCLK (1 << 1) /* Functional Clock Enable */
#define APBC_RST (1 << 2) /* Reset Generation */
#define APBC_POWER (1 << 7) /* Reset Generation */
#define to_clk_apbc(hw) container_of(hw, struct clk_apbc, hw)
struct clk_apbc {
struct clk_hw hw;
void __iomem *base;
unsigned int delay;
unsigned int flags;
spinlock_t *lock;
};
static int clk_apbc_prepare(struct clk_hw *hw)
{
struct clk_apbc *apbc = to_clk_apbc(hw);
unsigned int data;
unsigned long flags = 0;
/*
* It may share same register as MUX clock,
* and it will impact FNCLK enable. Spinlock is needed
*/
if (apbc->lock)
spin_lock_irqsave(apbc->lock, flags);
data = readl_relaxed(apbc->base);
if (apbc->flags & APBC_POWER_CTRL)
data |= APBC_POWER;
data |= APBC_FNCLK;
writel_relaxed(data, apbc->base);
if (apbc->lock)
spin_unlock_irqrestore(apbc->lock, flags);
udelay(apbc->delay);
if (apbc->lock)
spin_lock_irqsave(apbc->lock, flags);
data = readl_relaxed(apbc->base);
data |= APBC_APBCLK;
writel_relaxed(data, apbc->base);
if (apbc->lock)
spin_unlock_irqrestore(apbc->lock, flags);
udelay(apbc->delay);
if (!(apbc->flags & APBC_NO_BUS_CTRL)) {
if (apbc->lock)
spin_lock_irqsave(apbc->lock, flags);
data = readl_relaxed(apbc->base);
data &= ~APBC_RST;
writel_relaxed(data, apbc->base);
if (apbc->lock)
spin_unlock_irqrestore(apbc->lock, flags);
}
return 0;
}
static void clk_apbc_unprepare(struct clk_hw *hw)
{
struct clk_apbc *apbc = to_clk_apbc(hw);
unsigned long data;
unsigned long flags = 0;
if (apbc->lock)
spin_lock_irqsave(apbc->lock, flags);
data = readl_relaxed(apbc->base);
if (apbc->flags & APBC_POWER_CTRL)
data &= ~APBC_POWER;
data &= ~APBC_FNCLK;
writel_relaxed(data, apbc->base);
if (apbc->lock)
spin_unlock_irqrestore(apbc->lock, flags);
udelay(10);
if (apbc->lock)
spin_lock_irqsave(apbc->lock, flags);
data = readl_relaxed(apbc->base);
data &= ~APBC_APBCLK;
writel_relaxed(data, apbc->base);
if (apbc->lock)
spin_unlock_irqrestore(apbc->lock, flags);
}
struct clk_ops clk_apbc_ops = {
.prepare = clk_apbc_prepare,
.unprepare = clk_apbc_unprepare,
};
struct clk *mmp_clk_register_apbc(const char *name, const char *parent_name,
void __iomem *base, unsigned int delay,
unsigned int apbc_flags, spinlock_t *lock)
{
struct clk_apbc *apbc;
struct clk *clk;
struct clk_init_data init;
apbc = kzalloc(sizeof(*apbc), GFP_KERNEL);
if (!apbc)
return NULL;
init.name = name;
init.ops = &clk_apbc_ops;
init.flags = CLK_SET_RATE_PARENT;
init.parent_names = (parent_name ? &parent_name : NULL);
init.num_parents = (parent_name ? 1 : 0);
apbc->base = base;
apbc->delay = delay;
apbc->flags = apbc_flags;
apbc->lock = lock;
apbc->hw.init = &init;
clk = clk_register(NULL, &apbc->hw);
if (IS_ERR(clk))
kfree(apbc);
return clk;
}
/*
* mmp AXI peripharal clock operation source file
*
* Copyright (C) 2012 Marvell
* Chao Xie <xiechao.mail@gmail.com>
*
* This file is licensed under the terms of the GNU General Public
* License version 2. This program is licensed "as is" without any
* warranty of any kind, whether express or implied.
*/
#include <linux/kernel.h>
#include <linux/clk.h>
#include <linux/io.h>
#include <linux/err.h>
#include <linux/delay.h>
#include <linux/slab.h>
#include "clk.h"
#define to_clk_apmu(clk) (container_of(clk, struct clk_apmu, clk))
struct clk_apmu {
struct clk_hw hw;
void __iomem *base;
u32 rst_mask;
u32 enable_mask;
spinlock_t *lock;
};
static int clk_apmu_enable(struct clk_hw *hw)
{
struct clk_apmu *apmu = to_clk_apmu(hw);
unsigned long data;
unsigned long flags = 0;
if (apmu->lock)
spin_lock_irqsave(apmu->lock, flags);
data = readl_relaxed(apmu->base) | apmu->enable_mask;
writel_relaxed(data, apmu->base);
if (apmu->lock)
spin_unlock_irqrestore(apmu->lock, flags);
return 0;
}
static void clk_apmu_disable(struct clk_hw *hw)
{
struct clk_apmu *apmu = to_clk_apmu(hw);
unsigned long data;
unsigned long flags = 0;
if (apmu->lock)
spin_lock_irqsave(apmu->lock, flags);
data = readl_relaxed(apmu->base) & ~apmu->enable_mask;
writel_relaxed(data, apmu->base);
if (apmu->lock)
spin_unlock_irqrestore(apmu->lock, flags);
}
struct clk_ops clk_apmu_ops = {
.enable = clk_apmu_enable,
.disable = clk_apmu_disable,
};
struct clk *mmp_clk_register_apmu(const char *name, const char *parent_name,
void __iomem *base, u32 enable_mask, spinlock_t *lock)
{
struct clk_apmu *apmu;
struct clk *clk;
struct clk_init_data init;
apmu = kzalloc(sizeof(*apmu), GFP_KERNEL);
if (!apmu)
return NULL;
init.name = name;
init.ops = &clk_apmu_ops;
init.flags = CLK_SET_RATE_PARENT;
init.parent_names = (parent_name ? &parent_name : NULL);
init.num_parents = (parent_name ? 1 : 0);
apmu->base = base;
apmu->enable_mask = enable_mask;
apmu->lock = lock;
apmu->hw.init = &init;
clk = clk_register(NULL, &apmu->hw);
if (IS_ERR(clk))
kfree(apmu);
return clk;
}
/*
* mmp factor clock operation source file
*
* Copyright (C) 2012 Marvell
* Chao Xie <xiechao.mail@gmail.com>
*
* This file is licensed under the terms of the GNU General Public
* License version 2. This program is licensed "as is" without any
* warranty of any kind, whether express or implied.
*/
#include <linux/clk-provider.h>
#include <linux/slab.h>
#include <linux/io.h>
#include <linux/err.h>
#include "clk.h"
/*
* It is M/N clock
*
* Fout from synthesizer can be given from two equations:
* numerator/denominator = Fin / (Fout * factor)
*/
#define to_clk_factor(hw) container_of(hw, struct clk_factor, hw)
struct clk_factor {
struct clk_hw hw;
void __iomem *base;
struct clk_factor_masks *masks;
struct clk_factor_tbl *ftbl;
unsigned int ftbl_cnt;
};
static long clk_factor_round_rate(struct clk_hw *hw, unsigned long drate,
unsigned long *prate)
{
struct clk_factor *factor = to_clk_factor(hw);
unsigned long rate = 0, prev_rate;
int i;
for (i = 0; i < factor->ftbl_cnt; i++) {
prev_rate = rate;
rate = (((*prate / 10000) * factor->ftbl[i].num) /
(factor->ftbl[i].den * factor->masks->factor)) * 10000;
if (rate > drate)
break;
}
if (i == 0)
return rate;
else
return prev_rate;
}
static unsigned long clk_factor_recalc_rate(struct clk_hw *hw,
unsigned long parent_rate)
{
struct clk_factor *factor = to_clk_factor(hw);
struct clk_factor_masks *masks = factor->masks;
unsigned int val, num, den;
val = readl_relaxed(factor->base);
/* calculate numerator */
num = (val >> masks->num_shift) & masks->num_mask;
/* calculate denominator */
den = (val >> masks->den_shift) & masks->num_mask;
if (!den)
return 0;
return (((parent_rate / 10000) * den) /
(num * factor->masks->factor)) * 10000;
}
/* Configures new clock rate*/
static int clk_factor_set_rate(struct clk_hw *hw, unsigned long drate,
unsigned long prate)
{
struct clk_factor *factor = to_clk_factor(hw);
struct clk_factor_masks *masks = factor->masks;
int i;
unsigned long val;
unsigned long prev_rate, rate = 0;
for (i = 0; i < factor->ftbl_cnt; i++) {
prev_rate = rate;
rate = (((prate / 10000) * factor->ftbl[i].num) /
(factor->ftbl[i].den * factor->masks->factor)) * 10000;
if (rate > drate)
break;
}
if (i > 0)
i--;
val = readl_relaxed(factor->base);
val &= ~(masks->num_mask << masks->num_shift);
val |= (factor->ftbl[i].num & masks->num_mask) << masks->num_shift;
val &= ~(masks->den_mask << masks->den_shift);
val |= (factor->ftbl[i].den & masks->den_mask) << masks->den_shift;
writel_relaxed(val, factor->base);
return 0;
}
static struct clk_ops clk_factor_ops = {
.recalc_rate = clk_factor_recalc_rate,
.round_rate = clk_factor_round_rate,
.set_rate = clk_factor_set_rate,
};
struct clk *mmp_clk_register_factor(const char *name, const char *parent_name,
unsigned long flags, void __iomem *base,
struct clk_factor_masks *masks, struct clk_factor_tbl *ftbl,
unsigned int ftbl_cnt)
{
struct clk_factor *factor;
struct clk_init_data init;
struct clk *clk;
if (!masks) {
pr_err("%s: must pass a clk_factor_mask\n", __func__);
return ERR_PTR(-EINVAL);
}
factor = kzalloc(sizeof(*factor), GFP_KERNEL);
if (!factor) {
pr_err("%s: could not allocate factor clk\n", __func__);
return ERR_PTR(-ENOMEM);
}
/* struct clk_aux assignments */
factor->base = base;
factor->masks = masks;
factor->ftbl = ftbl;
factor->ftbl_cnt = ftbl_cnt;
factor->hw.init = &init;
init.name = name;
init.ops = &clk_factor_ops;
init.flags = flags;
init.parent_names = &parent_name;
init.num_parents = 1;
clk = clk_register(NULL, &factor->hw);
if (IS_ERR_OR_NULL(clk))
kfree(factor);
return clk;
}
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
#ifndef __MACH_MMP_CLK_H
#define __MACH_MMP_CLK_H
#include <linux/clk-provider.h>
#include <linux/clkdev.h>
#define APBC_NO_BUS_CTRL BIT(0)
#define APBC_POWER_CTRL BIT(1)
struct clk_factor_masks {
unsigned int factor;
unsigned int num_mask;
unsigned int den_mask;
unsigned int num_shift;
unsigned int den_shift;
};
struct clk_factor_tbl {
unsigned int num;
unsigned int den;
};
extern struct clk *mmp_clk_register_pll2(const char *name,
const char *parent_name, unsigned long flags);
extern struct clk *mmp_clk_register_apbc(const char *name,
const char *parent_name, void __iomem *base,
unsigned int delay, unsigned int apbc_flags, spinlock_t *lock);
extern struct clk *mmp_clk_register_apmu(const char *name,
const char *parent_name, void __iomem *base, u32 enable_mask,
spinlock_t *lock);
extern struct clk *mmp_clk_register_factor(const char *name,
const char *parent_name, unsigned long flags,
void __iomem *base, struct clk_factor_masks *masks,
struct clk_factor_tbl *ftbl, unsigned int ftbl_cnt);
#endif
#
# Makefile for ux500 clocks
#
# Clock types
obj-y += clk-prcc.o
obj-y += clk-prcmu.o
# Clock definitions
obj-y += u8500_clk.o
obj-y += u9540_clk.o
obj-y += u8540_clk.o
This diff is collapsed.
This diff is collapsed.
/*
* Clocks for ux500 platforms
*
* Copyright (C) 2012 ST-Ericsson SA
* Author: Ulf Hansson <ulf.hansson@linaro.org>
*
* License terms: GNU General Public License (GPL) version 2
*/
#ifndef __UX500_CLK_H
#define __UX500_CLK_H
#include <linux/clk.h>
struct clk *clk_reg_prcc_pclk(const char *name,
const char *parent_name,
unsigned int phy_base,
u32 cg_sel,
unsigned long flags);
struct clk *clk_reg_prcc_kclk(const char *name,
const char *parent_name,
unsigned int phy_base,
u32 cg_sel,
unsigned long flags);
struct clk *clk_reg_prcmu_scalable(const char *name,
const char *parent_name,
u8 cg_sel,
unsigned long rate,
unsigned long flags);
struct clk *clk_reg_prcmu_gate(const char *name,
const char *parent_name,
u8 cg_sel,
unsigned long flags);
struct clk *clk_reg_prcmu_rate(const char *name,
const char *parent_name,
u8 cg_sel,
unsigned long flags);
struct clk *clk_reg_prcmu_opp_gate(const char *name,
const char *parent_name,
u8 cg_sel,
unsigned long flags);
#endif /* __UX500_CLK_H */
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
# Makefile for Versatile-specific clocks
obj-$(CONFIG_ICST) += clk-icst.o
obj-$(CONFIG_ARCH_INTEGRATOR) += clk-integrator.o
obj-$(CONFIG_ARCH_REALVIEW) += clk-realview.o
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
void realview_clk_init(void __iomem *sysbase, bool is_pb1176);
This diff is collapsed.
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