Skip to content

Commit

Permalink
clk: sunxi-ng: Add common infrastructure
Browse files Browse the repository at this point in the history
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
  • Loading branch information
Maxime Ripard authored and Michael Turquette committed Jul 9, 2016
1 parent c0692d6 commit 1d80c14
Show file tree
Hide file tree
Showing 9 changed files with 293 additions and 0 deletions.
1 change: 1 addition & 0 deletions drivers/clk/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -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"

Expand Down
1 change: 1 addition & 0 deletions drivers/clk/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -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/
Expand Down
3 changes: 3 additions & 0 deletions drivers/clk/sunxi-ng/Kconfig
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
config SUNXI_CCU
bool "Clock support for Allwinner SoCs"
default ARCH_SUNXI
3 changes: 3 additions & 0 deletions drivers/clk/sunxi-ng/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# Common objects
obj-$(CONFIG_SUNXI_CCU) += ccu_common.o
obj-$(CONFIG_SUNXI_CCU) += ccu_reset.o
90 changes: 90 additions & 0 deletions drivers/clk/sunxi-ng/ccu_common.c
Original file line number Diff line number Diff line change
@@ -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;
}
85 changes: 85 additions & 0 deletions drivers/clk/sunxi-ng/ccu_common.h
Original file line number Diff line number Diff line change
@@ -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_ */
15 changes: 15 additions & 0 deletions drivers/clk/sunxi-ng/ccu_mult.h
Original file line number Diff line number Diff line change
@@ -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_ */
55 changes: 55 additions & 0 deletions drivers/clk/sunxi-ng/ccu_reset.c
Original file line number Diff line number Diff line change
@@ -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,
};
40 changes: 40 additions & 0 deletions drivers/clk/sunxi-ng/ccu_reset.h
Original file line number Diff line number Diff line change
@@ -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_ */

0 comments on commit 1d80c14

Please sign in to comment.