* [PATCH V3 2/5] MIPS: Add board support for Loongson1B
2011-11-17 9:39 [PATCH V3 1/5] MIPS: Add CPU support for Loongson1B keguang.zhang
@ 2011-11-17 9:39 ` keguang.zhang
2011-11-17 12:36 ` Ralf Baechle
2011-11-17 9:39 ` [PATCH V3 3/5] MIPS: Add Makefile and Kconfig " keguang.zhang
` (2 subsequent siblings)
3 siblings, 1 reply; 7+ messages in thread
From: keguang.zhang @ 2011-11-17 9:39 UTC (permalink / raw)
To: linux-mips, linux-kernel
Cc: ralf, zhzhl555, peppe.cavallaro, wuzhangjin, r0bertz,
Kelvin Cheung
From: Kelvin Cheung <keguang.zhang@gmail.com>
This patch adds basic platform support for Loongson1B
including serial port, ethernet, and interrupt handler.
Loongson1B UART is compatible with NS16550A.
Loongson1B GMAC is built around Synopsys IP Core.
Use normal descriptor instead of enhanced descriptor.
Thanks to Giuseppe for updating the normal descriptor
in stmmac driver.
Signed-off-by: Kelvin Cheung <keguang.zhang@gmail.com>
---
arch/mips/include/asm/mach-loongson1/irq.h | 68 +++++++++
arch/mips/include/asm/mach-loongson1/loongson1.h | 44 ++++++
arch/mips/include/asm/mach-loongson1/platform.h | 21 +++
arch/mips/include/asm/mach-loongson1/prom.h | 24 +++
arch/mips/include/asm/mach-loongson1/regs-clk.h | 32 ++++
arch/mips/include/asm/mach-loongson1/regs-wdt.h | 21 +++
arch/mips/include/asm/mach-loongson1/war.h | 25 ++++
arch/mips/loongson1/common/clock.c | 165 ++++++++++++++++++++++
arch/mips/loongson1/common/irq.c | 146 +++++++++++++++++++
arch/mips/loongson1/common/platform.c | 93 ++++++++++++
arch/mips/loongson1/common/prom.c | 88 ++++++++++++
arch/mips/loongson1/common/reset.c | 46 ++++++
arch/mips/loongson1/common/setup.c | 29 ++++
arch/mips/loongson1/ls1b/board.c | 31 ++++
14 files changed, 833 insertions(+), 0 deletions(-)
create mode 100644 arch/mips/include/asm/mach-loongson1/irq.h
create mode 100644 arch/mips/include/asm/mach-loongson1/loongson1.h
create mode 100644 arch/mips/include/asm/mach-loongson1/platform.h
create mode 100644 arch/mips/include/asm/mach-loongson1/prom.h
create mode 100644 arch/mips/include/asm/mach-loongson1/regs-clk.h
create mode 100644 arch/mips/include/asm/mach-loongson1/regs-wdt.h
create mode 100644 arch/mips/include/asm/mach-loongson1/war.h
create mode 100644 arch/mips/loongson1/common/clock.c
create mode 100644 arch/mips/loongson1/common/irq.c
create mode 100644 arch/mips/loongson1/common/platform.c
create mode 100644 arch/mips/loongson1/common/prom.c
create mode 100644 arch/mips/loongson1/common/reset.c
create mode 100644 arch/mips/loongson1/common/setup.c
create mode 100644 arch/mips/loongson1/ls1b/board.c
diff --git a/arch/mips/include/asm/mach-loongson1/irq.h b/arch/mips/include/asm/mach-loongson1/irq.h
new file mode 100644
index 0000000..762bd2b
--- /dev/null
+++ b/arch/mips/include/asm/mach-loongson1/irq.h
@@ -0,0 +1,68 @@
+/*
+ * Copyright (c) 2011 Zhang, Keguang <keguang.zhang@gmail.com>
+ *
+ * IRQ mappings for Loongson1.
+ *
+ * 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.
+ */
+
+
+#ifndef __ASM_MACH_LOONGSON1_IRQ_H
+#define __ASM_MACH_LOONGSON1_IRQ_H
+
+/*
+ * CPU core Interrupt Numbers
+ */
+#define MIPS_CPU_IRQ_BASE 0
+#define MIPS_CPU_IRQ(x) (MIPS_CPU_IRQ_BASE + (x))
+#define SOFTINT0_IRQ MIPS_CPU_IRQ(0)
+#define SOFTINT1_IRQ MIPS_CPU_IRQ(1)
+#define INT0_IRQ MIPS_CPU_IRQ(2)
+#define INT1_IRQ MIPS_CPU_IRQ(3)
+#define INT2_IRQ MIPS_CPU_IRQ(4)
+#define INT3_IRQ MIPS_CPU_IRQ(5)
+#define INT4_IRQ MIPS_CPU_IRQ(6)
+#define TIMER_IRQ MIPS_CPU_IRQ(7) /* cpu timer */
+
+/*
+ * INT0~3 Interrupt Numbers
+ */
+#define LS1X_IRQ_BASE 8
+#define LS1X_IRQ(n, x) (LS1X_IRQ_BASE + (n << 5) + (x))
+
+#define LS1X_UART0_IRQ LS1X_IRQ(0, 2)
+#define LS1X_UART1_IRQ LS1X_IRQ(0, 3)
+#define LS1X_UART2_IRQ LS1X_IRQ(0, 4)
+#define LS1X_UART3_IRQ LS1X_IRQ(0, 5)
+#define LS1X_CAN0_IRQ LS1X_IRQ(0, 6)
+#define LS1X_CAN1_IRQ LS1X_IRQ(0, 7)
+#define LS1X_SPI0_IRQ LS1X_IRQ(0, 8)
+#define LS1X_SPI1_IRQ LS1X_IRQ(0, 9)
+#define LS1X_AC97_IRQ LS1X_IRQ(0, 10)
+#define LS1X_DMA0_IRQ LS1X_IRQ(0, 13)
+#define LS1X_DMA1_IRQ LS1X_IRQ(0, 14)
+#define LS1X_DMA2_IRQ LS1X_IRQ(0, 15)
+#define LS1X_PWM0_IRQ LS1X_IRQ(0, 17)
+#define LS1X_PWM1_IRQ LS1X_IRQ(0, 18)
+#define LS1X_PWM2_IRQ LS1X_IRQ(0, 19)
+#define LS1X_PWM3_IRQ LS1X_IRQ(0, 20)
+#define LS1X_RTC_INT0_IRQ LS1X_IRQ(0, 21)
+#define LS1X_RTC_INT1_IRQ LS1X_IRQ(0, 22)
+#define LS1X_RTC_INT2_IRQ LS1X_IRQ(0, 23)
+#define LS1X_TOY_INT0_IRQ LS1X_IRQ(0, 24)
+#define LS1X_TOY_INT1_IRQ LS1X_IRQ(0, 25)
+#define LS1X_TOY_INT2_IRQ LS1X_IRQ(0, 26)
+#define LS1X_RTC_TICK_IRQ LS1X_IRQ(0, 27)
+#define LS1X_TOY_TICK_IRQ LS1X_IRQ(0, 28)
+
+#define LS1X_EHCI_IRQ LS1X_IRQ(1, 0)
+#define LS1X_OHCI_IRQ LS1X_IRQ(1, 1)
+#define LS1X_GMAC0_IRQ LS1X_IRQ(1, 2)
+#define LS1X_GMAC1_IRQ LS1X_IRQ(1, 3)
+
+#define NR_IRQS LS1X_GMAC1_IRQ
+
+#endif /* __ASM_MACH_LOONGSON1_IRQ_H */
diff --git a/arch/mips/include/asm/mach-loongson1/loongson1.h b/arch/mips/include/asm/mach-loongson1/loongson1.h
new file mode 100644
index 0000000..0440627
--- /dev/null
+++ b/arch/mips/include/asm/mach-loongson1/loongson1.h
@@ -0,0 +1,44 @@
+/*
+ * Copyright (c) 2011 Zhang, Keguang <keguang.zhang@gmail.com>
+ *
+ * Register mappings for Loongson1.
+ *
+ * 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.
+ */
+
+
+#ifndef __ASM_MACH_LOONGSON1_LOONGSON1_H
+#define __ASM_MACH_LOONGSON1_LOONGSON1_H
+
+#define DEFAULT_MEMSIZE 256 /* If no memsize provided */
+
+/* Loongson1 Register Bases */
+#define LS1X_INTC_BASE 0x1fd01040
+#define LS1X_EHCI_BASE 0x1fe00000
+#define LS1X_OHCI_BASE 0x1fe08000
+#define LS1X_GMAC0_BASE 0x1fe10000
+#define LS1X_GMAC1_BASE 0x1fe20000
+
+#define LS1X_UART0_BASE 0x1fe40000
+#define LS1X_UART1_BASE 0x1fe44000
+#define LS1X_UART2_BASE 0x1fe48000
+#define LS1X_UART3_BASE 0x1fe4c000
+#define LS1X_CAN0_BASE 0x1fe50000
+#define LS1X_CAN1_BASE 0x1fe54000
+#define LS1X_I2C0_BASE 0x1fe58000
+#define LS1X_I2C1_BASE 0x1fe68000
+#define LS1X_I2C2_BASE 0x1fe70000
+#define LS1X_PWM_BASE 0x1fe5c000
+#define LS1X_WDT_BASE 0x1fe5c060
+#define LS1X_RTC_BASE 0x1fe64000
+#define LS1X_AC97_BASE 0x1fe74000
+#define LS1X_NAND_BASE 0x1fe78000
+#define LS1X_CLK_BASE 0x1fe78030
+
+#include <regs-clk.h>
+#include <regs-wdt.h>
+
+#endif /* __ASM_MACH_LOONGSON1_LOONGSON1_H */
diff --git a/arch/mips/include/asm/mach-loongson1/platform.h b/arch/mips/include/asm/mach-loongson1/platform.h
new file mode 100644
index 0000000..1e4b313
--- /dev/null
+++ b/arch/mips/include/asm/mach-loongson1/platform.h
@@ -0,0 +1,21 @@
+/*
+ * Copyright (c) 2011 Zhang, Keguang <keguang.zhang@gmail.com>
+ *
+ * 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.
+ */
+
+
+#ifndef __ASM_MACH_LOONGSON1_PLATFORM_H
+#define __ASM_MACH_LOONGSON1_PLATFORM_H
+
+#include <linux/platform_device.h>
+
+extern struct platform_device ls1x_uart_device;
+extern struct platform_device ls1x_eth0_device;
+
+void ls1x_serial_setup(void);
+
+#endif /* __ASM_MACH_LOONGSON1_PLATFORM_H */
diff --git a/arch/mips/include/asm/mach-loongson1/prom.h b/arch/mips/include/asm/mach-loongson1/prom.h
new file mode 100644
index 0000000..b871dc4
--- /dev/null
+++ b/arch/mips/include/asm/mach-loongson1/prom.h
@@ -0,0 +1,24 @@
+/*
+ * Copyright (c) 2011 Zhang, Keguang <keguang.zhang@gmail.com>
+ *
+ * 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.
+ */
+
+#ifndef __ASM_MACH_LOONGSON1_PROM_H
+#define __ASM_MACH_LOONGSON1_PROM_H
+
+#include <linux/io.h>
+#include <linux/init.h>
+#include <linux/irq.h>
+
+/* environment arguments from bootloader */
+extern unsigned long memsize, highmemsize;
+
+/* loongson-specific command line, env and memory initialization */
+extern char *prom_getenv(char *name);
+extern void __init prom_init_cmdline(void);
+
+#endif /* __ASM_MACH_LOONGSON1_PROM_H */
diff --git a/arch/mips/include/asm/mach-loongson1/regs-clk.h b/arch/mips/include/asm/mach-loongson1/regs-clk.h
new file mode 100644
index 0000000..4f488c3
--- /dev/null
+++ b/arch/mips/include/asm/mach-loongson1/regs-clk.h
@@ -0,0 +1,32 @@
+/*
+ * Copyright (c) 2011 Zhang, Keguang <keguang.zhang@gmail.com>
+ *
+ * Loongson1 Clock Register Definitions.
+ *
+ * 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.
+ */
+
+#ifndef __ASM_MACH_LOONGSON1_REGS_CLK_H
+#define __ASM_MACH_LOONGSON1_REGS_CLK_H
+
+#define LS1X_CLK_REG(x) (ioremap(LS1X_CLK_BASE + (x), 4))
+
+#define LS1X_CLK_PLL_FREQ LS1X_CLK_REG(0x0)
+#define LS1X_CLK_PLL_DIV LS1X_CLK_REG(0x4)
+
+/* Clock PLL Divisor Register Bits */
+#define DIV_DC_EN (0x1 << 31)
+#define DIV_DC (0x1f << 26)
+#define DIV_CPU_EN (0x1 << 25)
+#define DIV_CPU (0x1f << 20)
+#define DIV_DDR_EN (0x1 << 19)
+#define DIV_DDR (0x1f << 14)
+
+#define DIV_DC_SHIFT 26
+#define DIV_CPU_SHIFT 20
+#define DIV_DDR_SHIFT 14
+
+#endif /* __ASM_MACH_LOONGSON1_REGS_CLK_H */
diff --git a/arch/mips/include/asm/mach-loongson1/regs-wdt.h b/arch/mips/include/asm/mach-loongson1/regs-wdt.h
new file mode 100644
index 0000000..875ce67
--- /dev/null
+++ b/arch/mips/include/asm/mach-loongson1/regs-wdt.h
@@ -0,0 +1,21 @@
+/*
+ * Copyright (c) 2011 Zhang, Keguang <keguang.zhang@gmail.com>
+ *
+ * Loongson1 Watchdog register definitions.
+ *
+ * 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.
+ */
+
+#ifndef __ASM_MACH_LOONGSON1_REGS_WDT_H
+#define __ASM_MACH_LOONGSON1_REGS_WDT_H
+
+#define LS1X_WDT_REG(x) (ioremap(LS1X_WDT_BASE + (x), 4))
+
+#define LS1X_WDT_EN LS1X_WDT_REG(0x0)
+#define LS1X_WDT_SET LS1X_WDT_REG(0x4)
+#define LS1X_WDT_TIMER LS1X_WDT_REG(0x8)
+
+#endif /* __ASM_MACH_LOONGSON1_REGS_WDT_H */
diff --git a/arch/mips/include/asm/mach-loongson1/war.h b/arch/mips/include/asm/mach-loongson1/war.h
new file mode 100644
index 0000000..e3680a8
--- /dev/null
+++ b/arch/mips/include/asm/mach-loongson1/war.h
@@ -0,0 +1,25 @@
+/*
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License. See the file "COPYING" in the main directory of this archive
+ * for more details.
+ *
+ * Copyright (C) 2002, 2004, 2007 by Ralf Baechle <ralf@linux-mips.org>
+ */
+#ifndef __ASM_MACH_LOONGSON1_WAR_H
+#define __ASM_MACH_LOONGSON1_WAR_H
+
+#define R4600_V1_INDEX_ICACHEOP_WAR 0
+#define R4600_V1_HIT_CACHEOP_WAR 0
+#define R4600_V2_HIT_CACHEOP_WAR 0
+#define R5432_CP0_INTERRUPT_WAR 0
+#define BCM1250_M3_WAR 0
+#define SIBYTE_1956_WAR 0
+#define MIPS4K_ICACHE_REFILL_WAR 0
+#define MIPS_CACHE_SYNC_WAR 0
+#define TX49XX_ICACHE_INDEX_INV_WAR 0
+#define RM9000_CDEX_SMP_WAR 0
+#define ICACHE_REFILLS_WORKAROUND_WAR 0
+#define R10000_LLSC_WAR 0
+#define MIPS34K_MISSED_ITLB_WAR 0
+
+#endif /* __ASM_MACH_LOONGSON1_WAR_H */
diff --git a/arch/mips/loongson1/common/clock.c b/arch/mips/loongson1/common/clock.c
new file mode 100644
index 0000000..2d98fb0
--- /dev/null
+++ b/arch/mips/loongson1/common/clock.c
@@ -0,0 +1,165 @@
+/*
+ * Copyright (c) 2011 Zhang, Keguang <keguang.zhang@gmail.com>
+ *
+ * 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.
+ */
+
+#include <linux/module.h>
+#include <linux/list.h>
+#include <linux/mutex.h>
+#include <linux/clk.h>
+#include <linux/err.h>
+#include <asm/clock.h>
+#include <asm/time.h>
+
+#include <loongson1.h>
+
+static LIST_HEAD(clocks);
+static DEFINE_MUTEX(clocks_mutex);
+
+struct clk *clk_get(struct device *dev, const char *name)
+{
+ struct clk *c;
+ struct clk *ret = NULL;
+
+ mutex_lock(&clocks_mutex);
+ list_for_each_entry(c, &clocks, node) {
+ if (!strcmp(c->name, name)) {
+ ret = c;
+ break;
+ }
+ }
+ mutex_unlock(&clocks_mutex);
+
+ return ret;
+}
+EXPORT_SYMBOL(clk_get);
+
+unsigned long clk_get_rate(struct clk *clk)
+{
+ return clk->rate;
+}
+EXPORT_SYMBOL(clk_get_rate);
+
+static void pll_clk_init(struct clk *clk)
+{
+ u32 pll;
+
+ pll = __raw_readl(LS1X_CLK_PLL_FREQ);
+ clk->rate = (12 + (pll & 0x3f)) * 33 / 2
+ + ((pll >> 8) & 0x3ff) * 33 / 1024 / 2;
+ clk->rate *= 1000000;
+}
+
+static void cpu_clk_init(struct clk *clk)
+{
+ u32 pll, ctrl;
+
+ pll = clk_get_rate(clk->parent);
+ ctrl = __raw_readl(LS1X_CLK_PLL_DIV) & DIV_CPU;
+ clk->rate = pll / (ctrl >> DIV_CPU_SHIFT);
+}
+
+static void ddr_clk_init(struct clk *clk)
+{
+ u32 pll, ctrl;
+
+ pll = clk_get_rate(clk->parent);
+ ctrl = __raw_readl(LS1X_CLK_PLL_DIV) & DIV_DDR;
+ clk->rate = pll / (ctrl >> DIV_DDR_SHIFT);
+}
+
+static void dc_clk_init(struct clk *clk)
+{
+ u32 pll, ctrl;
+
+ pll = clk_get_rate(clk->parent);
+ ctrl = __raw_readl(LS1X_CLK_PLL_DIV) & DIV_DC;
+ clk->rate = pll / (ctrl >> DIV_DC_SHIFT);
+}
+
+static struct clk_ops pll_clk_ops = {
+ .init = pll_clk_init,
+};
+
+static struct clk_ops cpu_clk_ops = {
+ .init = cpu_clk_init,
+};
+
+static struct clk_ops ddr_clk_ops = {
+ .init = ddr_clk_init,
+};
+
+static struct clk_ops dc_clk_ops = {
+ .init = dc_clk_init,
+};
+
+static struct clk pll_clk = {
+ .name = "pll",
+ .ops = &pll_clk_ops,
+};
+
+static struct clk cpu_clk = {
+ .name = "cpu",
+ .parent = &pll_clk,
+ .ops = &cpu_clk_ops,
+};
+
+static struct clk ddr_clk = {
+ .name = "ddr",
+ .parent = &pll_clk,
+ .ops = &ddr_clk_ops,
+};
+
+static struct clk dc_clk = {
+ .name = "dc",
+ .parent = &pll_clk,
+ .ops = &dc_clk_ops,
+};
+
+int clk_register(struct clk *clk)
+{
+ mutex_lock(&clocks_mutex);
+ list_add(&clk->node, &clocks);
+ if (clk->ops->init)
+ clk->ops->init(clk);
+ mutex_unlock(&clocks_mutex);
+
+ return 0;
+}
+EXPORT_SYMBOL(clk_register);
+
+static struct clk *ls1x_clks[] = {
+ &pll_clk,
+ &cpu_clk,
+ &ddr_clk,
+ &dc_clk,
+};
+
+int __init ls1x_clock_init(void)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(ls1x_clks); i++)
+ clk_register(ls1x_clks[i]);
+
+ return 0;
+}
+
+void __init plat_time_init(void)
+{
+ struct clk *clk;
+
+ /* Initialize LS1X clocks */
+ ls1x_clock_init();
+
+ /* setup mips r4k timer */
+ clk = clk_get(NULL, "cpu");
+ if (IS_ERR(clk))
+ panic("unable to get dc clock, err=%ld", PTR_ERR(clk));
+
+ mips_hpt_frequency = clk_get_rate(clk) / 2;
+}
diff --git a/arch/mips/loongson1/common/irq.c b/arch/mips/loongson1/common/irq.c
new file mode 100644
index 0000000..339bc6a
--- /dev/null
+++ b/arch/mips/loongson1/common/irq.c
@@ -0,0 +1,146 @@
+/*
+ * Copyright (c) 2011 Zhang, Keguang <keguang.zhang@gmail.com>
+ *
+ * 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.
+ */
+
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <asm/irq_cpu.h>
+
+#include <loongson1.h>
+#include <irq.h>
+
+#define LS1X_INTC_REG(n, x) \
+ (ioremap(LS1X_INTC_BASE + (n * 0x18) + (x), 4))
+
+#define LS1X_INTC_INTISR(n) LS1X_INTC_REG(n, 0x0)
+#define LS1X_INTC_INTIEN(n) LS1X_INTC_REG(n, 0x4)
+#define LS1X_INTC_INTSET(n) LS1X_INTC_REG(n, 0x8)
+#define LS1X_INTC_INTCLR(n) LS1X_INTC_REG(n, 0xc)
+#define LS1X_INTC_INTPOL(n) LS1X_INTC_REG(n, 0x10)
+#define LS1X_INTC_INTEDGE(n) LS1X_INTC_REG(n, 0x14)
+
+static void ls1x_irq_ack(struct irq_data *d)
+{
+ unsigned int bit = (d->irq - LS1X_IRQ_BASE) & 0x1f;
+ unsigned int n = (d->irq - LS1X_IRQ_BASE) >> 5;
+
+ __raw_writel(__raw_readl(LS1X_INTC_INTCLR(n))
+ | (1 << bit), LS1X_INTC_INTCLR(n));
+}
+
+static void ls1x_irq_mask(struct irq_data *d)
+{
+ unsigned int bit = (d->irq - LS1X_IRQ_BASE) & 0x1f;
+ unsigned int n = (d->irq - LS1X_IRQ_BASE) >> 5;
+
+ __raw_writel(__raw_readl(LS1X_INTC_INTIEN(n))
+ & ~(1 << bit), LS1X_INTC_INTIEN(n));
+}
+
+static void ls1x_irq_mask_ack(struct irq_data *d)
+{
+ unsigned int bit = (d->irq - LS1X_IRQ_BASE) & 0x1f;
+ unsigned int n = (d->irq - LS1X_IRQ_BASE) >> 5;
+
+ __raw_writel(__raw_readl(LS1X_INTC_INTIEN(n))
+ & ~(1 << bit), LS1X_INTC_INTIEN(n));
+ __raw_writel(__raw_readl(LS1X_INTC_INTCLR(n))
+ | (1 << bit), LS1X_INTC_INTCLR(n));
+}
+
+static void ls1x_irq_unmask(struct irq_data *d)
+{
+ unsigned int bit = (d->irq - LS1X_IRQ_BASE) & 0x1f;
+ unsigned int n = (d->irq - LS1X_IRQ_BASE) >> 5;
+
+ __raw_writel(__raw_readl(LS1X_INTC_INTIEN(n))
+ | (1 << bit), LS1X_INTC_INTIEN(n));
+}
+
+static struct irq_chip ls1x_irq_chip = {
+ .name = "LS1X-INTC",
+ .irq_ack = ls1x_irq_ack,
+ .irq_mask = ls1x_irq_mask,
+ .irq_mask_ack = ls1x_irq_mask_ack,
+ .irq_unmask = ls1x_irq_unmask,
+};
+
+static void ls1x_irq_dispatch(int n)
+{
+ u32 int_status, irq;
+
+ /* Get pending sources, masked by current enables */
+ int_status = __raw_readl(LS1X_INTC_INTISR(n)) &
+ __raw_readl(LS1X_INTC_INTIEN(n));
+
+ if (int_status) {
+ irq = LS1X_IRQ(n, __ffs(int_status));
+ do_IRQ(irq);
+ }
+}
+
+asmlinkage void plat_irq_dispatch(void)
+{
+ unsigned int pending;
+
+ pending = read_c0_cause() & read_c0_status() & ST0_IM;
+
+ if (pending & CAUSEF_IP7)
+ do_IRQ(TIMER_IRQ);
+ else if (pending & CAUSEF_IP2)
+ ls1x_irq_dispatch(0); /* INT0 */
+ else if (pending & CAUSEF_IP3)
+ ls1x_irq_dispatch(1); /* INT1 */
+ else if (pending & CAUSEF_IP4)
+ ls1x_irq_dispatch(2); /* INT2 */
+ else if (pending & CAUSEF_IP5)
+ ls1x_irq_dispatch(3); /* INT3 */
+ else if (pending & CAUSEF_IP6)
+ ls1x_irq_dispatch(4); /* INT4 */
+ else
+ spurious_interrupt();
+
+}
+
+struct irqaction cascade_irqaction = {
+ .handler = no_action,
+ .name = "cascade",
+ .flags = IRQF_NO_THREAD,
+};
+
+static void __init ls1x_irq_init(int base)
+{
+ int n;
+
+ /* Disable interrupts and clear pending,
+ * setup all IRQs as high level triggered
+ */
+ for (n = 0; n < 4; n++) {
+ __raw_writel(0x0, LS1X_INTC_INTIEN(n));
+ __raw_writel(0xffffffff, LS1X_INTC_INTCLR(n));
+ __raw_writel(0xffffffff, LS1X_INTC_INTPOL(n));
+ __raw_writel(0x0, LS1X_INTC_INTEDGE(n));
+ }
+
+
+ for (n = base; n < NR_IRQS; n++) {
+ irq_set_chip_and_handler(n, &ls1x_irq_chip,
+ handle_level_irq);
+ }
+
+ setup_irq(INT0_IRQ, &cascade_irqaction);
+ setup_irq(INT1_IRQ, &cascade_irqaction);
+ setup_irq(INT2_IRQ, &cascade_irqaction);
+ setup_irq(INT3_IRQ, &cascade_irqaction);
+}
+
+void __init arch_init_irq(void)
+{
+ mips_cpu_irq_init();
+ ls1x_irq_init(LS1X_IRQ_BASE);
+}
diff --git a/arch/mips/loongson1/common/platform.c b/arch/mips/loongson1/common/platform.c
new file mode 100644
index 0000000..1e573da
--- /dev/null
+++ b/arch/mips/loongson1/common/platform.c
@@ -0,0 +1,93 @@
+/*
+ * Copyright (c) 2011 Zhang, Keguang <keguang.zhang@gmail.com>
+ *
+ * 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.
+ */
+
+#include <linux/clk.h>
+#include <linux/err.h>
+#include <linux/phy.h>
+#include <linux/serial_8250.h>
+#include <linux/stmmac.h>
+#include <asm-generic/sizes.h>
+
+#include <loongson1.h>
+
+#define LS1X_UART(_id) \
+ { \
+ .mapbase = LS1X_UART ## _id ## _BASE, \
+ .irq = LS1X_UART ## _id ## _IRQ, \
+ .iotype = UPIO_MEM, \
+ .flags = UPF_IOREMAP | UPF_FIXED_TYPE, \
+ .type = PORT_16550A, \
+ }
+
+static struct plat_serial8250_port ls1x_serial8250_port[] = {
+ LS1X_UART(0),
+ LS1X_UART(1),
+ LS1X_UART(2),
+ LS1X_UART(3),
+ {},
+};
+
+struct platform_device ls1x_uart_device = {
+ .name = "serial8250",
+ .id = PLAT8250_DEV_PLATFORM,
+ .dev = {
+ .platform_data = ls1x_serial8250_port,
+ },
+};
+
+void __init ls1x_serial_setup(void)
+{
+ struct clk *clk;
+ struct plat_serial8250_port *p;
+
+ clk = clk_get(NULL, "dc");
+ if (IS_ERR(clk))
+ panic("unable to get dc clock, err=%ld", PTR_ERR(clk));
+
+ for (p = ls1x_serial8250_port; p->flags != 0; ++p)
+ p->uartclk = clk_get_rate(clk);
+}
+
+/* Synopsys Ethernet GMAC */
+static struct resource ls1x_eth0_resources[] = {
+ [0] = {
+ .start = LS1X_GMAC0_BASE,
+ .end = LS1X_GMAC0_BASE + SZ_64K - 1,
+ .flags = IORESOURCE_MEM,
+ },
+ [1] = {
+ .name = "macirq",
+ .start = LS1X_GMAC0_IRQ,
+ .flags = IORESOURCE_IRQ,
+ },
+};
+
+static struct stmmac_mdio_bus_data ls1x_mdio_bus_data = {
+ .bus_id = 0,
+ .phy_mask = 0,
+};
+
+static struct plat_stmmacenet_data ls1x_eth_data = {
+ .bus_id = 0,
+ .phy_addr = -1,
+ .mdio_bus_data = &ls1x_mdio_bus_data,
+ .pbl = 32,
+ .has_gmac = 1,
+ .tx_coe = 1,
+};
+
+struct platform_device ls1x_eth0_device = {
+ .name = "stmmaceth",
+ .id = 0,
+ .num_resources = ARRAY_SIZE(ls1x_eth0_resources),
+ .resource = ls1x_eth0_resources,
+ .dev = {
+ .platform_data = &ls1x_eth_data,
+ },
+};
diff --git a/arch/mips/loongson1/common/prom.c b/arch/mips/loongson1/common/prom.c
new file mode 100644
index 0000000..7e2f840
--- /dev/null
+++ b/arch/mips/loongson1/common/prom.c
@@ -0,0 +1,88 @@
+/*
+ * Copyright (c) 2011 Zhang, Keguang <keguang.zhang@gmail.com>
+ *
+ * Modified from arch/mips/pnx833x/common/prom.c.
+ *
+ * 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.
+ */
+
+#include <linux/serial_reg.h>
+#include <asm/bootinfo.h>
+
+#include <loongson1.h>
+#include <prom.h>
+
+int prom_argc;
+char **prom_argv, **prom_envp;
+unsigned long memsize, highmemsize;
+
+char *prom_getenv(char *envname)
+{
+ char **env = prom_envp;
+ int i;
+
+ i = strlen(envname);
+
+ while (*env) {
+ if (strncmp(envname, *env, i) == 0 && *(*env+i) == '=')
+ return *env + i + 1;
+ env++;
+ }
+
+ return 0;
+}
+
+static inline unsigned long env_or_default(char *env, unsigned long dfl)
+{
+ char *str = prom_getenv(env);
+ return str ? simple_strtol(str, 0, 0) : dfl;
+}
+
+void __init prom_init_cmdline(void)
+{
+ char *c = &(arcs_cmdline[0]);
+ int i;
+
+ for (i = 1; i < prom_argc; i++) {
+ strcpy(c, prom_argv[i]);
+ c += strlen(prom_argv[i]);
+ if (i < prom_argc-1)
+ *c++ = ' ';
+ }
+ *c = 0;
+}
+
+void __init prom_init(void)
+{
+ prom_argc = fw_arg0;
+ prom_argv = (char **)fw_arg1;
+ prom_envp = (char **)fw_arg2;
+
+ prom_init_cmdline();
+
+ memsize = env_or_default("memsize", DEFAULT_MEMSIZE);
+ highmemsize = env_or_default("highmemsize", 0x0);
+}
+
+void __init prom_free_prom_memory(void)
+{
+}
+
+#define UART_BASE LS1X_UART0_BASE
+#define PORT(base, offset) (u8 *)ioremap(base + offset, 1)
+
+void __init prom_putchar(char c)
+{
+ int timeout;
+
+ timeout = 1024;
+
+ while (((readb(PORT(UART_BASE, UART_LSR)) & UART_LSR_THRE) == 0)
+ && (timeout-- > 0))
+ ;
+
+ writeb(c, PORT(UART_BASE, UART_TX));
+}
diff --git a/arch/mips/loongson1/common/reset.c b/arch/mips/loongson1/common/reset.c
new file mode 100644
index 0000000..c913c56
--- /dev/null
+++ b/arch/mips/loongson1/common/reset.c
@@ -0,0 +1,46 @@
+/*
+ * Copyright (c) 2011 Zhang, Keguang <keguang.zhang@gmail.com>
+ *
+ * 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.
+ */
+
+#include <linux/io.h>
+#include <linux/pm.h>
+#include <asm/reboot.h>
+
+#include <loongson1.h>
+
+static void ls1x_restart(char *command)
+{
+ __raw_writel(0x1, LS1X_WDT_EN);
+ __raw_writel(0x5000000, LS1X_WDT_TIMER);
+ __raw_writel(0x1, LS1X_WDT_SET);
+}
+
+static void ls1x_halt(void)
+{
+ pr_notice("\n\n** You can safely turn off the power now **\n\n");
+ while (1) {
+ if (cpu_wait)
+ cpu_wait();
+ }
+}
+
+static void ls1x_power_off(void)
+{
+ ls1x_halt();
+}
+
+static int __init ls1x_reboot_setup(void)
+{
+ _machine_restart = ls1x_restart;
+ _machine_halt = ls1x_halt;
+ pm_power_off = ls1x_power_off;
+
+ return 0;
+}
+
+arch_initcall(ls1x_reboot_setup);
diff --git a/arch/mips/loongson1/common/setup.c b/arch/mips/loongson1/common/setup.c
new file mode 100644
index 0000000..62128cc
--- /dev/null
+++ b/arch/mips/loongson1/common/setup.c
@@ -0,0 +1,29 @@
+/*
+ * Copyright (c) 2011 Zhang, Keguang <keguang.zhang@gmail.com>
+ *
+ * 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.
+ */
+
+#include <asm/bootinfo.h>
+
+#include <prom.h>
+
+void __init plat_mem_setup(void)
+{
+ add_memory_region(0x0, (memsize << 20), BOOT_MEM_RAM);
+}
+
+const char *get_system_type(void)
+{
+ unsigned int processor_id = (¤t_cpu_data)->processor_id;
+
+ switch (processor_id & PRID_REV_MASK) {
+ case PRID_REV_LOONGSON1B:
+ return "LOONGSON LS1B";
+ default:
+ return "LOONGSON (unknown)";
+ }
+}
diff --git a/arch/mips/loongson1/ls1b/board.c b/arch/mips/loongson1/ls1b/board.c
new file mode 100644
index 0000000..dd4cd9c
--- /dev/null
+++ b/arch/mips/loongson1/ls1b/board.c
@@ -0,0 +1,31 @@
+/*
+ * Copyright (c) 2011 Zhang, Keguang <keguang.zhang@gmail.com>
+ *
+ * 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.
+ */
+
+#include <platform.h>
+
+#include <linux/serial_8250.h>
+#include <loongson1.h>
+
+static struct platform_device *ls1b_platform_devices[] __initdata = {
+ &ls1x_uart_device,
+ &ls1x_eth0_device,
+};
+
+static int __init ls1b_platform_init(void)
+{
+ int err;
+
+ ls1x_serial_setup();
+
+ err = platform_add_devices(ls1b_platform_devices,
+ ARRAY_SIZE(ls1b_platform_devices));
+ return err;
+}
+
+arch_initcall(ls1b_platform_init);
--
1.7.1
^ permalink raw reply related [flat|nested] 7+ messages in thread* [PATCH V3 4/5] MIPS: Add RTC support for Loongson1B
2011-11-17 9:39 [PATCH V3 1/5] MIPS: Add CPU support for Loongson1B keguang.zhang
2011-11-17 9:39 ` [PATCH V3 2/5] MIPS: Add board " keguang.zhang
2011-11-17 9:39 ` [PATCH V3 3/5] MIPS: Add Makefile and Kconfig " keguang.zhang
@ 2011-11-17 9:39 ` keguang.zhang
2011-11-17 12:42 ` Ralf Baechle
2011-11-17 9:39 ` [PATCH V3 5/5] MIPS: Add defconfig " keguang.zhang
3 siblings, 1 reply; 7+ messages in thread
From: keguang.zhang @ 2011-11-17 9:39 UTC (permalink / raw)
To: linux-mips, linux-kernel
Cc: ralf, zhzhl555, peppe.cavallaro, wuzhangjin, r0bertz,
Kelvin Cheung
From: Kelvin Cheung <keguang.zhang@gmail.com>
This patch adds RTC support(TOY counter0) for Loongson1B.
Thanks Zhao Zhang for implementing this.
Signed-off-by: zhao zhang <zhzhl555@gmail.com>
Signed-off-by: Kelvin Cheung <keguang.zhang@gmail.com>
---
arch/mips/include/asm/mach-loongson1/platform.h | 1 +
arch/mips/loongson1/common/platform.c | 5 +
arch/mips/loongson1/ls1b/board.c | 1 +
drivers/rtc/Kconfig | 10 +
drivers/rtc/Makefile | 1 +
drivers/rtc/rtc-ls1x.c | 210 +++++++++++++++++++++++
6 files changed, 228 insertions(+), 0 deletions(-)
create mode 100644 drivers/rtc/rtc-ls1x.c
diff --git a/arch/mips/include/asm/mach-loongson1/platform.h b/arch/mips/include/asm/mach-loongson1/platform.h
index 1e4b313..327df09 100644
--- a/arch/mips/include/asm/mach-loongson1/platform.h
+++ b/arch/mips/include/asm/mach-loongson1/platform.h
@@ -15,6 +15,7 @@
extern struct platform_device ls1x_uart_device;
extern struct platform_device ls1x_eth0_device;
+extern struct platform_device ls1x_rtc_device;
void ls1x_serial_setup(void);
diff --git a/arch/mips/loongson1/common/platform.c b/arch/mips/loongson1/common/platform.c
index 1e573da..1805775 100644
--- a/arch/mips/loongson1/common/platform.c
+++ b/arch/mips/loongson1/common/platform.c
@@ -91,3 +91,8 @@ struct platform_device ls1x_eth0_device = {
.platform_data = &ls1x_eth_data,
},
};
+
+struct platform_device ls1x_rtc_device = {
+ .name = "rtc-ls1b",
+ .id = -1,
+};
diff --git a/arch/mips/loongson1/ls1b/board.c b/arch/mips/loongson1/ls1b/board.c
index dd4cd9c..7aaebff 100644
--- a/arch/mips/loongson1/ls1b/board.c
+++ b/arch/mips/loongson1/ls1b/board.c
@@ -15,6 +15,7 @@
static struct platform_device *ls1b_platform_devices[] __initdata = {
&ls1x_uart_device,
&ls1x_eth0_device,
+ &ls1x_rtc_device,
};
static int __init ls1b_platform_init(void)
diff --git a/drivers/rtc/Kconfig b/drivers/rtc/Kconfig
index 5a538fc..6f8c2d7 100644
--- a/drivers/rtc/Kconfig
+++ b/drivers/rtc/Kconfig
@@ -1070,4 +1070,14 @@ config RTC_DRV_PUV3
This drive can also be built as a module. If so, the module
will be called rtc-puv3.
+config RTC_DRV_LOONGSON1
+ tristate "loongson1 RTC support"
+ depends on MACH_LOONGSON1
+ help
+ This is a driver for the loongson1 on-chip Counter0 (Time-Of-Year
+ counter) to be used as a RTC.
+
+ This driver can also be built as a module. If so, the module
+ will be called rtc-ls1x.
+
endif # RTC_CLASS
diff --git a/drivers/rtc/Makefile b/drivers/rtc/Makefile
index 6e69823..48153fe 100644
--- a/drivers/rtc/Makefile
+++ b/drivers/rtc/Makefile
@@ -109,3 +109,4 @@ obj-$(CONFIG_RTC_DRV_VT8500) += rtc-vt8500.o
obj-$(CONFIG_RTC_DRV_WM831X) += rtc-wm831x.o
obj-$(CONFIG_RTC_DRV_WM8350) += rtc-wm8350.o
obj-$(CONFIG_RTC_DRV_X1205) += rtc-x1205.o
+obj-$(CONFIG_RTC_DRV_LOONGSON1) += rtc-ls1x.o
diff --git a/drivers/rtc/rtc-ls1x.c b/drivers/rtc/rtc-ls1x.c
new file mode 100644
index 0000000..fc2b6fd
--- /dev/null
+++ b/drivers/rtc/rtc-ls1x.c
@@ -0,0 +1,210 @@
+/*
+ * Copyright (c) 2011 Zhao Zhang <zhzhl555@gmail.com>
+ *
+ * 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.
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/rtc.h>
+#include <linux/init.h>
+#include <linux/platform_device.h>
+#include <linux/delay.h>
+#include <linux/types.h>
+#include <linux/io.h>
+#include <loongson1.h>
+
+#define LS1X_RTC_REG_OFFSET (LS1X_RTC_BASE + 0x20)
+#define LS1X_RTC_REGS(x) (ioremap(LS1X_RTC_REG_OFFSET + (x), 4))
+
+/*RTC programmable counters 0 and 1*/
+#define SYS_COUNTER_CNTRL (LS1X_RTC_REGS(0x20))
+#define SYS_CNTRL_ERS (1 << 23)
+#define SYS_CNTRL_RTS (1 << 20)
+#define SYS_CNTRL_RM2 (1 << 19)
+#define SYS_CNTRL_RM1 (1 << 18)
+#define SYS_CNTRL_RM0 (1 << 17)
+#define SYS_CNTRL_RS (1 << 16)
+#define SYS_CNTRL_BP (1 << 14)
+#define SYS_CNTRL_REN (1 << 13)
+#define SYS_CNTRL_BRT (1 << 12)
+#define SYS_CNTRL_TEN (1 << 11)
+#define SYS_CNTRL_BTT (1 << 10)
+#define SYS_CNTRL_E0 (1 << 8)
+#define SYS_CNTRL_ETS (1 << 7)
+#define SYS_CNTRL_32S (1 << 5)
+#define SYS_CNTRL_TTS (1 << 4)
+#define SYS_CNTRL_TM2 (1 << 3)
+#define SYS_CNTRL_TM1 (1 << 2)
+#define SYS_CNTRL_TM0 (1 << 1)
+#define SYS_CNTRL_TS (1 << 0)
+
+/* Programmable Counter 0 Registers */
+#define SYS_TOYTRIM (LS1X_RTC_REGS(0))
+#define SYS_TOYWRITE0 (LS1X_RTC_REGS(4))
+#define SYS_TOYWRITE1 (LS1X_RTC_REGS(8))
+#define SYS_TOYREAD0 (LS1X_RTC_REGS(0xC))
+#define SYS_TOYREAD1 (LS1X_RTC_REGS(0x10))
+#define SYS_TOYMATCH0 (LS1X_RTC_REGS(0x14))
+#define SYS_TOYMATCH1 (LS1X_RTC_REGS(0x18))
+#define SYS_TOYMATCH2 (LS1X_RTC_REGS(0x1C))
+
+/* Programmable Counter 1 Registers */
+#define SYS_RTCTRIM (LS1X_RTC_REGS(0x40))
+#define SYS_RTCWRITE0 (LS1X_RTC_REGS(0x44))
+#define SYS_RTCREAD0 (LS1X_RTC_REGS(0x48))
+#define SYS_RTCMATCH0 (LS1X_RTC_REGS(0x4C))
+#define SYS_RTCMATCH1 (LS1X_RTC_REGS(0x50))
+#define SYS_RTCMATCH2 (LS1X_RTC_REGS(0x54))
+
+#define LS1X_SEC_OFFSET (4)
+#define LS1X_MIN_OFFSET (10)
+#define LS1X_HOUR_OFFSET (16)
+#define LS1X_DAY_OFFSET (21)
+#define LS1X_MONTH_OFFSET (26)
+
+
+#define LS1X_SEC_MASK (0x3f)
+#define LS1X_MIN_MASK (0x3f)
+#define LS1X_HOUR_MASK (0x1f)
+#define LS1X_DAY_MASK (0x1f)
+#define LS1X_MONTH_MASK (0x3f)
+#define LS1X_YEAR_MASK (0xffffffff)
+
+#define ls1x_get_sec(t) (((t) >> LS1X_SEC_OFFSET) & LS1X_SEC_MASK)
+#define ls1x_get_min(t) (((t) >> LS1X_MIN_OFFSET) & LS1X_MIN_MASK)
+#define ls1x_get_hour(t) (((t) >> LS1X_HOUR_OFFSET) & LS1X_HOUR_MASK)
+#define ls1x_get_day(t) (((t) >> LS1X_DAY_OFFSET) & LS1X_DAY_MASK)
+#define ls1x_get_month(t) (((t) >> LS1X_MONTH_OFFSET) & LS1X_MONTH_MASK)
+
+#define RTC_CNTR_OK (SYS_CNTRL_E0 | SYS_CNTRL_32S)
+
+static int ls1b_rtc_read_time(struct device *dev, struct rtc_time *rtm)
+{
+ unsigned long v, t;
+
+ v = __raw_readl(SYS_TOYREAD0);
+ t = __raw_readl(SYS_TOYREAD1);
+
+ memset(rtm, 0, sizeof(struct rtc_time));
+ t = mktime((t & LS1X_YEAR_MASK), ls1x_get_month(v),
+ ls1x_get_day(v), ls1x_get_hour(v),
+ ls1x_get_min(v), ls1x_get_sec(v));
+ rtc_time_to_tm(t, rtm);
+
+ return rtc_valid_tm(rtm);
+}
+
+static int ls1b_rtc_set_time(struct device *dev, struct rtc_time *rtm)
+{
+ unsigned long v, t;
+
+ v = ((rtm->tm_mon + 1) << LS1X_MONTH_OFFSET)
+ | (rtm->tm_mday << LS1X_DAY_OFFSET)
+ | (rtm->tm_hour << LS1X_HOUR_OFFSET)
+ | (rtm->tm_min << LS1X_MIN_OFFSET)
+ | (rtm->tm_sec << LS1X_SEC_OFFSET);
+
+ t = rtm->tm_year + 1900;
+ __raw_writel(v, SYS_TOYWRITE0);
+ while (__raw_readl(SYS_COUNTER_CNTRL) & SYS_CNTRL_TS)
+ usleep_range(1000, 3000);
+ __asm__ volatile ("sync");
+
+ __raw_writel(t, SYS_TOYWRITE1);
+ while (__raw_readl(SYS_COUNTER_CNTRL) & SYS_CNTRL_TS)
+ usleep_range(1000, 3000);
+ __asm__ volatile ("sync");
+
+ v = __raw_readl(SYS_COUNTER_CNTRL);
+ return 0;
+}
+
+static struct rtc_class_ops ls1b_rtc_ops = {
+ .read_time = ls1b_rtc_read_time,
+ .set_time = ls1b_rtc_set_time,
+};
+
+static int __devinit ls1b_rtc_probe(struct platform_device *pdev)
+{
+ struct rtc_device *rtcdev;
+ unsigned long v;
+ int ret;
+
+ v = __raw_readl(SYS_COUNTER_CNTRL);
+ if (!(v & RTC_CNTR_OK)) {
+ dev_err(&pdev->dev, "rtc counters not working\n");
+ ret = -ENODEV;
+ goto err;
+ }
+ ret = -ETIMEDOUT;
+ /*set to 1 HZ if needed*/
+ if (__raw_readl(SYS_TOYTRIM) != 32767) {
+ v = 0x100000;
+ while ((__raw_readl(SYS_COUNTER_CNTRL) & SYS_CNTRL_TTS) && --v)
+ usleep_range(1000, 3000);
+
+ if (!v) {
+ dev_err(&pdev->dev, "time out\n");
+ goto err;
+ }
+ __raw_writel(32767, SYS_TOYTRIM);
+ __asm__ volatile("sync");
+ }
+ /*this loop coundn't be endless*/
+ while (__raw_readl(SYS_COUNTER_CNTRL) & SYS_CNTRL_TTS)
+ usleep_range(1000, 3000);
+
+ rtcdev = rtc_device_register("rtc-ls1b", &pdev->dev,
+ &ls1b_rtc_ops , THIS_MODULE);
+ if (IS_ERR(rtcdev)) {
+ ret = PTR_ERR(rtcdev);
+ goto err;
+ }
+
+ platform_set_drvdata(pdev, rtcdev);
+ return 0;
+err:
+ return ret;
+}
+
+static int __devexit ls1b_rtc_remove(struct platform_device *pdev)
+{
+ struct rtc_device *rtcdev = platform_get_drvdata(pdev);
+
+ rtc_device_unregister(rtcdev);
+ platform_set_drvdata(pdev, NULL);
+
+ return 0;
+}
+
+static struct platform_driver ls1b_rtc_driver = {
+ .driver = {
+ .name = "rtc-ls1b",
+ .owner = THIS_MODULE,
+ },
+ .remove = __devexit_p(ls1b_rtc_remove),
+};
+
+static int __init ls1b_rtc_init(void)
+{
+ return platform_driver_probe(&ls1b_rtc_driver, ls1b_rtc_probe);
+}
+
+static void __exit ls1b_rtc_exit(void)
+{
+ platform_driver_unregister(&ls1b_rtc_driver);
+}
+
+
+module_init(ls1b_rtc_init);
+module_exit(ls1b_rtc_exit);
+
+
+MODULE_DESCRIPTION("ls1b TOY-counter-based RTC driver");
+MODULE_AUTHOR("zhao zhang <zhzhl555@gmail.com>");
+MODULE_LICENSE("GPL");
+
--
1.7.1
^ permalink raw reply related [flat|nested] 7+ messages in thread