Commit 0aa0c95f authored by Haojian Zhuang's avatar Haojian Zhuang

clk: hisilicon: add common clock support

Enable common clock driver of Hi3620 SoC. clkgate-seperated driver is
used to support the clock gate that enable/disable/status registers
are seperated.
Signed-off-by: default avatarHaojian Zhuang <haojian.zhuang@gmail.com>
parent 6ce4eac1
* Hisilicon Hi3620 Clock Controller
The Hi3620 clock controller generates and supplies clock to various
controllers within the Hi3620 SoC.
Required Properties:
- compatible: should be one of the following.
- "hisilicon,hi3620-clock" - controller compatible with Hi3620 SoC.
- reg: physical base address of the controller and length of memory mapped
region.
- #clock-cells: should be 1.
Each clock is assigned an identifier and client nodes use this identifier
to specify the clock which they consume.
All these identifier could be found in <dt-bindings/clock/hi3620-clock.h>.
...@@ -14,6 +14,7 @@ obj-$(CONFIG_ARCH_BCM2835) += clk-bcm2835.o ...@@ -14,6 +14,7 @@ obj-$(CONFIG_ARCH_BCM2835) += clk-bcm2835.o
obj-$(CONFIG_ARCH_EFM32) += clk-efm32gg.o obj-$(CONFIG_ARCH_EFM32) += clk-efm32gg.o
obj-$(CONFIG_ARCH_NOMADIK) += clk-nomadik.o obj-$(CONFIG_ARCH_NOMADIK) += clk-nomadik.o
obj-$(CONFIG_ARCH_HIGHBANK) += clk-highbank.o obj-$(CONFIG_ARCH_HIGHBANK) += clk-highbank.o
obj-$(CONFIG_ARCH_HI3xxx) += hisilicon/
obj-$(CONFIG_ARCH_NSPIRE) += clk-nspire.o obj-$(CONFIG_ARCH_NSPIRE) += clk-nspire.o
obj-$(CONFIG_ARCH_MXS) += mxs/ obj-$(CONFIG_ARCH_MXS) += mxs/
obj-$(CONFIG_ARCH_SOCFPGA) += socfpga/ obj-$(CONFIG_ARCH_SOCFPGA) += socfpga/
......
#
# Hisilicon Clock specific Makefile
#
obj-y += clk.o clkgate-separated.o clk-hi3620.o
This diff is collapsed.
/*
* Hisilicon clock driver
*
* Copyright (c) 2012-2013 Hisilicon Limited.
* Copyright (c) 2012-2013 Linaro Limited.
*
* Author: Haojian Zhuang <haojian.zhuang@linaro.org>
* Xin Li <li.xin@linaro.org>
*
* 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.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*
*/
#include <linux/kernel.h>
#include <linux/clk-provider.h>
#include <linux/clkdev.h>
#include <linux/delay.h>
#include <linux/io.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/of_device.h>
#include <linux/slab.h>
#include <linux/clk.h>
#include "clk.h"
static DEFINE_SPINLOCK(hisi_clk_lock);
static struct clk **clk_table;
static struct clk_onecell_data clk_data;
void __init hisi_clk_init(struct device_node *np, int nr_clks)
{
clk_table = kzalloc(sizeof(struct clk *) * nr_clks, GFP_KERNEL);
if (!clk_table) {
pr_err("%s: could not allocate clock lookup table\n", __func__);
return;
}
clk_data.clks = clk_table;
clk_data.clk_num = nr_clks;
of_clk_add_provider(np, of_clk_src_onecell_get, &clk_data);
}
void __init hisi_clk_register_fixed_rate(struct hisi_fixed_rate_clock *clks,
int nums, void __iomem *base)
{
struct clk *clk;
int i;
for (i = 0; i < nums; i++) {
clk = clk_register_fixed_rate(NULL, clks[i].name,
clks[i].parent_name,
clks[i].flags,
clks[i].fixed_rate);
if (IS_ERR(clk)) {
pr_err("%s: failed to register clock %s\n",
__func__, clks[i].name);
continue;
}
}
}
void __init hisi_clk_register_fixed_factor(struct hisi_fixed_factor_clock *clks,
int nums, void __iomem *base)
{
struct clk *clk;
int i;
for (i = 0; i < nums; i++) {
clk = clk_register_fixed_factor(NULL, clks[i].name,
clks[i].parent_name,
clks[i].flags, clks[i].mult,
clks[i].div);
if (IS_ERR(clk)) {
pr_err("%s: failed to register clock %s\n",
__func__, clks[i].name);
continue;
}
}
}
void __init hisi_clk_register_mux(struct hisi_mux_clock *clks,
int nums, void __iomem *base)
{
struct clk *clk;
int i;
for (i = 0; i < nums; i++) {
clk = clk_register_mux(NULL, clks[i].name, clks[i].parent_names,
clks[i].num_parents, clks[i].flags,
base + clks[i].offset, clks[i].shift,
clks[i].width, clks[i].mux_flags,
&hisi_clk_lock);
if (IS_ERR(clk)) {
pr_err("%s: failed to register clock %s\n",
__func__, clks[i].name);
continue;
}
if (clks[i].alias)
clk_register_clkdev(clk, clks[i].alias, NULL);
clk_table[clks[i].id] = clk;
}
}
void __init hisi_clk_register_divider(struct hisi_divider_clock *clks,
int nums, void __iomem *base)
{
struct clk *clk;
int i;
for (i = 0; i < nums; i++) {
clk = clk_register_divider_table(NULL, clks[i].name,
clks[i].parent_name,
clks[i].flags,
base + clks[i].offset,
clks[i].shift, clks[i].width,
clks[i].div_flags,
clks[i].table,
&hisi_clk_lock);
if (IS_ERR(clk)) {
pr_err("%s: failed to register clock %s\n",
__func__, clks[i].name);
continue;
}
if (clks[i].alias)
clk_register_clkdev(clk, clks[i].alias, NULL);
clk_table[clks[i].id] = clk;
}
}
void __init hisi_clk_register_gate_sep(struct hisi_gate_clock *clks,
int nums, void __iomem *base)
{
struct clk *clk;
int i;
for (i = 0; i < nums; i++) {
clk = hisi_register_clkgate_sep(NULL, clks[i].name,
clks[i].parent_name,
clks[i].flags,
base + clks[i].offset,
clks[i].bit_idx,
clks[i].gate_flags,
&hisi_clk_lock);
if (IS_ERR(clk)) {
pr_err("%s: failed to register clock %s\n",
__func__, clks[i].name);
continue;
}
if (clks[i].alias)
clk_register_clkdev(clk, clks[i].alias, NULL);
clk_table[clks[i].id] = clk;
}
}
/*
* Hisilicon Hi3620 clock gate driver
*
* Copyright (c) 2012-2013 Hisilicon Limited.
* Copyright (c) 2012-2013 Linaro Limited.
*
* Author: Haojian Zhuang <haojian.zhuang@linaro.org>
* Xin Li <li.xin@linaro.org>
*
* 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.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*
*/
#ifndef __HISI_CLK_H
#define __HISI_CLK_H
#include <linux/clk-provider.h>
#include <linux/io.h>
#include <linux/spinlock.h>
struct hisi_fixed_rate_clock {
unsigned int id;
char *name;
const char *parent_name;
unsigned long flags;
unsigned long fixed_rate;
};
struct hisi_fixed_factor_clock {
unsigned int id;
char *name;
const char *parent_name;
unsigned long mult;
unsigned long div;
unsigned long flags;
};
struct hisi_mux_clock {
unsigned int id;
const char *name;
const char **parent_names;
u8 num_parents;
unsigned long flags;
unsigned long offset;
u8 shift;
u8 width;
u8 mux_flags;
const char *alias;
};
struct hisi_divider_clock {
unsigned int id;
const char *name;
const char *parent_name;
unsigned long flags;
unsigned long offset;
u8 shift;
u8 width;
u8 div_flags;
struct clk_div_table *table;
const char *alias;
};
struct hisi_gate_clock {
unsigned int id;
const char *name;
const char *parent_name;
unsigned long flags;
unsigned long offset;
u8 bit_idx;
u8 gate_flags;
const char *alias;
};
struct clk *hisi_register_clkgate_sep(struct device *, const char *,
const char *, unsigned long,
void __iomem *, u8,
u8, spinlock_t *);
void __init hisi_clk_init(struct device_node *, int);
void __init hisi_clk_register_fixed_rate(struct hisi_fixed_rate_clock *,
int, void __iomem *);
void __init hisi_clk_register_fixed_factor(struct hisi_fixed_factor_clock *,
int, void __iomem *);
void __init hisi_clk_register_mux(struct hisi_mux_clock *, int,
void __iomem *);
void __init hisi_clk_register_divider(struct hisi_divider_clock *,
int, void __iomem *);
void __init hisi_clk_register_gate_sep(struct hisi_gate_clock *,
int, void __iomem *);
#endif /* __HISI_CLK_H */
/*
* Hisilicon clock separated gate driver
*
* Copyright (c) 2012-2013 Hisilicon Limited.
* Copyright (c) 2012-2013 Linaro Limited.
*
* Author: Haojian Zhuang <haojian.zhuang@linaro.org>
* Xin Li <li.xin@linaro.org>
*
* 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.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*
*/
#include <linux/kernel.h>
#include <linux/clk-provider.h>
#include <linux/clkdev.h>
#include <linux/io.h>
#include <linux/slab.h>
#include <linux/clk.h>
#include "clk.h"
/* clock separated gate register offset */
#define CLKGATE_SEPERATED_ENABLE 0x0
#define CLKGATE_SEPERATED_DISABLE 0x4
#define CLKGATE_SEPERATED_STATUS 0x8
struct clkgate_separated {
struct clk_hw hw;
void __iomem *enable; /* enable register */
u8 bit_idx; /* bits in enable/disable register */
u8 flags;
spinlock_t *lock;
};
static int clkgate_separated_enable(struct clk_hw *hw)
{
struct clkgate_separated *sclk;
unsigned long flags = 0;
u32 reg;
sclk = container_of(hw, struct clkgate_separated, hw);
if (sclk->lock)
spin_lock_irqsave(sclk->lock, flags);
reg = BIT(sclk->bit_idx);
writel_relaxed(reg, sclk->enable);
readl_relaxed(sclk->enable + CLKGATE_SEPERATED_STATUS);
if (sclk->lock)
spin_unlock_irqrestore(sclk->lock, flags);
return 0;
}
static void clkgate_separated_disable(struct clk_hw *hw)
{
struct clkgate_separated *sclk;
unsigned long flags = 0;
u32 reg;
sclk = container_of(hw, struct clkgate_separated, hw);
if (sclk->lock)
spin_lock_irqsave(sclk->lock, flags);
reg = BIT(sclk->bit_idx);
writel_relaxed(reg, sclk->enable + CLKGATE_SEPERATED_DISABLE);
readl_relaxed(sclk->enable + CLKGATE_SEPERATED_STATUS);
if (sclk->lock)
spin_unlock_irqrestore(sclk->lock, flags);
}
static int clkgate_separated_is_enabled(struct clk_hw *hw)
{
struct clkgate_separated *sclk;
u32 reg;
sclk = container_of(hw, struct clkgate_separated, hw);
reg = readl_relaxed(sclk->enable + CLKGATE_SEPERATED_STATUS);
reg &= BIT(sclk->bit_idx);
return reg ? 1 : 0;
}
static struct clk_ops clkgate_separated_ops = {
.enable = clkgate_separated_enable,
.disable = clkgate_separated_disable,
.is_enabled = clkgate_separated_is_enabled,
};
struct clk *hisi_register_clkgate_sep(struct device *dev, const char *name,
const char *parent_name,
unsigned long flags,
void __iomem *reg, u8 bit_idx,
u8 clk_gate_flags, spinlock_t *lock)
{
struct clkgate_separated *sclk;
struct clk *clk;
struct clk_init_data init;
sclk = kzalloc(sizeof(*sclk), GFP_KERNEL);
if (!sclk) {
pr_err("%s: fail to allocate separated gated clk\n", __func__);
return ERR_PTR(-ENOMEM);
}
init.name = name;
init.ops = &clkgate_separated_ops;
init.flags = flags | CLK_IS_BASIC;
init.parent_names = (parent_name ? &parent_name : NULL);
init.num_parents = (parent_name ? 1 : 0);
sclk->enable = reg + CLKGATE_SEPERATED_ENABLE;
sclk->bit_idx = bit_idx;
sclk->flags = clk_gate_flags;
sclk->hw.init = &init;
clk = clk_register(dev, &sclk->hw);
if (IS_ERR(clk))
kfree(sclk);
return clk;
}
/*
* Copyright (c) 2012-2013 Hisilicon Limited.
* Copyright (c) 2012-2013 Linaro Limited.
*
* Author: Haojian Zhuang <haojian.zhuang@linaro.org>
* Xin Li <li.xin@linaro.org>
*
* 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.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*
*/
#ifndef __DTS_HI3620_CLOCK_H
#define __DTS_HI3620_CLOCK_H
#define HI3620_NONE_CLOCK 0
/* fixed rate & fixed factor clocks */
#define HI3620_OSC32K 1
#define HI3620_OSC26M 2
#define HI3620_PCLK 3
#define HI3620_PLL_ARM0 4
#define HI3620_PLL_ARM1 5
#define HI3620_PLL_PERI 6
#define HI3620_PLL_USB 7
#define HI3620_PLL_HDMI 8
#define HI3620_PLL_GPU 9
#define HI3620_RCLK_TCXO 10
#define HI3620_RCLK_CFGAXI 11
#define HI3620_RCLK_PICO 12
/* mux clocks */
#define HI3620_TIMER0_MUX 32
#define HI3620_TIMER1_MUX 33
#define HI3620_TIMER2_MUX 34
#define HI3620_TIMER3_MUX 35
#define HI3620_TIMER4_MUX 36
#define HI3620_TIMER5_MUX 37
#define HI3620_TIMER6_MUX 38
#define HI3620_TIMER7_MUX 39
#define HI3620_TIMER8_MUX 40
#define HI3620_TIMER9_MUX 41
#define HI3620_UART0_MUX 42
#define HI3620_UART1_MUX 43
#define HI3620_UART2_MUX 44
#define HI3620_UART3_MUX 45
#define HI3620_UART4_MUX 46
#define HI3620_SPI0_MUX 47
#define HI3620_SPI1_MUX 48
#define HI3620_SPI2_MUX 49
#define HI3620_SAXI_MUX 50
#define HI3620_PWM0_MUX 51
#define HI3620_PWM1_MUX 52
#define HI3620_SD_MUX 53
#define HI3620_MMC1_MUX 54
#define HI3620_MMC1_MUX2 55
#define HI3620_G2D_MUX 56
#define HI3620_VENC_MUX 57
#define HI3620_VDEC_MUX 58
#define HI3620_VPP_MUX 59
#define HI3620_EDC0_MUX 60
#define HI3620_LDI0_MUX 61
#define HI3620_EDC1_MUX 62
#define HI3620_LDI1_MUX 63
#define HI3620_RCLK_HSIC 64
#define HI3620_MMC2_MUX 65
#define HI3620_MMC3_MUX 66
/* divider clocks */
#define HI3620_SHAREAXI_DIV 128
#define HI3620_CFGAXI_DIV 129
#define HI3620_SD_DIV 130
#define HI3620_MMC1_DIV 131
#define HI3620_HSIC_DIV 132
#define HI3620_MMC2_DIV 133
#define HI3620_MMC3_DIV 134
/* gate clocks */
#define HI3620_TIMERCLK01 160
#define HI3620_TIMER_RCLK01 161
#define HI3620_TIMERCLK23 162
#define HI3620_TIMER_RCLK23 163
#define HI3620_TIMERCLK45 164
#define HI3620_TIMERCLK67 165
#define HI3620_TIMERCLK89 166
#define HI3620_RTCCLK 167
#define HI3620_KPC_CLK 168
#define HI3620_GPIOCLK0 169
#define HI3620_GPIOCLK1 170
#define HI3620_GPIOCLK2 171
#define HI3620_GPIOCLK3 172
#define HI3620_GPIOCLK4 173
#define HI3620_GPIOCLK5 174
#define HI3620_GPIOCLK6 175
#define HI3620_GPIOCLK7 176
#define HI3620_GPIOCLK8 177
#define HI3620_GPIOCLK9 178
#define HI3620_GPIOCLK10 179
#define HI3620_GPIOCLK11 180
#define HI3620_GPIOCLK12 181
#define HI3620_GPIOCLK13 182
#define HI3620_GPIOCLK14 183
#define HI3620_GPIOCLK15 184
#define HI3620_GPIOCLK16 185
#define HI3620_GPIOCLK17 186
#define HI3620_GPIOCLK18 187
#define HI3620_GPIOCLK19 188
#define HI3620_GPIOCLK20 189
#define HI3620_GPIOCLK21 190
#define HI3620_DPHY0_CLK 191
#define HI3620_DPHY1_CLK 192
#define HI3620_DPHY2_CLK 193
#define HI3620_USBPHY_CLK 194
#define HI3620_ACP_CLK 195
#define HI3620_PWMCLK0 196
#define HI3620_PWMCLK1 197
#define HI3620_UARTCLK0 198
#define HI3620_UARTCLK1 199
#define HI3620_UARTCLK2 200
#define HI3620_UARTCLK3 201
#define HI3620_UARTCLK4 202
#define HI3620_SPICLK0 203
#define HI3620_SPICLK1 204
#define HI3620_SPICLK2 205
#define HI3620_I2CCLK0 206
#define HI3620_I2CCLK1 207
#define HI3620_I2CCLK2 208
#define HI3620_I2CCLK3 209
#define HI3620_SCI_CLK 210
#define HI3620_DDRC_PER_CLK 211
#define HI3620_DMAC_CLK 212
#define HI3620_USB2DVC_CLK 213
#define HI3620_SD_CLK 214
#define HI3620_MMC_CLK1 215
#define HI3620_MMC_CLK2 216
#define HI3620_MMC_CLK3 217
#define HI3620_MCU_CLK 218
#define HI3620_NR_CLKS 219
#endif /* __DTS_HI3620_CLOCK_H */
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