aboutsummaryrefslogtreecommitdiffstats
path: root/arch/mips/loongson1/common
diff options
context:
space:
mode:
authorKelvin Cheung <keguang.zhang@gmail.com>2012-07-25 10:17:24 -0400
committerRalf Baechle <ralf@linux-mips.org>2012-07-25 10:17:24 -0400
commitca585cf9fb818bfcfcac6968c2b242dcd0693b08 (patch)
treedd0d228367155b704e150bbfbbd56f7a6a6a00d1 /arch/mips/loongson1/common
parent2fa36399e63c911134f28b6878aada9b395c4209 (diff)
MIPS: Loongson 1B: Add board support
Adds basic platform devices for Loongson 1B, including serial port, ethernet, USB, RTC and interrupt handler. The Loongson 1B UART is compatible with NS16550A, the Loongson 1B GMAC is built around a Synopsys IP Core. Use normal instead of enhanced descriptors. Thanks to Giuseppe for updating the normal descriptor in stmmac driver. Thanks to Zhao Zhang for implementing the RTC driver. Signed-off-by: Kelvin Cheung <keguang.zhang@gmail.com> Cc: linux-mips@linux-mips.org Cc: linux-kernel@vger.kernel.org Cc: wuzhangjin@gmail.com Cc: zhzhl555@gmail.com Cc: Kelvin Cheung <keguang.zhang@gmail.com> Patchwork: https://patchwork.linux-mips.org/patch/4133/ Patchwork: https://patchwork.linux-mips.org/patch/4134/ Signed-off-by: Ralf Baechle <ralf@linux-mips.org>
Diffstat (limited to 'arch/mips/loongson1/common')
-rw-r--r--arch/mips/loongson1/common/Makefile5
-rw-r--r--arch/mips/loongson1/common/clock.c165
-rw-r--r--arch/mips/loongson1/common/irq.c147
-rw-r--r--arch/mips/loongson1/common/platform.c124
-rw-r--r--arch/mips/loongson1/common/prom.c87
-rw-r--r--arch/mips/loongson1/common/reset.c45
-rw-r--r--arch/mips/loongson1/common/setup.c29
7 files changed, 602 insertions, 0 deletions
diff --git a/arch/mips/loongson1/common/Makefile b/arch/mips/loongson1/common/Makefile
new file mode 100644
index 000000000000..b2797709ef5b
--- /dev/null
+++ b/arch/mips/loongson1/common/Makefile
@@ -0,0 +1,5 @@
1#
2# Makefile for common code of loongson1 based machines.
3#
4
5obj-y += clock.o irq.o platform.o prom.o reset.o setup.o
diff --git a/arch/mips/loongson1/common/clock.c b/arch/mips/loongson1/common/clock.c
new file mode 100644
index 000000000000..2d98fb030596
--- /dev/null
+++ b/arch/mips/loongson1/common/clock.c
@@ -0,0 +1,165 @@
1/*
2 * Copyright (c) 2011 Zhang, Keguang <keguang.zhang@gmail.com>
3 *
4 * This program is free software; you can redistribute it and/or modify it
5 * under the terms of the GNU General Public License as published by the
6 * Free Software Foundation; either version 2 of the License, or (at your
7 * option) any later version.
8 */
9
10#include <linux/module.h>
11#include <linux/list.h>
12#include <linux/mutex.h>
13#include <linux/clk.h>
14#include <linux/err.h>
15#include <asm/clock.h>
16#include <asm/time.h>
17
18#include <loongson1.h>
19
20static LIST_HEAD(clocks);
21static DEFINE_MUTEX(clocks_mutex);
22
23struct clk *clk_get(struct device *dev, const char *name)
24{
25 struct clk *c;
26 struct clk *ret = NULL;
27
28 mutex_lock(&clocks_mutex);
29 list_for_each_entry(c, &clocks, node) {
30 if (!strcmp(c->name, name)) {
31 ret = c;
32 break;
33 }
34 }
35 mutex_unlock(&clocks_mutex);
36
37 return ret;
38}
39EXPORT_SYMBOL(clk_get);
40
41unsigned long clk_get_rate(struct clk *clk)
42{
43 return clk->rate;
44}
45EXPORT_SYMBOL(clk_get_rate);
46
47static void pll_clk_init(struct clk *clk)
48{
49 u32 pll;
50
51 pll = __raw_readl(LS1X_CLK_PLL_FREQ);
52 clk->rate = (12 + (pll & 0x3f)) * 33 / 2
53 + ((pll >> 8) & 0x3ff) * 33 / 1024 / 2;
54 clk->rate *= 1000000;
55}
56
57static void cpu_clk_init(struct clk *clk)
58{
59 u32 pll, ctrl;
60
61 pll = clk_get_rate(clk->parent);
62 ctrl = __raw_readl(LS1X_CLK_PLL_DIV) & DIV_CPU;
63 clk->rate = pll / (ctrl >> DIV_CPU_SHIFT);
64}
65
66static void ddr_clk_init(struct clk *clk)
67{
68 u32 pll, ctrl;
69
70 pll = clk_get_rate(clk->parent);
71 ctrl = __raw_readl(LS1X_CLK_PLL_DIV) & DIV_DDR;
72 clk->rate = pll / (ctrl >> DIV_DDR_SHIFT);
73}
74
75static void dc_clk_init(struct clk *clk)
76{
77 u32 pll, ctrl;
78
79 pll = clk_get_rate(clk->parent);
80 ctrl = __raw_readl(LS1X_CLK_PLL_DIV) & DIV_DC;
81 clk->rate = pll / (ctrl >> DIV_DC_SHIFT);
82}
83
84static struct clk_ops pll_clk_ops = {
85 .init = pll_clk_init,
86};
87
88static struct clk_ops cpu_clk_ops = {
89 .init = cpu_clk_init,
90};
91
92static struct clk_ops ddr_clk_ops = {
93 .init = ddr_clk_init,
94};
95
96static struct clk_ops dc_clk_ops = {
97 .init = dc_clk_init,
98};
99
100static struct clk pll_clk = {
101 .name = "pll",
102 .ops = &pll_clk_ops,
103};
104
105static struct clk cpu_clk = {
106 .name = "cpu",
107 .parent = &pll_clk,
108 .ops = &cpu_clk_ops,
109};
110
111static struct clk ddr_clk = {
112 .name = "ddr",
113 .parent = &pll_clk,
114 .ops = &ddr_clk_ops,
115};
116
117static struct clk dc_clk = {
118 .name = "dc",
119 .parent = &pll_clk,
120 .ops = &dc_clk_ops,
121};
122
123int clk_register(struct clk *clk)
124{
125 mutex_lock(&clocks_mutex);
126 list_add(&clk->node, &clocks);
127 if (clk->ops->init)
128 clk->ops->init(clk);
129 mutex_unlock(&clocks_mutex);
130
131 return 0;
132}
133EXPORT_SYMBOL(clk_register);
134
135static struct clk *ls1x_clks[] = {
136 &pll_clk,
137 &cpu_clk,
138 &ddr_clk,
139 &dc_clk,
140};
141
142int __init ls1x_clock_init(void)
143{
144 int i;
145
146 for (i = 0; i < ARRAY_SIZE(ls1x_clks); i++)
147 clk_register(ls1x_clks[i]);
148
149 return 0;
150}
151
152void __init plat_time_init(void)
153{
154 struct clk *clk;
155
156 /* Initialize LS1X clocks */
157 ls1x_clock_init();
158
159 /* setup mips r4k timer */
160 clk = clk_get(NULL, "cpu");
161 if (IS_ERR(clk))
162 panic("unable to get dc clock, err=%ld", PTR_ERR(clk));
163
164 mips_hpt_frequency = clk_get_rate(clk) / 2;
165}
diff --git a/arch/mips/loongson1/common/irq.c b/arch/mips/loongson1/common/irq.c
new file mode 100644
index 000000000000..41bc8ffe7bba
--- /dev/null
+++ b/arch/mips/loongson1/common/irq.c
@@ -0,0 +1,147 @@
1/*
2 * Copyright (c) 2011 Zhang, Keguang <keguang.zhang@gmail.com>
3 *
4 * This program is free software; you can redistribute it and/or modify it
5 * under the terms of the GNU General Public License as published by the
6 * Free Software Foundation; either version 2 of the License, or (at your
7 * option) any later version.
8 */
9
10#include <linux/interrupt.h>
11#include <linux/irq.h>
12#include <asm/irq_cpu.h>
13
14#include <loongson1.h>
15#include <irq.h>
16
17#define LS1X_INTC_REG(n, x) \
18 ((void __iomem *)KSEG1ADDR(LS1X_INTC_BASE + (n * 0x18) + (x)))
19
20#define LS1X_INTC_INTISR(n) LS1X_INTC_REG(n, 0x0)
21#define LS1X_INTC_INTIEN(n) LS1X_INTC_REG(n, 0x4)
22#define LS1X_INTC_INTSET(n) LS1X_INTC_REG(n, 0x8)
23#define LS1X_INTC_INTCLR(n) LS1X_INTC_REG(n, 0xc)
24#define LS1X_INTC_INTPOL(n) LS1X_INTC_REG(n, 0x10)
25#define LS1X_INTC_INTEDGE(n) LS1X_INTC_REG(n, 0x14)
26
27static void ls1x_irq_ack(struct irq_data *d)
28{
29 unsigned int bit = (d->irq - LS1X_IRQ_BASE) & 0x1f;
30 unsigned int n = (d->irq - LS1X_IRQ_BASE) >> 5;
31
32 __raw_writel(__raw_readl(LS1X_INTC_INTCLR(n))
33 | (1 << bit), LS1X_INTC_INTCLR(n));
34}
35
36static void ls1x_irq_mask(struct irq_data *d)
37{
38 unsigned int bit = (d->irq - LS1X_IRQ_BASE) & 0x1f;
39 unsigned int n = (d->irq - LS1X_IRQ_BASE) >> 5;
40
41 __raw_writel(__raw_readl(LS1X_INTC_INTIEN(n))
42 & ~(1 << bit), LS1X_INTC_INTIEN(n));
43}
44
45static void ls1x_irq_mask_ack(struct irq_data *d)
46{
47 unsigned int bit = (d->irq - LS1X_IRQ_BASE) & 0x1f;
48 unsigned int n = (d->irq - LS1X_IRQ_BASE) >> 5;
49
50 __raw_writel(__raw_readl(LS1X_INTC_INTIEN(n))
51 & ~(1 << bit), LS1X_INTC_INTIEN(n));
52 __raw_writel(__raw_readl(LS1X_INTC_INTCLR(n))
53 | (1 << bit), LS1X_INTC_INTCLR(n));
54}
55
56static void ls1x_irq_unmask(struct irq_data *d)
57{
58 unsigned int bit = (d->irq - LS1X_IRQ_BASE) & 0x1f;
59 unsigned int n = (d->irq - LS1X_IRQ_BASE) >> 5;
60
61 __raw_writel(__raw_readl(LS1X_INTC_INTIEN(n))
62 | (1 << bit), LS1X_INTC_INTIEN(n));
63}
64
65static struct irq_chip ls1x_irq_chip = {
66 .name = "LS1X-INTC",
67 .irq_ack = ls1x_irq_ack,
68 .irq_mask = ls1x_irq_mask,
69 .irq_mask_ack = ls1x_irq_mask_ack,
70 .irq_unmask = ls1x_irq_unmask,
71};
72
73static void ls1x_irq_dispatch(int n)
74{
75 u32 int_status, irq;
76
77 /* Get pending sources, masked by current enables */
78 int_status = __raw_readl(LS1X_INTC_INTISR(n)) &
79 __raw_readl(LS1X_INTC_INTIEN(n));
80
81 if (int_status) {
82 irq = LS1X_IRQ(n, __ffs(int_status));
83 do_IRQ(irq);
84 }
85}
86
87asmlinkage void plat_irq_dispatch(void)
88{
89 unsigned int pending;
90
91 pending = read_c0_cause() & read_c0_status() & ST0_IM;
92
93 if (pending & CAUSEF_IP7)
94 do_IRQ(TIMER_IRQ);
95 else if (pending & CAUSEF_IP2)
96 ls1x_irq_dispatch(0); /* INT0 */
97 else if (pending & CAUSEF_IP3)
98 ls1x_irq_dispatch(1); /* INT1 */
99 else if (pending & CAUSEF_IP4)
100 ls1x_irq_dispatch(2); /* INT2 */
101 else if (pending & CAUSEF_IP5)
102 ls1x_irq_dispatch(3); /* INT3 */
103 else if (pending & CAUSEF_IP6)
104 ls1x_irq_dispatch(4); /* INT4 */
105 else
106 spurious_interrupt();
107
108}
109
110struct irqaction cascade_irqaction = {
111 .handler = no_action,
112 .name = "cascade",
113 .flags = IRQF_NO_THREAD,
114};
115
116static void __init ls1x_irq_init(int base)
117{
118 int n;
119
120 /* Disable interrupts and clear pending,
121 * setup all IRQs as high level triggered
122 */
123 for (n = 0; n < 4; n++) {
124 __raw_writel(0x0, LS1X_INTC_INTIEN(n));
125 __raw_writel(0xffffffff, LS1X_INTC_INTCLR(n));
126 __raw_writel(0xffffffff, LS1X_INTC_INTPOL(n));
127 /* set DMA0, DMA1 and DMA2 to edge trigger */
128 __raw_writel(n ? 0x0 : 0xe000, LS1X_INTC_INTEDGE(n));
129 }
130
131
132 for (n = base; n < LS1X_IRQS; n++) {
133 irq_set_chip_and_handler(n, &ls1x_irq_chip,
134 handle_level_irq);
135 }
136
137 setup_irq(INT0_IRQ, &cascade_irqaction);
138 setup_irq(INT1_IRQ, &cascade_irqaction);
139 setup_irq(INT2_IRQ, &cascade_irqaction);
140 setup_irq(INT3_IRQ, &cascade_irqaction);
141}
142
143void __init arch_init_irq(void)
144{
145 mips_cpu_irq_init();
146 ls1x_irq_init(LS1X_IRQ_BASE);
147}
diff --git a/arch/mips/loongson1/common/platform.c b/arch/mips/loongson1/common/platform.c
new file mode 100644
index 000000000000..e92d59c4bd78
--- /dev/null
+++ b/arch/mips/loongson1/common/platform.c
@@ -0,0 +1,124 @@
1/*
2 * Copyright (c) 2011 Zhang, Keguang <keguang.zhang@gmail.com>
3 *
4 * This program is free software; you can redistribute it and/or modify it
5 * under the terms of the GNU General Public License as published by the
6 * Free Software Foundation; either version 2 of the License, or (at your
7 * option) any later version.
8 */
9
10#include <linux/clk.h>
11#include <linux/dma-mapping.h>
12#include <linux/err.h>
13#include <linux/phy.h>
14#include <linux/serial_8250.h>
15#include <linux/stmmac.h>
16#include <asm-generic/sizes.h>
17
18#include <loongson1.h>
19
20#define LS1X_UART(_id) \
21 { \
22 .mapbase = LS1X_UART ## _id ## _BASE, \
23 .irq = LS1X_UART ## _id ## _IRQ, \
24 .iotype = UPIO_MEM, \
25 .flags = UPF_IOREMAP | UPF_FIXED_TYPE, \
26 .type = PORT_16550A, \
27 }
28
29static struct plat_serial8250_port ls1x_serial8250_port[] = {
30 LS1X_UART(0),
31 LS1X_UART(1),
32 LS1X_UART(2),
33 LS1X_UART(3),
34 {},
35};
36
37struct platform_device ls1x_uart_device = {
38 .name = "serial8250",
39 .id = PLAT8250_DEV_PLATFORM,
40 .dev = {
41 .platform_data = ls1x_serial8250_port,
42 },
43};
44
45void __init ls1x_serial_setup(void)
46{
47 struct clk *clk;
48 struct plat_serial8250_port *p;
49
50 clk = clk_get(NULL, "dc");
51 if (IS_ERR(clk))
52 panic("unable to get dc clock, err=%ld", PTR_ERR(clk));
53
54 for (p = ls1x_serial8250_port; p->flags != 0; ++p)
55 p->uartclk = clk_get_rate(clk);
56}
57
58/* Synopsys Ethernet GMAC */
59static struct resource ls1x_eth0_resources[] = {
60 [0] = {
61 .start = LS1X_GMAC0_BASE,
62 .end = LS1X_GMAC0_BASE + SZ_64K - 1,
63 .flags = IORESOURCE_MEM,
64 },
65 [1] = {
66 .name = "macirq",
67 .start = LS1X_GMAC0_IRQ,
68 .flags = IORESOURCE_IRQ,
69 },
70};
71
72static struct stmmac_mdio_bus_data ls1x_mdio_bus_data = {
73 .bus_id = 0,
74 .phy_mask = 0,
75};
76
77static struct plat_stmmacenet_data ls1x_eth_data = {
78 .bus_id = 0,
79 .phy_addr = -1,
80 .mdio_bus_data = &ls1x_mdio_bus_data,
81 .has_gmac = 1,
82 .tx_coe = 1,
83};
84
85struct platform_device ls1x_eth0_device = {
86 .name = "stmmaceth",
87 .id = 0,
88 .num_resources = ARRAY_SIZE(ls1x_eth0_resources),
89 .resource = ls1x_eth0_resources,
90 .dev = {
91 .platform_data = &ls1x_eth_data,
92 },
93};
94
95/* USB EHCI */
96static u64 ls1x_ehci_dmamask = DMA_BIT_MASK(32);
97
98static struct resource ls1x_ehci_resources[] = {
99 [0] = {
100 .start = LS1X_EHCI_BASE,
101 .end = LS1X_EHCI_BASE + SZ_32K - 1,
102 .flags = IORESOURCE_MEM,
103 },
104 [1] = {
105 .start = LS1X_EHCI_IRQ,
106 .flags = IORESOURCE_IRQ,
107 },
108};
109
110struct platform_device ls1x_ehci_device = {
111 .name = "ls1x-ehci",
112 .id = -1,
113 .num_resources = ARRAY_SIZE(ls1x_ehci_resources),
114 .resource = ls1x_ehci_resources,
115 .dev = {
116 .dma_mask = &ls1x_ehci_dmamask,
117 },
118};
119
120/* Real Time Clock */
121struct platform_device ls1x_rtc_device = {
122 .name = "ls1x-rtc",
123 .id = -1,
124};
diff --git a/arch/mips/loongson1/common/prom.c b/arch/mips/loongson1/common/prom.c
new file mode 100644
index 000000000000..1f8e49f9886d
--- /dev/null
+++ b/arch/mips/loongson1/common/prom.c
@@ -0,0 +1,87 @@
1/*
2 * Copyright (c) 2011 Zhang, Keguang <keguang.zhang@gmail.com>
3 *
4 * Modified from arch/mips/pnx833x/common/prom.c.
5 *
6 * This program is free software; you can redistribute it and/or modify it
7 * under the terms of the GNU General Public License as published by the
8 * Free Software Foundation; either version 2 of the License, or (at your
9 * option) any later version.
10 */
11
12#include <linux/serial_reg.h>
13#include <asm/bootinfo.h>
14
15#include <loongson1.h>
16#include <prom.h>
17
18int prom_argc;
19char **prom_argv, **prom_envp;
20unsigned long memsize, highmemsize;
21
22char *prom_getenv(char *envname)
23{
24 char **env = prom_envp;
25 int i;
26
27 i = strlen(envname);
28
29 while (*env) {
30 if (strncmp(envname, *env, i) == 0 && *(*env+i) == '=')
31 return *env + i + 1;
32 env++;
33 }
34
35 return 0;
36}
37
38static inline unsigned long env_or_default(char *env, unsigned long dfl)
39{
40 char *str = prom_getenv(env);
41 return str ? simple_strtol(str, 0, 0) : dfl;
42}
43
44void __init prom_init_cmdline(void)
45{
46 char *c = &(arcs_cmdline[0]);
47 int i;
48
49 for (i = 1; i < prom_argc; i++) {
50 strcpy(c, prom_argv[i]);
51 c += strlen(prom_argv[i]);
52 if (i < prom_argc-1)
53 *c++ = ' ';
54 }
55 *c = 0;
56}
57
58void __init prom_init(void)
59{
60 prom_argc = fw_arg0;
61 prom_argv = (char **)fw_arg1;
62 prom_envp = (char **)fw_arg2;
63
64 prom_init_cmdline();
65
66 memsize = env_or_default("memsize", DEFAULT_MEMSIZE);
67 highmemsize = env_or_default("highmemsize", 0x0);
68}
69
70void __init prom_free_prom_memory(void)
71{
72}
73
74#define PORT(offset) (u8 *)(KSEG1ADDR(LS1X_UART0_BASE + offset))
75
76void __init prom_putchar(char c)
77{
78 int timeout;
79
80 timeout = 1024;
81
82 while (((readb(PORT(UART_LSR)) & UART_LSR_THRE) == 0)
83 && (timeout-- > 0))
84 ;
85
86 writeb(c, PORT(UART_TX));
87}
diff --git a/arch/mips/loongson1/common/reset.c b/arch/mips/loongson1/common/reset.c
new file mode 100644
index 000000000000..fb979a784eca
--- /dev/null
+++ b/arch/mips/loongson1/common/reset.c
@@ -0,0 +1,45 @@
1/*
2 * Copyright (c) 2011 Zhang, Keguang <keguang.zhang@gmail.com>
3 *
4 * This program is free software; you can redistribute it and/or modify it
5 * under the terms of the GNU General Public License as published by the
6 * Free Software Foundation; either version 2 of the License, or (at your
7 * option) any later version.
8 */
9
10#include <linux/io.h>
11#include <linux/pm.h>
12#include <asm/reboot.h>
13
14#include <loongson1.h>
15
16static void ls1x_restart(char *command)
17{
18 __raw_writel(0x1, LS1X_WDT_EN);
19 __raw_writel(0x5000000, LS1X_WDT_TIMER);
20 __raw_writel(0x1, LS1X_WDT_SET);
21}
22
23static void ls1x_halt(void)
24{
25 while (1) {
26 if (cpu_wait)
27 cpu_wait();
28 }
29}
30
31static void ls1x_power_off(void)
32{
33 ls1x_halt();
34}
35
36static int __init ls1x_reboot_setup(void)
37{
38 _machine_restart = ls1x_restart;
39 _machine_halt = ls1x_halt;
40 pm_power_off = ls1x_power_off;
41
42 return 0;
43}
44
45arch_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 000000000000..62128cc27e68
--- /dev/null
+++ b/arch/mips/loongson1/common/setup.c
@@ -0,0 +1,29 @@
1/*
2 * Copyright (c) 2011 Zhang, Keguang <keguang.zhang@gmail.com>
3 *
4 * This program is free software; you can redistribute it and/or modify it
5 * under the terms of the GNU General Public License as published by the
6 * Free Software Foundation; either version 2 of the License, or (at your
7 * option) any later version.
8 */
9
10#include <asm/bootinfo.h>
11
12#include <prom.h>
13
14void __init plat_mem_setup(void)
15{
16 add_memory_region(0x0, (memsize << 20), BOOT_MEM_RAM);
17}
18
19const char *get_system_type(void)
20{
21 unsigned int processor_id = (&current_cpu_data)->processor_id;
22
23 switch (processor_id & PRID_REV_MASK) {
24 case PRID_REV_LOONGSON1B:
25 return "LOONGSON LS1B";
26 default:
27 return "LOONGSON (unknown)";
28 }
29}