Skip to content

Commit

Permalink
ARM: at91: introduce SAMA5 support
Browse files Browse the repository at this point in the history
This patch introduces the SAMA5 support and a generic board file for SAMA5
devices. It also updates the PMC driver to manage clock division which is a
requirement since some peripherals can't work at the bus frequency on SAMA5.

Signed-off-by: Ludovic Desroches <ludovic.desroches@atmel.com>
Signed-off-by: Jean-Christophe PLAGNIOL-VILLARD <plagnioj@jcrosoft.com>
Signed-off-by: Nicolas Ferre <nicolas.ferre@atmel.com>
  • Loading branch information
Ludovic Desroches authored and Nicolas Ferre committed Mar 26, 2013
1 parent 8f0cdcc commit 8f4b479
Show file tree
Hide file tree
Showing 11 changed files with 725 additions and 29 deletions.
33 changes: 33 additions & 0 deletions arch/arm/mach-at91/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,14 @@ config SOC_AT91SAM9
select MULTI_IRQ_HANDLER
select SPARSE_IRQ

config SOC_SAMA5
bool
select AT91_SAM9_TIME
select CPU_V7
select GENERIC_CLOCKEVENTS
select MULTI_IRQ_HANDLER
select SPARSE_IRQ

menu "Atmel AT91 System-on-Chip"

choice
Expand All @@ -41,10 +49,27 @@ config SOC_SAM_V4_V5
Select this if you are using one of Atmel's AT91SAM9, AT91RM9200
or AT91X40 SoC.

config SOC_SAM_V7
bool "Cortex A5"
help
Select this if you are using one of Atmel's SAMA5D3 SoC.

endchoice

comment "Atmel AT91 Processor"

if SOC_SAM_V7
config SOC_SAMA5D3
bool "SAMA5D3 family"
depends on SOC_SAM_V7
select SOC_SAMA5
select HAVE_FB_ATMEL
select HAVE_AT91_DBGU1
help
Select this if you are using one of Atmel's SAMA5D3 family SoC.
This support covers SAMA5D31, SAMA5D33, SAMA5D34, SAMA5D35.
endif

if SOC_SAM_V4_V5
config SOC_AT91RM9200
bool "AT91RM9200"
Expand Down Expand Up @@ -134,6 +159,14 @@ config MACH_AT91SAM9_DT
Select this if you want to experiment device-tree with
an Atmel Evaluation Kit.

config MACH_SAMA5_DT
bool "Atmel SAMA5 Evaluation Kits with device-tree support"
depends on SOC_SAMA5
select USE_OF
help
Select this if you want to experiment device-tree with
an Atmel Evaluation Kit.

# ----------------------------------------------------------

comment "AT91 Feature Selections"
Expand Down
4 changes: 4 additions & 0 deletions arch/arm/mach-at91/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ obj-$(CONFIG_SOC_AT91SAM9G45) += at91sam9g45.o
obj-$(CONFIG_SOC_AT91SAM9N12) += at91sam9n12.o
obj-$(CONFIG_SOC_AT91SAM9X5) += at91sam9x5.o
obj-$(CONFIG_SOC_AT91SAM9RL) += at91sam9rl.o
obj-$(CONFIG_SOC_SAMA5D3) += sama5d3.o

obj-$(CONFIG_ARCH_AT91RM9200) += at91rm9200_devices.o
obj-$(CONFIG_ARCH_AT91SAM9260) += at91sam9260_devices.o
Expand Down Expand Up @@ -91,6 +92,9 @@ obj-$(CONFIG_MACH_AT91SAM9M10G45EK) += board-sam9m10g45ek.o
obj-$(CONFIG_MACH_AT91RM9200_DT) += board-dt-rm9200.o
obj-$(CONFIG_MACH_AT91SAM9_DT) += board-dt-sam9.o

# SAMA5 board with device-tree
obj-$(CONFIG_MACH_SAMA5_DT) += board-dt-sama5.o

# AT91X40 board-specific support
obj-$(CONFIG_MACH_AT91EB01) += board-eb01.o

