Skip to content

Commit

Permalink
clk: at91: add a driver for the h32mx clock
Browse files Browse the repository at this point in the history
Newer SoCs have two different AHB interconnect. The AHB 32 bits Matrix
interconnect (h32mx) has a clock that can be setup at the half of the h64mx
clock (which is mck). The h32mx clock can not exceed 90 MHz.

Signed-off-by: Alexandre Belloni <alexandre.belloni@free-electrons.com>
Acked-by: Boris Brezillon <boris.brezillon@free-electrons.com>
Signed-off-by: Nicolas Ferre <nicolas.ferre@atmel.com>
  • Loading branch information
alexandrebelloni authored and Nicolas Ferre committed Sep 22, 2014
1 parent 5db722e commit bcc5fd4
Show file tree
Hide file tree
Showing 7 changed files with 153 additions and 0 deletions.
14 changes: 14 additions & 0 deletions Documentation/devicetree/bindings/clock/at91-clock.txt
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,9 @@ Required properties:
"atmel,at91sam9x5-clk-utmi":
at91 utmi clock

"atmel,sama5d4-clk-h32mx":
at91 h32mx clock

Required properties for SCKC node:
- reg : defines the IO memory reserved for the SCKC.
- #size-cells : shall be 0 (reg is used to encode clk id).
Expand Down Expand Up @@ -447,3 +450,14 @@ For example:
#clock-cells = <0>;
clocks = <&main>;
};

Required properties for 32 bits bus Matrix clock (h32mx clock):
- #clock-cells : from common clock binding; shall be set to 0.
- clocks : shall be the master clock source phandle.

For example:
h32ck: h32mxck {
#clock-cells = <0>;
compatible = "atmel,sama5d4-clk-h32mx";
clocks = <&mck>;
};
3 changes: 3 additions & 0 deletions arch/arm/mach-at91/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,9 @@ config AT91_SAM9_TIME
config HAVE_AT91_SMD
bool

config HAVE_AT91_H32MX
bool

