mirror of https://gitee.com/openkylin/linux.git
Merge branch 'emev2' of git://git.kernel.org/pub/scm/linux/kernel/git/rafael/renesas into next/soc
* 'emev2' of git://git.kernel.org/pub/scm/linux/kernel/git/rafael/renesas: mach-shmobile: Use DT_MACHINE for KZM9D V3 mach-shmobile: Emma Mobile EV2 DT support V3 mach-shmobile: KZM9D board Ethernet support V3 mach-shmobile: Emma Mobile EV2 GPIO support V3 mach-shmobile: Emma Mobile EV2 SMP support V3 mach-shmobile: KZM9D board support V3 mach-shmobile: Emma Mobile EV2 SoC base support V3 gpio: Emma Mobile GPIO driver V2
This commit is contained in:
commit
0804dcb2af
|
@ -0,0 +1,26 @@
|
|||
/*
|
||||
* Device Tree Source for the KZM9D board
|
||||
*
|
||||
* Copyright (C) 2012 Renesas Solutions Corp.
|
||||
*
|
||||
* This file is licensed under the terms of the GNU General Public License
|
||||
* version 2. This program is licensed "as is" without any warranty of any
|
||||
* kind, whether express or implied.
|
||||
*/
|
||||
/dts-v1/;
|
||||
|
||||
/include/ "emev2.dtsi"
|
||||
|
||||
/ {
|
||||
model = "EMEV2 KZM9D Board";
|
||||
compatible = "renesas,kzm9d", "renesas,emev2";
|
||||
|
||||
memory {
|
||||
device_type = "memory";
|
||||
reg = <0x40000000 0x8000000>;
|
||||
};
|
||||
|
||||
chosen {
|
||||
bootargs = "console=ttyS1,115200n81";
|
||||
};
|
||||
};
|
|
@ -0,0 +1,63 @@
|
|||
/*
|
||||
* Device Tree Source for the EMEV2 SoC
|
||||
*
|
||||
* Copyright (C) 2012 Renesas Solutions Corp.
|
||||
*
|
||||
* This file is licensed under the terms of the GNU General Public License
|
||||
* version 2. This program is licensed "as is" without any warranty of any
|
||||
* kind, whether express or implied.
|
||||
*/
|
||||
|
||||
/include/ "skeleton.dtsi"
|
||||
|
||||
/ {
|
||||
compatible = "renesas,emev2";
|
||||
interrupt-parent = <&gic>;
|
||||
|
||||
cpus {
|
||||
cpu@0 {
|
||||
compatible = "arm,cortex-a9";
|
||||
};
|
||||
cpu@1 {
|
||||
compatible = "arm,cortex-a9";
|
||||
};
|
||||
};
|
||||
|
||||
gic: interrupt-controller@e0020000 {
|
||||
compatible = "arm,cortex-a9-gic";
|
||||
interrupt-controller;
|
||||
#interrupt-cells = <3>;
|
||||
reg = <0xe0028000 0x1000>,
|
||||
<0xe0020000 0x0100>;
|
||||
};
|
||||
|
||||
sti@e0180000 {
|
||||
compatible = "renesas,em-sti";
|
||||
reg = <0xe0180000 0x54>;
|
||||
interrupts = <0 125 0>;
|
||||
};
|
||||
|
||||
uart@e1020000 {
|
||||
compatible = "renesas,em-uart";
|
||||
reg = <0xe1020000 0x38>;
|
||||
interrupts = <0 8 0>;
|
||||
};
|
||||
|
||||
uart@e1030000 {
|
||||
compatible = "renesas,em-uart";
|
||||
reg = <0xe1030000 0x38>;
|
||||
interrupts = <0 9 0>;
|
||||
};
|
||||
|
||||
uart@e1040000 {
|
||||
compatible = "renesas,em-uart";
|
||||
reg = <0xe1040000 0x38>;
|
||||
interrupts = <0 10 0>;
|
||||
};
|
||||
|
||||
uart@e1050000 {
|
||||
compatible = "renesas,em-uart";
|
||||
reg = <0xe1050000 0x38>;
|
||||
interrupts = <0 11 0>;
|
||||
};
|
||||
};
|
|
@ -41,6 +41,12 @@ config ARCH_R8A7779
|
|||
select ARM_GIC
|
||||
select ARCH_WANT_OPTIONAL_GPIOLIB
|
||||
|
||||
config ARCH_EMEV2
|
||||
bool "Emma Mobile EV2"
|
||||
select CPU_V7
|
||||
select ARM_GIC
|
||||
select ARCH_WANT_OPTIONAL_GPIOLIB
|
||||
|
||||
comment "SH-Mobile Board Type"
|
||||
|
||||
config MACH_G3EVM
|
||||
|
@ -98,6 +104,11 @@ config MACH_MARZEN
|
|||
depends on ARCH_R8A7779
|
||||
select ARCH_REQUIRE_GPIOLIB
|
||||
|
||||
config MACH_KZM9D
|
||||
bool "KZM9D board"
|
||||
depends on ARCH_EMEV2
|
||||
select USE_OF
|
||||
|
||||
comment "SH-Mobile System Configuration"
|
||||
|
||||
config CPU_HAS_INTEVT
|
||||
|
|
|
@ -12,12 +12,14 @@ obj-$(CONFIG_ARCH_SH7372) += setup-sh7372.o clock-sh7372.o intc-sh7372.o
|
|||
obj-$(CONFIG_ARCH_SH73A0) += setup-sh73a0.o clock-sh73a0.o intc-sh73a0.o
|
||||
obj-$(CONFIG_ARCH_R8A7740) += setup-r8a7740.o clock-r8a7740.o intc-r8a7740.o
|
||||
obj-$(CONFIG_ARCH_R8A7779) += setup-r8a7779.o clock-r8a7779.o intc-r8a7779.o
|
||||
obj-$(CONFIG_ARCH_EMEV2) += setup-emev2.o clock-emev2.o
|
||||
|
||||
# SMP objects
|
||||
smp-y := platsmp.o headsmp.o
|
||||
smp-$(CONFIG_HOTPLUG_CPU) += hotplug.o
|
||||
smp-$(CONFIG_ARCH_SH73A0) += smp-sh73a0.o
|
||||
smp-$(CONFIG_ARCH_R8A7779) += smp-r8a7779.o
|
||||
smp-$(CONFIG_ARCH_EMEV2) += smp-emev2.o
|
||||
|
||||
# Pinmux setup
|
||||
pfc-y :=
|
||||
|
@ -49,6 +51,7 @@ obj-$(CONFIG_MACH_MACKEREL) += board-mackerel.o
|
|||
obj-$(CONFIG_MACH_KOTA2) += board-kota2.o
|
||||
obj-$(CONFIG_MACH_BONITO) += board-bonito.o
|
||||
obj-$(CONFIG_MACH_MARZEN) += board-marzen.o
|
||||
obj-$(CONFIG_MACH_KZM9D) += board-kzm9d.o
|
||||
|
||||
# Framework support
|
||||
obj-$(CONFIG_SMP) += $(smp-y)
|
||||
|
|
|
@ -0,0 +1,85 @@
|
|||
/*
|
||||
* kzm9d board support
|
||||
*
|
||||
* Copyright (C) 2012 Renesas Solutions Corp.
|
||||
* Copyright (C) 2012 Magnus Damm
|
||||
*
|
||||
* 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; version 2 of the License.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/smsc911x.h>
|
||||
#include <mach/common.h>
|
||||
#include <mach/emev2.h>
|
||||
#include <asm/mach-types.h>
|
||||
#include <asm/mach/arch.h>
|
||||
#include <asm/hardware/gic.h>
|
||||
|
||||
/* Ether */
|
||||
static struct resource smsc911x_resources[] = {
|
||||
[0] = {
|
||||
.start = 0x20000000,
|
||||
.end = 0x2000ffff,
|
||||
.flags = IORESOURCE_MEM,
|
||||
},
|
||||
[1] = {
|
||||
.start = EMEV2_GPIO_IRQ(1),
|
||||
.flags = IORESOURCE_IRQ | IRQF_TRIGGER_HIGH,
|
||||
},
|
||||
};
|
||||
|
||||
static struct smsc911x_platform_config smsc911x_platdata = {
|
||||
.flags = SMSC911X_USE_32BIT,
|
||||
.irq_type = SMSC911X_IRQ_TYPE_PUSH_PULL,
|
||||
.irq_polarity = SMSC911X_IRQ_POLARITY_ACTIVE_HIGH,
|
||||
};
|
||||
|
||||
static struct platform_device smsc91x_device = {
|
||||
.name = "smsc911x",
|
||||
.id = 0,
|
||||
.dev = {
|
||||
.platform_data = &smsc911x_platdata,
|
||||
},
|
||||
.num_resources = ARRAY_SIZE(smsc911x_resources),
|
||||
.resource = smsc911x_resources,
|
||||
};
|
||||
|
||||
static struct platform_device *kzm9d_devices[] __initdata = {
|
||||
&smsc91x_device,
|
||||
};
|
||||
|
||||
void __init kzm9d_add_standard_devices(void)
|
||||
{
|
||||
emev2_add_standard_devices();
|
||||
|
||||
platform_add_devices(kzm9d_devices, ARRAY_SIZE(kzm9d_devices));
|
||||
}
|
||||
|
||||
static const char *kzm9d_boards_compat_dt[] __initdata = {
|
||||
"renesas,kzm9d",
|
||||
NULL,
|
||||
};
|
||||
|
||||
DT_MACHINE_START(KZM9D_DT, "kzm9d")
|
||||
.map_io = emev2_map_io,
|
||||
.init_early = emev2_add_early_devices,
|
||||
.nr_irqs = NR_IRQS_LEGACY,
|
||||
.init_irq = emev2_init_irq,
|
||||
.handle_irq = gic_handle_irq,
|
||||
.init_machine = kzm9d_add_standard_devices,
|
||||
.timer = &shmobile_timer,
|
||||
.dt_compat = kzm9d_boards_compat_dt,
|
||||
MACHINE_END
|
|
@ -0,0 +1,249 @@
|
|||
/*
|
||||
* Emma Mobile EV2 clock framework support
|
||||
*
|
||||
* Copyright (C) 2012 Magnus Damm
|
||||
*
|
||||
* 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; version 2 of the License.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
#include <linux/init.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/sh_clk.h>
|
||||
#include <linux/clkdev.h>
|
||||
#include <mach/common.h>
|
||||
|
||||
#define EMEV2_SMU_BASE 0xe0110000
|
||||
|
||||
/* EMEV2 SMU registers */
|
||||
#define USIAU0_RSTCTRL 0x094
|
||||
#define USIBU1_RSTCTRL 0x0ac
|
||||
#define USIBU2_RSTCTRL 0x0b0
|
||||
#define USIBU3_RSTCTRL 0x0b4
|
||||
#define STI_RSTCTRL 0x124
|
||||
#define USIAU0GCLKCTRL 0x4a0
|
||||
#define USIBU1GCLKCTRL 0x4b8
|
||||
#define USIBU2GCLKCTRL 0x4bc
|
||||
#define USIBU3GCLKCTRL 0x04c0
|
||||
#define STIGCLKCTRL 0x528
|
||||
#define USIAU0SCLKDIV 0x61c
|
||||
#define USIB2SCLKDIV 0x65c
|
||||
#define USIB3SCLKDIV 0x660
|
||||
#define STI_CLKSEL 0x688
|
||||
#define SMU_GENERAL_REG0 0x7c0
|
||||
|
||||
/* not pretty, but hey */
|
||||
static void __iomem *smu_base;
|
||||
|
||||
static void emev2_smu_write(unsigned long value, int offs)
|
||||
{
|
||||
BUG_ON(!smu_base || (offs >= PAGE_SIZE));
|
||||
iowrite32(value, smu_base + offs);
|
||||
}
|
||||
|
||||
void emev2_set_boot_vector(unsigned long value)
|
||||
{
|
||||
emev2_smu_write(value, SMU_GENERAL_REG0);
|
||||
}
|
||||
|
||||
static struct clk_mapping smu_mapping = {
|
||||
.phys = EMEV2_SMU_BASE,
|
||||
.len = PAGE_SIZE,
|
||||
};
|
||||
|
||||
/* Fixed 32 KHz root clock from C32K pin */
|
||||
static struct clk c32k_clk = {
|
||||
.rate = 32768,
|
||||
.mapping = &smu_mapping,
|
||||
};
|
||||
|
||||
/* PLL3 multiplies C32K with 7000 */
|
||||
static unsigned long pll3_recalc(struct clk *clk)
|
||||
{
|
||||
return clk->parent->rate * 7000;
|
||||
}
|
||||
|
||||
static struct sh_clk_ops pll3_clk_ops = {
|
||||
.recalc = pll3_recalc,
|
||||
};
|
||||
|
||||
static struct clk pll3_clk = {
|
||||
.ops = &pll3_clk_ops,
|
||||
.parent = &c32k_clk,
|
||||
};
|
||||
|
||||
static struct clk *main_clks[] = {
|
||||
&c32k_clk,
|
||||
&pll3_clk,
|
||||
};
|
||||
|
||||
enum { SCLKDIV_USIAU0, SCLKDIV_USIBU2, SCLKDIV_USIBU1, SCLKDIV_USIBU3,
|
||||
SCLKDIV_NR };
|
||||
|
||||
#define SCLKDIV(_reg, _shift) \
|
||||
{ \
|
||||
.parent = &pll3_clk, \
|
||||
.enable_reg = IOMEM(EMEV2_SMU_BASE + (_reg)), \
|
||||
.enable_bit = _shift, \
|
||||
}
|
||||
|
||||
static struct clk sclkdiv_clks[SCLKDIV_NR] = {
|
||||
[SCLKDIV_USIAU0] = SCLKDIV(USIAU0SCLKDIV, 0),
|
||||
[SCLKDIV_USIBU2] = SCLKDIV(USIB2SCLKDIV, 16),
|
||||
[SCLKDIV_USIBU1] = SCLKDIV(USIB2SCLKDIV, 0),
|
||||
[SCLKDIV_USIBU3] = SCLKDIV(USIB3SCLKDIV, 0),
|
||||
};
|
||||
|
||||
enum { GCLK_USIAU0_SCLK, GCLK_USIBU1_SCLK, GCLK_USIBU2_SCLK, GCLK_USIBU3_SCLK,
|
||||
GCLK_STI_SCLK,
|
||||
GCLK_NR };
|
||||
|
||||
#define GCLK_SCLK(_parent, _reg) \
|
||||
{ \
|
||||
.parent = _parent, \
|
||||
.enable_reg = IOMEM(EMEV2_SMU_BASE + (_reg)), \
|
||||
.enable_bit = 1, /* SCLK_GCC */ \
|
||||
}
|
||||
|
||||
static struct clk gclk_clks[GCLK_NR] = {
|
||||
[GCLK_USIAU0_SCLK] = GCLK_SCLK(&sclkdiv_clks[SCLKDIV_USIAU0],
|
||||
USIAU0GCLKCTRL),
|
||||
[GCLK_USIBU1_SCLK] = GCLK_SCLK(&sclkdiv_clks[SCLKDIV_USIBU1],
|
||||
USIBU1GCLKCTRL),
|
||||
[GCLK_USIBU2_SCLK] = GCLK_SCLK(&sclkdiv_clks[SCLKDIV_USIBU2],
|
||||
USIBU2GCLKCTRL),
|
||||
[GCLK_USIBU3_SCLK] = GCLK_SCLK(&sclkdiv_clks[SCLKDIV_USIBU3],
|
||||
USIBU3GCLKCTRL),
|
||||
[GCLK_STI_SCLK] = GCLK_SCLK(&c32k_clk, STIGCLKCTRL),
|
||||
};
|
||||
|
||||
static int emev2_gclk_enable(struct clk *clk)
|
||||
{
|
||||
iowrite32(ioread32(clk->mapped_reg) | (1 << clk->enable_bit),
|
||||
clk->mapped_reg);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void emev2_gclk_disable(struct clk *clk)
|
||||
{
|
||||
iowrite32(ioread32(clk->mapped_reg) & ~(1 << clk->enable_bit),
|
||||
clk->mapped_reg);
|
||||
}
|
||||
|
||||
static struct sh_clk_ops emev2_gclk_clk_ops = {
|
||||
.enable = emev2_gclk_enable,
|
||||
.disable = emev2_gclk_disable,
|
||||
.recalc = followparent_recalc,
|
||||
};
|
||||
|
||||
static int __init emev2_gclk_register(struct clk *clks, int nr)
|
||||
{
|
||||
struct clk *clkp;
|
||||
int ret = 0;
|
||||
int k;
|
||||
|
||||
for (k = 0; !ret && (k < nr); k++) {
|
||||
clkp = clks + k;
|
||||
clkp->ops = &emev2_gclk_clk_ops;
|
||||
ret |= clk_register(clkp);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static unsigned long emev2_sclkdiv_recalc(struct clk *clk)
|
||||
{
|
||||
unsigned int sclk_div;
|
||||
|
||||
sclk_div = (ioread32(clk->mapped_reg) >> clk->enable_bit) & 0xff;
|
||||
|
||||
return clk->parent->rate / (sclk_div + 1);
|
||||
}
|
||||
|
||||
static struct sh_clk_ops emev2_sclkdiv_clk_ops = {
|
||||
.recalc = emev2_sclkdiv_recalc,
|
||||
};
|
||||
|
||||
static int __init emev2_sclkdiv_register(struct clk *clks, int nr)
|
||||
{
|
||||
struct clk *clkp;
|
||||
int ret = 0;
|
||||
int k;
|
||||
|
||||
for (k = 0; !ret && (k < nr); k++) {
|
||||
clkp = clks + k;
|
||||
clkp->ops = &emev2_sclkdiv_clk_ops;
|
||||
ret |= clk_register(clkp);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static struct clk_lookup lookups[] = {
|
||||
CLKDEV_DEV_ID("serial8250-em.0", &gclk_clks[GCLK_USIAU0_SCLK]),
|
||||
CLKDEV_DEV_ID("e1020000.uart", &gclk_clks[GCLK_USIAU0_SCLK]),
|
||||
CLKDEV_DEV_ID("serial8250-em.1", &gclk_clks[GCLK_USIBU1_SCLK]),
|
||||
CLKDEV_DEV_ID("e1030000.uart", &gclk_clks[GCLK_USIBU1_SCLK]),
|
||||
CLKDEV_DEV_ID("serial8250-em.2", &gclk_clks[GCLK_USIBU2_SCLK]),
|
||||
CLKDEV_DEV_ID("e1040000.uart", &gclk_clks[GCLK_USIBU2_SCLK]),
|
||||
CLKDEV_DEV_ID("serial8250-em.3", &gclk_clks[GCLK_USIBU3_SCLK]),
|
||||
CLKDEV_DEV_ID("e1050000.uart", &gclk_clks[GCLK_USIBU3_SCLK]),
|
||||
CLKDEV_DEV_ID("em_sti.0", &gclk_clks[GCLK_STI_SCLK]),
|
||||
CLKDEV_DEV_ID("e0180000.sti", &gclk_clks[GCLK_STI_SCLK]),
|
||||
};
|
||||
|
||||
void __init emev2_clock_init(void)
|
||||
{
|
||||
int k, ret = 0;
|
||||
static int is_setup;
|
||||
|
||||
/* yuck, this is ugly as hell, but the non-smp case of clocks
|
||||
* code is now designed to rely on ioremap() instead of static
|
||||
* entity maps. in the case of smp we need access to the SMU
|
||||
* register earlier than ioremap() is actually working without
|
||||
* any static maps. to enable SMP in ugly but with dynamic
|
||||
* mappings we have to call emev2_clock_init() from different
|
||||
* places depending on UP and SMP...
|
||||
*/
|
||||
if (is_setup++)
|
||||
return;
|
||||
|
||||
smu_base = ioremap(EMEV2_SMU_BASE, PAGE_SIZE);
|
||||
BUG_ON(!smu_base);
|
||||
|
||||
/* setup STI timer to run on 37.768 kHz and deassert reset */
|
||||
emev2_smu_write(0, STI_CLKSEL);
|
||||
emev2_smu_write(1, STI_RSTCTRL);
|
||||
|
||||
/* deassert reset for UART0->UART3 */
|
||||
emev2_smu_write(2, USIAU0_RSTCTRL);
|
||||
emev2_smu_write(2, USIBU1_RSTCTRL);
|
||||
emev2_smu_write(2, USIBU2_RSTCTRL);
|
||||
emev2_smu_write(2, USIBU3_RSTCTRL);
|
||||
|
||||
for (k = 0; !ret && (k < ARRAY_SIZE(main_clks)); k++)
|
||||
ret = clk_register(main_clks[k]);
|
||||
|
||||
if (!ret)
|
||||
ret = emev2_sclkdiv_register(sclkdiv_clks, SCLKDIV_NR);
|
||||
|
||||
if (!ret)
|
||||
ret = emev2_gclk_register(gclk_clks, GCLK_NR);
|
||||
|
||||
clkdev_add_table(lookups, ARRAY_SIZE(lookups));
|
||||
|
||||
if (!ret)
|
||||
shmobile_clk_init();
|
||||
else
|
||||
panic("failed to setup emev2 clocks\n");
|
||||
}
|
|
@ -0,0 +1,19 @@
|
|||
#ifndef __ASM_EMEV2_H__
|
||||
#define __ASM_EMEV2_H__
|
||||
|
||||
extern void emev2_map_io(void);
|
||||
extern void emev2_init_irq(void);
|
||||
extern void emev2_add_early_devices(void);
|
||||
extern void emev2_add_standard_devices(void);
|
||||
extern void emev2_clock_init(void);
|
||||
extern void emev2_set_boot_vector(unsigned long value);
|
||||
extern unsigned int emev2_get_core_count(void);
|
||||
extern int emev2_platform_cpu_kill(unsigned int cpu);
|
||||
extern void emev2_secondary_init(unsigned int cpu);
|
||||
extern int emev2_boot_secondary(unsigned int cpu);
|
||||
extern void emev2_smp_prepare_cpus(void);
|
||||
|
||||
#define EMEV2_GPIO_BASE 200
|
||||
#define EMEV2_GPIO_IRQ(n) (EMEV2_GPIO_BASE + (n))
|
||||
|
||||
#endif /* __ASM_EMEV2_H__ */
|
|
@ -16,12 +16,15 @@
|
|||
#include <linux/device.h>
|
||||
#include <linux/smp.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/of.h>
|
||||
#include <asm/hardware/gic.h>
|
||||
#include <asm/mach-types.h>
|
||||
#include <mach/common.h>
|
||||
#include <mach/emev2.h>
|
||||
|
||||
#define is_sh73a0() (machine_is_ag5evm() || machine_is_kota2())
|
||||
#define is_r8a7779() machine_is_marzen()
|
||||
#define is_emev2() of_machine_is_compatible("renesas,emev2")
|
||||
|
||||
static unsigned int __init shmobile_smp_get_core_count(void)
|
||||
{
|
||||
|
@ -31,6 +34,9 @@ static unsigned int __init shmobile_smp_get_core_count(void)
|
|||
if (is_r8a7779())
|
||||
return r8a7779_get_core_count();
|
||||
|
||||
if (is_emev2())
|
||||
return emev2_get_core_count();
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
@ -41,6 +47,9 @@ static void __init shmobile_smp_prepare_cpus(void)
|
|||
|
||||
if (is_r8a7779())
|
||||
r8a7779_smp_prepare_cpus();
|
||||
|
||||
if (is_emev2())
|
||||
emev2_smp_prepare_cpus();
|
||||
}
|
||||
|
||||
int shmobile_platform_cpu_kill(unsigned int cpu)
|
||||
|
@ -48,6 +57,9 @@ int shmobile_platform_cpu_kill(unsigned int cpu)
|
|||
if (is_r8a7779())
|
||||
return r8a7779_platform_cpu_kill(cpu);
|
||||
|
||||
if (is_emev2())
|
||||
return emev2_platform_cpu_kill(cpu);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
@ -60,6 +72,9 @@ void __cpuinit platform_secondary_init(unsigned int cpu)
|
|||
|
||||
if (is_r8a7779())
|
||||
r8a7779_secondary_init(cpu);
|
||||
|
||||
if (is_emev2())
|
||||
emev2_secondary_init(cpu);
|
||||
}
|
||||
|
||||
int __cpuinit boot_secondary(unsigned int cpu, struct task_struct *idle)
|
||||
|
@ -70,6 +85,9 @@ int __cpuinit boot_secondary(unsigned int cpu, struct task_struct *idle)
|
|||
if (is_r8a7779())
|
||||
return r8a7779_boot_secondary(cpu);
|
||||
|
||||
if (is_emev2())
|
||||
return emev2_boot_secondary(cpu);
|
||||
|
||||
return -ENOSYS;
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,452 @@
|
|||
/*
|
||||
* Emma Mobile EV2 processor support
|
||||
*
|
||||
* Copyright (C) 2012 Magnus Damm
|
||||
*
|
||||
* 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; version 2 of the License.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/irq.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/platform_data/gpio-em.h>
|
||||
#include <linux/of_platform.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/input.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/of_irq.h>
|
||||
#include <mach/hardware.h>
|
||||
#include <mach/common.h>
|
||||
#include <mach/emev2.h>
|
||||
#include <mach/irqs.h>
|
||||
#include <asm/mach-types.h>
|
||||
#include <asm/mach/arch.h>
|
||||
#include <asm/mach/map.h>
|
||||
#include <asm/mach/time.h>
|
||||
#include <asm/hardware/gic.h>
|
||||
|
||||
static struct map_desc emev2_io_desc[] __initdata = {
|
||||
#ifdef CONFIG_SMP
|
||||
/* 128K entity map for 0xe0100000 (SMU) */
|
||||
{
|
||||
.virtual = 0xe0100000,
|
||||
.pfn = __phys_to_pfn(0xe0100000),
|
||||
.length = SZ_128K,
|
||||
.type = MT_DEVICE
|
||||
},
|
||||
/* 2M mapping for SCU + L2 controller */
|
||||
{
|
||||
.virtual = 0xf0000000,
|
||||
.pfn = __phys_to_pfn(0x1e000000),
|
||||
.length = SZ_2M,
|
||||
.type = MT_DEVICE
|
||||
},
|
||||
#endif
|
||||
};
|
||||
|
||||
void __init emev2_map_io(void)
|
||||
{
|
||||
iotable_init(emev2_io_desc, ARRAY_SIZE(emev2_io_desc));
|
||||
}
|
||||
|
||||
/* UART */
|
||||
static struct resource uart0_resources[] = {
|
||||
[0] = {
|
||||
.start = 0xe1020000,
|
||||
.end = 0xe1020037,
|
||||
.flags = IORESOURCE_MEM,
|
||||
},
|
||||
[1] = {
|
||||
.start = 40,
|
||||
.flags = IORESOURCE_IRQ,
|
||||
}
|
||||
};
|
||||
|
||||
static struct platform_device uart0_device = {
|
||||
.name = "serial8250-em",
|
||||
.id = 0,
|
||||
.num_resources = ARRAY_SIZE(uart0_resources),
|
||||
.resource = uart0_resources,
|
||||
};
|
||||
|
||||
static struct resource uart1_resources[] = {
|
||||
[0] = {
|
||||
.start = 0xe1030000,
|
||||
.end = 0xe1030037,
|
||||
.flags = IORESOURCE_MEM,
|
||||
},
|
||||
[1] = {
|
||||
.start = 41,
|
||||
.flags = IORESOURCE_IRQ,
|
||||
}
|
||||
};
|
||||
|
||||
static struct platform_device uart1_device = {
|
||||
.name = "serial8250-em",
|
||||
.id = 1,
|
||||
.num_resources = ARRAY_SIZE(uart1_resources),
|
||||
.resource = uart1_resources,
|
||||
};
|
||||
|
||||
static struct resource uart2_resources[] = {
|
||||
[0] = {
|
||||
.start = 0xe1040000,
|
||||
.end = 0xe1040037,
|
||||
.flags = IORESOURCE_MEM,
|
||||
},
|
||||
[1] = {
|
||||
.start = 42,
|
||||
.flags = IORESOURCE_IRQ,
|
||||
}
|
||||
};
|
||||
|
||||
static struct platform_device uart2_device = {
|
||||
.name = "serial8250-em",
|
||||
.id = 2,
|
||||
.num_resources = ARRAY_SIZE(uart2_resources),
|
||||
.resource = uart2_resources,
|
||||
};
|
||||
|
||||
static struct resource uart3_resources[] = {
|
||||
[0] = {
|
||||
.start = 0xe1050000,
|
||||
.end = 0xe1050037,
|
||||
.flags = IORESOURCE_MEM,
|
||||
},
|
||||
[1] = {
|
||||
.start = 43,
|
||||
.flags = IORESOURCE_IRQ,
|
||||
}
|
||||
};
|
||||
|
||||
static struct platform_device uart3_device = {
|
||||
.name = "serial8250-em",
|
||||
.id = 3,
|
||||
.num_resources = ARRAY_SIZE(uart3_resources),
|
||||
.resource = uart3_resources,
|
||||
};
|
||||
|
||||
/* STI */
|
||||
static struct resource sti_resources[] = {
|
||||
[0] = {
|
||||
.name = "STI",
|
||||
.start = 0xe0180000,
|
||||
.end = 0xe0180053,
|
||||
.flags = IORESOURCE_MEM,
|
||||
},
|
||||
[1] = {
|
||||
.start = 157,
|
||||
.flags = IORESOURCE_IRQ,
|
||||
},
|
||||
};
|
||||
|
||||
static struct platform_device sti_device = {
|
||||
.name = "em_sti",
|
||||
.id = 0,
|
||||
.resource = sti_resources,
|
||||
.num_resources = ARRAY_SIZE(sti_resources),
|
||||
};
|
||||
|
||||
|
||||
/* GIO */
|
||||
static struct gpio_em_config gio0_config = {
|
||||
.gpio_base = 0,
|
||||
.irq_base = EMEV2_GPIO_IRQ(0),
|
||||
.number_of_pins = 32,
|
||||
};
|
||||
|
||||
static struct resource gio0_resources[] = {
|
||||
[0] = {
|
||||
.name = "GIO_000",
|
||||
.start = 0xe0050000,
|
||||
.end = 0xe005002b,
|
||||
.flags = IORESOURCE_MEM,
|
||||
},
|
||||
[1] = {
|
||||
.name = "GIO_000",
|
||||
.start = 0xe0050040,
|
||||
.end = 0xe005005f,
|
||||
.flags = IORESOURCE_MEM,
|
||||
},
|
||||
[2] = {
|
||||
.start = 99,
|
||||
.flags = IORESOURCE_IRQ,
|
||||
},
|
||||
[3] = {
|
||||
.start = 100,
|
||||
.flags = IORESOURCE_IRQ,
|
||||
},
|
||||
};
|
||||
|
||||
static struct platform_device gio0_device = {
|
||||
.name = "em_gio",
|
||||
.id = 0,
|
||||
.resource = gio0_resources,
|
||||
.num_resources = ARRAY_SIZE(gio0_resources),
|
||||
.dev = {
|
||||
.platform_data = &gio0_config,
|
||||
},
|
||||
};
|
||||
|
||||
static struct gpio_em_config gio1_config = {
|
||||
.gpio_base = 32,
|
||||
.irq_base = EMEV2_GPIO_IRQ(32),
|
||||
.number_of_pins = 32,
|
||||
};
|
||||
|
||||
static struct resource gio1_resources[] = {
|
||||
[0] = {
|
||||
.name = "GIO_032",
|
||||
.start = 0xe0050080,
|
||||
.end = 0xe00500ab,
|
||||
.flags = IORESOURCE_MEM,
|
||||
},
|
||||
[1] = {
|
||||
.name = "GIO_032",
|
||||
.start = 0xe00500c0,
|
||||
.end = 0xe00500df,
|
||||
.flags = IORESOURCE_MEM,
|
||||
},
|
||||
[2] = {
|
||||
.start = 101,
|
||||
.flags = IORESOURCE_IRQ,
|
||||
},
|
||||
[3] = {
|
||||
.start = 102,
|
||||
.flags = IORESOURCE_IRQ,
|
||||
},
|
||||
};
|
||||
|
||||
static struct platform_device gio1_device = {
|
||||
.name = "em_gio",
|
||||
.id = 1,
|
||||
.resource = gio1_resources,
|
||||
.num_resources = ARRAY_SIZE(gio1_resources),
|
||||
.dev = {
|
||||
.platform_data = &gio1_config,
|
||||
},
|
||||
};
|
||||
|
||||
static struct gpio_em_config gio2_config = {
|
||||
.gpio_base = 64,
|
||||
.irq_base = EMEV2_GPIO_IRQ(64),
|
||||
.number_of_pins = 32,
|
||||
};
|
||||
|
||||
static struct resource gio2_resources[] = {
|
||||
[0] = {
|
||||
.name = "GIO_064",
|
||||
.start = 0xe0050100,
|
||||
.end = 0xe005012b,
|
||||
.flags = IORESOURCE_MEM,
|
||||
},
|
||||
[1] = {
|
||||
.name = "GIO_064",
|
||||
.start = 0xe0050140,
|
||||
.end = 0xe005015f,
|
||||
.flags = IORESOURCE_MEM,
|
||||
},
|
||||
[2] = {
|
||||
.start = 103,
|
||||
.flags = IORESOURCE_IRQ,
|
||||
},
|
||||
[3] = {
|
||||
.start = 104,
|
||||
.flags = IORESOURCE_IRQ,
|
||||
},
|
||||
};
|
||||
|
||||
static struct platform_device gio2_device = {
|
||||
.name = "em_gio",
|
||||
.id = 2,
|
||||
.resource = gio2_resources,
|
||||
.num_resources = ARRAY_SIZE(gio2_resources),
|
||||
.dev = {
|
||||
.platform_data = &gio2_config,
|
||||
},
|
||||
};
|
||||
|
||||
static struct gpio_em_config gio3_config = {
|
||||
.gpio_base = 96,
|
||||
.irq_base = EMEV2_GPIO_IRQ(96),
|
||||
.number_of_pins = 32,
|
||||
};
|
||||
|
||||
static struct resource gio3_resources[] = {
|
||||
[0] = {
|
||||
.name = "GIO_096",
|
||||
.start = 0xe0050100,
|
||||
.end = 0xe005012b,
|
||||
.flags = IORESOURCE_MEM,
|
||||
},
|
||||
[1] = {
|
||||
.name = "GIO_096",
|
||||
.start = 0xe0050140,
|
||||
.end = 0xe005015f,
|
||||
.flags = IORESOURCE_MEM,
|
||||
},
|
||||
[2] = {
|
||||
.start = 105,
|
||||
.flags = IORESOURCE_IRQ,
|
||||
},
|
||||
[3] = {
|
||||
.start = 106,
|
||||
.flags = IORESOURCE_IRQ,
|
||||
},
|
||||
};
|
||||
|
||||
static struct platform_device gio3_device = {
|
||||
.name = "em_gio",
|
||||
.id = 3,
|
||||
.resource = gio3_resources,
|
||||
.num_resources = ARRAY_SIZE(gio3_resources),
|
||||
.dev = {
|
||||
.platform_data = &gio3_config,
|
||||
},
|
||||
};
|
||||
|
||||
static struct gpio_em_config gio4_config = {
|
||||
.gpio_base = 128,
|
||||
.irq_base = EMEV2_GPIO_IRQ(128),
|
||||
.number_of_pins = 31,
|
||||
};
|
||||
|
||||
static struct resource gio4_resources[] = {
|
||||
[0] = {
|
||||
.name = "GIO_128",
|
||||
.start = 0xe0050200,
|
||||
.end = 0xe005022b,
|
||||
.flags = IORESOURCE_MEM,
|
||||
},
|
||||
[1] = {
|
||||
.name = "GIO_128",
|
||||
.start = 0xe0050240,
|
||||
.end = 0xe005025f,
|
||||
.flags = IORESOURCE_MEM,
|
||||
},
|
||||
[2] = {
|
||||
.start = 107,
|
||||
.flags = IORESOURCE_IRQ,
|
||||
},
|
||||
[3] = {
|
||||
.start = 108,
|
||||
.flags = IORESOURCE_IRQ,
|
||||
},
|
||||
};
|
||||
|
||||
static struct platform_device gio4_device = {
|
||||
.name = "em_gio",
|
||||
.id = 4,
|
||||
.resource = gio4_resources,
|
||||
.num_resources = ARRAY_SIZE(gio4_resources),
|
||||
.dev = {
|
||||
.platform_data = &gio4_config,
|
||||
},
|
||||
};
|
||||
|
||||
static struct platform_device *emev2_early_devices[] __initdata = {
|
||||
&uart0_device,
|
||||
&uart1_device,
|
||||
&uart2_device,
|
||||
&uart3_device,
|
||||
};
|
||||
|
||||
static struct platform_device *emev2_late_devices[] __initdata = {
|
||||
&sti_device,
|
||||
&gio0_device,
|
||||
&gio1_device,
|
||||
&gio2_device,
|
||||
&gio3_device,
|
||||
&gio4_device,
|
||||
};
|
||||
|
||||
void __init emev2_add_standard_devices(void)
|
||||
{
|
||||
emev2_clock_init();
|
||||
|
||||
platform_add_devices(emev2_early_devices,
|
||||
ARRAY_SIZE(emev2_early_devices));
|
||||
|
||||
platform_add_devices(emev2_late_devices,
|
||||
ARRAY_SIZE(emev2_late_devices));
|
||||
}
|
||||
|
||||
void __init emev2_init_delay(void)
|
||||
{
|
||||
shmobile_setup_delay(533, 1, 3); /* Cortex-A9 @ 533MHz */
|
||||
}
|
||||
|
||||
void __init emev2_add_early_devices(void)
|
||||
{
|
||||
emev2_init_delay();
|
||||
|
||||
early_platform_add_devices(emev2_early_devices,
|
||||
ARRAY_SIZE(emev2_early_devices));
|
||||
|
||||
/* setup early console here as well */
|
||||
shmobile_setup_console();
|
||||
}
|
||||
|
||||
void __init emev2_init_irq(void)
|
||||
{
|
||||
void __iomem *gic_dist_base;
|
||||
void __iomem *gic_cpu_base;
|
||||
|
||||
/* Static mappings, never released */
|
||||
gic_dist_base = ioremap(0xe0028000, PAGE_SIZE);
|
||||
gic_cpu_base = ioremap(0xe0020000, PAGE_SIZE);
|
||||
BUG_ON(!gic_dist_base || !gic_cpu_base);
|
||||
|
||||
/* Use GIC to handle interrupts */
|
||||
gic_init(0, 29, gic_dist_base, gic_cpu_base);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_USE_OF
|
||||
static const struct of_dev_auxdata emev2_auxdata_lookup[] __initconst = {
|
||||
{ }
|
||||
};
|
||||
|
||||
void __init emev2_add_standard_devices_dt(void)
|
||||
{
|
||||
of_platform_populate(NULL, of_default_bus_match_table,
|
||||
emev2_auxdata_lookup, NULL);
|
||||
}
|
||||
|
||||
static const struct of_device_id emev2_dt_irq_match[] = {
|
||||
{ .compatible = "arm,cortex-a9-gic", .data = gic_of_init, },
|
||||
{},
|
||||
};
|
||||
|
||||
static const char *emev2_boards_compat_dt[] __initdata = {
|
||||
"renesas,emev2",
|
||||
NULL,
|
||||
};
|
||||
|
||||
void __init emev2_init_irq_dt(void)
|
||||
{
|
||||
of_irq_init(emev2_dt_irq_match);
|
||||
}
|
||||
|
||||
DT_MACHINE_START(EMEV2_DT, "Generic Emma Mobile EV2 (Flattened Device Tree)")
|
||||
.init_early = emev2_init_delay,
|
||||
.nr_irqs = NR_IRQS_LEGACY,
|
||||
.init_irq = emev2_init_irq_dt,
|
||||
.handle_irq = gic_handle_irq,
|
||||
.init_machine = emev2_add_standard_devices_dt,
|
||||
.timer = &shmobile_timer,
|
||||
.dt_compat = emev2_boards_compat_dt,
|
||||
MACHINE_END
|
||||
|
||||
#endif /* CONFIG_USE_OF */
|
|
@ -0,0 +1,97 @@
|
|||
/*
|
||||
* SMP support for Emma Mobile EV2
|
||||
*
|
||||
* Copyright (C) 2012 Renesas Solutions Corp.
|
||||
* Copyright (C) 2012 Magnus Damm
|
||||
*
|
||||
* 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; version 2 of the License.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/smp.h>
|
||||
#include <linux/spinlock.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/delay.h>
|
||||
#include <mach/common.h>
|
||||
#include <mach/emev2.h>
|
||||
#include <asm/smp_plat.h>
|
||||
#include <asm/smp_scu.h>
|
||||
#include <asm/hardware/gic.h>
|
||||
#include <asm/cacheflush.h>
|
||||
|
||||
#define EMEV2_SCU_BASE 0x1e000000
|
||||
|
||||
static DEFINE_SPINLOCK(scu_lock);
|
||||
static void __iomem *scu_base;
|
||||
|
||||
static void modify_scu_cpu_psr(unsigned long set, unsigned long clr)
|
||||
{
|
||||
unsigned long tmp;
|
||||
|
||||
/* we assume this code is running on a different cpu
|
||||
* than the one that is changing coherency setting */
|
||||
spin_lock(&scu_lock);
|
||||
tmp = readl(scu_base + 8);
|
||||
tmp &= ~clr;
|
||||
tmp |= set;
|
||||
writel(tmp, scu_base + 8);
|
||||
spin_unlock(&scu_lock);
|
||||
|
||||
}
|
||||
|
||||
unsigned int __init emev2_get_core_count(void)
|
||||
{
|
||||
if (!scu_base) {
|
||||
scu_base = ioremap(EMEV2_SCU_BASE, PAGE_SIZE);
|
||||
emev2_clock_init(); /* need ioremapped SMU */
|
||||
}
|
||||
|
||||
WARN_ON_ONCE(!scu_base);
|
||||
|
||||
return scu_base ? scu_get_core_count(scu_base) : 1;
|
||||
}
|
||||
|
||||
int emev2_platform_cpu_kill(unsigned int cpu)
|
||||
{
|
||||
return 0; /* not supported yet */
|
||||
}
|
||||
|
||||
void __cpuinit emev2_secondary_init(unsigned int cpu)
|
||||
{
|
||||
gic_secondary_init(0);
|
||||
}
|
||||
|
||||
int __cpuinit emev2_boot_secondary(unsigned int cpu)
|
||||
{
|
||||
cpu = cpu_logical_map(cpu);
|
||||
|
||||
/* enable cache coherency */
|
||||
modify_scu_cpu_psr(0, 3 << (cpu * 8));
|
||||
|
||||
/* Tell ROM loader about our vector (in headsmp.S) */
|
||||
emev2_set_boot_vector(__pa(shmobile_secondary_vector));
|
||||
|
||||
gic_raise_softirq(cpumask_of(cpu), 1);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void __init emev2_smp_prepare_cpus(void)
|
||||
{
|
||||
int cpu = cpu_logical_map(0);
|
||||
|
||||
scu_enable(scu_base);
|
||||
|
||||
/* enable cache coherency on CPU0 */
|
||||
modify_scu_cpu_psr(0, 3 << (cpu * 8));
|
||||
}
|
|
@ -91,6 +91,12 @@ config GPIO_IT8761E
|
|||
help
|
||||
Say yes here to support GPIO functionality of IT8761E super I/O chip.
|
||||
|
||||
config GPIO_EM
|
||||
tristate "Emma Mobile GPIO"
|
||||
depends on ARM
|
||||
help
|
||||
Say yes here to support GPIO on Renesas Emma Mobile SoCs.
|
||||
|
||||
config GPIO_EP93XX
|
||||
def_bool y
|
||||
depends on ARCH_EP93XX
|
||||
|
|
|
@ -15,6 +15,7 @@ obj-$(CONFIG_GPIO_BT8XX) += gpio-bt8xx.o
|
|||
obj-$(CONFIG_GPIO_CS5535) += gpio-cs5535.o
|
||||
obj-$(CONFIG_GPIO_DA9052) += gpio-da9052.o
|
||||
obj-$(CONFIG_ARCH_DAVINCI) += gpio-davinci.o
|
||||
obj-$(CONFIG_GPIO_EM) += gpio-em.o
|
||||
obj-$(CONFIG_GPIO_EP93XX) += gpio-ep93xx.o
|
||||
obj-$(CONFIG_GPIO_GE_FPGA) += gpio-ge.o
|
||||
obj-$(CONFIG_GPIO_IT8761E) += gpio-it8761e.o
|
||||
|
|
|
@ -0,0 +1,418 @@
|
|||
/*
|
||||
* Emma Mobile GPIO Support - GIO
|
||||
*
|
||||
* Copyright (C) 2012 Magnus Damm
|
||||
*
|
||||
* 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
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#include <linux/init.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/spinlock.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/ioport.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/irq.h>
|
||||
#include <linux/irqdomain.h>
|
||||
#include <linux/bitops.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/gpio.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/platform_data/gpio-em.h>
|
||||
|
||||
struct em_gio_priv {
|
||||
void __iomem *base0;
|
||||
void __iomem *base1;
|
||||
unsigned int irq_base;
|
||||
spinlock_t sense_lock;
|
||||
struct platform_device *pdev;
|
||||
struct gpio_chip gpio_chip;
|
||||
struct irq_chip irq_chip;
|
||||
struct irq_domain *irq_domain;
|
||||
};
|
||||
|
||||
#define GIO_E1 0x00
|
||||
#define GIO_E0 0x04
|
||||
#define GIO_EM 0x04
|
||||
#define GIO_OL 0x08
|
||||
#define GIO_OH 0x0c
|
||||
#define GIO_I 0x10
|
||||
#define GIO_IIA 0x14
|
||||
#define GIO_IEN 0x18
|
||||
#define GIO_IDS 0x1c
|
||||
#define GIO_IIM 0x1c
|
||||
#define GIO_RAW 0x20
|
||||
#define GIO_MST 0x24
|
||||
#define GIO_IIR 0x28
|
||||
|
||||
#define GIO_IDT0 0x40
|
||||
#define GIO_IDT1 0x44
|
||||
#define GIO_IDT2 0x48
|
||||
#define GIO_IDT3 0x4c
|
||||
#define GIO_RAWBL 0x50
|
||||
#define GIO_RAWBH 0x54
|
||||
#define GIO_IRBL 0x58
|
||||
#define GIO_IRBH 0x5c
|
||||
|
||||
#define GIO_IDT(n) (GIO_IDT0 + ((n) * 4))
|
||||
|
||||
static inline unsigned long em_gio_read(struct em_gio_priv *p, int offs)
|
||||
{
|
||||
if (offs < GIO_IDT0)
|
||||
return ioread32(p->base0 + offs);
|
||||
else
|
||||
return ioread32(p->base1 + (offs - GIO_IDT0));
|
||||
}
|
||||
|
||||
static inline void em_gio_write(struct em_gio_priv *p, int offs,
|
||||
unsigned long value)
|
||||
{
|
||||
if (offs < GIO_IDT0)
|
||||
iowrite32(value, p->base0 + offs);
|
||||
else
|
||||
iowrite32(value, p->base1 + (offs - GIO_IDT0));
|
||||
}
|
||||
|
||||
static inline struct em_gio_priv *irq_to_priv(struct irq_data *d)
|
||||
{
|
||||
struct irq_chip *chip = irq_data_get_irq_chip(d);
|
||||
return container_of(chip, struct em_gio_priv, irq_chip);
|
||||
}
|
||||
|
||||
static void em_gio_irq_disable(struct irq_data *d)
|
||||
{
|
||||
struct em_gio_priv *p = irq_to_priv(d);
|
||||
|
||||
em_gio_write(p, GIO_IDS, BIT(irqd_to_hwirq(d)));
|
||||
}
|
||||
|
||||
static void em_gio_irq_enable(struct irq_data *d)
|
||||
{
|
||||
struct em_gio_priv *p = irq_to_priv(d);
|
||||
|
||||
em_gio_write(p, GIO_IEN, BIT(irqd_to_hwirq(d)));
|
||||
}
|
||||
|
||||
#define GIO_ASYNC(x) (x + 8)
|
||||
|
||||
static unsigned char em_gio_sense_table[IRQ_TYPE_SENSE_MASK + 1] = {
|
||||
[IRQ_TYPE_EDGE_RISING] = GIO_ASYNC(0x00),
|
||||
[IRQ_TYPE_EDGE_FALLING] = GIO_ASYNC(0x01),
|
||||
[IRQ_TYPE_LEVEL_HIGH] = GIO_ASYNC(0x02),
|
||||
[IRQ_TYPE_LEVEL_LOW] = GIO_ASYNC(0x03),
|
||||
[IRQ_TYPE_EDGE_BOTH] = GIO_ASYNC(0x04),
|
||||
};
|
||||
|
||||
static int em_gio_irq_set_type(struct irq_data *d, unsigned int type)
|
||||
{
|
||||
unsigned char value = em_gio_sense_table[type & IRQ_TYPE_SENSE_MASK];
|
||||
struct em_gio_priv *p = irq_to_priv(d);
|
||||
unsigned int reg, offset, shift;
|
||||
unsigned long flags;
|
||||
unsigned long tmp;
|
||||
|
||||
if (!value)
|
||||
return -EINVAL;
|
||||
|
||||
offset = irqd_to_hwirq(d);
|
||||
|
||||
pr_debug("gio: sense irq = %d, mode = %d\n", offset, value);
|
||||
|
||||
/* 8 x 4 bit fields in 4 IDT registers */
|
||||
reg = GIO_IDT(offset >> 3);
|
||||
shift = (offset & 0x07) << 4;
|
||||
|
||||
spin_lock_irqsave(&p->sense_lock, flags);
|
||||
|
||||
/* disable the interrupt in IIA */
|
||||
tmp = em_gio_read(p, GIO_IIA);
|
||||
tmp &= ~BIT(offset);
|
||||
em_gio_write(p, GIO_IIA, tmp);
|
||||
|
||||
/* change the sense setting in IDT */
|
||||
tmp = em_gio_read(p, reg);
|
||||
tmp &= ~(0xf << shift);
|
||||
tmp |= value << shift;
|
||||
em_gio_write(p, reg, tmp);
|
||||
|
||||
/* clear pending interrupts */
|
||||
em_gio_write(p, GIO_IIR, BIT(offset));
|
||||
|
||||
/* enable the interrupt in IIA */
|
||||
tmp = em_gio_read(p, GIO_IIA);
|
||||
tmp |= BIT(offset);
|
||||
em_gio_write(p, GIO_IIA, tmp);
|
||||
|
||||
spin_unlock_irqrestore(&p->sense_lock, flags);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static irqreturn_t em_gio_irq_handler(int irq, void *dev_id)
|
||||
{
|
||||
struct em_gio_priv *p = dev_id;
|
||||
unsigned long pending;
|
||||
unsigned int offset, irqs_handled = 0;
|
||||
|
||||
while ((pending = em_gio_read(p, GIO_MST))) {
|
||||
offset = __ffs(pending);
|
||||
em_gio_write(p, GIO_IIR, BIT(offset));
|
||||
generic_handle_irq(irq_find_mapping(p->irq_domain, offset));
|
||||
irqs_handled++;
|
||||
}
|
||||
|
||||
return irqs_handled ? IRQ_HANDLED : IRQ_NONE;
|
||||
}
|
||||
|
||||
static inline struct em_gio_priv *gpio_to_priv(struct gpio_chip *chip)
|
||||
{
|
||||
return container_of(chip, struct em_gio_priv, gpio_chip);
|
||||
}
|
||||
|
||||
static int em_gio_direction_input(struct gpio_chip *chip, unsigned offset)
|
||||
{
|
||||
em_gio_write(gpio_to_priv(chip), GIO_E0, BIT(offset));
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int em_gio_get(struct gpio_chip *chip, unsigned offset)
|
||||
{
|
||||
return (int)(em_gio_read(gpio_to_priv(chip), GIO_I) & BIT(offset));
|
||||
}
|
||||
|
||||
static void __em_gio_set(struct gpio_chip *chip, unsigned int reg,
|
||||
unsigned shift, int value)
|
||||
{
|
||||
/* upper 16 bits contains mask and lower 16 actual value */
|
||||
em_gio_write(gpio_to_priv(chip), reg,
|
||||
(1 << (shift + 16)) | (value << shift));
|
||||
}
|
||||
|
||||
static void em_gio_set(struct gpio_chip *chip, unsigned offset, int value)
|
||||
{
|
||||
/* output is split into two registers */
|
||||
if (offset < 16)
|
||||
__em_gio_set(chip, GIO_OL, offset, value);
|
||||
else
|
||||
__em_gio_set(chip, GIO_OH, offset - 16, value);
|
||||
}
|
||||
|
||||
static int em_gio_direction_output(struct gpio_chip *chip, unsigned offset,
|
||||
int value)
|
||||
{
|
||||
/* write GPIO value to output before selecting output mode of pin */
|
||||
em_gio_set(chip, offset, value);
|
||||
em_gio_write(gpio_to_priv(chip), GIO_E1, BIT(offset));
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int em_gio_to_irq(struct gpio_chip *chip, unsigned offset)
|
||||
{
|
||||
return irq_find_mapping(gpio_to_priv(chip)->irq_domain, offset);
|
||||
}
|
||||
|
||||
static int em_gio_irq_domain_map(struct irq_domain *h, unsigned int virq,
|
||||
irq_hw_number_t hw)
|
||||
{
|
||||
struct em_gio_priv *p = h->host_data;
|
||||
|
||||
pr_debug("gio: map hw irq = %d, virq = %d\n", (int)hw, virq);
|
||||
|
||||
irq_set_chip_data(virq, h->host_data);
|
||||
irq_set_chip_and_handler(virq, &p->irq_chip, handle_level_irq);
|
||||
set_irq_flags(virq, IRQF_VALID); /* kill me now */
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct irq_domain_ops em_gio_irq_domain_ops = {
|
||||
.map = em_gio_irq_domain_map,
|
||||
};
|
||||
|
||||
static int __devinit em_gio_irq_domain_init(struct em_gio_priv *p)
|
||||
{
|
||||
struct platform_device *pdev = p->pdev;
|
||||
struct gpio_em_config *pdata = pdev->dev.platform_data;
|
||||
|
||||
p->irq_base = irq_alloc_descs(pdata->irq_base, 0,
|
||||
pdata->number_of_pins, numa_node_id());
|
||||
if (IS_ERR_VALUE(p->irq_base)) {
|
||||
dev_err(&pdev->dev, "cannot get irq_desc\n");
|
||||
return -ENXIO;
|
||||
}
|
||||
pr_debug("gio: hw base = %d, nr = %d, sw base = %d\n",
|
||||
pdata->gpio_base, pdata->number_of_pins, p->irq_base);
|
||||
|
||||
p->irq_domain = irq_domain_add_legacy(pdev->dev.of_node,
|
||||
pdata->number_of_pins,
|
||||
p->irq_base, 0,
|
||||
&em_gio_irq_domain_ops, p);
|
||||
if (!p->irq_domain) {
|
||||
irq_free_descs(p->irq_base, pdata->number_of_pins);
|
||||
return -ENXIO;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void __devexit em_gio_irq_domain_cleanup(struct em_gio_priv *p)
|
||||
{
|
||||
struct gpio_em_config *pdata = p->pdev->dev.platform_data;
|
||||
|
||||
irq_free_descs(p->irq_base, pdata->number_of_pins);
|
||||
/* FIXME: irq domain wants to be freed! */
|
||||
}
|
||||
|
||||
static int __devinit em_gio_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct gpio_em_config *pdata = pdev->dev.platform_data;
|
||||
struct em_gio_priv *p;
|
||||
struct resource *io[2], *irq[2];
|
||||
struct gpio_chip *gpio_chip;
|
||||
struct irq_chip *irq_chip;
|
||||
const char *name = dev_name(&pdev->dev);
|
||||
int ret;
|
||||
|
||||
p = kzalloc(sizeof(*p), GFP_KERNEL);
|
||||
if (!p) {
|
||||
dev_err(&pdev->dev, "failed to allocate driver data\n");
|
||||
ret = -ENOMEM;
|
||||
goto err0;
|
||||
}
|
||||
|
||||
p->pdev = pdev;
|
||||
platform_set_drvdata(pdev, p);
|
||||
spin_lock_init(&p->sense_lock);
|
||||
|
||||
io[0] = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
io[1] = platform_get_resource(pdev, IORESOURCE_MEM, 1);
|
||||
irq[0] = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
|
||||
irq[1] = platform_get_resource(pdev, IORESOURCE_IRQ, 1);
|
||||
|
||||
if (!io[0] || !io[1] || !irq[0] || !irq[1] || !pdata) {
|
||||
dev_err(&pdev->dev, "missing IRQ, IOMEM or configuration\n");
|
||||
ret = -EINVAL;
|
||||
goto err1;
|
||||
}
|
||||
|
||||
p->base0 = ioremap_nocache(io[0]->start, resource_size(io[0]));
|
||||
if (!p->base0) {
|
||||
dev_err(&pdev->dev, "failed to remap low I/O memory\n");
|
||||
ret = -ENXIO;
|
||||
goto err1;
|
||||
}
|
||||
|
||||
p->base1 = ioremap_nocache(io[1]->start, resource_size(io[1]));
|
||||
if (!p->base1) {
|
||||
dev_err(&pdev->dev, "failed to remap high I/O memory\n");
|
||||
ret = -ENXIO;
|
||||
goto err2;
|
||||
}
|
||||
|
||||
gpio_chip = &p->gpio_chip;
|
||||
gpio_chip->direction_input = em_gio_direction_input;
|
||||
gpio_chip->get = em_gio_get;
|
||||
gpio_chip->direction_output = em_gio_direction_output;
|
||||
gpio_chip->set = em_gio_set;
|
||||
gpio_chip->to_irq = em_gio_to_irq;
|
||||
gpio_chip->label = name;
|
||||
gpio_chip->owner = THIS_MODULE;
|
||||
gpio_chip->base = pdata->gpio_base;
|
||||
gpio_chip->ngpio = pdata->number_of_pins;
|
||||
|
||||
irq_chip = &p->irq_chip;
|
||||
irq_chip->name = name;
|
||||
irq_chip->irq_mask = em_gio_irq_disable;
|
||||
irq_chip->irq_unmask = em_gio_irq_enable;
|
||||
irq_chip->irq_enable = em_gio_irq_enable;
|
||||
irq_chip->irq_disable = em_gio_irq_disable;
|
||||
irq_chip->irq_set_type = em_gio_irq_set_type;
|
||||
irq_chip->flags = IRQCHIP_SKIP_SET_WAKE;
|
||||
|
||||
ret = em_gio_irq_domain_init(p);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "cannot initialize irq domain\n");
|
||||
goto err3;
|
||||
}
|
||||
|
||||
if (request_irq(irq[0]->start, em_gio_irq_handler, 0, name, p)) {
|
||||
dev_err(&pdev->dev, "failed to request low IRQ\n");
|
||||
ret = -ENOENT;
|
||||
goto err4;
|
||||
}
|
||||
|
||||
if (request_irq(irq[1]->start, em_gio_irq_handler, 0, name, p)) {
|
||||
dev_err(&pdev->dev, "failed to request high IRQ\n");
|
||||
ret = -ENOENT;
|
||||
goto err5;
|
||||
}
|
||||
|
||||
ret = gpiochip_add(gpio_chip);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "failed to add GPIO controller\n");
|
||||
goto err6;
|
||||
}
|
||||
return 0;
|
||||
|
||||
err6:
|
||||
free_irq(irq[1]->start, pdev);
|
||||
err5:
|
||||
free_irq(irq[0]->start, pdev);
|
||||
err4:
|
||||
em_gio_irq_domain_cleanup(p);
|
||||
err3:
|
||||
iounmap(p->base1);
|
||||
err2:
|
||||
iounmap(p->base0);
|
||||
err1:
|
||||
kfree(p);
|
||||
err0:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int __devexit em_gio_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct em_gio_priv *p = platform_get_drvdata(pdev);
|
||||
struct resource *irq[2];
|
||||
int ret;
|
||||
|
||||
ret = gpiochip_remove(&p->gpio_chip);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
irq[0] = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
|
||||
irq[1] = platform_get_resource(pdev, IORESOURCE_IRQ, 1);
|
||||
|
||||
free_irq(irq[1]->start, pdev);
|
||||
free_irq(irq[0]->start, pdev);
|
||||
em_gio_irq_domain_cleanup(p);
|
||||
iounmap(p->base1);
|
||||
iounmap(p->base0);
|
||||
kfree(p);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct platform_driver em_gio_device_driver = {
|
||||
.probe = em_gio_probe,
|
||||
.remove = __devexit_p(em_gio_remove),
|
||||
.driver = {
|
||||
.name = "em_gio",
|
||||
}
|
||||
};
|
||||
|
||||
module_platform_driver(em_gio_device_driver);
|
||||
|
||||
MODULE_AUTHOR("Magnus Damm");
|
||||
MODULE_DESCRIPTION("Renesas Emma Mobile GIO Driver");
|
||||
MODULE_LICENSE("GPL v2");
|
|
@ -0,0 +1,10 @@
|
|||
#ifndef __GPIO_EM_H__
|
||||
#define __GPIO_EM_H__
|
||||
|
||||
struct gpio_em_config {
|
||||
unsigned int gpio_base;
|
||||
unsigned int irq_base;
|
||||
unsigned int number_of_pins;
|
||||
};
|
||||
|
||||
#endif /* __GPIO_EM_H__ */
|
Loading…
Reference in New Issue