forked from torvalds/linux
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Signed-off-by: Yoshinori Sato <[email protected]>
- Loading branch information
Showing
6 changed files
with
250 additions
and
0 deletions.
There are no files selected for viewing
24 changes: 24 additions & 0 deletions
24
Documentation/devicetree/bindings/clock/renesas,h8300-div-clock.txt
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,24 @@ | ||
* Renesas H8/300 divider clock | ||
|
||
Required Properties: | ||
|
||
- compatible: Must be "renesas,sh73a0-h8300-div-clock" | ||
|
||
- clocks: Reference to the parent clocks ("extal1" and "extal2") | ||
|
||
- #clock-cells: Must be 1 | ||
|
||
- reg: Base address and length of the divide rate selector | ||
|
||
- renesas,width: bit width of selector | ||
|
||
Example | ||
------- | ||
|
||
cclk: cclk { | ||
compatible = "renesas,h8300-div-clock"; | ||
clocks = <&xclk>; | ||
#clock-cells = <0>; | ||
reg = <0xfee01b 2>; | ||
renesas,width = <2>; | ||
}; |
23 changes: 23 additions & 0 deletions
23
Documentation/devicetree/bindings/clock/renesas,h8s2678-pll-clock.txt
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,23 @@ | ||
Renesas H8S2678 PLL clock | ||
|
||
This device is Clock multiplyer | ||
|
||
Required Properties: | ||
|
||
- compatible: Must be "renesas,h8s2678-pll-clock" | ||
|
||
- clocks: Reference to the parent clocks | ||
|
||
- #clock-cells: Must be 0 | ||
|
||
- reg: Two rate selector (Multiply / Divide) register address | ||
|
||
Example | ||
------- | ||
|
||
pllclk: pllclk { | ||
compatible = "renesas,h8s2678-pll-clock"; | ||
clocks = <&xclk>; | ||
#clock-cells = <0>; | ||
reg = <0xfee03b 2>, <0xfee045 2>; | ||
}; |
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 |
---|---|---|
@@ -0,0 +1,2 @@ | ||
obj-y += clk-div.o | ||
obj-$(CONFIG_H8S2678) += clk-h8s2678.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,53 @@ | ||
/* | ||
* H8/300 divide clock driver | ||
* | ||
* Copyright 2015 Yoshinori Sato <[email protected]> | ||
*/ | ||
|
||
#include <linux/clk.h> | ||
#include <linux/clkdev.h> | ||
#include <linux/clk-provider.h> | ||
#include <linux/err.h> | ||
#include <linux/of.h> | ||
#include <linux/of_address.h> | ||
|
||
static DEFINE_SPINLOCK(clklock); | ||
|
||
static void __init h8300_div_clk_setup(struct device_node *node) | ||
{ | ||
unsigned int num_parents; | ||
struct clk *clk; | ||
const char *clk_name = node->name; | ||
const char *parent_name; | ||
void __iomem *divcr = NULL; | ||
int width; | ||
|
||
num_parents = of_clk_get_parent_count(node); | ||
if (num_parents < 1) { | ||
pr_err("%s: no parent found", clk_name); | ||
return; | ||
} | ||
|
||
divcr = of_iomap(node, 0); | ||
if (divcr == NULL) { | ||
pr_err("%s: failed to map divide register", clk_name); | ||
goto error; | ||
} | ||
|
||
parent_name = of_clk_get_parent_name(node, 0); | ||
of_property_read_u32(node, "renesas,width", &width); | ||
clk = clk_register_divider(NULL, clk_name, parent_name, | ||
CLK_SET_RATE_GATE, divcr, 0, width, | ||
CLK_DIVIDER_POWER_OF_TWO, &clklock); | ||
if (!IS_ERR(clk)) { | ||
of_clk_add_provider(node, of_clk_src_simple_get, clk); | ||
return; | ||
} | ||
pr_err("%s: failed to register %s div clock (%ld)\n", | ||
__func__, clk_name, PTR_ERR(clk)); | ||
error: | ||
if (divcr) | ||
iounmap(divcr); | ||
} | ||
|
||
CLK_OF_DECLARE(h8300_div_clk, "renesas,h8300-div-clock", h8300_div_clk_setup); |
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,147 @@ | ||
/* | ||
* H8S2678 clock driver | ||
* | ||
* Copyright 2015 Yoshinori Sato <[email protected]> | ||
*/ | ||
|
||
#include <linux/clk.h> | ||
#include <linux/clkdev.h> | ||
#include <linux/clk-provider.h> | ||
#include <linux/err.h> | ||
#include <linux/device.h> | ||
#include <linux/of_address.h> | ||
|
||
static DEFINE_SPINLOCK(clklock); | ||
|
||
#define MAX_FREQ 33333333 | ||
#define MIN_FREQ 8000000 | ||
|
||
struct pll_clock { | ||
struct clk_hw hw; | ||
void __iomem *sckcr; | ||
void __iomem *pllcr; | ||
}; | ||
|
||
#define to_pll_clock(_hw) container_of(_hw, struct pll_clock, hw) | ||
|
||
static unsigned long pll_recalc_rate(struct clk_hw *hw, | ||
unsigned long parent_rate) | ||
{ | ||
struct pll_clock *pll_clock = to_pll_clock(hw); | ||
int mul = 1 << (ctrl_inb((unsigned long)pll_clock->pllcr) & 3); | ||
|
||
return parent_rate * mul; | ||
} | ||
|
||
static long pll_round_rate(struct clk_hw *hw, unsigned long rate, | ||
unsigned long *prate) | ||
{ | ||
int i, m = -1; | ||
long offset[3]; | ||
|
||
if (rate > MAX_FREQ) | ||
rate = MAX_FREQ; | ||
if (rate < MIN_FREQ) | ||
rate = MIN_FREQ; | ||
|
||
for (i = 0; i < 3; i++) | ||
offset[i] = abs(rate - (*prate * (1 << i))); | ||
for (i = 0; i < 3; i++) | ||
if (m < 0) | ||
m = i; | ||
else | ||
m = (offset[i] < offset[m])?i:m; | ||
|
||
return *prate * (1 << m); | ||
} | ||
|
||
static int pll_set_rate(struct clk_hw *hw, unsigned long rate, | ||
unsigned long parent_rate) | ||
{ | ||
int pll; | ||
unsigned char val; | ||
unsigned long flags; | ||
struct pll_clock *pll_clock = to_pll_clock(hw); | ||
|
||
pll = ((rate / parent_rate) / 2) & 0x03; | ||
spin_lock_irqsave(&clklock, flags); | ||
val = ctrl_inb((unsigned long)pll_clock->sckcr); | ||
val |= 0x08; | ||
ctrl_outb(val, (unsigned long)pll_clock->sckcr); | ||
val = ctrl_inb((unsigned long)pll_clock->pllcr); | ||
val &= ~0x03; | ||
val |= pll; | ||
ctrl_outb(val, (unsigned long)pll_clock->pllcr); | ||
spin_unlock_irqrestore(&clklock, flags); | ||
return 0; | ||
} | ||
|
||
static const struct clk_ops pll_ops = { | ||
.recalc_rate = pll_recalc_rate, | ||
.round_rate = pll_round_rate, | ||
.set_rate = pll_set_rate, | ||
}; | ||
|
||
static void __init h8s2678_pll_clk_setup(struct device_node *node) | ||
{ | ||
unsigned int num_parents; | ||
struct clk *clk; | ||
const char *clk_name = node->name; | ||
const char *parent_name; | ||
struct pll_clock *pll_clock; | ||
struct clk_init_data init; | ||
|
||
num_parents = of_clk_get_parent_count(node); | ||
if (num_parents < 1) { | ||
pr_err("%s: no parent found", clk_name); | ||
return; | ||
} | ||
|
||
|
||
pll_clock = kzalloc(sizeof(struct pll_clock), GFP_KERNEL); | ||
if (!pll_clock) { | ||
pr_err("%s: failed to alloc memory", clk_name); | ||
return; | ||
} | ||
|
||
pll_clock->sckcr = of_iomap(node, 0); | ||
if (pll_clock->sckcr == NULL) { | ||
pr_err("%s: failed to map divide register", clk_name); | ||
goto error; | ||
} | ||
|
||
pll_clock->pllcr = of_iomap(node, 1); | ||
if (pll_clock->pllcr == NULL) { | ||
pr_err("%s: failed to map multiply register", clk_name); | ||
goto error; | ||
} | ||
|
||
parent_name = of_clk_get_parent_name(node, 0); | ||
init.name = clk_name; | ||
init.ops = &pll_ops; | ||
init.flags = CLK_IS_BASIC; | ||
init.parent_names = &parent_name; | ||
init.num_parents = 1; | ||
pll_clock->hw.init = &init; | ||
|
||
clk = clk_register(NULL, &pll_clock->hw); | ||
if (IS_ERR(clk)) | ||
kfree(pll_clock); | ||
if (!IS_ERR(clk)) { | ||
of_clk_add_provider(node, of_clk_src_simple_get, clk); | ||
return; | ||
} | ||
pr_err("%s: failed to register %s div clock (%ld)\n", | ||
__func__, clk_name, PTR_ERR(clk)); | ||
error: | ||
if (pll_clock) { | ||
if (pll_clock->sckcr) | ||
iounmap(pll_clock->sckcr); | ||
if (pll_clock->pllcr) | ||
iounmap(pll_clock->pllcr); | ||
kfree(pll_clock); | ||
} | ||
} | ||
|
||
CLK_OF_DECLARE(h8s2678_div_clk, "renesas,h8s2678-pll-clock", | ||
h8s2678_pll_clk_setup); |