config SOC_AT91SAM9
bool
select AT91_SAM9_TIME
Expand Down
1 change: 1 addition & 0 deletions drivers/clk/at91/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -9,3 +9,4 @@ obj-y += clk-system.o clk-peripheral.o clk-programmable.o
obj-$(CONFIG_HAVE_AT91_UTMI) += clk-utmi.o
obj-$(CONFIG_HAVE_AT91_USB_CLK) += clk-usb.o
obj-$(CONFIG_HAVE_AT91_SMD) += clk-smd.o
obj-$(CONFIG_HAVE_AT91_H32MX) += clk-h32mx.o
123 changes: 123 additions & 0 deletions drivers/clk/at91/clk-h32mx.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
/*
* clk-h32mx.c
*
* Copyright (C) 2014 Atmel
*
* Alexandre Belloni <alexandre.belloni@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/clkdev.h>
#include <linux/clk/at91_pmc.h>
#include <linux/delay.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/of_irq.h>
#include <linux/io.h>
#include <linux/interrupt.h>
#include <linux/irq.h>
#include <linux/sched.h>
#include <linux/wait.h>

#include "pmc.h"

#define H32MX_MAX_FREQ 90000000

struct clk_sama5d4_h32mx {
struct clk_hw hw;
struct at91_pmc *pmc;
};

#define to_clk_sama5d4_h32mx(hw) container_of(hw, struct clk_sama5d4_h32mx, hw)

static unsigned long clk_sama5d4_h32mx_recalc_rate(struct clk_hw *hw,
unsigned long parent_rate)
{
struct clk_sama5d4_h32mx *h32mxclk = to_clk_sama5d4_h32mx(hw);

if (pmc_read(h32mxclk->pmc, AT91_PMC_MCKR) & AT91_PMC_H32MXDIV)
return parent_rate / 2;

if (parent_rate > H32MX_MAX_FREQ)
pr_warn("H32MX clock is too fast\n");
return parent_rate;
}

static long clk_sama5d4_h32mx_round_rate(struct clk_hw *hw, unsigned long rate,
unsigned long *parent_rate)
{
unsigned long div;

if (rate > *parent_rate)
return *parent_rate;
div = *parent_rate / 2;
if (rate < div)
return div;

if (rate - div < *parent_rate - rate)
return div;

return *parent_rate;
}

static int clk_sama5d4_h32mx_set_rate(struct clk_hw *hw, unsigned long rate,
unsigned long parent_rate)
{
struct clk_sama5d4_h32mx *h32mxclk = to_clk_sama5d4_h32mx(hw);
struct at91_pmc *pmc = h32mxclk->pmc;
u32 tmp;

if (parent_rate != rate && (parent_rate / 2) != rate)
return -EINVAL;

pmc_lock(pmc);
tmp = pmc_read(pmc, AT91_PMC_MCKR) & ~AT91_PMC_H32MXDIV;
if ((parent_rate / 2) == rate)
tmp |= AT91_PMC_H32MXDIV;
pmc_write(pmc, AT91_PMC_MCKR, tmp);
pmc_unlock(pmc);

return 0;
}

static const struct clk_ops h32mx_ops = {
.recalc_rate = clk_sama5d4_h32mx_recalc_rate,
.round_rate = clk_sama5d4_h32mx_round_rate,
.set_rate = clk_sama5d4_h32mx_set_rate,
};

void __init of_sama5d4_clk_h32mx_setup(struct device_node *np,
struct at91_pmc *pmc)
{
struct clk_sama5d4_h32mx *h32mxclk;
struct clk_init_data init;
const char *parent_name;
struct clk *clk;

h32mxclk = kzalloc(sizeof(*h32mxclk), GFP_KERNEL);
if (!h32mxclk)
return;

parent_name = of_clk_get_parent_name(np, 0);

init.name = np->name;
init.ops = &h32mx_ops;
init.parent_names = parent_name ? &parent_name : NULL;
init.num_parents = parent_name ? 1 : 0;
init.flags = CLK_SET_RATE_GATE;

h32mxclk->hw.init = &init;
h32mxclk->pmc = pmc;

clk = clk_register(NULL, &h32mxclk->hw);
if (!clk)
return;

of_clk_add_provider(np, of_clk_src_simple_get, clk);
}
6 changes: 6 additions & 0 deletions drivers/clk/at91/pmc.c
Original file line number Diff line number Diff line change
Expand Up @@ -336,6 +336,12 @@ static const struct of_device_id pmc_clk_ids[] __initconst = {
.compatible = "atmel,at91sam9x5-clk-smd",
.data = of_at91sam9x5_clk_smd_setup,
},
#endif
#if defined(CONFIG_HAVE_AT91_H32MX)
{
.compatible = "atmel,sama5d4-clk-h32mx",
.data = of_sama5d4_clk_h32mx_setup,
},
#endif
{ /*sentinel*/ }
};
Expand Down
5 changes: 5 additions & 0 deletions drivers/clk/at91/pmc.h
Original file line number Diff line number Diff line change
Expand Up @@ -120,4 +120,9 @@ extern void __init of_at91sam9x5_clk_smd_setup(struct device_node *np,
struct at91_pmc *pmc);
#endif

#if defined(CONFIG_HAVE_AT91_SMD)
extern void __init of_sama5d4_clk_h32mx_setup(struct device_node *np,
struct at91_pmc *pmc);
#endif

#endif /* __PMC_H_ */
1 change: 1 addition & 0 deletions include/linux/clk/at91_pmc.h
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,7 @@ extern void __iomem *at91_pmc_base;
#define AT91_PMC_PLLADIV2 (1 << 12) /* PLLA divisor by 2 [some SAM9 only] */
#define AT91_PMC_PLLADIV2_OFF (0 << 12)
#define AT91_PMC_PLLADIV2_ON (1 << 12)
#define AT91_PMC_H32MXDIV BIT(24)

#define AT91_PMC_USB 0x38 /* USB Clock Register [some SAM9 only] */
#define AT91_PMC_USBS (0x1 << 0) /* USB OHCI Input clock selection */
Expand Down

0 comments on commit bcc5fd4

Please sign in to comment.