mirror of
https://mirrors.bfsu.edu.cn/git/linux.git
synced 2024-11-18 17:54:13 +08:00
Integrator updates for the v3.19 merge cycle on
top of the multiplatform patches, this moves out some drivers and reduced the amount of code carried in arch/arm/mach-integrator. - Move the Integrator/AP timer to drivers/clocksource - Move the restart functionality to the device tree, patches to enable restart for the Integrator have been merged to the reset tree (orthogonal) - Move debug LEDs to device tree (using the syscon LED driver merged for v3.18) - Move core module LEDs to device tree (using the syscon LED driver merged for v3.18) - Move the SoC driver (chip ID etc) to drivers/soc/versatile/soc-integrator.c -----BEGIN PGP SIGNATURE----- Version: GnuPG v1 iQIcBAABAgAGBQJUavcgAAoJEEEQszewGV1zxHgP/3i1LxdQ+B1ztOpZRZ+Bi4iQ 5KU9j93eMMTlZ3nASvRBIx4MgnUVItuUQ5idnmATCDfoP2lhj/JWPw7G5vyEvRoj 2yuTB9IoutGoTPm58UeglPPedwcv2Lc9p7Boz/cnZVc4KfLldU66yoIV+s1vlijN OAhgOyShIjLUv5ikwNPRAjEXw7fJZc6ixhTt/EJrPbiMhjkXMKJs/SXovzhOw25a UVmMu36rwnVyQllfoS4yd+wpHa+Jg6ZhxH3nIveQpEVt8MOukOPiYa5ePmglXpkF B0u0vaACrZfiPCeQL3RD4LN8Z9QLR9Mt1VDbFkWnTRTKdNA034dhIoD0ZxsoGepb cV9QfA1n1Q6hKUqg59PfZv4MKwPXA1TmF5J3ZBNL1xK+bsRBHZmBq1dKg4rww1bu tyxJfg3LmUCQe5PkcT7HZJFv1yXnEXduXUX0TYclPhp+GnAyudwNkw4/7mmz2gPT XEhd64xjB+yVyzN8zu34KIrsDvv/Z7CyKzYAsaR+yY4DbUuh2p2sNupCnci4Hs/k PO09w76mxRulFBVw5ZuLBzevPpa3hasny05bqnku6/VTp1RfOM+5i60B/gGnIWFo KLxaDcvbyuP+nAdQIl+/X8Zv0tQ0KAqlZFvgroxCeUtF5JwXlbdYbbVEO0FVmMB/ TjfE+8xCchI2cZPci9J2 =rplU -----END PGP SIGNATURE----- Merge tag 'integrator-v3.19-arm-soc-2' of git://git.kernel.org/pub/scm/linux/kernel/git/linusw/linux-integrator into next/soc Pull "ARM SoC Integrator updates for v3.19" from Linus Walleij: Integrator updates for the v3.19 merge cycle on top of the multiplatform patches, this moves out some drivers and reduced the amount of code carried in arch/arm/mach-integrator. - Move the Integrator/AP timer to drivers/clocksource - Move the restart functionality to the device tree, patches to enable restart for the Integrator have been merged to the reset tree (orthogonal) - Move debug LEDs to device tree (using the syscon LED driver merged for v3.18) - Move core module LEDs to device tree (using the syscon LED driver merged for v3.18) - Move the SoC driver (chip ID etc) to drivers/soc/versatile/soc-integrator.c * tag 'integrator-v3.19-arm-soc-2' of git://git.kernel.org/pub/scm/linux/kernel/git/linusw/linux-integrator: soc: move SoC driver for the ARM Integrator ARM: integrator: move core module LED to device tree ARM: integrator: move debug LEDs to syscon LED driver ARM: integrator: move restart to the device tree ARM: integrator: move AP timer to clocksource Signed-off-by: Arnd Bergmann <arnd@arndb.de>
This commit is contained in:
commit
4eca459bc1
@ -6,8 +6,18 @@
|
||||
|
||||
/ {
|
||||
core-module@10000000 {
|
||||
compatible = "arm,core-module-integrator";
|
||||
compatible = "arm,core-module-integrator", "syscon";
|
||||
reg = <0x10000000 0x200>;
|
||||
|
||||
/* Use core module LED to indicate CPU load */
|
||||
led@0c.0 {
|
||||
compatible = "register-bit-led";
|
||||
offset = <0x0c>;
|
||||
mask = <0x01>;
|
||||
label = "integrator:core_module";
|
||||
linux,default-trigger = "cpu0";
|
||||
default-state = "on";
|
||||
};
|
||||
};
|
||||
|
||||
ebi@12000000 {
|
||||
@ -82,5 +92,41 @@
|
||||
reg = <0x19000000 0x1000>;
|
||||
interrupts = <4>;
|
||||
};
|
||||
|
||||
syscon {
|
||||
/* Debug registers mapped as syscon */
|
||||
compatible = "syscon";
|
||||
reg = <0x1a000000 0x10>;
|
||||
|
||||
led@04.0 {
|
||||
compatible = "register-bit-led";
|
||||
offset = <0x04>;
|
||||
mask = <0x01>;
|
||||
label = "integrator:green0";
|
||||
linux,default-trigger = "heartbeat";
|
||||
default-state = "on";
|
||||
};
|
||||
led@04.1 {
|
||||
compatible = "register-bit-led";
|
||||
offset = <0x04>;
|
||||
mask = <0x02>;
|
||||
label = "integrator:yellow";
|
||||
default-state = "off";
|
||||
};
|
||||
led@04.2 {
|
||||
compatible = "register-bit-led";
|
||||
offset = <0x04>;
|
||||
mask = <0x04>;
|
||||
label = "integrator:red";
|
||||
default-state = "off";
|
||||
};
|
||||
led@04.3 {
|
||||
compatible = "register-bit-led";
|
||||
offset = <0x04>;
|
||||
mask = <0x08>;
|
||||
label = "integrator:green1";
|
||||
default-state = "off";
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
|
@ -8,8 +8,13 @@ config ARCH_INTEGRATOR
|
||||
select GENERIC_CLOCKEVENTS
|
||||
select HAVE_TCM
|
||||
select ICST
|
||||
select MFD_SYSCON
|
||||
select MULTI_IRQ_HANDLER
|
||||
select PLAT_VERSATILE
|
||||
select POWER_RESET
|
||||
select POWER_RESET_VERSATILE
|
||||
select POWER_SUPPLY
|
||||
select SOC_INTEGRATOR_CM
|
||||
select SPARSE_IRQ
|
||||
select USE_OF
|
||||
select VERSATILE_FPGA_IRQ
|
||||
|
@ -4,7 +4,7 @@
|
||||
|
||||
# Object file lists.
|
||||
|
||||
obj-y := core.o lm.o leds.o
|
||||
obj-y := core.o lm.o
|
||||
obj-$(CONFIG_ARCH_INTEGRATOR_AP) += integrator_ap.o
|
||||
obj-$(CONFIG_ARCH_INTEGRATOR_CP) += integrator_cp.o
|
||||
|
||||
|
@ -11,7 +11,6 @@ void cm_clear_irqs(void);
|
||||
#define CM_CTRL_LED (1 << 0)
|
||||
#define CM_CTRL_nMBDET (1 << 1)
|
||||
#define CM_CTRL_REMAP (1 << 2)
|
||||
#define CM_CTRL_RESET (1 << 3)
|
||||
|
||||
/*
|
||||
* Integrator/AP,PP2 specific
|
||||
|
@ -4,5 +4,3 @@ extern struct amba_pl010_data ap_uart_data;
|
||||
void integrator_init_early(void);
|
||||
int integrator_init(bool is_cp);
|
||||
void integrator_reserve(void);
|
||||
void integrator_restart(enum reboot_mode, const char *);
|
||||
void integrator_init_sysfs(struct device *parent, u32 id);
|
||||
|
@ -60,40 +60,6 @@ void cm_control(u32 mask, u32 set)
|
||||
raw_spin_unlock_irqrestore(&cm_lock, flags);
|
||||
}
|
||||
|
||||
static const char *integrator_arch_str(u32 id)
|
||||
{
|
||||
switch ((id >> 16) & 0xff) {
|
||||
case 0x00:
|
||||
return "ASB little-endian";
|
||||
case 0x01:
|
||||
return "AHB little-endian";
|
||||
case 0x03:
|
||||
return "AHB-Lite system bus, bi-endian";
|
||||
case 0x04:
|
||||
return "AHB";
|
||||
case 0x08:
|
||||
return "AHB system bus, ASB processor bus";
|
||||
default:
|
||||
return "Unknown";
|
||||
}
|
||||
}
|
||||
|
||||
static const char *integrator_fpga_str(u32 id)
|
||||
{
|
||||
switch ((id >> 12) & 0xf) {
|
||||
case 0x01:
|
||||
return "XC4062";
|
||||
case 0x02:
|
||||
return "XC4085";
|
||||
case 0x03:
|
||||
return "XVC600";
|
||||
case 0x04:
|
||||
return "EPM7256AE (Altera PLD)";
|
||||
default:
|
||||
return "Unknown";
|
||||
}
|
||||
}
|
||||
|
||||
void cm_clear_irqs(void)
|
||||
{
|
||||
/* disable core module IRQs */
|
||||
@ -109,7 +75,6 @@ static const struct of_device_id cm_match[] = {
|
||||
void cm_init(void)
|
||||
{
|
||||
struct device_node *cm = of_find_matching_node(NULL, cm_match);
|
||||
u32 val;
|
||||
|
||||
if (!cm) {
|
||||
pr_crit("no core module node found in device tree\n");
|
||||
@ -121,13 +86,6 @@ void cm_init(void)
|
||||
return;
|
||||
}
|
||||
cm_clear_irqs();
|
||||
val = readl(cm_base + INTEGRATOR_HDR_ID_OFFSET);
|
||||
pr_info("Detected ARM core module:\n");
|
||||
pr_info(" Manufacturer: %02x\n", (val >> 24));
|
||||
pr_info(" Architecture: %s\n", integrator_arch_str(val));
|
||||
pr_info(" FPGA: %s\n", integrator_fpga_str(val));
|
||||
pr_info(" Build: %02x\n", (val >> 4) & 0xFF);
|
||||
pr_info(" Rev: %c\n", ('A' + (val & 0x03)));
|
||||
}
|
||||
|
||||
/*
|
||||
@ -139,64 +97,3 @@ void __init integrator_reserve(void)
|
||||
{
|
||||
memblock_reserve(PHYS_OFFSET, __pa(swapper_pg_dir) - PHYS_OFFSET);
|
||||
}
|
||||
|
||||
/*
|
||||
* To reset, we hit the on-board reset register in the system FPGA
|
||||
*/
|
||||
void integrator_restart(enum reboot_mode mode, const char *cmd)
|
||||
{
|
||||
cm_control(CM_CTRL_RESET, CM_CTRL_RESET);
|
||||
}
|
||||
|
||||
static u32 integrator_id;
|
||||
|
||||
static ssize_t intcp_get_manf(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
return sprintf(buf, "%02x\n", integrator_id >> 24);
|
||||
}
|
||||
|
||||
static struct device_attribute intcp_manf_attr =
|
||||
__ATTR(manufacturer, S_IRUGO, intcp_get_manf, NULL);
|
||||
|
||||
static ssize_t intcp_get_arch(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
return sprintf(buf, "%s\n", integrator_arch_str(integrator_id));
|
||||
}
|
||||
|
||||
static struct device_attribute intcp_arch_attr =
|
||||
__ATTR(architecture, S_IRUGO, intcp_get_arch, NULL);
|
||||
|
||||
static ssize_t intcp_get_fpga(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
return sprintf(buf, "%s\n", integrator_fpga_str(integrator_id));
|
||||
}
|
||||
|
||||
static struct device_attribute intcp_fpga_attr =
|
||||
__ATTR(fpga, S_IRUGO, intcp_get_fpga, NULL);
|
||||
|
||||
static ssize_t intcp_get_build(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
return sprintf(buf, "%02x\n", (integrator_id >> 4) & 0xFF);
|
||||
}
|
||||
|
||||
static struct device_attribute intcp_build_attr =
|
||||
__ATTR(build, S_IRUGO, intcp_get_build, NULL);
|
||||
|
||||
|
||||
|
||||
void integrator_init_sysfs(struct device *parent, u32 id)
|
||||
{
|
||||
integrator_id = id;
|
||||
device_create_file(parent, &intcp_manf_attr);
|
||||
device_create_file(parent, &intcp_arch_attr);
|
||||
device_create_file(parent, &intcp_fpga_attr);
|
||||
device_create_file(parent, &intcp_build_attr);
|
||||
}
|
||||
|
@ -27,22 +27,15 @@
|
||||
#include <linux/syscore_ops.h>
|
||||
#include <linux/amba/bus.h>
|
||||
#include <linux/amba/kmi.h>
|
||||
#include <linux/clocksource.h>
|
||||
#include <linux/clockchips.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/irqchip.h>
|
||||
#include <linux/mtd/physmap.h>
|
||||
#include <linux/clk.h>
|
||||
#include <linux/platform_data/clk-integrator.h>
|
||||
#include <linux/of_irq.h>
|
||||
#include <linux/of_address.h>
|
||||
#include <linux/of_platform.h>
|
||||
#include <linux/stat.h>
|
||||
#include <linux/sys_soc.h>
|
||||
#include <linux/termios.h>
|
||||
#include <linux/sched_clock.h>
|
||||
#include <linux/clk-provider.h>
|
||||
|
||||
#include <asm/hardware/arm_timer.h>
|
||||
#include <asm/setup.h>
|
||||
@ -89,11 +82,6 @@ static void __iomem *ebi_base;
|
||||
|
||||
static struct map_desc ap_io_desc[] __initdata __maybe_unused = {
|
||||
{
|
||||
.virtual = IO_ADDRESS(INTEGRATOR_CT_BASE),
|
||||
.pfn = __phys_to_pfn(INTEGRATOR_CT_BASE),
|
||||
.length = SZ_4K,
|
||||
.type = MT_DEVICE
|
||||
}, {
|
||||
.virtual = IO_ADDRESS(INTEGRATOR_IC_BASE),
|
||||
.pfn = __phys_to_pfn(INTEGRATOR_IC_BASE),
|
||||
.length = SZ_4K,
|
||||
@ -257,188 +245,10 @@ struct amba_pl010_data ap_uart_data = {
|
||||
.set_mctrl = integrator_uart_set_mctrl,
|
||||
};
|
||||
|
||||
/*
|
||||
* Where is the timer (VA)?
|
||||
*/
|
||||
#define TIMER0_VA_BASE __io_address(INTEGRATOR_TIMER0_BASE)
|
||||
#define TIMER1_VA_BASE __io_address(INTEGRATOR_TIMER1_BASE)
|
||||
#define TIMER2_VA_BASE __io_address(INTEGRATOR_TIMER2_BASE)
|
||||
|
||||
static unsigned long timer_reload;
|
||||
|
||||
static u64 notrace integrator_read_sched_clock(void)
|
||||
{
|
||||
return -readl((void __iomem *) TIMER2_VA_BASE + TIMER_VALUE);
|
||||
}
|
||||
|
||||
static void integrator_clocksource_init(unsigned long inrate,
|
||||
void __iomem *base)
|
||||
{
|
||||
u32 ctrl = TIMER_CTRL_ENABLE | TIMER_CTRL_PERIODIC;
|
||||
unsigned long rate = inrate;
|
||||
|
||||
if (rate >= 1500000) {
|
||||
rate /= 16;
|
||||
ctrl |= TIMER_CTRL_DIV16;
|
||||
}
|
||||
|
||||
writel(0xffff, base + TIMER_LOAD);
|
||||
writel(ctrl, base + TIMER_CTRL);
|
||||
|
||||
clocksource_mmio_init(base + TIMER_VALUE, "timer2",
|
||||
rate, 200, 16, clocksource_mmio_readl_down);
|
||||
sched_clock_register(integrator_read_sched_clock, 16, rate);
|
||||
}
|
||||
|
||||
static void __iomem * clkevt_base;
|
||||
|
||||
/*
|
||||
* IRQ handler for the timer
|
||||
*/
|
||||
static irqreturn_t integrator_timer_interrupt(int irq, void *dev_id)
|
||||
{
|
||||
struct clock_event_device *evt = dev_id;
|
||||
|
||||
/* clear the interrupt */
|
||||
writel(1, clkevt_base + TIMER_INTCLR);
|
||||
|
||||
evt->event_handler(evt);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static void clkevt_set_mode(enum clock_event_mode mode, struct clock_event_device *evt)
|
||||
{
|
||||
u32 ctrl = readl(clkevt_base + TIMER_CTRL) & ~TIMER_CTRL_ENABLE;
|
||||
|
||||
/* Disable timer */
|
||||
writel(ctrl, clkevt_base + TIMER_CTRL);
|
||||
|
||||
switch (mode) {
|
||||
case CLOCK_EVT_MODE_PERIODIC:
|
||||
/* Enable the timer and start the periodic tick */
|
||||
writel(timer_reload, clkevt_base + TIMER_LOAD);
|
||||
ctrl |= TIMER_CTRL_PERIODIC | TIMER_CTRL_ENABLE;
|
||||
writel(ctrl, clkevt_base + TIMER_CTRL);
|
||||
break;
|
||||
case CLOCK_EVT_MODE_ONESHOT:
|
||||
/* Leave the timer disabled, .set_next_event will enable it */
|
||||
ctrl &= ~TIMER_CTRL_PERIODIC;
|
||||
writel(ctrl, clkevt_base + TIMER_CTRL);
|
||||
break;
|
||||
case CLOCK_EVT_MODE_UNUSED:
|
||||
case CLOCK_EVT_MODE_SHUTDOWN:
|
||||
case CLOCK_EVT_MODE_RESUME:
|
||||
default:
|
||||
/* Just leave in disabled state */
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static int clkevt_set_next_event(unsigned long next, struct clock_event_device *evt)
|
||||
{
|
||||
unsigned long ctrl = readl(clkevt_base + TIMER_CTRL);
|
||||
|
||||
writel(ctrl & ~TIMER_CTRL_ENABLE, clkevt_base + TIMER_CTRL);
|
||||
writel(next, clkevt_base + TIMER_LOAD);
|
||||
writel(ctrl | TIMER_CTRL_ENABLE, clkevt_base + TIMER_CTRL);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct clock_event_device integrator_clockevent = {
|
||||
.name = "timer1",
|
||||
.features = CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_ONESHOT,
|
||||
.set_mode = clkevt_set_mode,
|
||||
.set_next_event = clkevt_set_next_event,
|
||||
.rating = 300,
|
||||
};
|
||||
|
||||
static struct irqaction integrator_timer_irq = {
|
||||
.name = "timer",
|
||||
.flags = IRQF_TIMER | IRQF_IRQPOLL,
|
||||
.handler = integrator_timer_interrupt,
|
||||
.dev_id = &integrator_clockevent,
|
||||
};
|
||||
|
||||
static void integrator_clockevent_init(unsigned long inrate,
|
||||
void __iomem *base, int irq)
|
||||
{
|
||||
unsigned long rate = inrate;
|
||||
unsigned int ctrl = 0;
|
||||
|
||||
clkevt_base = base;
|
||||
/* Calculate and program a divisor */
|
||||
if (rate > 0x100000 * HZ) {
|
||||
rate /= 256;
|
||||
ctrl |= TIMER_CTRL_DIV256;
|
||||
} else if (rate > 0x10000 * HZ) {
|
||||
rate /= 16;
|
||||
ctrl |= TIMER_CTRL_DIV16;
|
||||
}
|
||||
timer_reload = rate / HZ;
|
||||
writel(ctrl, clkevt_base + TIMER_CTRL);
|
||||
|
||||
setup_irq(irq, &integrator_timer_irq);
|
||||
clockevents_config_and_register(&integrator_clockevent,
|
||||
rate,
|
||||
1,
|
||||
0xffffU);
|
||||
}
|
||||
|
||||
void __init ap_init_early(void)
|
||||
{
|
||||
}
|
||||
|
||||
static void __init ap_of_timer_init(void)
|
||||
{
|
||||
struct device_node *node;
|
||||
const char *path;
|
||||
void __iomem *base;
|
||||
int err;
|
||||
int irq;
|
||||
struct clk *clk;
|
||||
unsigned long rate;
|
||||
|
||||
of_clk_init(NULL);
|
||||
|
||||
err = of_property_read_string(of_aliases,
|
||||
"arm,timer-primary", &path);
|
||||
if (WARN_ON(err))
|
||||
return;
|
||||
node = of_find_node_by_path(path);
|
||||
base = of_iomap(node, 0);
|
||||
if (WARN_ON(!base))
|
||||
return;
|
||||
|
||||
clk = of_clk_get(node, 0);
|
||||
BUG_ON(IS_ERR(clk));
|
||||
clk_prepare_enable(clk);
|
||||
rate = clk_get_rate(clk);
|
||||
|
||||
writel(0, base + TIMER_CTRL);
|
||||
integrator_clocksource_init(rate, base);
|
||||
|
||||
err = of_property_read_string(of_aliases,
|
||||
"arm,timer-secondary", &path);
|
||||
if (WARN_ON(err))
|
||||
return;
|
||||
node = of_find_node_by_path(path);
|
||||
base = of_iomap(node, 0);
|
||||
if (WARN_ON(!base))
|
||||
return;
|
||||
irq = irq_of_parse_and_map(node, 0);
|
||||
|
||||
clk = of_clk_get(node, 0);
|
||||
BUG_ON(IS_ERR(clk));
|
||||
clk_prepare_enable(clk);
|
||||
rate = clk_get_rate(clk);
|
||||
|
||||
writel(0, base + TIMER_CTRL);
|
||||
integrator_clockevent_init(rate, base, irq);
|
||||
}
|
||||
|
||||
static void __init ap_init_irq_of(void)
|
||||
{
|
||||
cm_init();
|
||||
@ -477,10 +287,6 @@ static void __init ap_init_of(void)
|
||||
unsigned long sc_dec;
|
||||
struct device_node *syscon;
|
||||
struct device_node *ebi;
|
||||
struct device *parent;
|
||||
struct soc_device *soc_dev;
|
||||
struct soc_device_attribute *soc_dev_attr;
|
||||
u32 ap_sc_id;
|
||||
int i;
|
||||
|
||||
syscon = of_find_matching_node(NULL, ap_syscon_match);
|
||||
@ -500,28 +306,6 @@ static void __init ap_init_of(void)
|
||||
of_platform_populate(NULL, of_default_bus_match_table,
|
||||
ap_auxdata_lookup, NULL);
|
||||
|
||||
ap_sc_id = readl(ap_syscon_base);
|
||||
|
||||
soc_dev_attr = kzalloc(sizeof(*soc_dev_attr), GFP_KERNEL);
|
||||
if (!soc_dev_attr)
|
||||
return;
|
||||
|
||||
soc_dev_attr->soc_id = "XVC";
|
||||
soc_dev_attr->machine = "Integrator/AP";
|
||||
soc_dev_attr->family = "Integrator";
|
||||
soc_dev_attr->revision = kasprintf(GFP_KERNEL, "%c",
|
||||
'A' + (ap_sc_id & 0x0f));
|
||||
|
||||
soc_dev = soc_device_register(soc_dev_attr);
|
||||
if (IS_ERR(soc_dev)) {
|
||||
kfree(soc_dev_attr->revision);
|
||||
kfree(soc_dev_attr);
|
||||
return;
|
||||
}
|
||||
|
||||
parent = soc_device_to_device(soc_dev);
|
||||
integrator_init_sysfs(parent, ap_sc_id);
|
||||
|
||||
sc_dec = readl(ap_syscon_base + INTEGRATOR_SC_DEC_OFFSET);
|
||||
for (i = 0; i < 4; i++) {
|
||||
struct lm_device *lmdev;
|
||||
@ -553,8 +337,6 @@ DT_MACHINE_START(INTEGRATOR_AP_DT, "ARM Integrator/AP (Device Tree)")
|
||||
.map_io = ap_map_io,
|
||||
.init_early = ap_init_early,
|
||||
.init_irq = ap_init_irq_of,
|
||||
.init_time = ap_of_timer_init,
|
||||
.init_machine = ap_init_of,
|
||||
.restart = integrator_restart,
|
||||
.dt_compat = ap_dt_board_compat,
|
||||
MACHINE_END
|
||||
|
@ -27,7 +27,6 @@
|
||||
#include <linux/of_irq.h>
|
||||
#include <linux/of_address.h>
|
||||
#include <linux/of_platform.h>
|
||||
#include <linux/sys_soc.h>
|
||||
#include <linux/sched_clock.h>
|
||||
|
||||
#include <asm/setup.h>
|
||||
@ -274,10 +273,6 @@ static const struct of_device_id intcp_syscon_match[] = {
|
||||
static void __init intcp_init_of(void)
|
||||
{
|
||||
struct device_node *cpcon;
|
||||
struct device *parent;
|
||||
struct soc_device *soc_dev;
|
||||
struct soc_device_attribute *soc_dev_attr;
|
||||
u32 intcp_sc_id;
|
||||
|
||||
cpcon = of_find_matching_node(NULL, intcp_syscon_match);
|
||||
if (!cpcon)
|
||||
@ -289,28 +284,6 @@ static void __init intcp_init_of(void)
|
||||
|
||||
of_platform_populate(NULL, of_default_bus_match_table,
|
||||
intcp_auxdata_lookup, NULL);
|
||||
|
||||
intcp_sc_id = readl(intcp_con_base);
|
||||
|
||||
soc_dev_attr = kzalloc(sizeof(*soc_dev_attr), GFP_KERNEL);
|
||||
if (!soc_dev_attr)
|
||||
return;
|
||||
|
||||
soc_dev_attr->soc_id = "XCV";
|
||||
soc_dev_attr->machine = "Integrator/CP";
|
||||
soc_dev_attr->family = "Integrator";
|
||||
soc_dev_attr->revision = kasprintf(GFP_KERNEL, "%c",
|
||||
'A' + (intcp_sc_id & 0x0f));
|
||||
|
||||
soc_dev = soc_device_register(soc_dev_attr);
|
||||
if (IS_ERR(soc_dev)) {
|
||||
kfree(soc_dev_attr->revision);
|
||||
kfree(soc_dev_attr);
|
||||
return;
|
||||
}
|
||||
|
||||
parent = soc_device_to_device(soc_dev);
|
||||
integrator_init_sysfs(parent, intcp_sc_id);
|
||||
}
|
||||
|
||||
static const char * intcp_dt_board_compat[] = {
|
||||
@ -324,6 +297,5 @@ DT_MACHINE_START(INTEGRATOR_CP_DT, "ARM Integrator/CP (Device Tree)")
|
||||
.init_early = intcp_init_early,
|
||||
.init_irq = intcp_init_irq_of,
|
||||
.init_machine = intcp_init_of,
|
||||
.restart = integrator_restart,
|
||||
.dt_compat = intcp_dt_board_compat,
|
||||
MACHINE_END
|
||||
|
@ -1,124 +0,0 @@
|
||||
/*
|
||||
* Driver for the 4 user LEDs found on the Integrator AP/CP baseboard
|
||||
* Based on Versatile and RealView machine LED code
|
||||
*
|
||||
* License terms: GNU General Public License (GPL) version 2
|
||||
* Author: Bryan Wu <bryan.wu@canonical.com>
|
||||
*/
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/leds.h>
|
||||
|
||||
#include "hardware.h"
|
||||
#include "cm.h"
|
||||
|
||||
#if defined(CONFIG_NEW_LEDS) && defined(CONFIG_LEDS_CLASS)
|
||||
|
||||
#define ALPHA_REG __io_address(INTEGRATOR_DBG_BASE)
|
||||
#define LEDREG (__io_address(INTEGRATOR_DBG_BASE) + INTEGRATOR_DBG_LEDS_OFFSET)
|
||||
|
||||
struct integrator_led {
|
||||
struct led_classdev cdev;
|
||||
u8 mask;
|
||||
};
|
||||
|
||||
/*
|
||||
* The triggers lines up below will only be used if the
|
||||
* LED triggers are compiled in.
|
||||
*/
|
||||
static const struct {
|
||||
const char *name;
|
||||
const char *trigger;
|
||||
} integrator_leds[] = {
|
||||
{ "integrator:green0", "heartbeat", },
|
||||
{ "integrator:yellow", },
|
||||
{ "integrator:red", },
|
||||
{ "integrator:green1", },
|
||||
{ "integrator:core_module", "cpu0", },
|
||||
};
|
||||
|
||||
static void integrator_led_set(struct led_classdev *cdev,
|
||||
enum led_brightness b)
|
||||
{
|
||||
struct integrator_led *led = container_of(cdev,
|
||||
struct integrator_led, cdev);
|
||||
u32 reg = __raw_readl(LEDREG);
|
||||
|
||||
if (b != LED_OFF)
|
||||
reg |= led->mask;
|
||||
else
|
||||
reg &= ~led->mask;
|
||||
|
||||
while (__raw_readl(ALPHA_REG) & 1)
|
||||
cpu_relax();
|
||||
|
||||
__raw_writel(reg, LEDREG);
|
||||
}
|
||||
|
||||
static enum led_brightness integrator_led_get(struct led_classdev *cdev)
|
||||
{
|
||||
struct integrator_led *led = container_of(cdev,
|
||||
struct integrator_led, cdev);
|
||||
u32 reg = __raw_readl(LEDREG);
|
||||
|
||||
return (reg & led->mask) ? LED_FULL : LED_OFF;
|
||||
}
|
||||
|
||||
static void cm_led_set(struct led_classdev *cdev,
|
||||
enum led_brightness b)
|
||||
{
|
||||
if (b != LED_OFF)
|
||||
cm_control(CM_CTRL_LED, CM_CTRL_LED);
|
||||
else
|
||||
cm_control(CM_CTRL_LED, 0);
|
||||
}
|
||||
|
||||
static enum led_brightness cm_led_get(struct led_classdev *cdev)
|
||||
{
|
||||
u32 reg = cm_get();
|
||||
|
||||
return (reg & CM_CTRL_LED) ? LED_FULL : LED_OFF;
|
||||
}
|
||||
|
||||
static int __init integrator_leds_init(void)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(integrator_leds); i++) {
|
||||
struct integrator_led *led;
|
||||
|
||||
led = kzalloc(sizeof(*led), GFP_KERNEL);
|
||||
if (!led)
|
||||
break;
|
||||
|
||||
|
||||
led->cdev.name = integrator_leds[i].name;
|
||||
|
||||
if (i == 4) { /* Setting for LED in core module */
|
||||
led->cdev.brightness_set = cm_led_set;
|
||||
led->cdev.brightness_get = cm_led_get;
|
||||
} else {
|
||||
led->cdev.brightness_set = integrator_led_set;
|
||||
led->cdev.brightness_get = integrator_led_get;
|
||||
}
|
||||
|
||||
led->cdev.default_trigger = integrator_leds[i].trigger;
|
||||
led->mask = BIT(i);
|
||||
|
||||
if (led_classdev_register(NULL, &led->cdev) < 0) {
|
||||
kfree(led);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Since we may have triggers on any subsystem, defer registration
|
||||
* until after subsystem_init.
|
||||
*/
|
||||
fs_initcall(integrator_leds_init);
|
||||
#endif
|
@ -45,4 +45,5 @@ obj-$(CONFIG_ARM_GLOBAL_TIMER) += arm_global_timer.o
|
||||
obj-$(CONFIG_CLKSRC_METAG_GENERIC) += metag_generic.o
|
||||
obj-$(CONFIG_ARCH_HAS_TICK_BROADCAST) += dummy_timer.o
|
||||
obj-$(CONFIG_ARCH_KEYSTONE) += timer-keystone.o
|
||||
obj-$(CONFIG_ARCH_INTEGRATOR_AP) += timer-integrator-ap.o
|
||||
obj-$(CONFIG_CLKSRC_VERSATILE) += versatile.o
|
||||
|
210
drivers/clocksource/timer-integrator-ap.c
Normal file
210
drivers/clocksource/timer-integrator-ap.c
Normal file
@ -0,0 +1,210 @@
|
||||
/*
|
||||
* Integrator/AP timer driver
|
||||
* Copyright (C) 2000-2003 Deep Blue Solutions Ltd
|
||||
* Copyright (c) 2014, Linaro Limited
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* 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/clk.h>
|
||||
#include <linux/clocksource.h>
|
||||
#include <linux/of_irq.h>
|
||||
#include <linux/of_address.h>
|
||||
#include <linux/of_platform.h>
|
||||
#include <linux/clockchips.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/sched_clock.h>
|
||||
#include <asm/hardware/arm_timer.h>
|
||||
|
||||
static void __iomem * sched_clk_base;
|
||||
|
||||
static u64 notrace integrator_read_sched_clock(void)
|
||||
{
|
||||
return -readl(sched_clk_base + TIMER_VALUE);
|
||||
}
|
||||
|
||||
static void integrator_clocksource_init(unsigned long inrate,
|
||||
void __iomem *base)
|
||||
{
|
||||
u32 ctrl = TIMER_CTRL_ENABLE | TIMER_CTRL_PERIODIC;
|
||||
unsigned long rate = inrate;
|
||||
|
||||
if (rate >= 1500000) {
|
||||
rate /= 16;
|
||||
ctrl |= TIMER_CTRL_DIV16;
|
||||
}
|
||||
|
||||
writel(0xffff, base + TIMER_LOAD);
|
||||
writel(ctrl, base + TIMER_CTRL);
|
||||
|
||||
clocksource_mmio_init(base + TIMER_VALUE, "timer2",
|
||||
rate, 200, 16, clocksource_mmio_readl_down);
|
||||
|
||||
sched_clk_base = base;
|
||||
sched_clock_register(integrator_read_sched_clock, 16, rate);
|
||||
}
|
||||
|
||||
static unsigned long timer_reload;
|
||||
static void __iomem * clkevt_base;
|
||||
|
||||
/*
|
||||
* IRQ handler for the timer
|
||||
*/
|
||||
static irqreturn_t integrator_timer_interrupt(int irq, void *dev_id)
|
||||
{
|
||||
struct clock_event_device *evt = dev_id;
|
||||
|
||||
/* clear the interrupt */
|
||||
writel(1, clkevt_base + TIMER_INTCLR);
|
||||
|
||||
evt->event_handler(evt);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static void clkevt_set_mode(enum clock_event_mode mode, struct clock_event_device *evt)
|
||||
{
|
||||
u32 ctrl = readl(clkevt_base + TIMER_CTRL) & ~TIMER_CTRL_ENABLE;
|
||||
|
||||
/* Disable timer */
|
||||
writel(ctrl, clkevt_base + TIMER_CTRL);
|
||||
|
||||
switch (mode) {
|
||||
case CLOCK_EVT_MODE_PERIODIC:
|
||||
/* Enable the timer and start the periodic tick */
|
||||
writel(timer_reload, clkevt_base + TIMER_LOAD);
|
||||
ctrl |= TIMER_CTRL_PERIODIC | TIMER_CTRL_ENABLE;
|
||||
writel(ctrl, clkevt_base + TIMER_CTRL);
|
||||
break;
|
||||
case CLOCK_EVT_MODE_ONESHOT:
|
||||
/* Leave the timer disabled, .set_next_event will enable it */
|
||||
ctrl &= ~TIMER_CTRL_PERIODIC;
|
||||
writel(ctrl, clkevt_base + TIMER_CTRL);
|
||||
break;
|
||||
case CLOCK_EVT_MODE_UNUSED:
|
||||
case CLOCK_EVT_MODE_SHUTDOWN:
|
||||
case CLOCK_EVT_MODE_RESUME:
|
||||
default:
|
||||
/* Just leave in disabled state */
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static int clkevt_set_next_event(unsigned long next, struct clock_event_device *evt)
|
||||
{
|
||||
unsigned long ctrl = readl(clkevt_base + TIMER_CTRL);
|
||||
|
||||
writel(ctrl & ~TIMER_CTRL_ENABLE, clkevt_base + TIMER_CTRL);
|
||||
writel(next, clkevt_base + TIMER_LOAD);
|
||||
writel(ctrl | TIMER_CTRL_ENABLE, clkevt_base + TIMER_CTRL);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct clock_event_device integrator_clockevent = {
|
||||
.name = "timer1",
|
||||
.features = CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_ONESHOT,
|
||||
.set_mode = clkevt_set_mode,
|
||||
.set_next_event = clkevt_set_next_event,
|
||||
.rating = 300,
|
||||
};
|
||||
|
||||
static struct irqaction integrator_timer_irq = {
|
||||
.name = "timer",
|
||||
.flags = IRQF_TIMER | IRQF_IRQPOLL,
|
||||
.handler = integrator_timer_interrupt,
|
||||
.dev_id = &integrator_clockevent,
|
||||
};
|
||||
|
||||
static void integrator_clockevent_init(unsigned long inrate,
|
||||
void __iomem *base, int irq)
|
||||
{
|
||||
unsigned long rate = inrate;
|
||||
unsigned int ctrl = 0;
|
||||
|
||||
clkevt_base = base;
|
||||
/* Calculate and program a divisor */
|
||||
if (rate > 0x100000 * HZ) {
|
||||
rate /= 256;
|
||||
ctrl |= TIMER_CTRL_DIV256;
|
||||
} else if (rate > 0x10000 * HZ) {
|
||||
rate /= 16;
|
||||
ctrl |= TIMER_CTRL_DIV16;
|
||||
}
|
||||
timer_reload = rate / HZ;
|
||||
writel(ctrl, clkevt_base + TIMER_CTRL);
|
||||
|
||||
setup_irq(irq, &integrator_timer_irq);
|
||||
clockevents_config_and_register(&integrator_clockevent,
|
||||
rate,
|
||||
1,
|
||||
0xffffU);
|
||||
}
|
||||
|
||||
static void __init integrator_ap_timer_init_of(struct device_node *node)
|
||||
{
|
||||
const char *path;
|
||||
void __iomem *base;
|
||||
int err;
|
||||
int irq;
|
||||
struct clk *clk;
|
||||
unsigned long rate;
|
||||
struct device_node *pri_node;
|
||||
struct device_node *sec_node;
|
||||
|
||||
base = of_io_request_and_map(node, 0, "integrator-timer");
|
||||
if (!base)
|
||||
return;
|
||||
|
||||
clk = of_clk_get(node, 0);
|
||||
if (IS_ERR(clk)) {
|
||||
pr_err("No clock for %s\n", node->name);
|
||||
return;
|
||||
}
|
||||
clk_prepare_enable(clk);
|
||||
rate = clk_get_rate(clk);
|
||||
writel(0, base + TIMER_CTRL);
|
||||
|
||||
err = of_property_read_string(of_aliases,
|
||||
"arm,timer-primary", &path);
|
||||
if (WARN_ON(err))
|
||||
return;
|
||||
pri_node = of_find_node_by_path(path);
|
||||
err = of_property_read_string(of_aliases,
|
||||
"arm,timer-secondary", &path);
|
||||
if (WARN_ON(err))
|
||||
return;
|
||||
sec_node = of_find_node_by_path(path);
|
||||
|
||||
if (node == pri_node) {
|
||||
/* The primary timer lacks IRQ, use as clocksource */
|
||||
integrator_clocksource_init(rate, base);
|
||||
return;
|
||||
}
|
||||
|
||||
if (node == sec_node) {
|
||||
/* The secondary timer will drive the clock event */
|
||||
irq = irq_of_parse_and_map(node, 0);
|
||||
integrator_clockevent_init(rate, base, irq);
|
||||
return;
|
||||
}
|
||||
|
||||
pr_info("Timer @%p unused\n", base);
|
||||
clk_disable_unprepare(clk);
|
||||
}
|
||||
|
||||
CLOCKSOURCE_OF_DECLARE(integrator_ap_timer, "arm,integrator-timer",
|
||||
integrator_ap_timer_init_of);
|
@ -1,6 +1,15 @@
|
||||
#
|
||||
# ARM Versatile SoC drivers
|
||||
#
|
||||
config SOC_INTEGRATOR_CM
|
||||
bool "SoC bus device for the ARM Integrator platform core modules"
|
||||
depends on ARCH_INTEGRATOR
|
||||
select SOC_BUS
|
||||
help
|
||||
Include support for the SoC bus on the ARM Integrator platform
|
||||
core modules providing some sysfs information about the ASIC
|
||||
variant.
|
||||
|
||||
config SOC_REALVIEW
|
||||
bool "SoC bus device for the ARM RealView platforms"
|
||||
depends on ARCH_REALVIEW
|
||||
|
@ -1 +1,2 @@
|
||||
obj-$(CONFIG_SOC_INTEGRATOR_CM) += soc-integrator.o
|
||||
obj-$(CONFIG_SOC_REALVIEW) += soc-realview.o
|
||||
|
154
drivers/soc/versatile/soc-integrator.c
Normal file
154
drivers/soc/versatile/soc-integrator.c
Normal file
@ -0,0 +1,154 @@
|
||||
/*
|
||||
* Copyright (C) 2014 Linaro Ltd.
|
||||
*
|
||||
* Author: Linus Walleij <linus.walleij@linaro.org>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
*/
|
||||
#include <linux/init.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/sys_soc.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/mfd/syscon.h>
|
||||
#include <linux/regmap.h>
|
||||
#include <linux/of.h>
|
||||
|
||||
#define INTEGRATOR_HDR_ID_OFFSET 0x00
|
||||
|
||||
static u32 integrator_coreid;
|
||||
|
||||
static const struct of_device_id integrator_cm_match[] = {
|
||||
{ .compatible = "arm,core-module-integrator", },
|
||||
};
|
||||
|
||||
static const char *integrator_arch_str(u32 id)
|
||||
{
|
||||
switch ((id >> 16) & 0xff) {
|
||||
case 0x00:
|
||||
return "ASB little-endian";
|
||||
case 0x01:
|
||||
return "AHB little-endian";
|
||||
case 0x03:
|
||||
return "AHB-Lite system bus, bi-endian";
|
||||
case 0x04:
|
||||
return "AHB";
|
||||
case 0x08:
|
||||
return "AHB system bus, ASB processor bus";
|
||||
default:
|
||||
return "Unknown";
|
||||
}
|
||||
}
|
||||
|
||||
static const char *integrator_fpga_str(u32 id)
|
||||
{
|
||||
switch ((id >> 12) & 0xf) {
|
||||
case 0x01:
|
||||
return "XC4062";
|
||||
case 0x02:
|
||||
return "XC4085";
|
||||
case 0x03:
|
||||
return "XVC600";
|
||||
case 0x04:
|
||||
return "EPM7256AE (Altera PLD)";
|
||||
default:
|
||||
return "Unknown";
|
||||
}
|
||||
}
|
||||
|
||||
static ssize_t integrator_get_manf(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
return sprintf(buf, "%02x\n", integrator_coreid >> 24);
|
||||
}
|
||||
|
||||
static struct device_attribute integrator_manf_attr =
|
||||
__ATTR(manufacturer, S_IRUGO, integrator_get_manf, NULL);
|
||||
|
||||
static ssize_t integrator_get_arch(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
return sprintf(buf, "%s\n", integrator_arch_str(integrator_coreid));
|
||||
}
|
||||
|
||||
static struct device_attribute integrator_arch_attr =
|
||||
__ATTR(arch, S_IRUGO, integrator_get_arch, NULL);
|
||||
|
||||
static ssize_t integrator_get_fpga(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
return sprintf(buf, "%s\n", integrator_fpga_str(integrator_coreid));
|
||||
}
|
||||
|
||||
static struct device_attribute integrator_fpga_attr =
|
||||
__ATTR(fpga, S_IRUGO, integrator_get_fpga, NULL);
|
||||
|
||||
static ssize_t integrator_get_build(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
return sprintf(buf, "%02x\n", (integrator_coreid >> 4) & 0xFF);
|
||||
}
|
||||
|
||||
static struct device_attribute integrator_build_attr =
|
||||
__ATTR(build, S_IRUGO, integrator_get_build, NULL);
|
||||
|
||||
static int __init integrator_soc_init(void)
|
||||
{
|
||||
static struct regmap *syscon_regmap;
|
||||
struct soc_device *soc_dev;
|
||||
struct soc_device_attribute *soc_dev_attr;
|
||||
struct device_node *np;
|
||||
struct device *dev;
|
||||
u32 val;
|
||||
int ret;
|
||||
|
||||
np = of_find_matching_node(NULL, integrator_cm_match);
|
||||
if (!np)
|
||||
return -ENODEV;
|
||||
|
||||
syscon_regmap = syscon_node_to_regmap(np);
|
||||
if (IS_ERR(syscon_regmap))
|
||||
return PTR_ERR(syscon_regmap);
|
||||
|
||||
ret = regmap_read(syscon_regmap, INTEGRATOR_HDR_ID_OFFSET,
|
||||
&val);
|
||||
if (ret)
|
||||
return -ENODEV;
|
||||
integrator_coreid = val;
|
||||
|
||||
soc_dev_attr = kzalloc(sizeof(*soc_dev_attr), GFP_KERNEL);
|
||||
if (!soc_dev_attr)
|
||||
return -ENOMEM;
|
||||
|
||||
soc_dev_attr->soc_id = "Integrator";
|
||||
soc_dev_attr->machine = "Integrator";
|
||||
soc_dev_attr->family = "Versatile";
|
||||
soc_dev = soc_device_register(soc_dev_attr);
|
||||
if (IS_ERR(soc_dev)) {
|
||||
kfree(soc_dev_attr);
|
||||
return -ENODEV;
|
||||
}
|
||||
dev = soc_device_to_device(soc_dev);
|
||||
|
||||
device_create_file(dev, &integrator_manf_attr);
|
||||
device_create_file(dev, &integrator_arch_attr);
|
||||
device_create_file(dev, &integrator_fpga_attr);
|
||||
device_create_file(dev, &integrator_build_attr);
|
||||
|
||||
dev_info(dev, "Detected ARM core module:\n");
|
||||
dev_info(dev, " Manufacturer: %02x\n", (val >> 24));
|
||||
dev_info(dev, " Architecture: %s\n", integrator_arch_str(val));
|
||||
dev_info(dev, " FPGA: %s\n", integrator_fpga_str(val));
|
||||
dev_info(dev, " Build: %02x\n", (val >> 4) & 0xFF);
|
||||
dev_info(dev, " Rev: %c\n", ('A' + (val & 0x03)));
|
||||
|
||||
return 0;
|
||||
}
|
||||
device_initcall(integrator_soc_init);
|
Loading…
Reference in New Issue
Block a user