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/renesas/Kconfig"
|
||||
source "drivers/clk/samsung/Kconfig"
|
||||
source "drivers/clk/sunxi-ng/Kconfig"
|
||||
source "drivers/clk/tegra/Kconfig"
|
||||
source "drivers/clk/ti/Kconfig"
|
||||
|
||||
|
|
|
@ -79,6 +79,7 @@ obj-$(CONFIG_ARCH_SOCFPGA) += socfpga/
|
|||
obj-$(CONFIG_PLAT_SPEAR) += spear/
|
||||
obj-$(CONFIG_ARCH_STI) += st/
|
||||
obj-$(CONFIG_ARCH_SUNXI) += sunxi/
|
||||
obj-$(CONFIG_ARCH_SUNXI) += sunxi-ng/
|
||||
obj-$(CONFIG_ARCH_TEGRA) += tegra/
|
||||
obj-y += ti/
|
||||
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