Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
L
linux
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
0
Merge Requests
0
Analytics
Analytics
Repository
Value Stream
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Commits
Issue Boards
Open sidebar
Kirill Smelkov
linux
Commits
7adb7695
Commit
7adb7695
authored
8 years ago
by
Michael Turquette
Browse files
Options
Browse Files
Download
Plain Diff
Merge branch 'clk-sunxi-ng' into clk-next
parents
a0649829
0577e485
Changes
34
Expand all
Show whitespace changes
Inline
Side-by-side
Showing
34 changed files
with
3669 additions
and
0 deletions
+3669
-0
Documentation/devicetree/bindings/clock/sunxi-ccu.txt
Documentation/devicetree/bindings/clock/sunxi-ccu.txt
+24
-0
drivers/clk/Kconfig
drivers/clk/Kconfig
+1
-0
drivers/clk/Makefile
drivers/clk/Makefile
+1
-0
drivers/clk/sunxi-ng/Kconfig
drivers/clk/sunxi-ng/Kconfig
+65
-0
drivers/clk/sunxi-ng/Makefile
drivers/clk/sunxi-ng/Makefile
+20
-0
drivers/clk/sunxi-ng/ccu-sun8i-h3.c
drivers/clk/sunxi-ng/ccu-sun8i-h3.c
+826
-0
drivers/clk/sunxi-ng/ccu-sun8i-h3.h
drivers/clk/sunxi-ng/ccu-sun8i-h3.h
+62
-0
drivers/clk/sunxi-ng/ccu_common.c
drivers/clk/sunxi-ng/ccu_common.c
+90
-0
drivers/clk/sunxi-ng/ccu_common.h
drivers/clk/sunxi-ng/ccu_common.h
+85
-0
drivers/clk/sunxi-ng/ccu_div.c
drivers/clk/sunxi-ng/ccu_div.c
+136
-0
drivers/clk/sunxi-ng/ccu_div.h
drivers/clk/sunxi-ng/ccu_div.h
+133
-0
drivers/clk/sunxi-ng/ccu_frac.c
drivers/clk/sunxi-ng/ccu_frac.c
+110
-0
drivers/clk/sunxi-ng/ccu_frac.h
drivers/clk/sunxi-ng/ccu_frac.h
+53
-0
drivers/clk/sunxi-ng/ccu_gate.c
drivers/clk/sunxi-ng/ccu_gate.c
+82
-0
drivers/clk/sunxi-ng/ccu_gate.h
drivers/clk/sunxi-ng/ccu_gate.h
+52
-0
drivers/clk/sunxi-ng/ccu_mp.c
drivers/clk/sunxi-ng/ccu_mp.c
+158
-0
drivers/clk/sunxi-ng/ccu_mp.h
drivers/clk/sunxi-ng/ccu_mp.h
+77
-0
drivers/clk/sunxi-ng/ccu_mult.h
drivers/clk/sunxi-ng/ccu_mult.h
+15
-0
drivers/clk/sunxi-ng/ccu_mux.c
drivers/clk/sunxi-ng/ccu_mux.c
+187
-0
drivers/clk/sunxi-ng/ccu_mux.h
drivers/clk/sunxi-ng/ccu_mux.h
+91
-0
drivers/clk/sunxi-ng/ccu_nk.c
drivers/clk/sunxi-ng/ccu_nk.c
+147
-0
drivers/clk/sunxi-ng/ccu_nk.h
drivers/clk/sunxi-ng/ccu_nk.h
+71
-0
drivers/clk/sunxi-ng/ccu_nkm.c
drivers/clk/sunxi-ng/ccu_nkm.c
+153
-0
drivers/clk/sunxi-ng/ccu_nkm.h
drivers/clk/sunxi-ng/ccu_nkm.h
+68
-0
drivers/clk/sunxi-ng/ccu_nkmp.c
drivers/clk/sunxi-ng/ccu_nkmp.c
+167
-0
drivers/clk/sunxi-ng/ccu_nkmp.h
drivers/clk/sunxi-ng/ccu_nkmp.h
+71
-0
drivers/clk/sunxi-ng/ccu_nm.c
drivers/clk/sunxi-ng/ccu_nm.c
+114
-0
drivers/clk/sunxi-ng/ccu_nm.h
drivers/clk/sunxi-ng/ccu_nm.h
+91
-0
drivers/clk/sunxi-ng/ccu_phase.c
drivers/clk/sunxi-ng/ccu_phase.c
+126
-0
drivers/clk/sunxi-ng/ccu_phase.h
drivers/clk/sunxi-ng/ccu_phase.h
+50
-0
drivers/clk/sunxi-ng/ccu_reset.c
drivers/clk/sunxi-ng/ccu_reset.c
+55
-0
drivers/clk/sunxi-ng/ccu_reset.h
drivers/clk/sunxi-ng/ccu_reset.h
+40
-0
include/dt-bindings/clock/sun8i-h3-ccu.h
include/dt-bindings/clock/sun8i-h3-ccu.h
+145
-0
include/dt-bindings/reset/sun8i-h3-ccu.h
include/dt-bindings/reset/sun8i-h3-ccu.h
+103
-0
No files found.
Documentation/devicetree/bindings/clock/sunxi-ccu.txt
0 → 100644
View file @
7adb7695
Allwinner Clock Control Unit Binding
------------------------------------
Required properties :
- compatible: must contain one of the following compatible:
- "allwinner,sun8i-h3-ccu"
- reg: Must contain the registers base address and length
- clocks: phandle to the oscillators feeding the CCU. Two are needed:
- "hosc": the high frequency oscillator (usually at 24MHz)
- "losc": the low frequency oscillator (usually at 32kHz)
- clock-names: Must contain the clock names described just above
- #clock-cells : must contain 1
- #reset-cells : must contain 1
Example:
ccu: clock@01c20000 {
compatible = "allwinner,sun8i-h3-ccu";
reg = <0x01c20000 0x400>;
clocks = <&osc24M>, <&osc32k>;
clock-names = "hosc", "losc";
#clock-cells = <1>;
#reset-cells = <1>;
};
This diff is collapsed.
Click to expand it.
drivers/clk/Kconfig
View file @
7adb7695
...
@@ -214,6 +214,7 @@ source "drivers/clk/mvebu/Kconfig"
...
@@ -214,6 +214,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"
...
...
This diff is collapsed.
Click to expand it.
drivers/clk/Makefile
View file @
7adb7695
...
@@ -79,6 +79,7 @@ obj-$(CONFIG_ARCH_SOCFPGA) += socfpga/
...
@@ -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/
...
...
This diff is collapsed.
Click to expand it.
drivers/clk/sunxi-ng/Kconfig
0 → 100644
View file @
7adb7695
config SUNXI_CCU
bool "Clock support for Allwinner SoCs"
default ARCH_SUNXI
if SUNXI_CCU
# Base clock types
config SUNXI_CCU_DIV
bool
select SUNXI_CCU_MUX
config SUNXI_CCU_FRAC
bool
config SUNXI_CCU_GATE
bool
config SUNXI_CCU_MUX
bool
config SUNXI_CCU_PHASE
bool
# Multi-factor clocks
config SUNXI_CCU_NK
bool
select SUNXI_CCU_GATE
config SUNXI_CCU_NKM
bool
select RATIONAL
select SUNXI_CCU_GATE
config SUNXI_CCU_NKMP
bool
select RATIONAL
select SUNXI_CCU_GATE
config SUNXI_CCU_NM
bool
select RATIONAL
select SUNXI_CCU_FRAC
select SUNXI_CCU_GATE
config SUNXI_CCU_MP
bool
select SUNXI_CCU_GATE
select SUNXI_CCU_MUX
# SoC Drivers
config SUN8I_H3_CCU
bool "Support for the Allwinner H3 CCU"
select SUNXI_CCU_DIV
select SUNXI_CCU_NK
select SUNXI_CCU_NKM
select SUNXI_CCU_NKMP
select SUNXI_CCU_NM
select SUNXI_CCU_MP
select SUNXI_CCU_PHASE
default ARCH_SUN8I
endif
This diff is collapsed.
Click to expand it.
drivers/clk/sunxi-ng/Makefile
0 → 100644
View file @
7adb7695
# Common objects
obj-$(CONFIG_SUNXI_CCU)
+=
ccu_common.o
obj-$(CONFIG_SUNXI_CCU)
+=
ccu_reset.o
# Base clock types
obj-$(CONFIG_SUNXI_CCU_DIV)
+=
ccu_div.o
obj-$(CONFIG_SUNXI_CCU_FRAC)
+=
ccu_frac.o
obj-$(CONFIG_SUNXI_CCU_GATE)
+=
ccu_gate.o
obj-$(CONFIG_SUNXI_CCU_MUX)
+=
ccu_mux.o
obj-$(CONFIG_SUNXI_CCU_PHASE)
+=
ccu_phase.o
# Multi-factor clocks
obj-$(CONFIG_SUNXI_CCU_NK)
+=
ccu_nk.o
obj-$(CONFIG_SUNXI_CCU_NKM)
+=
ccu_nkm.o
obj-$(CONFIG_SUNXI_CCU_NKMP)
+=
ccu_nkmp.o
obj-$(CONFIG_SUNXI_CCU_NM)
+=
ccu_nm.o
obj-$(CONFIG_SUNXI_CCU_MP)
+=
ccu_mp.o
# SoC support
obj-$(CONFIG_SUN8I_H3_CCU)
+=
ccu-sun8i-h3.o
This diff is collapsed.
Click to expand it.
drivers/clk/sunxi-ng/ccu-sun8i-h3.c
0 → 100644
View file @
7adb7695
This diff is collapsed.
Click to expand it.
drivers/clk/sunxi-ng/ccu-sun8i-h3.h
0 → 100644
View file @
7adb7695
/*
* 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.
*/
#ifndef _CCU_SUN8I_H3_H_
#define _CCU_SUN8I_H3_H_
#include <dt-bindings/clock/sun8i-h3-ccu.h>
#include <dt-bindings/reset/sun8i-h3-ccu.h>
#define CLK_PLL_CPUX 0
#define CLK_PLL_AUDIO_BASE 1
#define CLK_PLL_AUDIO 2
#define CLK_PLL_AUDIO_2X 3
#define CLK_PLL_AUDIO_4X 4
#define CLK_PLL_AUDIO_8X 5
#define CLK_PLL_VIDEO 6
#define CLK_PLL_VE 7
#define CLK_PLL_DDR 8
#define CLK_PLL_PERIPH0 9
#define CLK_PLL_PERIPH0_2X 10
#define CLK_PLL_GPU 11
#define CLK_PLL_PERIPH1 12
#define CLK_PLL_DE 13
/* The CPUX clock is exported */
#define CLK_AXI 15
#define CLK_AHB1 16
#define CLK_APB1 17
#define CLK_APB2 18
#define CLK_AHB2 19
/* All the bus gates are exported */
/* The first bunch of module clocks are exported */
#define CLK_DRAM 96
/* All the DRAM gates are exported */
/* Some more module clocks are exported */
#define CLK_MBUS 113
/* And the GPU module clock is exported */
#define CLK_NUMBER (CLK_GPU + 1)
#endif
/* _CCU_SUN8I_H3_H_ */
This diff is collapsed.
Click to expand it.
drivers/clk/sunxi-ng/ccu_common.c
0 → 100644
View file @
7adb7695
/*
* 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
;
}
This diff is collapsed.
Click to expand it.
drivers/clk/sunxi-ng/ccu_common.h
0 → 100644
View file @
7adb7695
/*
* 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_ */
This diff is collapsed.
Click to expand it.
drivers/clk/sunxi-ng/ccu_div.c
0 → 100644
View file @
7adb7695
/*
* 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/clk-provider.h>
#include "ccu_gate.h"
#include "ccu_div.h"
static
unsigned
long
ccu_div_round_rate
(
struct
ccu_mux_internal
*
mux
,
unsigned
long
parent_rate
,
unsigned
long
rate
,
void
*
data
)
{
struct
ccu_div
*
cd
=
data
;
unsigned
long
val
;
/*
* We can't use divider_round_rate that assumes that there's
* several parents, while we might be called to evaluate
* several different parents.
*/
val
=
divider_get_val
(
rate
,
parent_rate
,
cd
->
div
.
table
,
cd
->
div
.
width
,
cd
->
div
.
flags
);
return
divider_recalc_rate
(
&
cd
->
common
.
hw
,
parent_rate
,
val
,
cd
->
div
.
table
,
cd
->
div
.
flags
);
}
static
void
ccu_div_disable
(
struct
clk_hw
*
hw
)
{
struct
ccu_div
*
cd
=
hw_to_ccu_div
(
hw
);
return
ccu_gate_helper_disable
(
&
cd
->
common
,
cd
->
enable
);
}
static
int
ccu_div_enable
(
struct
clk_hw
*
hw
)
{
struct
ccu_div
*
cd
=
hw_to_ccu_div
(
hw
);
return
ccu_gate_helper_enable
(
&
cd
->
common
,
cd
->
enable
);
}
static
int
ccu_div_is_enabled
(
struct
clk_hw
*
hw
)
{
struct
ccu_div
*
cd
=
hw_to_ccu_div
(
hw
);
return
ccu_gate_helper_is_enabled
(
&
cd
->
common
,
cd
->
enable
);
}
static
unsigned
long
ccu_div_recalc_rate
(
struct
clk_hw
*
hw
,
unsigned
long
parent_rate
)
{
struct
ccu_div
*
cd
=
hw_to_ccu_div
(
hw
);
unsigned
long
val
;
u32
reg
;
reg
=
readl
(
cd
->
common
.
base
+
cd
->
common
.
reg
);
val
=
reg
>>
cd
->
div
.
shift
;
val
&=
(
1
<<
cd
->
div
.
width
)
-
1
;
ccu_mux_helper_adjust_parent_for_prediv
(
&
cd
->
common
,
&
cd
->
mux
,
-
1
,
&
parent_rate
);
return
divider_recalc_rate
(
hw
,
parent_rate
,
val
,
cd
->
div
.
table
,
cd
->
div
.
flags
);
}
static
int
ccu_div_determine_rate
(
struct
clk_hw
*
hw
,
struct
clk_rate_request
*
req
)
{
struct
ccu_div
*
cd
=
hw_to_ccu_div
(
hw
);
return
ccu_mux_helper_determine_rate
(
&
cd
->
common
,
&
cd
->
mux
,
req
,
ccu_div_round_rate
,
cd
);
}
static
int
ccu_div_set_rate
(
struct
clk_hw
*
hw
,
unsigned
long
rate
,
unsigned
long
parent_rate
)
{
struct
ccu_div
*
cd
=
hw_to_ccu_div
(
hw
);
unsigned
long
flags
;
unsigned
long
val
;
u32
reg
;
ccu_mux_helper_adjust_parent_for_prediv
(
&
cd
->
common
,
&
cd
->
mux
,
-
1
,
&
parent_rate
);
val
=
divider_get_val
(
rate
,
parent_rate
,
cd
->
div
.
table
,
cd
->
div
.
width
,
cd
->
div
.
flags
);
spin_lock_irqsave
(
cd
->
common
.
lock
,
flags
);
reg
=
readl
(
cd
->
common
.
base
+
cd
->
common
.
reg
);
reg
&=
~
GENMASK
(
cd
->
div
.
width
+
cd
->
div
.
shift
-
1
,
cd
->
div
.
shift
);
writel
(
reg
|
(
val
<<
cd
->
div
.
shift
),
cd
->
common
.
base
+
cd
->
common
.
reg
);
spin_unlock_irqrestore
(
cd
->
common
.
lock
,
flags
);
return
0
;
}
static
u8
ccu_div_get_parent
(
struct
clk_hw
*
hw
)
{
struct
ccu_div
*
cd
=
hw_to_ccu_div
(
hw
);
return
ccu_mux_helper_get_parent
(
&
cd
->
common
,
&
cd
->
mux
);
}
static
int
ccu_div_set_parent
(
struct
clk_hw
*
hw
,
u8
index
)
{
struct
ccu_div
*
cd
=
hw_to_ccu_div
(
hw
);
return
ccu_mux_helper_set_parent
(
&
cd
->
common
,
&
cd
->
mux
,
index
);
}
const
struct
clk_ops
ccu_div_ops
=
{
.
disable
=
ccu_div_disable
,
.
enable
=
ccu_div_enable
,
.
is_enabled
=
ccu_div_is_enabled
,
.
get_parent
=
ccu_div_get_parent
,
.
set_parent
=
ccu_div_set_parent
,
.
determine_rate
=
ccu_div_determine_rate
,
.
recalc_rate
=
ccu_div_recalc_rate
,
.
set_rate
=
ccu_div_set_rate
,
};
This diff is collapsed.
Click to expand it.
drivers/clk/sunxi-ng/ccu_div.h
0 → 100644
View file @
7adb7695
/*
* 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_DIV_H_
#define _CCU_DIV_H_
#include <linux/clk-provider.h>
#include "ccu_common.h"
#include "ccu_mux.h"
struct
_ccu_div
{
u8
shift
;
u8
width
;
u32
flags
;
struct
clk_div_table
*
table
;
};
#define _SUNXI_CCU_DIV_TABLE_FLAGS(_shift, _width, _table, _flags) \
{ \
.shift = _shift, \
.width = _width, \
.flags = _flags, \
.table = _table, \
}
#define _SUNXI_CCU_DIV_FLAGS(_shift, _width, _flags) \
_SUNXI_CCU_DIV_TABLE_FLAGS(_shift, _width, NULL, _flags)
#define _SUNXI_CCU_DIV_TABLE(_shift, _width, _table) \
_SUNXI_CCU_DIV_TABLE_FLAGS(_shift, _width, _table, 0)
#define _SUNXI_CCU_DIV(_shift, _width) \
_SUNXI_CCU_DIV_TABLE_FLAGS(_shift, _width, NULL, 0)
struct
ccu_div
{
u32
enable
;
struct
_ccu_div
div
;
struct
ccu_mux_internal
mux
;
struct
ccu_common
common
;
};
#define SUNXI_CCU_DIV_TABLE_WITH_GATE(_struct, _name, _parent, _reg, \
_shift, _width, \
_table, _gate, _flags) \
struct ccu_div _struct = { \
.div = _SUNXI_CCU_DIV_TABLE(_shift, _width, \
_table), \
.enable = _gate, \
.common = { \
.reg = _reg, \
.hw.init = CLK_HW_INIT(_name, \
_parent, \
&ccu_div_ops, \
_flags), \
} \
}
#define SUNXI_CCU_DIV_TABLE(_struct, _name, _parent, _reg, \
_shift, _width, \
_table, _flags) \
SUNXI_CCU_DIV_TABLE_WITH_GATE(_struct, _name, _parent, _reg, \
_shift, _width, _table, 0, \
_flags)
#define SUNXI_CCU_M_WITH_MUX_GATE(_struct, _name, _parents, _reg, \
_mshift, _mwidth, _muxshift, _muxwidth, \
_gate, _flags) \
struct ccu_div _struct = { \
.enable = _gate, \
.div = _SUNXI_CCU_DIV(_mshift, _mwidth), \
.mux = SUNXI_CLK_MUX(_muxshift, _muxwidth), \
.common = { \
.reg = _reg, \
.hw.init = CLK_HW_INIT_PARENTS(_name, \
_parents, \
&ccu_div_ops, \
_flags), \
}, \
}
#define SUNXI_CCU_M_WITH_MUX(_struct, _name, _parents, _reg, \
_mshift, _mwidth, _muxshift, _muxwidth, \
_flags) \
SUNXI_CCU_M_WITH_MUX_GATE(_struct, _name, _parents, _reg, \
_mshift, _mwidth, _muxshift, _muxwidth, \
0, _flags)
#define SUNXI_CCU_M_WITH_GATE(_struct, _name, _parent, _reg, \
_mshift, _mwidth, _gate, \
_flags) \
struct ccu_div _struct = { \
.enable = _gate, \
.div = _SUNXI_CCU_DIV(_mshift, _mwidth), \
.common = { \
.reg = _reg, \
.hw.init = CLK_HW_INIT(_name, \
_parent, \
&ccu_div_ops, \
_flags), \
}, \
}
#define SUNXI_CCU_M(_struct, _name, _parent, _reg, _mshift, _mwidth, \
_flags) \
SUNXI_CCU_M_WITH_GATE(_struct, _name, _parent, _reg, \
_mshift, _mwidth, 0, _flags)
static
inline
struct
ccu_div
*
hw_to_ccu_div
(
struct
clk_hw
*
hw
)
{
struct
ccu_common
*
common
=
hw_to_ccu_common
(
hw
);
return
container_of
(
common
,
struct
ccu_div
,
common
);
}
extern
const
struct
clk_ops
ccu_div_ops
;
#endif
/* _CCU_DIV_H_ */
This diff is collapsed.
Click to expand it.
drivers/clk/sunxi-ng/ccu_frac.c
0 → 100644
View file @
7adb7695
/*
* 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/clk-provider.h>
#include <linux/spinlock.h>
#include "ccu_frac.h"
bool
ccu_frac_helper_is_enabled
(
struct
ccu_common
*
common
,
struct
_ccu_frac
*
cf
)
{
if
(
!
(
common
->
features
&
CCU_FEATURE_FRACTIONAL
))
return
false
;
return
!
(
readl
(
common
->
base
+
common
->
reg
)
&
cf
->
enable
);
}
void
ccu_frac_helper_enable
(
struct
ccu_common
*
common
,
struct
_ccu_frac
*
cf
)
{
unsigned
long
flags
;
u32
reg
;
if
(
!
(
common
->
features
&
CCU_FEATURE_FRACTIONAL
))
return
;
spin_lock_irqsave
(
common
->
lock
,
flags
);
reg
=
readl
(
common
->
base
+
common
->
reg
);
writel
(
reg
&
~
cf
->
enable
,
common
->
base
+
common
->
reg
);
spin_unlock_irqrestore
(
common
->
lock
,
flags
);
}
void
ccu_frac_helper_disable
(
struct
ccu_common
*
common
,
struct
_ccu_frac
*
cf
)
{
unsigned
long
flags
;
u32
reg
;
if
(
!
(
common
->
features
&
CCU_FEATURE_FRACTIONAL
))
return
;
spin_lock_irqsave
(
common
->
lock
,
flags
);
reg
=
readl
(
common
->
base
+
common
->
reg
);
writel
(
reg
|
cf
->
enable
,
common
->
base
+
common
->
reg
);
spin_unlock_irqrestore
(
common
->
lock
,
flags
);
}
bool
ccu_frac_helper_has_rate
(
struct
ccu_common
*
common
,
struct
_ccu_frac
*
cf
,
unsigned
long
rate
)
{
if
(
!
(
common
->
features
&
CCU_FEATURE_FRACTIONAL
))
return
false
;
return
(
cf
->
rates
[
0
]
==
rate
)
||
(
cf
->
rates
[
1
]
==
rate
);
}
unsigned
long
ccu_frac_helper_read_rate
(
struct
ccu_common
*
common
,
struct
_ccu_frac
*
cf
)
{
u32
reg
;
printk
(
"%s: Read fractional
\n
"
,
clk_hw_get_name
(
&
common
->
hw
));
if
(
!
(
common
->
features
&
CCU_FEATURE_FRACTIONAL
))
return
0
;
printk
(
"%s: clock is fractional (rates %lu and %lu)
\n
"
,
clk_hw_get_name
(
&
common
->
hw
),
cf
->
rates
[
0
],
cf
->
rates
[
1
]);
reg
=
readl
(
common
->
base
+
common
->
reg
);
printk
(
"%s: clock reg is 0x%x (select is 0x%x)
\n
"
,
clk_hw_get_name
(
&
common
->
hw
),
reg
,
cf
->
select
);
return
(
reg
&
cf
->
select
)
?
cf
->
rates
[
1
]
:
cf
->
rates
[
0
];
}
int
ccu_frac_helper_set_rate
(
struct
ccu_common
*
common
,
struct
_ccu_frac
*
cf
,
unsigned
long
rate
)
{
unsigned
long
flags
;
u32
reg
,
sel
;
if
(
!
(
common
->
features
&
CCU_FEATURE_FRACTIONAL
))
return
-
EINVAL
;
if
(
cf
->
rates
[
0
]
==
rate
)
sel
=
0
;
else
if
(
cf
->
rates
[
1
]
==
rate
)
sel
=
cf
->
select
;
else
return
-
EINVAL
;
spin_lock_irqsave
(
common
->
lock
,
flags
);
reg
=
readl
(
common
->
base
+
common
->
reg
);
reg
&=
~
cf
->
select
;
writel
(
reg
|
sel
,
common
->
base
+
common
->
reg
);
spin_unlock_irqrestore
(
common
->
lock
,
flags
);
return
0
;
}
This diff is collapsed.
Click to expand it.
drivers/clk/sunxi-ng/ccu_frac.h
0 → 100644
View file @
7adb7695
/*
* 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_FRAC_H_
#define _CCU_FRAC_H_
#include <linux/clk-provider.h>
#include "ccu_common.h"
struct
_ccu_frac
{
u32
enable
;
u32
select
;
unsigned
long
rates
[
2
];
};
#define _SUNXI_CCU_FRAC(_enable, _select, _rate1, _rate2) \
{ \
.enable = _enable, \
.select = _select, \
.rates = { _rate1, _rate2 }, \
}
bool
ccu_frac_helper_is_enabled
(
struct
ccu_common
*
common
,
struct
_ccu_frac
*
cf
);
void
ccu_frac_helper_enable
(
struct
ccu_common
*
common
,
struct
_ccu_frac
*
cf
);
void
ccu_frac_helper_disable
(
struct
ccu_common
*
common
,
struct
_ccu_frac
*
cf
);
bool
ccu_frac_helper_has_rate
(
struct
ccu_common
*
common
,
struct
_ccu_frac
*
cf
,
unsigned
long
rate
);
unsigned
long
ccu_frac_helper_read_rate
(
struct
ccu_common
*
common
,
struct
_ccu_frac
*
cf
);
int
ccu_frac_helper_set_rate
(
struct
ccu_common
*
common
,
struct
_ccu_frac
*
cf
,
unsigned
long
rate
);
#endif
/* _CCU_FRAC_H_ */
This diff is collapsed.
Click to expand it.
drivers/clk/sunxi-ng/ccu_gate.c
0 → 100644
View file @
7adb7695
/*
* 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/clk-provider.h>
#include "ccu_gate.h"
void
ccu_gate_helper_disable
(
struct
ccu_common
*
common
,
u32
gate
)
{
unsigned
long
flags
;
u32
reg
;
if
(
!
gate
)
return
;
spin_lock_irqsave
(
common
->
lock
,
flags
);
reg
=
readl
(
common
->
base
+
common
->
reg
);
writel
(
reg
&
~
gate
,
common
->
base
+
common
->
reg
);
spin_unlock_irqrestore
(
common
->
lock
,
flags
);
}
static
void
ccu_gate_disable
(
struct
clk_hw
*
hw
)
{
struct
ccu_gate
*
cg
=
hw_to_ccu_gate
(
hw
);
return
ccu_gate_helper_disable
(
&
cg
->
common
,
cg
->
enable
);
}
int
ccu_gate_helper_enable
(
struct
ccu_common
*
common
,
u32
gate
)
{
unsigned
long
flags
;
u32
reg
;
if
(
!
gate
)
return
0
;
spin_lock_irqsave
(
common
->
lock
,
flags
);
reg
=
readl
(
common
->
base
+
common
->
reg
);
writel
(
reg
|
gate
,
common
->
base
+
common
->
reg
);
spin_unlock_irqrestore
(
common
->
lock
,
flags
);
return
0
;
}
static
int
ccu_gate_enable
(
struct
clk_hw
*
hw
)
{
struct
ccu_gate
*
cg
=
hw_to_ccu_gate
(
hw
);
return
ccu_gate_helper_enable
(
&
cg
->
common
,
cg
->
enable
);
}
int
ccu_gate_helper_is_enabled
(
struct
ccu_common
*
common
,
u32
gate
)
{
if
(
!
gate
)
return
1
;
return
readl
(
common
->
base
+
common
->
reg
)
&
gate
;
}
static
int
ccu_gate_is_enabled
(
struct
clk_hw
*
hw
)
{
struct
ccu_gate
*
cg
=
hw_to_ccu_gate
(
hw
);
return
ccu_gate_helper_is_enabled
(
&
cg
->
common
,
cg
->
enable
);
}
const
struct
clk_ops
ccu_gate_ops
=
{
.
disable
=
ccu_gate_disable
,
.
enable
=
ccu_gate_enable
,
.
is_enabled
=
ccu_gate_is_enabled
,
};
This diff is collapsed.
Click to expand it.
drivers/clk/sunxi-ng/ccu_gate.h
0 → 100644
View file @
7adb7695
/*
* 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_GATE_H_
#define _CCU_GATE_H_
#include <linux/clk-provider.h>
#include "ccu_common.h"
struct
ccu_gate
{
u32
enable
;
struct
ccu_common
common
;
};
#define SUNXI_CCU_GATE(_struct, _name, _parent, _reg, _gate, _flags) \
struct ccu_gate _struct = { \
.enable = _gate, \
.common = { \
.reg = _reg, \
.hw.init = CLK_HW_INIT(_name, \
_parent, \
&ccu_gate_ops, \
_flags), \
} \
}
static
inline
struct
ccu_gate
*
hw_to_ccu_gate
(
struct
clk_hw
*
hw
)
{
struct
ccu_common
*
common
=
hw_to_ccu_common
(
hw
);
return
container_of
(
common
,
struct
ccu_gate
,
common
);
}
void
ccu_gate_helper_disable
(
struct
ccu_common
*
common
,
u32
gate
);
int
ccu_gate_helper_enable
(
struct
ccu_common
*
common
,
u32
gate
);
int
ccu_gate_helper_is_enabled
(
struct
ccu_common
*
common
,
u32
gate
);
extern
const
struct
clk_ops
ccu_gate_ops
;
#endif
/* _CCU_GATE_H_ */
This diff is collapsed.
Click to expand it.
drivers/clk/sunxi-ng/ccu_mp.c
0 → 100644
View file @
7adb7695
/*
* 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/clk-provider.h>
#include "ccu_gate.h"
#include "ccu_mp.h"
static
void
ccu_mp_find_best
(
unsigned
long
parent
,
unsigned
long
rate
,
unsigned
int
max_m
,
unsigned
int
max_p
,
unsigned
int
*
m
,
unsigned
int
*
p
)
{
unsigned
long
best_rate
=
0
;
unsigned
int
best_m
=
0
,
best_p
=
0
;
unsigned
int
_m
,
_p
;
for
(
_p
=
0
;
_p
<=
max_p
;
_p
++
)
{
for
(
_m
=
1
;
_m
<=
max_m
;
_m
++
)
{
unsigned
long
tmp_rate
=
(
parent
>>
_p
)
/
_m
;
if
(
tmp_rate
>
rate
)
continue
;
if
((
rate
-
tmp_rate
)
<
(
rate
-
best_rate
))
{
best_rate
=
tmp_rate
;
best_m
=
_m
;
best_p
=
_p
;
}
}
}
*
m
=
best_m
;
*
p
=
best_p
;
}
static
unsigned
long
ccu_mp_round_rate
(
struct
ccu_mux_internal
*
mux
,
unsigned
long
parent_rate
,
unsigned
long
rate
,
void
*
data
)
{
struct
ccu_mp
*
cmp
=
data
;
unsigned
int
m
,
p
;
ccu_mp_find_best
(
parent_rate
,
rate
,
1
<<
cmp
->
m
.
width
,
(
1
<<
cmp
->
p
.
width
)
-
1
,
&
m
,
&
p
);
return
(
parent_rate
>>
p
)
/
m
;
}
static
void
ccu_mp_disable
(
struct
clk_hw
*
hw
)
{
struct
ccu_mp
*
cmp
=
hw_to_ccu_mp
(
hw
);
return
ccu_gate_helper_disable
(
&
cmp
->
common
,
cmp
->
enable
);
}
static
int
ccu_mp_enable
(
struct
clk_hw
*
hw
)
{
struct
ccu_mp
*
cmp
=
hw_to_ccu_mp
(
hw
);
return
ccu_gate_helper_enable
(
&
cmp
->
common
,
cmp
->
enable
);
}
static
int
ccu_mp_is_enabled
(
struct
clk_hw
*
hw
)
{
struct
ccu_mp
*
cmp
=
hw_to_ccu_mp
(
hw
);
return
ccu_gate_helper_is_enabled
(
&
cmp
->
common
,
cmp
->
enable
);
}
static
unsigned
long
ccu_mp_recalc_rate
(
struct
clk_hw
*
hw
,
unsigned
long
parent_rate
)
{
struct
ccu_mp
*
cmp
=
hw_to_ccu_mp
(
hw
);
unsigned
int
m
,
p
;
u32
reg
;
reg
=
readl
(
cmp
->
common
.
base
+
cmp
->
common
.
reg
);
m
=
reg
>>
cmp
->
m
.
shift
;
m
&=
(
1
<<
cmp
->
m
.
width
)
-
1
;
p
=
reg
>>
cmp
->
p
.
shift
;
p
&=
(
1
<<
cmp
->
p
.
width
)
-
1
;
return
(
parent_rate
>>
p
)
/
(
m
+
1
);
}
static
int
ccu_mp_determine_rate
(
struct
clk_hw
*
hw
,
struct
clk_rate_request
*
req
)
{
struct
ccu_mp
*
cmp
=
hw_to_ccu_mp
(
hw
);
return
ccu_mux_helper_determine_rate
(
&
cmp
->
common
,
&
cmp
->
mux
,
req
,
ccu_mp_round_rate
,
cmp
);
}
static
int
ccu_mp_set_rate
(
struct
clk_hw
*
hw
,
unsigned
long
rate
,
unsigned
long
parent_rate
)
{
struct
ccu_mp
*
cmp
=
hw_to_ccu_mp
(
hw
);
unsigned
long
flags
;
unsigned
int
m
,
p
;
u32
reg
;
ccu_mp_find_best
(
parent_rate
,
rate
,
1
<<
cmp
->
m
.
width
,
(
1
<<
cmp
->
p
.
width
)
-
1
,
&
m
,
&
p
);
spin_lock_irqsave
(
cmp
->
common
.
lock
,
flags
);
reg
=
readl
(
cmp
->
common
.
base
+
cmp
->
common
.
reg
);
reg
&=
~
GENMASK
(
cmp
->
m
.
width
+
cmp
->
m
.
shift
-
1
,
cmp
->
m
.
shift
);
reg
&=
~
GENMASK
(
cmp
->
p
.
width
+
cmp
->
p
.
shift
-
1
,
cmp
->
p
.
shift
);
writel
(
reg
|
(
p
<<
cmp
->
p
.
shift
)
|
((
m
-
1
)
<<
cmp
->
m
.
shift
),
cmp
->
common
.
base
+
cmp
->
common
.
reg
);
spin_unlock_irqrestore
(
cmp
->
common
.
lock
,
flags
);
return
0
;
}
static
u8
ccu_mp_get_parent
(
struct
clk_hw
*
hw
)
{
struct
ccu_mp
*
cmp
=
hw_to_ccu_mp
(
hw
);
return
ccu_mux_helper_get_parent
(
&
cmp
->
common
,
&
cmp
->
mux
);
}
static
int
ccu_mp_set_parent
(
struct
clk_hw
*
hw
,
u8
index
)
{
struct
ccu_mp
*
cmp
=
hw_to_ccu_mp
(
hw
);
return
ccu_mux_helper_set_parent
(
&
cmp
->
common
,
&
cmp
->
mux
,
index
);
}
const
struct
clk_ops
ccu_mp_ops
=
{
.
disable
=
ccu_mp_disable
,
.
enable
=
ccu_mp_enable
,
.
is_enabled
=
ccu_mp_is_enabled
,
.
get_parent
=
ccu_mp_get_parent
,
.
set_parent
=
ccu_mp_set_parent
,
.
determine_rate
=
ccu_mp_determine_rate
,
.
recalc_rate
=
ccu_mp_recalc_rate
,
.
set_rate
=
ccu_mp_set_rate
,
};
This diff is collapsed.
Click to expand it.
drivers/clk/sunxi-ng/ccu_mp.h
0 → 100644
View file @
7adb7695
/*
* 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_MP_H_
#define _CCU_MP_H_
#include <linux/clk-provider.h>
#include "ccu_common.h"
#include "ccu_div.h"
#include "ccu_mult.h"
#include "ccu_mux.h"
/*
* struct ccu_mp - Definition of an M-P clock
*
* Clocks based on the formula parent >> P / M
*/
struct
ccu_mp
{
u32
enable
;
struct
_ccu_div
m
;
struct
_ccu_div
p
;
struct
ccu_mux_internal
mux
;
struct
ccu_common
common
;
};
#define SUNXI_CCU_MP_WITH_MUX_GATE(_struct, _name, _parents, _reg, \
_mshift, _mwidth, \
_pshift, _pwidth, \
_muxshift, _muxwidth, \
_gate, _flags) \
struct ccu_mp _struct = { \
.enable = _gate, \
.m = _SUNXI_CCU_DIV(_mshift, _mwidth), \
.p = _SUNXI_CCU_DIV(_pshift, _pwidth), \
.mux = SUNXI_CLK_MUX(_muxshift, _muxwidth), \
.common = { \
.reg = _reg, \
.hw.init = CLK_HW_INIT_PARENTS(_name, \
_parents, \
&ccu_mp_ops, \
_flags), \
} \
}
#define SUNXI_CCU_MP_WITH_MUX(_struct, _name, _parents, _reg, \
_mshift, _mwidth, \
_pshift, _pwidth, \
_muxshift, _muxwidth, \
_flags) \
SUNXI_CCU_MP_WITH_MUX_GATE(_struct, _name, _parents, _reg, \
_mshift, _mwidth, \
_pshift, _pwidth, \
_muxshift, _muxwidth, \
0, _flags)
static
inline
struct
ccu_mp
*
hw_to_ccu_mp
(
struct
clk_hw
*
hw
)
{
struct
ccu_common
*
common
=
hw_to_ccu_common
(
hw
);
return
container_of
(
common
,
struct
ccu_mp
,
common
);
}
extern
const
struct
clk_ops
ccu_mp_ops
;
#endif
/* _CCU_MP_H_ */
This diff is collapsed.
Click to expand it.
drivers/clk/sunxi-ng/ccu_mult.h
0 → 100644
View file @
7adb7695
#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_ */
This diff is collapsed.
Click to expand it.
drivers/clk/sunxi-ng/ccu_mux.c
0 → 100644
View file @
7adb7695
/*
* 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/clk-provider.h>
#include "ccu_gate.h"
#include "ccu_mux.h"
void
ccu_mux_helper_adjust_parent_for_prediv
(
struct
ccu_common
*
common
,
struct
ccu_mux_internal
*
cm
,
int
parent_index
,
unsigned
long
*
parent_rate
)
{
u8
prediv
=
1
;
u32
reg
;
if
(
!
((
common
->
features
&
CCU_FEATURE_FIXED_PREDIV
)
||
(
common
->
features
&
CCU_FEATURE_VARIABLE_PREDIV
)))
return
;
reg
=
readl
(
common
->
base
+
common
->
reg
);
if
(
parent_index
<
0
)
{
parent_index
=
reg
>>
cm
->
shift
;
parent_index
&=
(
1
<<
cm
->
width
)
-
1
;
}
if
(
common
->
features
&
CCU_FEATURE_FIXED_PREDIV
)
if
(
parent_index
==
cm
->
fixed_prediv
.
index
)
prediv
=
cm
->
fixed_prediv
.
div
;
if
(
common
->
features
&
CCU_FEATURE_VARIABLE_PREDIV
)
if
(
parent_index
==
cm
->
variable_prediv
.
index
)
{
u8
div
;
div
=
reg
>>
cm
->
variable_prediv
.
shift
;
div
&=
(
1
<<
cm
->
variable_prediv
.
width
)
-
1
;
prediv
=
div
+
1
;
}
*
parent_rate
=
*
parent_rate
/
prediv
;
}
int
ccu_mux_helper_determine_rate
(
struct
ccu_common
*
common
,
struct
ccu_mux_internal
*
cm
,
struct
clk_rate_request
*
req
,
unsigned
long
(
*
round
)(
struct
ccu_mux_internal
*
,
unsigned
long
,
unsigned
long
,
void
*
),
void
*
data
)
{
unsigned
long
best_parent_rate
=
0
,
best_rate
=
0
;
struct
clk_hw
*
best_parent
,
*
hw
=
&
common
->
hw
;
unsigned
int
i
;
for
(
i
=
0
;
i
<
clk_hw_get_num_parents
(
hw
);
i
++
)
{
unsigned
long
tmp_rate
,
parent_rate
;
struct
clk_hw
*
parent
;
parent
=
clk_hw_get_parent_by_index
(
hw
,
i
);
if
(
!
parent
)
continue
;
parent_rate
=
clk_hw_get_rate
(
parent
);
ccu_mux_helper_adjust_parent_for_prediv
(
common
,
cm
,
i
,
&
parent_rate
);
tmp_rate
=
round
(
cm
,
clk_hw_get_rate
(
parent
),
req
->
rate
,
data
);
if
(
tmp_rate
==
req
->
rate
)
{
best_parent
=
parent
;
best_parent_rate
=
parent_rate
;
best_rate
=
tmp_rate
;
goto
out
;
}
if
((
req
->
rate
-
tmp_rate
)
<
(
req
->
rate
-
best_rate
))
{
best_rate
=
tmp_rate
;
best_parent_rate
=
parent_rate
;
best_parent
=
parent
;
}
}
if
(
best_rate
==
0
)
return
-
EINVAL
;
out:
req
->
best_parent_hw
=
best_parent
;
req
->
best_parent_rate
=
best_parent_rate
;
req
->
rate
=
best_rate
;
return
0
;
}
u8
ccu_mux_helper_get_parent
(
struct
ccu_common
*
common
,
struct
ccu_mux_internal
*
cm
)
{
u32
reg
;
u8
parent
;
reg
=
readl
(
common
->
base
+
common
->
reg
);
parent
=
reg
>>
cm
->
shift
;
parent
&=
(
1
<<
cm
->
width
)
-
1
;
return
parent
;
}
int
ccu_mux_helper_set_parent
(
struct
ccu_common
*
common
,
struct
ccu_mux_internal
*
cm
,
u8
index
)
{
unsigned
long
flags
;
u32
reg
;
spin_lock_irqsave
(
common
->
lock
,
flags
);
reg
=
readl
(
common
->
base
+
common
->
reg
);
reg
&=
~
GENMASK
(
cm
->
width
+
cm
->
shift
-
1
,
cm
->
shift
);
writel
(
reg
|
(
index
<<
cm
->
shift
),
common
->
base
+
common
->
reg
);
spin_unlock_irqrestore
(
common
->
lock
,
flags
);
return
0
;
}
static
void
ccu_mux_disable
(
struct
clk_hw
*
hw
)
{
struct
ccu_mux
*
cm
=
hw_to_ccu_mux
(
hw
);
return
ccu_gate_helper_disable
(
&
cm
->
common
,
cm
->
enable
);
}
static
int
ccu_mux_enable
(
struct
clk_hw
*
hw
)
{
struct
ccu_mux
*
cm
=
hw_to_ccu_mux
(
hw
);
return
ccu_gate_helper_enable
(
&
cm
->
common
,
cm
->
enable
);
}
static
int
ccu_mux_is_enabled
(
struct
clk_hw
*
hw
)
{
struct
ccu_mux
*
cm
=
hw_to_ccu_mux
(
hw
);
return
ccu_gate_helper_is_enabled
(
&
cm
->
common
,
cm
->
enable
);
}
static
u8
ccu_mux_get_parent
(
struct
clk_hw
*
hw
)
{
struct
ccu_mux
*
cm
=
hw_to_ccu_mux
(
hw
);
return
ccu_mux_helper_get_parent
(
&
cm
->
common
,
&
cm
->
mux
);
}
static
int
ccu_mux_set_parent
(
struct
clk_hw
*
hw
,
u8
index
)
{
struct
ccu_mux
*
cm
=
hw_to_ccu_mux
(
hw
);
return
ccu_mux_helper_set_parent
(
&
cm
->
common
,
&
cm
->
mux
,
index
);
}
static
unsigned
long
ccu_mux_recalc_rate
(
struct
clk_hw
*
hw
,
unsigned
long
parent_rate
)
{
struct
ccu_mux
*
cm
=
hw_to_ccu_mux
(
hw
);
ccu_mux_helper_adjust_parent_for_prediv
(
&
cm
->
common
,
&
cm
->
mux
,
-
1
,
&
parent_rate
);
return
parent_rate
;
}
const
struct
clk_ops
ccu_mux_ops
=
{
.
disable
=
ccu_mux_disable
,
.
enable
=
ccu_mux_enable
,
.
is_enabled
=
ccu_mux_is_enabled
,
.
get_parent
=
ccu_mux_get_parent
,
.
set_parent
=
ccu_mux_set_parent
,
.
determine_rate
=
__clk_mux_determine_rate
,
.
recalc_rate
=
ccu_mux_recalc_rate
,
};
This diff is collapsed.
Click to expand it.
drivers/clk/sunxi-ng/ccu_mux.h
0 → 100644
View file @
7adb7695
#ifndef _CCU_MUX_H_
#define _CCU_MUX_H_
#include <linux/clk-provider.h>
#include "ccu_common.h"
struct
ccu_mux_internal
{
u8
shift
;
u8
width
;
struct
{
u8
index
;
u8
div
;
}
fixed_prediv
;
struct
{
u8
index
;
u8
shift
;
u8
width
;
}
variable_prediv
;
};
#define SUNXI_CLK_MUX(_shift, _width) \
{ \
.shift = _shift, \
.width = _width, \
}
struct
ccu_mux
{
u16
reg
;
u32
enable
;
struct
ccu_mux_internal
mux
;
struct
ccu_common
common
;
};
#define SUNXI_CCU_MUX(_struct, _name, _parents, _reg, _shift, _width, _flags) \
struct ccu_mux _struct = { \
.mux = SUNXI_CLK_MUX(_shift, _width), \
.common = { \
.reg = _reg, \
.hw.init = CLK_HW_INIT_PARENTS(_name, \
_parents, \
&ccu_mux_ops, \
_flags), \
} \
}
#define SUNXI_CCU_MUX_WITH_GATE(_struct, _name, _parents, _reg, \
_shift, _width, _gate, _flags) \
struct ccu_mux _struct = { \
.enable = _gate, \
.mux = SUNXI_CLK_MUX(_shift, _width), \
.common = { \
.reg = _reg, \
.hw.init = CLK_HW_INIT_PARENTS(_name, \
_parents, \
&ccu_mux_ops, \
_flags), \
} \
}
static
inline
struct
ccu_mux
*
hw_to_ccu_mux
(
struct
clk_hw
*
hw
)
{
struct
ccu_common
*
common
=
hw_to_ccu_common
(
hw
);
return
container_of
(
common
,
struct
ccu_mux
,
common
);
}
extern
const
struct
clk_ops
ccu_mux_ops
;
void
ccu_mux_helper_adjust_parent_for_prediv
(
struct
ccu_common
*
common
,
struct
ccu_mux_internal
*
cm
,
int
parent_index
,
unsigned
long
*
parent_rate
);
int
ccu_mux_helper_determine_rate
(
struct
ccu_common
*
common
,
struct
ccu_mux_internal
*
cm
,
struct
clk_rate_request
*
req
,
unsigned
long
(
*
round
)(
struct
ccu_mux_internal
*
,
unsigned
long
,
unsigned
long
,
void
*
),
void
*
data
);
u8
ccu_mux_helper_get_parent
(
struct
ccu_common
*
common
,
struct
ccu_mux_internal
*
cm
);
int
ccu_mux_helper_set_parent
(
struct
ccu_common
*
common
,
struct
ccu_mux_internal
*
cm
,
u8
index
);
#endif
/* _CCU_MUX_H_ */
This diff is collapsed.
Click to expand it.
drivers/clk/sunxi-ng/ccu_nk.c
0 → 100644
View file @
7adb7695
/*
* 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/clk-provider.h>
#include <linux/rational.h>
#include "ccu_gate.h"
#include "ccu_nk.h"
void
ccu_nk_find_best
(
unsigned
long
parent
,
unsigned
long
rate
,
unsigned
int
max_n
,
unsigned
int
max_k
,
unsigned
int
*
n
,
unsigned
int
*
k
)
{
unsigned
long
best_rate
=
0
;
unsigned
int
best_k
=
0
,
best_n
=
0
;
unsigned
int
_k
,
_n
;
for
(
_k
=
1
;
_k
<=
max_k
;
_k
++
)
{
for
(
_n
=
1
;
_n
<=
max_n
;
_n
++
)
{
unsigned
long
tmp_rate
=
parent
*
_n
*
_k
;
if
(
tmp_rate
>
rate
)
continue
;
if
((
rate
-
tmp_rate
)
<
(
rate
-
best_rate
))
{
best_rate
=
tmp_rate
;
best_k
=
_k
;
best_n
=
_n
;
}
}
}
*
k
=
best_k
;
*
n
=
best_n
;
}
static
void
ccu_nk_disable
(
struct
clk_hw
*
hw
)
{
struct
ccu_nk
*
nk
=
hw_to_ccu_nk
(
hw
);
return
ccu_gate_helper_disable
(
&
nk
->
common
,
nk
->
enable
);
}
static
int
ccu_nk_enable
(
struct
clk_hw
*
hw
)
{
struct
ccu_nk
*
nk
=
hw_to_ccu_nk
(
hw
);
return
ccu_gate_helper_enable
(
&
nk
->
common
,
nk
->
enable
);
}
static
int
ccu_nk_is_enabled
(
struct
clk_hw
*
hw
)
{
struct
ccu_nk
*
nk
=
hw_to_ccu_nk
(
hw
);
return
ccu_gate_helper_is_enabled
(
&
nk
->
common
,
nk
->
enable
);
}
static
unsigned
long
ccu_nk_recalc_rate
(
struct
clk_hw
*
hw
,
unsigned
long
parent_rate
)
{
struct
ccu_nk
*
nk
=
hw_to_ccu_nk
(
hw
);
unsigned
long
rate
,
n
,
k
;
u32
reg
;
reg
=
readl
(
nk
->
common
.
base
+
nk
->
common
.
reg
);
n
=
reg
>>
nk
->
n
.
shift
;
n
&=
(
1
<<
nk
->
n
.
width
)
-
1
;
k
=
reg
>>
nk
->
k
.
shift
;
k
&=
(
1
<<
nk
->
k
.
width
)
-
1
;
rate
=
parent_rate
*
(
n
+
1
)
*
(
k
+
1
);
if
(
nk
->
common
.
features
&
CCU_FEATURE_FIXED_POSTDIV
)
rate
/=
nk
->
fixed_post_div
;
return
rate
;
}
static
long
ccu_nk_round_rate
(
struct
clk_hw
*
hw
,
unsigned
long
rate
,
unsigned
long
*
parent_rate
)
{
struct
ccu_nk
*
nk
=
hw_to_ccu_nk
(
hw
);
unsigned
int
n
,
k
;
if
(
nk
->
common
.
features
&
CCU_FEATURE_FIXED_POSTDIV
)
rate
*=
nk
->
fixed_post_div
;
ccu_nk_find_best
(
*
parent_rate
,
rate
,
1
<<
nk
->
n
.
width
,
1
<<
nk
->
k
.
width
,
&
n
,
&
k
);
rate
=
*
parent_rate
*
n
*
k
;
if
(
nk
->
common
.
features
&
CCU_FEATURE_FIXED_POSTDIV
)
rate
=
rate
/
nk
->
fixed_post_div
;
return
rate
;
}
static
int
ccu_nk_set_rate
(
struct
clk_hw
*
hw
,
unsigned
long
rate
,
unsigned
long
parent_rate
)
{
struct
ccu_nk
*
nk
=
hw_to_ccu_nk
(
hw
);
unsigned
long
flags
;
unsigned
int
n
,
k
;
u32
reg
;
if
(
nk
->
common
.
features
&
CCU_FEATURE_FIXED_POSTDIV
)
rate
=
rate
*
nk
->
fixed_post_div
;
ccu_nk_find_best
(
parent_rate
,
rate
,
1
<<
nk
->
n
.
width
,
1
<<
nk
->
k
.
width
,
&
n
,
&
k
);
spin_lock_irqsave
(
nk
->
common
.
lock
,
flags
);
reg
=
readl
(
nk
->
common
.
base
+
nk
->
common
.
reg
);
reg
&=
~
GENMASK
(
nk
->
n
.
width
+
nk
->
n
.
shift
-
1
,
nk
->
n
.
shift
);
reg
&=
~
GENMASK
(
nk
->
k
.
width
+
nk
->
k
.
shift
-
1
,
nk
->
k
.
shift
);
writel
(
reg
|
((
k
-
1
)
<<
nk
->
k
.
shift
)
|
((
n
-
1
)
<<
nk
->
n
.
shift
),
nk
->
common
.
base
+
nk
->
common
.
reg
);
spin_unlock_irqrestore
(
nk
->
common
.
lock
,
flags
);
ccu_helper_wait_for_lock
(
&
nk
->
common
,
nk
->
lock
);
return
0
;
}
const
struct
clk_ops
ccu_nk_ops
=
{
.
disable
=
ccu_nk_disable
,
.
enable
=
ccu_nk_enable
,
.
is_enabled
=
ccu_nk_is_enabled
,
.
recalc_rate
=
ccu_nk_recalc_rate
,
.
round_rate
=
ccu_nk_round_rate
,
.
set_rate
=
ccu_nk_set_rate
,
};
This diff is collapsed.
Click to expand it.
drivers/clk/sunxi-ng/ccu_nk.h
0 → 100644
View file @
7adb7695
/*
* 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_NK_H_
#define _CCU_NK_H_
#include <linux/clk-provider.h>
#include "ccu_common.h"
#include "ccu_div.h"
#include "ccu_mult.h"
/*
* struct ccu_nk - Definition of an N-K clock
*
* Clocks based on the formula parent * N * K
*/
struct
ccu_nk
{
u16
reg
;
u32
enable
;
u32
lock
;
struct
_ccu_mult
n
;
struct
_ccu_mult
k
;
unsigned
int
fixed_post_div
;
struct
ccu_common
common
;
};
#define SUNXI_CCU_NK_WITH_GATE_LOCK_POSTDIV(_struct, _name, _parent, _reg, \
_nshift, _nwidth, \
_kshift, _kwidth, \
_gate, _lock, _postdiv, \
_flags) \
struct ccu_nk _struct = { \
.enable = _gate, \
.lock = _lock, \
.k = _SUNXI_CCU_MULT(_kshift, _kwidth), \
.n = _SUNXI_CCU_MULT(_nshift, _nwidth), \
.fixed_post_div = _postdiv, \
.common = { \
.reg = _reg, \
.features = CCU_FEATURE_FIXED_POSTDIV, \
.hw.init = CLK_HW_INIT(_name, \
_parent, \
&ccu_nk_ops, \
_flags), \
}, \
}
static
inline
struct
ccu_nk
*
hw_to_ccu_nk
(
struct
clk_hw
*
hw
)
{
struct
ccu_common
*
common
=
hw_to_ccu_common
(
hw
);
return
container_of
(
common
,
struct
ccu_nk
,
common
);
}
extern
const
struct
clk_ops
ccu_nk_ops
;
#endif
/* _CCU_NK_H_ */
This diff is collapsed.
Click to expand it.
drivers/clk/sunxi-ng/ccu_nkm.c
0 → 100644
View file @
7adb7695
/*
* 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/clk-provider.h>
#include <linux/rational.h>
#include "ccu_gate.h"
#include "ccu_nkm.h"
struct
_ccu_nkm
{
unsigned
long
n
,
max_n
;
unsigned
long
k
,
max_k
;
unsigned
long
m
,
max_m
;
};
static
void
ccu_nkm_find_best
(
unsigned
long
parent
,
unsigned
long
rate
,
struct
_ccu_nkm
*
nkm
)
{
unsigned
long
best_rate
=
0
;
unsigned
long
best_n
=
0
,
best_k
=
0
,
best_m
=
0
;
unsigned
long
_n
,
_k
,
_m
;
for
(
_k
=
1
;
_k
<=
nkm
->
max_k
;
_k
++
)
{
unsigned
long
tmp_rate
;
rational_best_approximation
(
rate
/
_k
,
parent
,
nkm
->
max_n
,
nkm
->
max_m
,
&
_n
,
&
_m
);
tmp_rate
=
parent
*
_n
*
_k
/
_m
;
if
(
tmp_rate
>
rate
)
continue
;
if
((
rate
-
tmp_rate
)
<
(
rate
-
best_rate
))
{
best_rate
=
tmp_rate
;
best_n
=
_n
;
best_k
=
_k
;
best_m
=
_m
;
}
}
nkm
->
n
=
best_n
;
nkm
->
k
=
best_k
;
nkm
->
m
=
best_m
;
}
static
void
ccu_nkm_disable
(
struct
clk_hw
*
hw
)
{
struct
ccu_nkm
*
nkm
=
hw_to_ccu_nkm
(
hw
);
return
ccu_gate_helper_disable
(
&
nkm
->
common
,
nkm
->
enable
);
}
static
int
ccu_nkm_enable
(
struct
clk_hw
*
hw
)
{
struct
ccu_nkm
*
nkm
=
hw_to_ccu_nkm
(
hw
);
return
ccu_gate_helper_enable
(
&
nkm
->
common
,
nkm
->
enable
);
}
static
int
ccu_nkm_is_enabled
(
struct
clk_hw
*
hw
)
{
struct
ccu_nkm
*
nkm
=
hw_to_ccu_nkm
(
hw
);
return
ccu_gate_helper_is_enabled
(
&
nkm
->
common
,
nkm
->
enable
);
}
static
unsigned
long
ccu_nkm_recalc_rate
(
struct
clk_hw
*
hw
,
unsigned
long
parent_rate
)
{
struct
ccu_nkm
*
nkm
=
hw_to_ccu_nkm
(
hw
);
unsigned
long
n
,
m
,
k
;
u32
reg
;
reg
=
readl
(
nkm
->
common
.
base
+
nkm
->
common
.
reg
);
n
=
reg
>>
nkm
->
n
.
shift
;
n
&=
(
1
<<
nkm
->
n
.
width
)
-
1
;
k
=
reg
>>
nkm
->
k
.
shift
;
k
&=
(
1
<<
nkm
->
k
.
width
)
-
1
;
m
=
reg
>>
nkm
->
m
.
shift
;
m
&=
(
1
<<
nkm
->
m
.
width
)
-
1
;
return
parent_rate
*
(
n
+
1
)
*
(
k
+
1
)
/
(
m
+
1
);
}
static
long
ccu_nkm_round_rate
(
struct
clk_hw
*
hw
,
unsigned
long
rate
,
unsigned
long
*
parent_rate
)
{
struct
ccu_nkm
*
nkm
=
hw_to_ccu_nkm
(
hw
);
struct
_ccu_nkm
_nkm
;
_nkm
.
max_n
=
1
<<
nkm
->
n
.
width
;
_nkm
.
max_k
=
1
<<
nkm
->
k
.
width
;
_nkm
.
max_m
=
1
<<
nkm
->
m
.
width
;
ccu_nkm_find_best
(
*
parent_rate
,
rate
,
&
_nkm
);
return
*
parent_rate
*
_nkm
.
n
*
_nkm
.
k
/
_nkm
.
m
;
}
static
int
ccu_nkm_set_rate
(
struct
clk_hw
*
hw
,
unsigned
long
rate
,
unsigned
long
parent_rate
)
{
struct
ccu_nkm
*
nkm
=
hw_to_ccu_nkm
(
hw
);
struct
_ccu_nkm
_nkm
;
unsigned
long
flags
;
u32
reg
;
_nkm
.
max_n
=
1
<<
nkm
->
n
.
width
;
_nkm
.
max_k
=
1
<<
nkm
->
k
.
width
;
_nkm
.
max_m
=
1
<<
nkm
->
m
.
width
;
ccu_nkm_find_best
(
parent_rate
,
rate
,
&
_nkm
);
spin_lock_irqsave
(
nkm
->
common
.
lock
,
flags
);
reg
=
readl
(
nkm
->
common
.
base
+
nkm
->
common
.
reg
);
reg
&=
~
GENMASK
(
nkm
->
n
.
width
+
nkm
->
n
.
shift
-
1
,
nkm
->
n
.
shift
);
reg
&=
~
GENMASK
(
nkm
->
k
.
width
+
nkm
->
k
.
shift
-
1
,
nkm
->
k
.
shift
);
reg
&=
~
GENMASK
(
nkm
->
m
.
width
+
nkm
->
m
.
shift
-
1
,
nkm
->
m
.
shift
);
reg
|=
(
_nkm
.
n
-
1
)
<<
nkm
->
n
.
shift
;
reg
|=
(
_nkm
.
k
-
1
)
<<
nkm
->
k
.
shift
;
reg
|=
(
_nkm
.
m
-
1
)
<<
nkm
->
m
.
shift
;
writel
(
reg
,
nkm
->
common
.
base
+
nkm
->
common
.
reg
);
spin_unlock_irqrestore
(
nkm
->
common
.
lock
,
flags
);
ccu_helper_wait_for_lock
(
&
nkm
->
common
,
nkm
->
lock
);
return
0
;
}
const
struct
clk_ops
ccu_nkm_ops
=
{
.
disable
=
ccu_nkm_disable
,
.
enable
=
ccu_nkm_enable
,
.
is_enabled
=
ccu_nkm_is_enabled
,
.
recalc_rate
=
ccu_nkm_recalc_rate
,
.
round_rate
=
ccu_nkm_round_rate
,
.
set_rate
=
ccu_nkm_set_rate
,
};
This diff is collapsed.
Click to expand it.
drivers/clk/sunxi-ng/ccu_nkm.h
0 → 100644
View file @
7adb7695
/*
* 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_NKM_H_
#define _CCU_NKM_H_
#include <linux/clk-provider.h>
#include "ccu_common.h"
#include "ccu_div.h"
#include "ccu_mult.h"
/*
* struct ccu_nkm - Definition of an N-K-M clock
*
* Clocks based on the formula parent * N * K / M
*/
struct
ccu_nkm
{
u32
enable
;
u32
lock
;
struct
_ccu_mult
n
;
struct
_ccu_mult
k
;
struct
_ccu_div
m
;
struct
ccu_common
common
;
};
#define SUNXI_CCU_NKM_WITH_GATE_LOCK(_struct, _name, _parent, _reg, \
_nshift, _nwidth, \
_kshift, _kwidth, \
_mshift, _mwidth, \
_gate, _lock, _flags) \
struct ccu_nkm _struct = { \
.enable = _gate, \
.lock = _lock, \
.k = _SUNXI_CCU_MULT(_kshift, _kwidth), \
.n = _SUNXI_CCU_MULT(_nshift, _nwidth), \
.m = _SUNXI_CCU_DIV(_mshift, _mwidth), \
.common = { \
.reg = _reg, \
.hw.init = CLK_HW_INIT(_name, \
_parent, \
&ccu_nkm_ops, \
_flags), \
}, \
}
static
inline
struct
ccu_nkm
*
hw_to_ccu_nkm
(
struct
clk_hw
*
hw
)
{
struct
ccu_common
*
common
=
hw_to_ccu_common
(
hw
);
return
container_of
(
common
,
struct
ccu_nkm
,
common
);
}
extern
const
struct
clk_ops
ccu_nkm_ops
;
#endif
/* _CCU_NKM_H_ */
This diff is collapsed.
Click to expand it.
drivers/clk/sunxi-ng/ccu_nkmp.c
0 → 100644
View file @
7adb7695
/*
* 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/clk-provider.h>
#include <linux/rational.h>
#include "ccu_gate.h"
#include "ccu_nkmp.h"
struct
_ccu_nkmp
{
unsigned
long
n
,
max_n
;
unsigned
long
k
,
max_k
;
unsigned
long
m
,
max_m
;
unsigned
long
p
,
max_p
;
};
static
void
ccu_nkmp_find_best
(
unsigned
long
parent
,
unsigned
long
rate
,
struct
_ccu_nkmp
*
nkmp
)
{
unsigned
long
best_rate
=
0
;
unsigned
long
best_n
=
0
,
best_k
=
0
,
best_m
=
0
,
best_p
=
0
;
unsigned
long
_n
,
_k
,
_m
,
_p
;
for
(
_k
=
1
;
_k
<=
nkmp
->
max_k
;
_k
++
)
{
for
(
_p
=
0
;
_p
<=
nkmp
->
max_p
;
_p
++
)
{
unsigned
long
tmp_rate
;
rational_best_approximation
(
rate
/
_k
,
parent
>>
_p
,
nkmp
->
max_n
,
nkmp
->
max_m
,
&
_n
,
&
_m
);
tmp_rate
=
(
parent
*
_n
*
_k
>>
_p
)
/
_m
;
if
(
tmp_rate
>
rate
)
continue
;
if
((
rate
-
tmp_rate
)
<
(
rate
-
best_rate
))
{
best_rate
=
tmp_rate
;
best_n
=
_n
;
best_k
=
_k
;
best_m
=
_m
;
best_p
=
_p
;
}
}
}
nkmp
->
n
=
best_n
;
nkmp
->
k
=
best_k
;
nkmp
->
m
=
best_m
;
nkmp
->
p
=
best_p
;
}
static
void
ccu_nkmp_disable
(
struct
clk_hw
*
hw
)
{
struct
ccu_nkmp
*
nkmp
=
hw_to_ccu_nkmp
(
hw
);
return
ccu_gate_helper_disable
(
&
nkmp
->
common
,
nkmp
->
enable
);
}
static
int
ccu_nkmp_enable
(
struct
clk_hw
*
hw
)
{
struct
ccu_nkmp
*
nkmp
=
hw_to_ccu_nkmp
(
hw
);
return
ccu_gate_helper_enable
(
&
nkmp
->
common
,
nkmp
->
enable
);
}
static
int
ccu_nkmp_is_enabled
(
struct
clk_hw
*
hw
)
{
struct
ccu_nkmp
*
nkmp
=
hw_to_ccu_nkmp
(
hw
);
return
ccu_gate_helper_is_enabled
(
&
nkmp
->
common
,
nkmp
->
enable
);
}
static
unsigned
long
ccu_nkmp_recalc_rate
(
struct
clk_hw
*
hw
,
unsigned
long
parent_rate
)
{
struct
ccu_nkmp
*
nkmp
=
hw_to_ccu_nkmp
(
hw
);
unsigned
long
n
,
m
,
k
,
p
;
u32
reg
;
reg
=
readl
(
nkmp
->
common
.
base
+
nkmp
->
common
.
reg
);
n
=
reg
>>
nkmp
->
n
.
shift
;
n
&=
(
1
<<
nkmp
->
n
.
width
)
-
1
;
k
=
reg
>>
nkmp
->
k
.
shift
;
k
&=
(
1
<<
nkmp
->
k
.
width
)
-
1
;
m
=
reg
>>
nkmp
->
m
.
shift
;
m
&=
(
1
<<
nkmp
->
m
.
width
)
-
1
;
p
=
reg
>>
nkmp
->
p
.
shift
;
p
&=
(
1
<<
nkmp
->
p
.
width
)
-
1
;
return
(
parent_rate
*
(
n
+
1
)
*
(
k
+
1
)
>>
p
)
/
(
m
+
1
);
}
static
long
ccu_nkmp_round_rate
(
struct
clk_hw
*
hw
,
unsigned
long
rate
,
unsigned
long
*
parent_rate
)
{
struct
ccu_nkmp
*
nkmp
=
hw_to_ccu_nkmp
(
hw
);
struct
_ccu_nkmp
_nkmp
;
_nkmp
.
max_n
=
1
<<
nkmp
->
n
.
width
;
_nkmp
.
max_k
=
1
<<
nkmp
->
k
.
width
;
_nkmp
.
max_m
=
1
<<
nkmp
->
m
.
width
;
_nkmp
.
max_p
=
(
1
<<
nkmp
->
p
.
width
)
-
1
;
ccu_nkmp_find_best
(
*
parent_rate
,
rate
,
&
_nkmp
);
return
(
*
parent_rate
*
_nkmp
.
n
*
_nkmp
.
k
>>
_nkmp
.
p
)
/
_nkmp
.
m
;
}
static
int
ccu_nkmp_set_rate
(
struct
clk_hw
*
hw
,
unsigned
long
rate
,
unsigned
long
parent_rate
)
{
struct
ccu_nkmp
*
nkmp
=
hw_to_ccu_nkmp
(
hw
);
struct
_ccu_nkmp
_nkmp
;
unsigned
long
flags
;
u32
reg
;
_nkmp
.
max_n
=
1
<<
nkmp
->
n
.
width
;
_nkmp
.
max_k
=
1
<<
nkmp
->
k
.
width
;
_nkmp
.
max_m
=
1
<<
nkmp
->
m
.
width
;
_nkmp
.
max_p
=
(
1
<<
nkmp
->
p
.
width
)
-
1
;
ccu_nkmp_find_best
(
parent_rate
,
rate
,
&
_nkmp
);
spin_lock_irqsave
(
nkmp
->
common
.
lock
,
flags
);
reg
=
readl
(
nkmp
->
common
.
base
+
nkmp
->
common
.
reg
);
reg
&=
~
GENMASK
(
nkmp
->
n
.
width
+
nkmp
->
n
.
shift
-
1
,
nkmp
->
n
.
shift
);
reg
&=
~
GENMASK
(
nkmp
->
k
.
width
+
nkmp
->
k
.
shift
-
1
,
nkmp
->
k
.
shift
);
reg
&=
~
GENMASK
(
nkmp
->
m
.
width
+
nkmp
->
m
.
shift
-
1
,
nkmp
->
m
.
shift
);
reg
&=
~
GENMASK
(
nkmp
->
p
.
width
+
nkmp
->
p
.
shift
-
1
,
nkmp
->
p
.
shift
);
reg
|=
(
_nkmp
.
n
-
1
)
<<
nkmp
->
n
.
shift
;
reg
|=
(
_nkmp
.
k
-
1
)
<<
nkmp
->
k
.
shift
;
reg
|=
(
_nkmp
.
m
-
1
)
<<
nkmp
->
m
.
shift
;
reg
|=
_nkmp
.
p
<<
nkmp
->
p
.
shift
;
writel
(
reg
,
nkmp
->
common
.
base
+
nkmp
->
common
.
reg
);
spin_unlock_irqrestore
(
nkmp
->
common
.
lock
,
flags
);
ccu_helper_wait_for_lock
(
&
nkmp
->
common
,
nkmp
->
lock
);
return
0
;
}
const
struct
clk_ops
ccu_nkmp_ops
=
{
.
disable
=
ccu_nkmp_disable
,
.
enable
=
ccu_nkmp_enable
,
.
is_enabled
=
ccu_nkmp_is_enabled
,
.
recalc_rate
=
ccu_nkmp_recalc_rate
,
.
round_rate
=
ccu_nkmp_round_rate
,
.
set_rate
=
ccu_nkmp_set_rate
,
};
This diff is collapsed.
Click to expand it.
drivers/clk/sunxi-ng/ccu_nkmp.h
0 → 100644
View file @
7adb7695
/*
* 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_NKMP_H_
#define _CCU_NKMP_H_
#include <linux/clk-provider.h>
#include "ccu_common.h"
#include "ccu_div.h"
#include "ccu_mult.h"
/*
* struct ccu_nkmp - Definition of an N-K-M-P clock
*
* Clocks based on the formula parent * N * K >> P / M
*/
struct
ccu_nkmp
{
u32
enable
;
u32
lock
;
struct
_ccu_mult
n
;
struct
_ccu_mult
k
;
struct
_ccu_div
m
;
struct
_ccu_div
p
;
struct
ccu_common
common
;
};
#define SUNXI_CCU_NKMP_WITH_GATE_LOCK(_struct, _name, _parent, _reg, \
_nshift, _nwidth, \
_kshift, _kwidth, \
_mshift, _mwidth, \
_pshift, _pwidth, \
_gate, _lock, _flags) \
struct ccu_nkmp _struct = { \
.enable = _gate, \
.lock = _lock, \
.n = _SUNXI_CCU_MULT(_nshift, _nwidth), \
.k = _SUNXI_CCU_MULT(_kshift, _kwidth), \
.m = _SUNXI_CCU_DIV(_mshift, _mwidth), \
.p = _SUNXI_CCU_DIV(_pshift, _pwidth), \
.common = { \
.reg = _reg, \
.hw.init = CLK_HW_INIT(_name, \
_parent, \
&ccu_nkmp_ops, \
_flags), \
}, \
}
static
inline
struct
ccu_nkmp
*
hw_to_ccu_nkmp
(
struct
clk_hw
*
hw
)
{
struct
ccu_common
*
common
=
hw_to_ccu_common
(
hw
);
return
container_of
(
common
,
struct
ccu_nkmp
,
common
);
}
extern
const
struct
clk_ops
ccu_nkmp_ops
;
#endif
/* _CCU_NKMP_H_ */
This diff is collapsed.
Click to expand it.
drivers/clk/sunxi-ng/ccu_nm.c
0 → 100644
View file @
7adb7695
/*
* 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/clk-provider.h>
#include <linux/rational.h>
#include "ccu_frac.h"
#include "ccu_gate.h"
#include "ccu_nm.h"
static
void
ccu_nm_disable
(
struct
clk_hw
*
hw
)
{
struct
ccu_nm
*
nm
=
hw_to_ccu_nm
(
hw
);
return
ccu_gate_helper_disable
(
&
nm
->
common
,
nm
->
enable
);
}
static
int
ccu_nm_enable
(
struct
clk_hw
*
hw
)
{
struct
ccu_nm
*
nm
=
hw_to_ccu_nm
(
hw
);
return
ccu_gate_helper_enable
(
&
nm
->
common
,
nm
->
enable
);
}
static
int
ccu_nm_is_enabled
(
struct
clk_hw
*
hw
)
{
struct
ccu_nm
*
nm
=
hw_to_ccu_nm
(
hw
);
return
ccu_gate_helper_is_enabled
(
&
nm
->
common
,
nm
->
enable
);
}
static
unsigned
long
ccu_nm_recalc_rate
(
struct
clk_hw
*
hw
,
unsigned
long
parent_rate
)
{
struct
ccu_nm
*
nm
=
hw_to_ccu_nm
(
hw
);
unsigned
long
n
,
m
;
u32
reg
;
if
(
ccu_frac_helper_is_enabled
(
&
nm
->
common
,
&
nm
->
frac
))
return
ccu_frac_helper_read_rate
(
&
nm
->
common
,
&
nm
->
frac
);
reg
=
readl
(
nm
->
common
.
base
+
nm
->
common
.
reg
);
n
=
reg
>>
nm
->
n
.
shift
;
n
&=
(
1
<<
nm
->
n
.
width
)
-
1
;
m
=
reg
>>
nm
->
m
.
shift
;
m
&=
(
1
<<
nm
->
m
.
width
)
-
1
;
return
parent_rate
*
(
n
+
1
)
/
(
m
+
1
);
}
static
long
ccu_nm_round_rate
(
struct
clk_hw
*
hw
,
unsigned
long
rate
,
unsigned
long
*
parent_rate
)
{
struct
ccu_nm
*
nm
=
hw_to_ccu_nm
(
hw
);
unsigned
long
n
,
m
;
rational_best_approximation
(
rate
,
*
parent_rate
,
1
<<
nm
->
n
.
width
,
1
<<
nm
->
m
.
width
,
&
n
,
&
m
);
return
*
parent_rate
*
n
/
m
;
}
static
int
ccu_nm_set_rate
(
struct
clk_hw
*
hw
,
unsigned
long
rate
,
unsigned
long
parent_rate
)
{
struct
ccu_nm
*
nm
=
hw_to_ccu_nm
(
hw
);
unsigned
long
flags
;
unsigned
long
n
,
m
;
u32
reg
;
if
(
ccu_frac_helper_has_rate
(
&
nm
->
common
,
&
nm
->
frac
,
rate
))
return
ccu_frac_helper_set_rate
(
&
nm
->
common
,
&
nm
->
frac
,
rate
);
else
ccu_frac_helper_disable
(
&
nm
->
common
,
&
nm
->
frac
);
rational_best_approximation
(
rate
,
parent_rate
,
1
<<
nm
->
n
.
width
,
1
<<
nm
->
m
.
width
,
&
n
,
&
m
);
spin_lock_irqsave
(
nm
->
common
.
lock
,
flags
);
reg
=
readl
(
nm
->
common
.
base
+
nm
->
common
.
reg
);
reg
&=
~
GENMASK
(
nm
->
n
.
width
+
nm
->
n
.
shift
-
1
,
nm
->
n
.
shift
);
reg
&=
~
GENMASK
(
nm
->
m
.
width
+
nm
->
m
.
shift
-
1
,
nm
->
m
.
shift
);
writel
(
reg
|
((
m
-
1
)
<<
nm
->
m
.
shift
)
|
((
n
-
1
)
<<
nm
->
n
.
shift
),
nm
->
common
.
base
+
nm
->
common
.
reg
);
spin_unlock_irqrestore
(
nm
->
common
.
lock
,
flags
);
ccu_helper_wait_for_lock
(
&
nm
->
common
,
nm
->
lock
);
return
0
;
}
const
struct
clk_ops
ccu_nm_ops
=
{
.
disable
=
ccu_nm_disable
,
.
enable
=
ccu_nm_enable
,
.
is_enabled
=
ccu_nm_is_enabled
,
.
recalc_rate
=
ccu_nm_recalc_rate
,
.
round_rate
=
ccu_nm_round_rate
,
.
set_rate
=
ccu_nm_set_rate
,
};
This diff is collapsed.
Click to expand it.
drivers/clk/sunxi-ng/ccu_nm.h
0 → 100644
View file @
7adb7695
/*
* 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_NM_H_
#define _CCU_NM_H_
#include <linux/clk-provider.h>
#include "ccu_common.h"
#include "ccu_div.h"
#include "ccu_frac.h"
#include "ccu_mult.h"
/*
* struct ccu_nm - Definition of an N-M clock
*
* Clocks based on the formula parent * N / M
*/
struct
ccu_nm
{
u32
enable
;
u32
lock
;
struct
_ccu_mult
n
;
struct
_ccu_div
m
;
struct
_ccu_frac
frac
;
struct
ccu_common
common
;
};
#define SUNXI_CCU_NM_WITH_FRAC_GATE_LOCK(_struct, _name, _parent, _reg, \
_nshift, _nwidth, \
_mshift, _mwidth, \
_frac_en, _frac_sel, \
_frac_rate_0, _frac_rate_1, \
_gate, _lock, _flags) \
struct ccu_nm _struct = { \
.enable = _gate, \
.lock = _lock, \
.n = _SUNXI_CCU_MULT(_nshift, _nwidth), \
.m = _SUNXI_CCU_DIV(_mshift, _mwidth), \
.frac = _SUNXI_CCU_FRAC(_frac_en, _frac_sel, \
_frac_rate_0, \
_frac_rate_1), \
.common = { \
.reg = _reg, \
.features = CCU_FEATURE_FRACTIONAL, \
.hw.init = CLK_HW_INIT(_name, \
_parent, \
&ccu_nm_ops, \
_flags), \
}, \
}
#define SUNXI_CCU_NM_WITH_GATE_LOCK(_struct, _name, _parent, _reg, \
_nshift, _nwidth, \
_mshift, _mwidth, \
_gate, _lock, _flags) \
struct ccu_nm _struct = { \
.enable = _gate, \
.lock = _lock, \
.n = _SUNXI_CCU_MULT(_nshift, _nwidth), \
.m = _SUNXI_CCU_DIV(_mshift, _mwidth), \
.common = { \
.reg = _reg, \
.hw.init = CLK_HW_INIT(_name, \
_parent, \
&ccu_nm_ops, \
_flags), \
}, \
}
static
inline
struct
ccu_nm
*
hw_to_ccu_nm
(
struct
clk_hw
*
hw
)
{
struct
ccu_common
*
common
=
hw_to_ccu_common
(
hw
);
return
container_of
(
common
,
struct
ccu_nm
,
common
);
}
extern
const
struct
clk_ops
ccu_nm_ops
;
#endif
/* _CCU_NM_H_ */
This diff is collapsed.
Click to expand it.
drivers/clk/sunxi-ng/ccu_phase.c
0 → 100644
View file @
7adb7695
/*
* 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/clk-provider.h>
#include <linux/spinlock.h>
#include "ccu_phase.h"
static
int
ccu_phase_get_phase
(
struct
clk_hw
*
hw
)
{
struct
ccu_phase
*
phase
=
hw_to_ccu_phase
(
hw
);
struct
clk_hw
*
parent
,
*
grandparent
;
unsigned
int
parent_rate
,
grandparent_rate
;
u16
step
,
parent_div
;
u32
reg
;
u8
delay
;
reg
=
readl
(
phase
->
common
.
base
+
phase
->
common
.
reg
);
delay
=
(
reg
>>
phase
->
shift
);
delay
&=
(
1
<<
phase
->
width
)
-
1
;
if
(
!
delay
)
return
180
;
/* Get our parent clock, it's the one that can adjust its rate */
parent
=
clk_hw_get_parent
(
hw
);
if
(
!
parent
)
return
-
EINVAL
;
/* And its rate */
parent_rate
=
clk_hw_get_rate
(
parent
);
if
(
!
parent_rate
)
return
-
EINVAL
;
/* Now, get our parent's parent (most likely some PLL) */
grandparent
=
clk_hw_get_parent
(
parent
);
if
(
!
grandparent
)
return
-
EINVAL
;
/* And its rate */
grandparent_rate
=
clk_hw_get_rate
(
grandparent
);
if
(
!
grandparent_rate
)
return
-
EINVAL
;
/* Get our parent clock divider */
parent_div
=
grandparent_rate
/
parent_rate
;
step
=
DIV_ROUND_CLOSEST
(
360
,
parent_div
);
return
delay
*
step
;
}
static
int
ccu_phase_set_phase
(
struct
clk_hw
*
hw
,
int
degrees
)
{
struct
ccu_phase
*
phase
=
hw_to_ccu_phase
(
hw
);
struct
clk_hw
*
parent
,
*
grandparent
;
unsigned
int
parent_rate
,
grandparent_rate
;
unsigned
long
flags
;
u32
reg
;
u8
delay
;
/* Get our parent clock, it's the one that can adjust its rate */
parent
=
clk_hw_get_parent
(
hw
);
if
(
!
parent
)
return
-
EINVAL
;
/* And its rate */
parent_rate
=
clk_hw_get_rate
(
parent
);
if
(
!
parent_rate
)
return
-
EINVAL
;
/* Now, get our parent's parent (most likely some PLL) */
grandparent
=
clk_hw_get_parent
(
parent
);
if
(
!
grandparent
)
return
-
EINVAL
;
/* And its rate */
grandparent_rate
=
clk_hw_get_rate
(
grandparent
);
if
(
!
grandparent_rate
)
return
-
EINVAL
;
if
(
degrees
!=
180
)
{
u16
step
,
parent_div
;
/* Get our parent divider */
parent_div
=
grandparent_rate
/
parent_rate
;
/*
* We can only outphase the clocks by multiple of the
* PLL's period.
*
* Since our parent clock is only a divider, and the
* formula to get the outphasing in degrees is deg =
* 360 * delta / period
*
* If we simplify this formula, we can see that the
* only thing that we're concerned about is the number
* of period we want to outphase our clock from, and
* the divider set by our parent clock.
*/
step
=
DIV_ROUND_CLOSEST
(
360
,
parent_div
);
delay
=
DIV_ROUND_CLOSEST
(
degrees
,
step
);
}
else
{
delay
=
0
;
}
spin_lock_irqsave
(
phase
->
common
.
lock
,
flags
);
reg
=
readl
(
phase
->
common
.
base
+
phase
->
common
.
reg
);
reg
&=
~
GENMASK
(
phase
->
width
+
phase
->
shift
-
1
,
phase
->
shift
);
writel
(
reg
|
(
delay
<<
phase
->
shift
),
phase
->
common
.
base
+
phase
->
common
.
reg
);
spin_unlock_irqrestore
(
phase
->
common
.
lock
,
flags
);
return
0
;
}
const
struct
clk_ops
ccu_phase_ops
=
{
.
get_phase
=
ccu_phase_get_phase
,
.
set_phase
=
ccu_phase_set_phase
,
};
This diff is collapsed.
Click to expand it.
drivers/clk/sunxi-ng/ccu_phase.h
0 → 100644
View file @
7adb7695
/*
* 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_PHASE_H_
#define _CCU_PHASE_H_
#include <linux/clk-provider.h>
#include "ccu_common.h"
struct
ccu_phase
{
u8
shift
;
u8
width
;
struct
ccu_common
common
;
};
#define SUNXI_CCU_PHASE(_struct, _name, _parent, _reg, _shift, _width, _flags) \
struct ccu_phase _struct = { \
.shift = _shift, \
.width = _width, \
.common = { \
.reg = _reg, \
.hw.init = CLK_HW_INIT(_name, \
_parent, \
&ccu_phase_ops, \
_flags), \
} \
}
static
inline
struct
ccu_phase
*
hw_to_ccu_phase
(
struct
clk_hw
*
hw
)
{
struct
ccu_common
*
common
=
hw_to_ccu_common
(
hw
);
return
container_of
(
common
,
struct
ccu_phase
,
common
);
}
extern
const
struct
clk_ops
ccu_phase_ops
;
#endif
/* _CCU_PHASE_H_ */
This diff is collapsed.
Click to expand it.
drivers/clk/sunxi-ng/ccu_reset.c
0 → 100644
View file @
7adb7695
/*
* 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
,
};
This diff is collapsed.
Click to expand it.
drivers/clk/sunxi-ng/ccu_reset.h
0 → 100644
View file @
7adb7695
/*
* 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_ */
This diff is collapsed.
Click to expand it.
include/dt-bindings/clock/sun8i-h3-ccu.h
0 → 100644
View file @
7adb7695
/*
* Copyright (C) 2016 Maxime Ripard <maxime.ripard@free-electrons.com>
*
* This file is dual-licensed: you can use it either under the terms
* of the GPL or the X11 license, at your option. Note that this dual
* licensing only applies to this file, and not this project as a
* whole.
*
* a) This file 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 file 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.
*
* Or, alternatively,
*
* b) Permission is hereby granted, free of charge, to any person
* obtaining a copy of this software and associated documentation
* files (the "Software"), to deal in the Software without
* restriction, including without limitation the rights to use,
* copy, modify, merge, publish, distribute, sublicense, and/or
* sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following
* conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
* OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
* OTHER DEALINGS IN THE SOFTWARE.
*/
#ifndef _DT_BINDINGS_CLK_SUN8I_H3_H_
#define _DT_BINDINGS_CLK_SUN8I_H3_H_
#define CLK_CPUX 14
#define CLK_BUS_CE 20
#define CLK_BUS_DMA 21
#define CLK_BUS_MMC0 22
#define CLK_BUS_MMC1 23
#define CLK_BUS_MMC2 24
#define CLK_BUS_NAND 25
#define CLK_BUS_DRAM 26
#define CLK_BUS_EMAC 27
#define CLK_BUS_TS 28
#define CLK_BUS_HSTIMER 29
#define CLK_BUS_SPI0 30
#define CLK_BUS_SPI1 31
#define CLK_BUS_OTG 32
#define CLK_BUS_EHCI0 33
#define CLK_BUS_EHCI1 34
#define CLK_BUS_EHCI2 35
#define CLK_BUS_EHCI3 36
#define CLK_BUS_OHCI0 37
#define CLK_BUS_OHCI1 38
#define CLK_BUS_OHCI2 39
#define CLK_BUS_OHCI3 40
#define CLK_BUS_VE 41
#define CLK_BUS_TCON0 42
#define CLK_BUS_TCON1 43
#define CLK_BUS_DEINTERLACE 44
#define CLK_BUS_CSI 45
#define CLK_BUS_TVE 46
#define CLK_BUS_HDMI 47
#define CLK_BUS_DE 48
#define CLK_BUS_GPU 49
#define CLK_BUS_MSGBOX 50
#define CLK_BUS_SPINLOCK 51
#define CLK_BUS_CODEC 52
#define CLK_BUS_SPDIF 53
#define CLK_BUS_PIO 54
#define CLK_BUS_THS 55
#define CLK_BUS_I2S0 56
#define CLK_BUS_I2S1 57
#define CLK_BUS_I2S2 58
#define CLK_BUS_I2C0 59
#define CLK_BUS_I2C1 60
#define CLK_BUS_I2C2 61
#define CLK_BUS_UART0 62
#define CLK_BUS_UART1 63
#define CLK_BUS_UART2 64
#define CLK_BUS_UART3 65
#define CLK_BUS_SCR 66
#define CLK_BUS_EPHY 67
#define CLK_BUS_DBG 68
#define CLK_THS 69
#define CLK_NAND 70
#define CLK_MMC0 71
#define CLK_MMC0_SAMPLE 72
#define CLK_MMC0_OUTPUT 73
#define CLK_MMC1 74
#define CLK_MMC1_SAMPLE 75
#define CLK_MMC1_OUTPUT 76
#define CLK_MMC2 77
#define CLK_MMC2_SAMPLE 78
#define CLK_MMC2_OUTPUT 79
#define CLK_TS 80
#define CLK_CE 81
#define CLK_SPI0 82
#define CLK_SPI1 83
#define CLK_I2S0 84
#define CLK_I2S1 85
#define CLK_I2S2 86
#define CLK_SPDIF 87
#define CLK_USB_PHY0 88
#define CLK_USB_PHY1 89
#define CLK_USB_PHY2 90
#define CLK_USB_PHY3 91
#define CLK_USB_OHCI0 92
#define CLK_USB_OHCI1 93
#define CLK_USB_OHCI2 94
#define CLK_USB_OHCI3 95
#define CLK_DRAM_VE 97
#define CLK_DRAM_CSI 98
#define CLK_DRAM_DEINTERLACE 99
#define CLK_DRAM_TS 100
#define CLK_DE 101
#define CLK_TCON0 102
#define CLK_TVE 103
#define CLK_DEINTERLACE 104
#define CLK_CSI_MISC 105
#define CLK_CSI_SCLK 106
#define CLK_CSI_MCLK 107
#define CLK_VE 108
#define CLK_AC_DIG 109
#define CLK_AVS 110
#define CLK_HDMI 111
#define CLK_HDMI_DDC 112
#define CLK_GPU 114
#endif
/* _DT_BINDINGS_CLK_SUN8I_H3_H_ */
This diff is collapsed.
Click to expand it.
include/dt-bindings/reset/sun8i-h3-ccu.h
0 → 100644
View file @
7adb7695
/*
* Copyright (C) 2016 Maxime Ripard <maxime.ripard@free-electrons.com>
*
* This file is dual-licensed: you can use it either under the terms
* of the GPL or the X11 license, at your option. Note that this dual
* licensing only applies to this file, and not this project as a
* whole.
*
* a) This file 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 file 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.
*
* Or, alternatively,
*
* b) Permission is hereby granted, free of charge, to any person
* obtaining a copy of this software and associated documentation
* files (the "Software"), to deal in the Software without
* restriction, including without limitation the rights to use,
* copy, modify, merge, publish, distribute, sublicense, and/or
* sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following
* conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
* OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
* OTHER DEALINGS IN THE SOFTWARE.
*/
#ifndef _DT_BINDINGS_RST_SUN8I_H3_H_
#define _DT_BINDINGS_RST_SUN8I_H3_H_
#define RST_USB_PHY0 0
#define RST_USB_PHY1 1
#define RST_USB_PHY2 2
#define RST_USB_PHY3 3
#define RST_MBUS 4
#define RST_BUS_CE 5
#define RST_BUS_DMA 6
#define RST_BUS_MMC0 7
#define RST_BUS_MMC1 8
#define RST_BUS_MMC2 9
#define RST_BUS_NAND 10
#define RST_BUS_DRAM 11
#define RST_BUS_EMAC 12
#define RST_BUS_TS 13
#define RST_BUS_HSTIMER 14
#define RST_BUS_SPI0 15
#define RST_BUS_SPI1 16
#define RST_BUS_OTG 17
#define RST_BUS_EHCI0 18
#define RST_BUS_EHCI1 19
#define RST_BUS_EHCI2 20
#define RST_BUS_EHCI3 21
#define RST_BUS_OHCI0 22
#define RST_BUS_OHCI1 23
#define RST_BUS_OHCI2 24
#define RST_BUS_OHCI3 25
#define RST_BUS_VE 26
#define RST_BUS_TCON0 27
#define RST_BUS_TCON1 28
#define RST_BUS_DEINTERLACE 29
#define RST_BUS_CSI 30
#define RST_BUS_TVE 31
#define RST_BUS_HDMI0 32
#define RST_BUS_HDMI1 33
#define RST_BUS_DE 34
#define RST_BUS_GPU 35
#define RST_BUS_MSGBOX 36
#define RST_BUS_SPINLOCK 37
#define RST_BUS_DBG 38
#define RST_BUS_EPHY 39
#define RST_BUS_CODEC 40
#define RST_BUS_SPDIF 41
#define RST_BUS_THS 42
#define RST_BUS_I2S0 43
#define RST_BUS_I2S1 44
#define RST_BUS_I2S2 45
#define RST_BUS_I2C0 46
#define RST_BUS_I2C1 47
#define RST_BUS_I2C2 48
#define RST_BUS_UART0 49
#define RST_BUS_UART1 50
#define RST_BUS_UART2 51
#define RST_BUS_UART3 52
#define RST_BUS_SCR 53
#endif
/* _DT_BINDINGS_RST_SUN8I_H3_H_ */
This diff is collapsed.
Click to expand it.
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment