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 Original line Diff line number Diff line
@@ -29,6 +29,14 @@ config SOC_AT91SAM9
	select MULTI_IRQ_HANDLER
	select MULTI_IRQ_HANDLER
	select SPARSE_IRQ
	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"
menu "Atmel AT91 System-on-Chip"


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


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

endchoice
endchoice


comment "Atmel AT91 Processor"
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
if SOC_SAM_V4_V5
config SOC_AT91RM9200
config SOC_AT91RM9200
	bool "AT91RM9200"
	bool "AT91RM9200"
@@ -134,6 +159,14 @@ config MACH_AT91SAM9_DT
	  Select this if you want to experiment device-tree with
	  Select this if you want to experiment device-tree with
	  an Atmel Evaluation Kit.
	  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"
comment "AT91 Feature Selections"
+4 −0
Original line number Original line Diff line number Diff line
@@ -22,6 +22,7 @@ obj-$(CONFIG_SOC_AT91SAM9G45) += at91sam9g45.o
obj-$(CONFIG_SOC_AT91SAM9N12)	+= at91sam9n12.o
obj-$(CONFIG_SOC_AT91SAM9N12)	+= at91sam9n12.o
obj-$(CONFIG_SOC_AT91SAM9X5)	+= at91sam9x5.o
obj-$(CONFIG_SOC_AT91SAM9X5)	+= at91sam9x5.o
obj-$(CONFIG_SOC_AT91SAM9RL)	+= at91sam9rl.o
obj-$(CONFIG_SOC_AT91SAM9RL)	+= at91sam9rl.o
obj-$(CONFIG_SOC_SAMA5D3)	+= sama5d3.o


obj-$(CONFIG_ARCH_AT91RM9200)	+= at91rm9200_devices.o
obj-$(CONFIG_ARCH_AT91RM9200)	+= at91rm9200_devices.o
obj-$(CONFIG_ARCH_AT91SAM9260)	+= at91sam9260_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_AT91RM9200_DT) += board-dt-rm9200.o
obj-$(CONFIG_MACH_AT91SAM9_DT) += board-dt-sam9.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
# AT91X40 board-specific support
obj-$(CONFIG_MACH_AT91EB01)	+= board-eb01.o
obj-$(CONFIG_MACH_AT91EB01)	+= board-eb01.o


+86 −0
Original line number Original line 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 Original line Diff line number Diff line
@@ -54,7 +54,10 @@ EXPORT_SYMBOL_GPL(at91_pmc_base);
 */
 */
#define cpu_has_utmi()		(  cpu_is_at91sam9rl() \
#define cpu_has_utmi()		(  cpu_is_at91sam9rl() \
				|| cpu_is_at91sam9g45() \
				|| 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() \
#define cpu_has_800M_plla()	(  cpu_is_at91sam9g20() \
				|| cpu_is_at91sam9g45() \
				|| cpu_is_at91sam9g45() \
@@ -75,7 +78,8 @@ EXPORT_SYMBOL_GPL(at91_pmc_base);
				|| cpu_is_at91sam9n12()))
				|| cpu_is_at91sam9n12()))


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


/* USB host HS & FS */
/* USB host HS & FS */
#define cpu_has_uhp()		(!cpu_is_at91sam9rl())
#define cpu_has_uhp()		(!cpu_is_at91sam9rl())
@@ -83,18 +87,22 @@ EXPORT_SYMBOL_GPL(at91_pmc_base);
/* USB device FS only */
/* USB device FS only */
#define cpu_has_udpfs()		(!(cpu_is_at91sam9rl() \
#define cpu_has_udpfs()		(!(cpu_is_at91sam9rl() \
				|| cpu_is_at91sam9g45() \
				|| cpu_is_at91sam9g45() \
				|| cpu_is_at91sam9x5()))
				|| cpu_is_at91sam9x5() \
				|| cpu_is_sama5d3()))


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


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


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


static LIST_HEAD(clocks);
static LIST_HEAD(clocks);
static DEFINE_SPINLOCK(clk_lock);
static DEFINE_SPINLOCK(clk_lock);
@@ -210,11 +218,27 @@ struct clk mck = {


static void pmc_periph_mode(struct clk *clk, int is_on)
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)
		if (is_on)
			at91_pmc_write(AT91_PMC_PCER, clk->pmc_mask);
			at91_pmc_write(AT91_PMC_PCER, clk->pmc_mask);
		else
		else
			at91_pmc_write(AT91_PMC_PCDR, clk->pmc_mask);
			at91_pmc_write(AT91_PMC_PCDR, clk->pmc_mask);
	}
	}
}


