mirror of
				https://github.com/torvalds/linux.git
				synced 2025-11-04 10:40:15 +02:00 
			
		
		
		
	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
					
				
					 9 changed files with 293 additions and 0 deletions
				
			
		| 
						 | 
				
			
			@ -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/
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										3
									
								
								drivers/clk/sunxi-ng/Kconfig
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										3
									
								
								drivers/clk/sunxi-ng/Kconfig
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,3 @@
 | 
			
		|||
config SUNXI_CCU
 | 
			
		||||
	bool "Clock support for Allwinner SoCs"
 | 
			
		||||
	default ARCH_SUNXI
 | 
			
		||||
							
								
								
									
										3
									
								
								drivers/clk/sunxi-ng/Makefile
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										3
									
								
								drivers/clk/sunxi-ng/Makefile
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,3 @@
 | 
			
		|||
# Common objects
 | 
			
		||||
obj-$(CONFIG_SUNXI_CCU)		+= ccu_common.o
 | 
			
		||||
obj-$(CONFIG_SUNXI_CCU)		+= ccu_reset.o
 | 
			
		||||
							
								
								
									
										90
									
								
								drivers/clk/sunxi-ng/ccu_common.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										90
									
								
								drivers/clk/sunxi-ng/ccu_common.c
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -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
									
								
								drivers/clk/sunxi-ng/ccu_common.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										85
									
								
								drivers/clk/sunxi-ng/ccu_common.h
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -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
									
								
								drivers/clk/sunxi-ng/ccu_mult.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										15
									
								
								drivers/clk/sunxi-ng/ccu_mult.h
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -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
									
								
								drivers/clk/sunxi-ng/ccu_reset.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										55
									
								
								drivers/clk/sunxi-ng/ccu_reset.c
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -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
									
								
								drivers/clk/sunxi-ng/ccu_reset.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										40
									
								
								drivers/clk/sunxi-ng/ccu_reset.h
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -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 a new issue