Commit 2c0c86d5 authored by Linus Torvalds's avatar Linus Torvalds

Merge tag 'clk-for-linus' of git://git.linaro.org/people/mturquette/linux

Pull clk framework update from Michael Turquette:
 "The common clk framework changes for 3.7 are dominated by ARM platform
  ports to the framework along with one MIPS port, one MFD port, one
  minor framework enhancement and one helper function for platforms
  expressing their clock data through device tree."

* tag 'clk-for-linus' of git://git.linaro.org/people/mturquette/linux:
  clk: add of_clk_src_onecell_get() support
  clk: ux500: Define smp_twd clock for u8500
  mfd: dbx500: Provide a more accurate smp_twd clock
  clk: ux500: Support for prmcu_rate clock
  clk: Provide option for clk_get_rate to issue hw for new rate
  clock: max77686: Add driver for Maxim 77686 32Khz crystal oscillator.
  ARM: ux500: Switch to use common clock framework
  clk: ux500: Clock definitions for u8500
  clk: ux500: First version of clock definitions for ux500
  clk: ux500: Adapt PRCMU and PRCC clocks for common clk
  clk: versatile: make config option boolean
  clk: add Loongson1B clock support
  arm: mmp: make all SOCs use common clock by default
  clk: mmp: add clock definition for mmp2
  clk: mmp: add clock definition for pxa910
  clk: mmp: add clock definition for pxa168
  clk: mmp: add mmp specific clocks
  clk: convert ARM RealView to common clk
  clk: prima2: move from arch/arm/mach to drivers/clk
  ARM: PRIMA2: convert to common clk and finish full clk tree
parents fdb2f9c2 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
......@@ -10,7 +10,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