clk: sunxi-ng: Add common infrastructure
Start our new clock infrastructure by adding the registration code, common structure and common code. Signed-off-by: Maxime Ripard <maxime.ripard@free-electrons.com> Signed-off-by: Michael Turquette <mturquette@baylibre.com> Link: lkml.kernel.org/r/20160629190535.11855-3-maxime.ripard@free-electrons.com
This commit is contained in:
parent
c0692d68a8
commit
1d80c14248
|
@ -212,6 +212,7 @@ source "drivers/clk/mvebu/Kconfig"
|
||||||
source "drivers/clk/qcom/Kconfig"
|
source "drivers/clk/qcom/Kconfig"
|
||||||
source "drivers/clk/renesas/Kconfig"
|
source "drivers/clk/renesas/Kconfig"
|
||||||
source "drivers/clk/samsung/Kconfig"
|
source "drivers/clk/samsung/Kconfig"
|
||||||
|
source "drivers/clk/sunxi-ng/Kconfig"
|
||||||
source "drivers/clk/tegra/Kconfig"
|
source "drivers/clk/tegra/Kconfig"
|
||||||
source "drivers/clk/ti/Kconfig"
|
source "drivers/clk/ti/Kconfig"
|
||||||
|
|
||||||
|
|
|
@ -79,6 +79,7 @@ obj-$(CONFIG_ARCH_SOCFPGA) += socfpga/
|
||||||
obj-$(CONFIG_PLAT_SPEAR) += spear/
|
obj-$(CONFIG_PLAT_SPEAR) += spear/
|
||||||
obj-$(CONFIG_ARCH_STI) += st/
|
obj-$(CONFIG_ARCH_STI) += st/
|
||||||
obj-$(CONFIG_ARCH_SUNXI) += sunxi/
|
obj-$(CONFIG_ARCH_SUNXI) += sunxi/
|
||||||
|
obj-$(CONFIG_ARCH_SUNXI) += sunxi-ng/
|
||||||
obj-$(CONFIG_ARCH_TEGRA) += tegra/
|
obj-$(CONFIG_ARCH_TEGRA) += tegra/
|
||||||
obj-y += ti/
|
obj-y += ti/
|
||||||
obj-$(CONFIG_ARCH_U8500) += ux500/
|
obj-$(CONFIG_ARCH_U8500) += ux500/
|
||||||
|
|
|
@ -0,0 +1,3 @@
|
||||||
|
config SUNXI_CCU
|
||||||
|
bool "Clock support for Allwinner SoCs"
|
||||||
|
default ARCH_SUNXI
|
|
@ -0,0 +1,3 @@
|
||||||
|
# Common objects
|
||||||
|
obj-$(CONFIG_SUNXI_CCU) += ccu_common.o
|
||||||
|
obj-$(CONFIG_SUNXI_CCU) += ccu_reset.o
|
|
@ -0,0 +1,90 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2016 Maxime Ripard
|
||||||
|
*
|
||||||
|
* Maxime Ripard <maxime.ripard@free-electrons.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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <linux/clk-provider.h>
|
||||||
|
#include <linux/iopoll.h>
|
||||||
|
#include <linux/slab.h>
|
||||||
|
|
||||||
|
#include "ccu_common.h"
|
||||||
|
#include "ccu_reset.h"
|
||||||
|
|
||||||
|
static DEFINE_SPINLOCK(ccu_lock);
|
||||||
|
|
||||||
|
void ccu_helper_wait_for_lock(struct ccu_common *common, u32 lock)
|
||||||
|
{
|
||||||
|
u32 reg;
|
||||||
|
|
||||||
|
if (!lock)
|
||||||
|
return;
|
||||||
|
|
||||||
|
WARN_ON(readl_relaxed_poll_timeout(common->base + common->reg, reg,
|
||||||
|
!(reg & lock), 100, 70000));
|
||||||
|
}
|
||||||
|
|
||||||
|
int sunxi_ccu_probe(struct device_node *node, void __iomem *reg,
|
||||||
|
const struct sunxi_ccu_desc *desc)
|
||||||
|
{
|
||||||
|
struct ccu_reset *reset;
|
||||||
|
int i, ret;
|
||||||
|
|
||||||
|
for (i = 0; i < desc->num_ccu_clks; i++) {
|
||||||
|
struct ccu_common *cclk = desc->ccu_clks[i];
|
||||||
|
|
||||||
|
if (!cclk)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
cclk->base = reg;
|
||||||
|
cclk->lock = &ccu_lock;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (i = 0; i < desc->hw_clks->num ; i++) {
|
||||||
|
struct clk_hw *hw = desc->hw_clks->hws[i];
|
||||||
|
|
||||||
|
if (!hw)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
ret = clk_hw_register(NULL, hw);
|
||||||
|
if (ret) {
|
||||||
|
pr_err("Couldn't register clock %s\n",
|
||||||
|
clk_hw_get_name(hw));
|
||||||
|
goto err_clk_unreg;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = of_clk_add_hw_provider(node, of_clk_hw_onecell_get,
|
||||||
|
desc->hw_clks);
|
||||||
|
if (ret)
|
||||||
|
goto err_clk_unreg;
|
||||||
|
|
||||||
|
reset = kzalloc(sizeof(*reset), GFP_KERNEL);
|
||||||
|
reset->rcdev.of_node = node;
|
||||||
|
reset->rcdev.ops = &ccu_reset_ops;
|
||||||
|
reset->rcdev.owner = THIS_MODULE;
|
||||||
|
reset->rcdev.nr_resets = desc->num_resets;
|
||||||
|
reset->base = reg;
|
||||||
|
reset->lock = &ccu_lock;
|
||||||
|
reset->reset_map = desc->resets;
|
||||||
|
|
||||||
|
ret = reset_controller_register(&reset->rcdev);
|
||||||
|
if (ret)
|
||||||
|
goto err_of_clk_unreg;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
err_of_clk_unreg:
|
||||||
|
err_clk_unreg:
|
||||||
|
return ret;
|
||||||
|
}
|
|
@ -0,0 +1,85 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2016 Maxime Ripard. All rights reserved.
|
||||||
|
*
|
||||||
|
* This software is licensed under the terms of the GNU General Public
|
||||||
|
* License version 2, as published by the Free Software Foundation, and
|
||||||
|
* may be copied, distributed, and modified under those terms.
|
||||||
|
*
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef _COMMON_H_
|
||||||
|
#define _COMMON_H_
|
||||||
|
|
||||||
|
#include <linux/compiler.h>
|
||||||
|
#include <linux/clk-provider.h>
|
||||||
|
|
||||||
|
#define CCU_FEATURE_FRACTIONAL BIT(0)
|
||||||
|
#define CCU_FEATURE_VARIABLE_PREDIV BIT(1)
|
||||||
|
#define CCU_FEATURE_FIXED_PREDIV BIT(2)
|
||||||
|
#define CCU_FEATURE_FIXED_POSTDIV BIT(3)
|
||||||
|
|
||||||
|
struct device_node;
|
||||||
|
|
||||||
|
#define CLK_HW_INIT(_name, _parent, _ops, _flags) \
|
||||||
|
&(struct clk_init_data) { \
|
||||||
|
.flags = _flags, \
|
||||||
|
.name = _name, \
|
||||||
|
.parent_names = (const char *[]) { _parent }, \
|
||||||
|
.num_parents = 1, \
|
||||||
|
.ops = _ops, \
|
||||||
|
}
|
||||||
|
|
||||||
|
#define CLK_HW_INIT_PARENTS(_name, _parents, _ops, _flags) \
|
||||||
|
&(struct clk_init_data) { \
|
||||||
|
.flags = _flags, \
|
||||||
|
.name = _name, \
|
||||||
|
.parent_names = _parents, \
|
||||||
|
.num_parents = ARRAY_SIZE(_parents), \
|
||||||
|
.ops = _ops, \
|
||||||
|
}
|
||||||
|
|
||||||
|
#define CLK_FIXED_FACTOR(_struct, _name, _parent, \
|
||||||
|
_div, _mult, _flags) \
|
||||||
|
struct clk_fixed_factor _struct = { \
|
||||||
|
.div = _div, \
|
||||||
|
.mult = _mult, \
|
||||||
|
.hw.init = CLK_HW_INIT(_name, \
|
||||||
|
_parent, \
|
||||||
|
&clk_fixed_factor_ops, \
|
||||||
|
_flags), \
|
||||||
|
}
|
||||||
|
|
||||||
|
struct ccu_common {
|
||||||
|
void __iomem *base;
|
||||||
|
u16 reg;
|
||||||
|
|
||||||
|
unsigned long features;
|
||||||
|
spinlock_t *lock;
|
||||||
|
struct clk_hw hw;
|
||||||
|
};
|
||||||
|
|
||||||
|
static inline struct ccu_common *hw_to_ccu_common(struct clk_hw *hw)
|
||||||
|
{
|
||||||
|
return container_of(hw, struct ccu_common, hw);
|
||||||
|
}
|
||||||
|
|
||||||
|
struct sunxi_ccu_desc {
|
||||||
|
struct ccu_common **ccu_clks;
|
||||||
|
unsigned long num_ccu_clks;
|
||||||
|
|
||||||
|
struct clk_hw_onecell_data *hw_clks;
|
||||||
|
|
||||||
|
struct ccu_reset_map *resets;
|
||||||
|
unsigned long num_resets;
|
||||||
|
};
|
||||||
|
|
||||||
|
void ccu_helper_wait_for_lock(struct ccu_common *common, u32 lock);
|
||||||
|
|
||||||
|
int sunxi_ccu_probe(struct device_node *node, void __iomem *reg,
|
||||||
|
const struct sunxi_ccu_desc *desc);
|
||||||
|
|
||||||
|
#endif /* _COMMON_H_ */
|
|
@ -0,0 +1,15 @@
|
||||||
|
#ifndef _CCU_MULT_H_
|
||||||
|
#define _CCU_MULT_H_
|
||||||
|
|
||||||
|
struct _ccu_mult {
|
||||||
|
u8 shift;
|
||||||
|
u8 width;
|
||||||
|
};
|
||||||
|
|
||||||
|
#define _SUNXI_CCU_MULT(_shift, _width) \
|
||||||
|
{ \
|
||||||
|
.shift = _shift, \
|
||||||
|
.width = _width, \
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif /* _CCU_MULT_H_ */
|
|
@ -0,0 +1,55 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2016 Maxime Ripard
|
||||||
|
* Maxime Ripard <maxime.ripard@free-electrons.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/io.h>
|
||||||
|
#include <linux/reset-controller.h>
|
||||||
|
|
||||||
|
#include "ccu_reset.h"
|
||||||
|
|
||||||
|
static int ccu_reset_assert(struct reset_controller_dev *rcdev,
|
||||||
|
unsigned long id)
|
||||||
|
{
|
||||||
|
struct ccu_reset *ccu = rcdev_to_ccu_reset(rcdev);
|
||||||
|
const struct ccu_reset_map *map = &ccu->reset_map[id];
|
||||||
|
unsigned long flags;
|
||||||
|
u32 reg;
|
||||||
|
|
||||||
|
spin_lock_irqsave(ccu->lock, flags);
|
||||||
|
|
||||||
|
reg = readl(ccu->base + map->reg);
|
||||||
|
writel(reg & ~map->bit, ccu->base + map->reg);
|
||||||
|
|
||||||
|
spin_unlock_irqrestore(ccu->lock, flags);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int ccu_reset_deassert(struct reset_controller_dev *rcdev,
|
||||||
|
unsigned long id)
|
||||||
|
{
|
||||||
|
struct ccu_reset *ccu = rcdev_to_ccu_reset(rcdev);
|
||||||
|
const struct ccu_reset_map *map = &ccu->reset_map[id];
|
||||||
|
unsigned long flags;
|
||||||
|
u32 reg;
|
||||||
|
|
||||||
|
spin_lock_irqsave(ccu->lock, flags);
|
||||||
|
|
||||||
|
reg = readl(ccu->base + map->reg);
|
||||||
|
writel(reg | map->bit, ccu->base + map->reg);
|
||||||
|
|
||||||
|
spin_unlock_irqrestore(ccu->lock, flags);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
const struct reset_control_ops ccu_reset_ops = {
|
||||||
|
.assert = ccu_reset_assert,
|
||||||
|
.deassert = ccu_reset_deassert,
|
||||||
|
};
|
|
@ -0,0 +1,40 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2016 Maxime Ripard. All rights reserved.
|
||||||
|
*
|
||||||
|
* This software is licensed under the terms of the GNU General Public
|
||||||
|
* License version 2, as published by the Free Software Foundation, and
|
||||||
|
* may be copied, distributed, and modified under those terms.
|
||||||
|
*
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef _CCU_RESET_H_
|
||||||
|
#define _CCU_RESET_H_
|
||||||
|
|
||||||
|
#include <linux/reset-controller.h>
|
||||||
|
|
||||||
|
struct ccu_reset_map {
|
||||||
|
u16 reg;
|
||||||
|
u32 bit;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
struct ccu_reset {
|
||||||
|
void __iomem *base;
|
||||||
|
struct ccu_reset_map *reset_map;
|
||||||
|
spinlock_t *lock;
|
||||||
|
|
||||||
|
struct reset_controller_dev rcdev;
|
||||||
|
};
|
||||||
|
|
||||||
|
static inline struct ccu_reset *rcdev_to_ccu_reset(struct reset_controller_dev *rcdev)
|
||||||
|
{
|
||||||
|
return container_of(rcdev, struct ccu_reset, rcdev);
|
||||||
|
}
|
||||||
|
|
||||||
|
extern const struct reset_control_ops ccu_reset_ops;
|
||||||
|
|
||||||
|
#endif /* _CCU_RESET_H_ */
|
Loading…
Reference in New Issue