static struct clk __init *at91_css_to_clk(unsigned long css)
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)
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;
	struct clk	*clk;


	scsr = at91_pmc_read(AT91_PMC_SCSR);
	scsr = at91_pmc_read(AT91_PMC_SCSR);
	pcsr = at91_pmc_read(AT91_PMC_PCSR);
	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);
	sr = at91_pmc_read(AT91_PMC_SR);
	seq_printf(s, "SCSR = %8x\n", scsr);
	seq_printf(s, "SCSR = %8x\n", scsr);
	seq_printf(s, "PCSR = %8x\n", pcsr);
	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, "MOR  = %8x\n", at91_pmc_read(AT91_CKGR_MOR));
	seq_printf(s, "MCFR = %8x\n", at91_pmc_read(AT91_CKGR_MCFR));
	seq_printf(s, "MCFR = %8x\n", at91_pmc_read(AT91_CKGR_MCFR));
	seq_printf(s, "PLLA = %8x\n", at91_pmc_read(AT91_CKGR_PLLAR));
	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) {
	list_for_each_entry(clk, &clocks, node) {
		char	*state;
		char	*state;


		if (clk->mode == pmc_sys_mode)
		if (clk->mode == pmc_sys_mode) {
			state = (scsr & clk->pmc_mask) ? "on" : "off";
			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";
				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";
			state = (uckr & clk->pmc_mask) ? "on" : "off";
		else if (clk->pmc_mask)
		} else if (clk->pmc_mask) {
			state = (sr & clk->pmc_mask) ? "on" : "off";
			state = (sr & clk->pmc_mask) ? "on" : "off";
		else if (clk == &clk32k || clk == &main_clk)
		} else if (clk == &clk32k || clk == &main_clk) {
			state = "on";
			state = "on";
		else
		} else {
			state = "";
			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->name, clk->users, state, clk_get_rate(clk),
			clk->parent ? clk->parent->name : "");
			clk->parent ? clk->parent->name : "");
	}
	}
@@ -530,6 +568,9 @@ int __init clk_register(struct clk *clk)
	if (clk_is_peripheral(clk)) {
	if (clk_is_peripheral(clk)) {
		if (!clk->parent)
		if (!clk->parent)
			clk->parent = &mck;
			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;
		clk->mode = pmc_periph_mode;
	}
	}
	else if (clk_is_sys(clk)) {
	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;
	unsigned mul, div;


	div = reg & 0xff;
	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) {
	if (div && mul) {
		freq /= div;
		freq /= div;
		freq *= mul + 1;
		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 */
	/* report if PLLA is more than mildly overclocked */
	plla.rate_hz = at91_pll_rate(&plla, main_clock, at91_pmc_read(AT91_CKGR_PLLAR));
	plla.rate_hz = at91_pll_rate(&plla, main_clock, at91_pmc_read(AT91_CKGR_PLLAR));
	if (cpu_has_300M_plla()) {
	if (cpu_has_1056M_plla()) {
		if (plla.rate_hz > 300000000)
		if (plla.rate_hz > 1056000000)
			pll_overclock = true;
			pll_overclock = true;
	} else if (cpu_has_800M_plla()) {
	} else if (cpu_has_800M_plla()) {
		if (plla.rate_hz > 800000000)
		if (plla.rate_hz > 800000000)
			pll_overclock = true;
			pll_overclock = true;
	} else if (cpu_has_300M_plla()) {
		if (plla.rate_hz > 300000000)
			pll_overclock = true;
	} else if (cpu_has_240M_plla()) {
	} else if (cpu_has_240M_plla()) {
		if (plla.rate_hz > 240000000)
		if (plla.rate_hz > 240000000)
			pll_overclock = true;
			pll_overclock = true;
@@ -872,6 +920,7 @@ int __init at91_clock_init(unsigned long main_clock)
static int __init at91_clock_reset(void)
static int __init at91_clock_reset(void)
{
{
	unsigned long pcdr = 0;
	unsigned long pcdr = 0;
	unsigned long pcdr1 = 0;
	unsigned long scdr = 0;
	unsigned long scdr = 0;
	struct clk *clk;
	struct clk *clk;


@@ -879,8 +928,17 @@ static int __init at91_clock_reset(void)
		if (clk->users > 0)
		if (clk->users > 0)
			continue;
			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;
				pcdr |= clk->pmc_mask;
		}


		if (clk->mode == pmc_sys_mode)
		if (clk->mode == pmc_sys_mode)
			scdr |= clk->pmc_mask;
			scdr |= clk->pmc_mask;
@@ -888,8 +946,9 @@ static int __init at91_clock_reset(void)
		pr_debug("Clocks: disable unused %s\n", clk->name);
		pr_debug("Clocks: disable unused %s\n", clk->name);
	}
	}


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


	return 0;
	return 0;
}
}
+2 −0
Original line number Original line Diff line number Diff line
@@ -20,7 +20,9 @@ struct clk {
	const char	*name;		/* unique clock name */
	const char	*name;		/* unique clock name */
	struct clk_lookup cl;
	struct clk_lookup cl;
	unsigned long	rate_hz;
	unsigned long	rate_hz;
	unsigned	div;		/* parent clock divider */
	struct clk	*parent;
	struct clk	*parent;
	unsigned	pid;		/* peripheral ID */
	u32		pmc_mask;
	u32		pmc_mask;
	void		(*mode)(struct clk *, int);
	void		(*mode)(struct clk *, int);
	unsigned	id:3;		/* PCK0..4, or 32k/main/a/b */
	unsigned	id:3;		/* PCK0..4, or 32k/main/a/b */
Loading