Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 8f4b4794 authored by Ludovic Desroches's avatar Ludovic Desroches Committed by Nicolas Ferre
Browse files

ARM: at91: introduce SAMA5 support



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: default avatarLudovic Desroches <ludovic.desroches@atmel.com>
Signed-off-by: default avatarJean-Christophe PLAGNIOL-VILLARD <plagnioj@jcrosoft.com>
Signed-off-by: default avatarNicolas Ferre <nicolas.ferre@atmel.com>
parent 8f0cdcc5
Loading
Loading
Loading
Loading
+33 −0
Original line number Diff line number Diff line
@@ -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
@@ -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"
@@ -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"
+4 −0
Original line number Diff line number Diff line
@@ -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
@@ -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

+86 −0
Original line number Diff line number Diff line
/*
 *  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
+84 −25
Original line number Diff line number Diff line
@@ -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() \
@@ -75,7 +78,8 @@ 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())
@@ -83,18 +87,22 @@ EXPORT_SYMBOL_GPL(at91_pmc_base);
/* 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);
@@ -210,11 +218,27 @@ struct clk mck = {

static void pmc_periph_mode(struct clk *clk, int is_on)
{
	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)
{
@@ -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));
@@ -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)
		} 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)
			}
		} 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 : "");
	}
@@ -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)) {
@@ -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;
@@ -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;
@@ -872,6 +920,7 @@ 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;

@@ -879,8 +928,17 @@ static int __init at91_clock_reset(void)
		if (clk->users > 0)
			continue;

		if (clk->mode == pmc_periph_mode)
		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;
@@ -888,8 +946,9 @@ static int __init at91_clock_reset(void)
		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;
}
+2 −0
Original line number Diff line number Diff line
@@ -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 */
Loading