forked from Freescale/linux-fslc
-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
clk: socfpga: stratix10: add clock driver for Stratix10 platform
Add a clock driver for the Stratix10 SoC. The driver is similar to the Cyclone5/Arria10 platforms, with the exception that this driver only uses one single clock binding. Signed-off-by: Dinh Nguyen <[email protected]> Signed-off-by: Stephen Boyd <[email protected]>
- Loading branch information
Showing
8 changed files
with
854 additions
and
5 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,6 +1,5 @@ | ||
# SPDX-License-Identifier: GPL-2.0 | ||
obj-y += clk.o | ||
obj-y += clk-gate.o | ||
obj-y += clk-pll.o | ||
obj-y += clk-periph.o | ||
obj-y += clk-pll-a10.o clk-periph-a10.o clk-gate-a10.o | ||
obj-$(CONFIG_ARCH_SOCFPGA) += clk.o clk-gate.o clk-pll.o clk-periph.o | ||
obj-$(CONFIG_ARCH_SOCFPGA) += clk-pll-a10.o clk-periph-a10.o clk-gate-a10.o | ||
obj-$(CONFIG_ARCH_STRATIX10) += clk-s10.o | ||
obj-$(CONFIG_ARCH_STRATIX10) += clk-pll-s10.o clk-periph-s10.o clk-gate-s10.o |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,125 @@ | ||
// SPDX-License-Identifier: GPL-2.0 | ||
/* | ||
* Copyright (C) 2017, Intel Corporation | ||
*/ | ||
#include <linux/clk-provider.h> | ||
#include <linux/slab.h> | ||
#include "stratix10-clk.h" | ||
#include "clk.h" | ||
|
||
#define SOCFPGA_CS_PDBG_CLK "cs_pdbg_clk" | ||
#define to_socfpga_gate_clk(p) container_of(p, struct socfpga_gate_clk, hw.hw) | ||
|
||
static unsigned long socfpga_gate_clk_recalc_rate(struct clk_hw *hwclk, | ||
unsigned long parent_rate) | ||
{ | ||
struct socfpga_gate_clk *socfpgaclk = to_socfpga_gate_clk(hwclk); | ||
u32 div = 1, val; | ||
|
||
if (socfpgaclk->fixed_div) { | ||
div = socfpgaclk->fixed_div; | ||
} else if (socfpgaclk->div_reg) { | ||
val = readl(socfpgaclk->div_reg) >> socfpgaclk->shift; | ||
val &= GENMASK(socfpgaclk->width - 1, 0); | ||
div = (1 << val); | ||
} | ||
return parent_rate / div; | ||
} | ||
|
||
static unsigned long socfpga_dbg_clk_recalc_rate(struct clk_hw *hwclk, | ||
unsigned long parent_rate) | ||
{ | ||
struct socfpga_gate_clk *socfpgaclk = to_socfpga_gate_clk(hwclk); | ||
u32 div = 1, val; | ||
|
||
val = readl(socfpgaclk->div_reg) >> socfpgaclk->shift; | ||
val &= GENMASK(socfpgaclk->width - 1, 0); | ||
div = (1 << val); | ||
div = div ? 4 : 1; | ||
|
||
return parent_rate / div; | ||
} | ||
|
||
static u8 socfpga_gate_get_parent(struct clk_hw *hwclk) | ||
{ | ||
struct socfpga_gate_clk *socfpgaclk = to_socfpga_gate_clk(hwclk); | ||
u32 mask; | ||
u8 parent = 0; | ||
|
||
if (socfpgaclk->bypass_reg) { | ||
mask = (0x1 << socfpgaclk->bypass_shift); | ||
parent = ((readl(socfpgaclk->bypass_reg) & mask) >> | ||
socfpgaclk->bypass_shift); | ||
} | ||
return parent; | ||
} | ||
|
||
static struct clk_ops gateclk_ops = { | ||
.recalc_rate = socfpga_gate_clk_recalc_rate, | ||
.get_parent = socfpga_gate_get_parent, | ||
}; | ||
|
||
static const struct clk_ops dbgclk_ops = { | ||
.recalc_rate = socfpga_dbg_clk_recalc_rate, | ||
.get_parent = socfpga_gate_get_parent, | ||
}; | ||
|
||
struct clk *s10_register_gate(const char *name, const char *parent_name, | ||
const char * const *parent_names, | ||
u8 num_parents, unsigned long flags, | ||
void __iomem *regbase, unsigned long gate_reg, | ||
unsigned long gate_idx, unsigned long div_reg, | ||
unsigned long div_offset, u8 div_width, | ||
unsigned long bypass_reg, u8 bypass_shift, | ||
u8 fixed_div) | ||
{ | ||
struct clk *clk; | ||
struct socfpga_gate_clk *socfpga_clk; | ||
struct clk_init_data init; | ||
|
||
socfpga_clk = kzalloc(sizeof(*socfpga_clk), GFP_KERNEL); | ||
if (!socfpga_clk) | ||
return NULL; | ||
|
||
socfpga_clk->hw.reg = regbase + gate_reg; | ||
socfpga_clk->hw.bit_idx = gate_idx; | ||
|
||
gateclk_ops.enable = clk_gate_ops.enable; | ||
gateclk_ops.disable = clk_gate_ops.disable; | ||
|
||
socfpga_clk->fixed_div = fixed_div; | ||
|
||
if (div_reg) | ||
socfpga_clk->div_reg = regbase + div_reg; | ||
else | ||
socfpga_clk->div_reg = NULL; | ||
|
||
socfpga_clk->width = div_width; | ||
socfpga_clk->shift = div_offset; | ||
|
||
if (bypass_reg) | ||
socfpga_clk->bypass_reg = regbase + bypass_reg; | ||
else | ||
socfpga_clk->bypass_reg = NULL; | ||
socfpga_clk->bypass_shift = bypass_shift; | ||
|
||
if (streq(name, "cs_pdbg_clk")) | ||
init.ops = &dbgclk_ops; | ||
else | ||
init.ops = &gateclk_ops; | ||
|
||
init.name = name; | ||
init.flags = flags; | ||
|
||
init.num_parents = num_parents; | ||
init.parent_names = parent_names ? parent_names : &parent_name; | ||
socfpga_clk->hw.hw.init = &init; | ||
|
||
clk = clk_register(NULL, &socfpga_clk->hw.hw); | ||
if (WARN_ON(IS_ERR(clk))) { | ||
kfree(socfpga_clk); | ||
return NULL; | ||
} | ||
|
||
return clk; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,149 @@ | ||
// SPDX-License-Identifier: GPL-2.0 | ||
/* | ||
* Copyright (C) 2017, Intel Corporation | ||
*/ | ||
#include <linux/slab.h> | ||
#include <linux/clk-provider.h> | ||
|
||
#include "stratix10-clk.h" | ||
#include "clk.h" | ||
|
||
#define CLK_MGR_FREE_SHIFT 16 | ||
#define CLK_MGR_FREE_MASK 0x7 | ||
#define SWCTRLBTCLKSEN_SHIFT 8 | ||
|
||
#define to_periph_clk(p) container_of(p, struct socfpga_periph_clk, hw.hw) | ||
|
||
static unsigned long clk_peri_c_clk_recalc_rate(struct clk_hw *hwclk, | ||
unsigned long parent_rate) | ||
{ | ||
struct socfpga_periph_clk *socfpgaclk = to_periph_clk(hwclk); | ||
unsigned long div = 1; | ||
u32 val; | ||
|
||
val = readl(socfpgaclk->hw.reg); | ||
val &= GENMASK(SWCTRLBTCLKSEN_SHIFT - 1, 0); | ||
parent_rate /= val; | ||
|
||
return parent_rate / div; | ||
} | ||
|
||
static unsigned long clk_peri_cnt_clk_recalc_rate(struct clk_hw *hwclk, | ||
unsigned long parent_rate) | ||
{ | ||
struct socfpga_periph_clk *socfpgaclk = to_periph_clk(hwclk); | ||
unsigned long div = 1; | ||
|
||
if (socfpgaclk->fixed_div) { | ||
div = socfpgaclk->fixed_div; | ||
} else { | ||
if (!socfpgaclk->bypass_reg) | ||
div = ((readl(socfpgaclk->hw.reg) & 0x7ff) + 1); | ||
} | ||
|
||
return parent_rate / div; | ||
} | ||
|
||
static u8 clk_periclk_get_parent(struct clk_hw *hwclk) | ||
{ | ||
struct socfpga_periph_clk *socfpgaclk = to_periph_clk(hwclk); | ||
u32 clk_src, mask; | ||
u8 parent; | ||
|
||
if (socfpgaclk->bypass_reg) { | ||
mask = (0x1 << socfpgaclk->bypass_shift); | ||
parent = ((readl(socfpgaclk->bypass_reg) & mask) >> | ||
socfpgaclk->bypass_shift); | ||
} else { | ||
clk_src = readl(socfpgaclk->hw.reg); | ||
parent = (clk_src >> CLK_MGR_FREE_SHIFT) & | ||
CLK_MGR_FREE_MASK; | ||
} | ||
return parent; | ||
} | ||
|
||
static const struct clk_ops peri_c_clk_ops = { | ||
.recalc_rate = clk_peri_c_clk_recalc_rate, | ||
.get_parent = clk_periclk_get_parent, | ||
}; | ||
|
||
static const struct clk_ops peri_cnt_clk_ops = { | ||
.recalc_rate = clk_peri_cnt_clk_recalc_rate, | ||
.get_parent = clk_periclk_get_parent, | ||
}; | ||
|
||
struct clk *s10_register_periph(const char *name, const char *parent_name, | ||
const char * const *parent_names, | ||
u8 num_parents, unsigned long flags, | ||
void __iomem *reg, unsigned long offset) | ||
{ | ||
struct clk *clk; | ||
struct socfpga_periph_clk *periph_clk; | ||
struct clk_init_data init; | ||
|
||
periph_clk = kzalloc(sizeof(*periph_clk), GFP_KERNEL); | ||
if (WARN_ON(!periph_clk)) | ||
return NULL; | ||
|
||
periph_clk->hw.reg = reg + offset; | ||
|
||
init.name = name; | ||
init.ops = &peri_c_clk_ops; | ||
init.flags = flags; | ||
|
||
init.num_parents = num_parents; | ||
init.parent_names = parent_names ? parent_names : &parent_name; | ||
|
||
periph_clk->hw.hw.init = &init; | ||
|
||
clk = clk_register(NULL, &periph_clk->hw.hw); | ||
if (WARN_ON(IS_ERR(clk))) { | ||
kfree(periph_clk); | ||
return NULL; | ||
} | ||
return clk; | ||
} | ||
|
||
struct clk *s10_register_cnt_periph(const char *name, const char *parent_name, | ||
const char * const *parent_names, | ||
u8 num_parents, unsigned long flags, | ||
void __iomem *regbase, unsigned long offset, | ||
u8 fixed_divider, unsigned long bypass_reg, | ||
unsigned long bypass_shift) | ||
{ | ||
struct clk *clk; | ||
struct socfpga_periph_clk *periph_clk; | ||
struct clk_init_data init; | ||
|
||
periph_clk = kzalloc(sizeof(*periph_clk), GFP_KERNEL); | ||
if (WARN_ON(!periph_clk)) | ||
return NULL; | ||
|
||
if (offset) | ||
periph_clk->hw.reg = regbase + offset; | ||
else | ||
periph_clk->hw.reg = NULL; | ||
|
||
if (bypass_reg) | ||
periph_clk->bypass_reg = regbase + bypass_reg; | ||
else | ||
periph_clk->bypass_reg = NULL; | ||
periph_clk->bypass_shift = bypass_shift; | ||
periph_clk->fixed_div = fixed_divider; | ||
|
||
init.name = name; | ||
init.ops = &peri_cnt_clk_ops; | ||
init.flags = flags; | ||
|
||
init.num_parents = num_parents; | ||
init.parent_names = parent_names ? parent_names : &parent_name; | ||
|
||
periph_clk->hw.hw.init = &init; | ||
|
||
clk = clk_register(NULL, &periph_clk->hw.hw); | ||
if (WARN_ON(IS_ERR(clk))) { | ||
kfree(periph_clk); | ||
return NULL; | ||
} | ||
return clk; | ||
} |
Oops, something went wrong.