Expand Down
86 changes: 86 additions & 0 deletions arch/arm/mach-at91/board-dt-sama5.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
/*
* Setup code for SAMA5 Evaluation Kits with Device Tree support
*
* Copyright (C) 2013 Atmel,
* 2013 Ludovic Desroches <ludovic.desroches@atmel.com>
*
* Licensed under GPLv2 or later.
*/

#include <linux/types.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/gpio.h>
#include <linux/micrel_phy.h>
#include <linux/of.h>
#include <linux/of_irq.h>
#include <linux/of_platform.h>
#include <linux/phy.h>

#include <asm/setup.h>
#include <asm/irq.h>
#include <asm/mach/arch.h>
#include <asm/mach/map.h>
#include <asm/mach/irq.h>

#include "at91_aic.h"
#include "generic.h"


static const struct of_device_id irq_of_match[] __initconst = {

{ .compatible = "atmel,sama5d3-aic", .data = at91_aic5_of_init },
{ /*sentinel*/ }
};

static void __init at91_dt_init_irq(void)
{
of_irq_init(irq_of_match);
}

static int ksz9021rn_phy_fixup(struct phy_device *phy)
{
int value;

#define GMII_RCCPSR 260
#define GMII_RRDPSR 261
#define GMII_ERCR 11
#define GMII_ERDWR 12

/* Set delay values */
value = GMII_RCCPSR | 0x8000;
phy_write(phy, GMII_ERCR, value);
value = 0xF2F4;
phy_write(phy, GMII_ERDWR, value);
value = GMII_RRDPSR | 0x8000;
phy_write(phy, GMII_ERCR, value);
value = 0x2222;
phy_write(phy, GMII_ERDWR, value);

return 0;
}

static void __init sama5_dt_device_init(void)
{
if (of_machine_is_compatible("atmel,sama5d3xcm"))
phy_register_fixup_for_uid(PHY_ID_KSZ9021, MICREL_PHY_ID_MASK,
ksz9021rn_phy_fixup);

of_platform_populate(NULL, of_default_bus_match_table, NULL, NULL);
}

static const char *sama5_dt_board_compat[] __initdata = {
"atmel,sama5",
NULL
};

DT_MACHINE_START(sama5_dt, "Atmel SAMA5 (Device Tree)")
/* Maintainer: Atmel */
.init_time = at91sam926x_pit_init,
.map_io = at91_map_io,
.handle_irq = at91_aic5_handle_irq,
.init_early = at91_dt_initialize,
.init_irq = at91_dt_init_irq,
.init_machine = sama5_dt_device_init,
.dt_compat = sama5_dt_board_compat,
MACHINE_END
109 changes: 84 additions & 25 deletions arch/arm/mach-at91/clock.c
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,10 @@ EXPORT_SYMBOL_GPL(at91_pmc_base);
*/
#define cpu_has_utmi() ( cpu_is_at91sam9rl() \
|| cpu_is_at91sam9g45() \
|| cpu_is_at91sam9x5())
|| cpu_is_at91sam9x5() \
|| cpu_is_sama5d3())

#define cpu_has_1056M_plla() (cpu_is_sama5d3())

#define cpu_has_800M_plla() ( cpu_is_at91sam9g20() \
|| cpu_is_at91sam9g45() \
Expand All @@ -75,26 +78,31 @@ EXPORT_SYMBOL_GPL(at91_pmc_base);
|| cpu_is_at91sam9n12()))

#define cpu_has_upll() (cpu_is_at91sam9g45() \
|| cpu_is_at91sam9x5())
|| cpu_is_at91sam9x5() \
|| cpu_is_sama5d3())

/* USB host HS & FS */
#define cpu_has_uhp() (!cpu_is_at91sam9rl())

/* USB device FS only */
#define cpu_has_udpfs() (!(cpu_is_at91sam9rl() \
|| cpu_is_at91sam9g45() \
|| cpu_is_at91sam9x5()))
|| cpu_is_at91sam9x5() \
|| cpu_is_sama5d3()))

#define cpu_has_plladiv2() (cpu_is_at91sam9g45() \
|| cpu_is_at91sam9x5() \
|| cpu_is_at91sam9n12())
|| cpu_is_at91sam9n12() \
|| cpu_is_sama5d3())

#define cpu_has_mdiv3() (cpu_is_at91sam9g45() \
|| cpu_is_at91sam9x5() \
|| cpu_is_at91sam9n12())
|| cpu_is_at91sam9n12() \
|| cpu_is_sama5d3())

#define cpu_has_alt_prescaler() (cpu_is_at91sam9x5() \
|| cpu_is_at91sam9n12())
|| cpu_is_at91sam9n12() \
|| cpu_is_sama5d3())

static LIST_HEAD(clocks);
static DEFINE_SPINLOCK(clk_lock);
Expand Down Expand Up @@ -210,10 +218,26 @@ struct clk mck = {

static void pmc_periph_mode(struct clk *clk, int is_on)
{
if (is_on)
at91_pmc_write(AT91_PMC_PCER, clk->pmc_mask);
else
at91_pmc_write(AT91_PMC_PCDR, clk->pmc_mask);
u32 regval = 0;

/*
* With sama5d3 devices, we are managing clock division so we have to
* use the Peripheral Control Register introduced from at91sam9x5
* devices.
*/
if (cpu_is_sama5d3()) {
regval |= AT91_PMC_PCR_CMD; /* write command */
regval |= clk->pid & AT91_PMC_PCR_PID; /* peripheral selection */
regval |= AT91_PMC_PCR_DIV(clk->div);
if (is_on)
regval |= AT91_PMC_PCR_EN; /* enable clock */
at91_pmc_write(AT91_PMC_PCR, regval);
} else {
if (is_on)
at91_pmc_write(AT91_PMC_PCER, clk->pmc_mask);
else
at91_pmc_write(AT91_PMC_PCDR, clk->pmc_mask);
}
}

static struct clk __init *at91_css_to_clk(unsigned long css)
Expand Down Expand Up @@ -443,14 +467,18 @@ static void __init init_programmable_clock(struct clk *clk)

static int at91_clk_show(struct seq_file *s, void *unused)
{
u32 scsr, pcsr, uckr = 0, sr;
u32 scsr, pcsr, pcsr1 = 0, uckr = 0, sr;
struct clk *clk;

scsr = at91_pmc_read(AT91_PMC_SCSR);
pcsr = at91_pmc_read(AT91_PMC_PCSR);
if (cpu_is_sama5d3())
pcsr1 = at91_pmc_read(AT91_PMC_PCSR1);
sr = at91_pmc_read(AT91_PMC_SR);
seq_printf(s, "SCSR = %8x\n", scsr);
seq_printf(s, "PCSR = %8x\n", pcsr);
if (cpu_is_sama5d3())
seq_printf(s, "PCSR1 = %8x\n", pcsr1);
seq_printf(s, "MOR = %8x\n", at91_pmc_read(AT91_CKGR_MOR));
seq_printf(s, "MCFR = %8x\n", at91_pmc_read(AT91_CKGR_MCFR));
seq_printf(s, "PLLA = %8x\n", at91_pmc_read(AT91_CKGR_PLLAR));
Expand All @@ -470,20 +498,30 @@ static int at91_clk_show(struct seq_file *s, void *unused)
list_for_each_entry(clk, &clocks, node) {
char *state;

if (clk->mode == pmc_sys_mode)
if (clk->mode == pmc_sys_mode) {
state = (scsr & clk->pmc_mask) ? "on" : "off";
else if (clk->mode == pmc_periph_mode)
state = (pcsr & clk->pmc_mask) ? "on" : "off";
else if (clk->mode == pmc_uckr_mode)
} else if (clk->mode == pmc_periph_mode) {
if (cpu_is_sama5d3()) {
u32 pmc_mask = 1 << (clk->pid % 32);

if (clk->pid > 31)
state = (pcsr1 & pmc_mask) ? "on" : "off";
else
state = (pcsr & pmc_mask) ? "on" : "off";
} else {
state = (pcsr & clk->pmc_mask) ? "on" : "off";
}
} else if (clk->mode == pmc_uckr_mode) {
state = (uckr & clk->pmc_mask) ? "on" : "off";
else if (clk->pmc_mask)
} else if (clk->pmc_mask) {
state = (sr & clk->pmc_mask) ? "on" : "off";
else if (clk == &clk32k || clk == &main_clk)
} else if (clk == &clk32k || clk == &main_clk) {
state = "on";
else
} else {
state = "";
}

seq_printf(s, "%-10s users=%2d %-3s %9ld Hz %s\n",
seq_printf(s, "%-10s users=%2d %-3s %9lu Hz %s\n",
clk->name, clk->users, state, clk_get_rate(clk),
clk->parent ? clk->parent->name : "");
}
Expand Down Expand Up @@ -530,6 +568,9 @@ int __init clk_register(struct clk *clk)
if (clk_is_peripheral(clk)) {
if (!clk->parent)
clk->parent = &mck;
if (cpu_is_sama5d3())
clk->rate_hz = DIV_ROUND_UP(clk->parent->rate_hz,
1 << clk->div);
clk->mode = pmc_periph_mode;
}
else if (clk_is_sys(clk)) {
Expand All @@ -555,7 +596,11 @@ static u32 __init at91_pll_rate(struct clk *pll, u32 freq, u32 reg)
unsigned mul, div;

div = reg & 0xff;
mul = (reg >> 16) & 0x7ff;
if (cpu_is_sama5d3())
mul = AT91_PMC3_MUL_GET(reg);
else
mul = AT91_PMC_MUL_GET(reg);

if (div && mul) {
freq /= div;
freq *= mul + 1;
Expand Down Expand Up @@ -706,12 +751,15 @@ static int __init at91_pmc_init(unsigned long main_clock)

/* report if PLLA is more than mildly overclocked */
plla.rate_hz = at91_pll_rate(&plla, main_clock, at91_pmc_read(AT91_CKGR_PLLAR));
if (cpu_has_300M_plla()) {
if (plla.rate_hz > 300000000)
if (cpu_has_1056M_plla()) {
if (plla.rate_hz > 1056000000)
pll_overclock = true;
} else if (cpu_has_800M_plla()) {
if (plla.rate_hz > 800000000)
pll_overclock = true;
} else if (cpu_has_300M_plla()) {
if (plla.rate_hz > 300000000)
pll_overclock = true;
} else if (cpu_has_240M_plla()) {
if (plla.rate_hz > 240000000)
pll_overclock = true;
Expand Down Expand Up @@ -872,24 +920,35 @@ int __init at91_clock_init(unsigned long main_clock)
static int __init at91_clock_reset(void)
{
unsigned long pcdr = 0;
unsigned long pcdr1 = 0;
unsigned long scdr = 0;
struct clk *clk;

list_for_each_entry(clk, &clocks, node) {
if (clk->users > 0)
continue;

if (clk->mode == pmc_periph_mode)
pcdr |= clk->pmc_mask;
if (clk->mode == pmc_periph_mode) {
if (cpu_is_sama5d3()) {
u32 pmc_mask = 1 << (clk->pid % 32);

if (clk->pid > 31)
pcdr1 |= pmc_mask;
else
pcdr |= pmc_mask;
} else
pcdr |= clk->pmc_mask;
}

if (clk->mode == pmc_sys_mode)
scdr |= clk->pmc_mask;

pr_debug("Clocks: disable unused %s\n", clk->name);
}

at91_pmc_write(AT91_PMC_PCDR, pcdr);
at91_pmc_write(AT91_PMC_SCDR, scdr);
if (cpu_is_sama5d3())
at91_pmc_write(AT91_PMC_PCDR1, pcdr1);

return 0;
}
Expand Down
2 changes: 2 additions & 0 deletions arch/arm/mach-at91/clock.h
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,9 @@ struct clk {
const char *name; /* unique clock name */
struct clk_lookup cl;
unsigned long rate_hz;
unsigned div; /* parent clock divider */
struct clk *parent;
unsigned pid; /* peripheral ID */
u32 pmc_mask;
void (*mode)(struct clk *, int);
unsigned id:3; /* PCK0..4, or 32k/main/a/b */
Expand Down
Loading

0 comments on commit 8f4b479

Please sign in to comment.