aboutsummaryrefslogtreecommitdiffstats
path: root/arch/mips/loongson32
diff options
context:
space:
mode:
Diffstat (limited to 'arch/mips/loongson32')
-rw-r--r--arch/mips/loongson32/Kconfig75
-rw-r--r--arch/mips/loongson32/Makefile18
-rw-r--r--arch/mips/loongson32/Platform3
-rw-r--r--arch/mips/loongson32/common/Makefile6
-rw-r--r--arch/mips/loongson32/common/irq.c191
-rw-r--r--arch/mips/loongson32/common/platform.c311
-rw-r--r--arch/mips/loongson32/common/prom.c46
-rw-r--r--arch/mips/loongson32/common/reset.c51
-rw-r--r--arch/mips/loongson32/common/setup.c26
-rw-r--r--arch/mips/loongson32/common/time.c232
-rw-r--r--arch/mips/loongson32/ls1b/Makefile6
-rw-r--r--arch/mips/loongson32/ls1b/board.c58
-rw-r--r--arch/mips/loongson32/ls1c/Makefile6
-rw-r--r--arch/mips/loongson32/ls1c/board.c23
14 files changed, 1052 insertions, 0 deletions
diff --git a/arch/mips/loongson32/Kconfig b/arch/mips/loongson32/Kconfig
new file mode 100644
index 000000000..e27879b48
--- /dev/null
+++ b/arch/mips/loongson32/Kconfig
@@ -0,0 +1,75 @@
1# SPDX-License-Identifier: GPL-2.0
2if MACH_LOONGSON32
3
4choice
5 prompt "Machine Type"
6
7config LOONGSON1_LS1B
8 bool "Loongson LS1B board"
9 select CEVT_R4K if !MIPS_EXTERNAL_TIMER
10 select CSRC_R4K if !MIPS_EXTERNAL_TIMER
11 select SYS_HAS_CPU_LOONGSON1B
12 select DMA_NONCOHERENT
13 select BOOT_ELF32
14 select IRQ_MIPS_CPU
15 select SYS_SUPPORTS_32BIT_KERNEL
16 select SYS_SUPPORTS_LITTLE_ENDIAN
17 select SYS_SUPPORTS_HIGHMEM
18 select SYS_HAS_EARLY_PRINTK
19 select USE_GENERIC_EARLY_PRINTK_8250
20 select COMMON_CLK
21
22config LOONGSON1_LS1C
23 bool "Loongson LS1C board"
24 select CEVT_R4K if !MIPS_EXTERNAL_TIMER
25 select CSRC_R4K if !MIPS_EXTERNAL_TIMER
26 select SYS_HAS_CPU_LOONGSON1C
27 select DMA_NONCOHERENT
28 select BOOT_ELF32
29 select IRQ_MIPS_CPU
30 select SYS_SUPPORTS_32BIT_KERNEL
31 select SYS_SUPPORTS_LITTLE_ENDIAN
32 select SYS_SUPPORTS_HIGHMEM
33 select SYS_HAS_EARLY_PRINTK
34 select USE_GENERIC_EARLY_PRINTK_8250
35 select COMMON_CLK
36endchoice
37
38menuconfig CEVT_CSRC_LS1X
39 bool "Use PWM Timer for clockevent/clocksource"
40 select MIPS_EXTERNAL_TIMER
41 depends on CPU_LOONGSON32
42 help
43 This option changes the default clockevent/clocksource to PWM Timer,
44 and is required by Loongson1 CPUFreq support.
45
46 If unsure, say N.
47
48choice
49 prompt "Select clockevent/clocksource"
50 depends on CEVT_CSRC_LS1X
51 default TIMER_USE_PWM0
52
53config TIMER_USE_PWM0
54 bool "Use PWM Timer 0"
55 help
56 Use PWM Timer 0 as the default clockevent/clocksourcer.
57
58config TIMER_USE_PWM1
59 bool "Use PWM Timer 1"
60 help
61 Use PWM Timer 1 as the default clockevent/clocksourcer.
62
63config TIMER_USE_PWM2
64 bool "Use PWM Timer 2"
65 help
66 Use PWM Timer 2 as the default clockevent/clocksourcer.
67
68config TIMER_USE_PWM3
69 bool "Use PWM Timer 3"
70 help
71 Use PWM Timer 3 as the default clockevent/clocksourcer.
72
73endchoice
74
75endif # MACH_LOONGSON32
diff --git a/arch/mips/loongson32/Makefile b/arch/mips/loongson32/Makefile
new file mode 100644
index 000000000..ba10954b4
--- /dev/null
+++ b/arch/mips/loongson32/Makefile
@@ -0,0 +1,18 @@
1# SPDX-License-Identifier: GPL-2.0-only
2#
3# Common code for all Loongson 1 based systems
4#
5
6obj-$(CONFIG_MACH_LOONGSON32) += common/
7
8#
9# Loongson LS1B board
10#
11
12obj-$(CONFIG_LOONGSON1_LS1B) += ls1b/
13
14#
15# Loongson LS1C board
16#
17
18obj-$(CONFIG_LOONGSON1_LS1C) += ls1c/
diff --git a/arch/mips/loongson32/Platform b/arch/mips/loongson32/Platform
new file mode 100644
index 000000000..3b9673e7a
--- /dev/null
+++ b/arch/mips/loongson32/Platform
@@ -0,0 +1,3 @@
1cflags-$(CONFIG_CPU_LOONGSON32) += -march=mips32r2 -Wa,--trap
2cflags-$(CONFIG_MACH_LOONGSON32) += -I$(srctree)/arch/mips/include/asm/mach-loongson32
3load-$(CONFIG_CPU_LOONGSON32) += 0xffffffff80200000
diff --git a/arch/mips/loongson32/common/Makefile b/arch/mips/loongson32/common/Makefile
new file mode 100644
index 000000000..7b49c8260
--- /dev/null
+++ b/arch/mips/loongson32/common/Makefile
@@ -0,0 +1,6 @@
1# SPDX-License-Identifier: GPL-2.0-only
2#
3# Makefile for common code of loongson1 based machines.
4#
5
6obj-y += time.o irq.o platform.o prom.o reset.o setup.o
diff --git a/arch/mips/loongson32/common/irq.c b/arch/mips/loongson32/common/irq.c
new file mode 100644
index 000000000..9a50070f7
--- /dev/null
+++ b/arch/mips/loongson32/common/irq.c
@@ -0,0 +1,191 @@
1// SPDX-License-Identifier: GPL-2.0-or-later
2/*
3 * Copyright (c) 2011 Zhang, Keguang <keguang.zhang@gmail.com>
4 */
5
6#include <linux/interrupt.h>
7#include <linux/irq.h>
8#include <asm/irq_cpu.h>
9
10#include <loongson1.h>
11#include <irq.h>
12
13#define LS1X_INTC_REG(n, x) \
14 ((void __iomem *)KSEG1ADDR(LS1X_INTC_BASE + (n * 0x18) + (x)))
15
16#define LS1X_INTC_INTISR(n) LS1X_INTC_REG(n, 0x0)
17#define LS1X_INTC_INTIEN(n) LS1X_INTC_REG(n, 0x4)
18#define LS1X_INTC_INTSET(n) LS1X_INTC_REG(n, 0x8)
19#define LS1X_INTC_INTCLR(n) LS1X_INTC_REG(n, 0xc)
20#define LS1X_INTC_INTPOL(n) LS1X_INTC_REG(n, 0x10)
21#define LS1X_INTC_INTEDGE(n) LS1X_INTC_REG(n, 0x14)
22
23static void ls1x_irq_ack(struct irq_data *d)
24{
25 unsigned int bit = (d->irq - LS1X_IRQ_BASE) & 0x1f;
26 unsigned int n = (d->irq - LS1X_IRQ_BASE) >> 5;
27
28 __raw_writel(__raw_readl(LS1X_INTC_INTCLR(n))
29 | (1 << bit), LS1X_INTC_INTCLR(n));
30}
31
32static void ls1x_irq_mask(struct irq_data *d)
33{
34 unsigned int bit = (d->irq - LS1X_IRQ_BASE) & 0x1f;
35 unsigned int n = (d->irq - LS1X_IRQ_BASE) >> 5;
36
37 __raw_writel(__raw_readl(LS1X_INTC_INTIEN(n))
38 & ~(1 << bit), LS1X_INTC_INTIEN(n));
39}
40
41static void ls1x_irq_mask_ack(struct irq_data *d)
42{
43 unsigned int bit = (d->irq - LS1X_IRQ_BASE) & 0x1f;
44 unsigned int n = (d->irq - LS1X_IRQ_BASE) >> 5;
45
46 __raw_writel(__raw_readl(LS1X_INTC_INTIEN(n))
47 & ~(1 << bit), LS1X_INTC_INTIEN(n));
48 __raw_writel(__raw_readl(LS1X_INTC_INTCLR(n))
49 | (1 << bit), LS1X_INTC_INTCLR(n));
50}
51
52static void ls1x_irq_unmask(struct irq_data *d)
53{
54 unsigned int bit = (d->irq - LS1X_IRQ_BASE) & 0x1f;
55 unsigned int n = (d->irq - LS1X_IRQ_BASE) >> 5;
56
57 __raw_writel(__raw_readl(LS1X_INTC_INTIEN(n))
58 | (1 << bit), LS1X_INTC_INTIEN(n));
59}
60
61static int ls1x_irq_settype(struct irq_data *d, unsigned int type)
62{
63 unsigned int bit = (d->irq - LS1X_IRQ_BASE) & 0x1f;
64 unsigned int n = (d->irq - LS1X_IRQ_BASE) >> 5;
65
66 switch (type) {
67 case IRQ_TYPE_LEVEL_HIGH:
68 __raw_writel(__raw_readl(LS1X_INTC_INTPOL(n))
69 | (1 << bit), LS1X_INTC_INTPOL(n));
70 __raw_writel(__raw_readl(LS1X_INTC_INTEDGE(n))
71 & ~(1 << bit), LS1X_INTC_INTEDGE(n));
72 break;
73 case IRQ_TYPE_LEVEL_LOW:
74 __raw_writel(__raw_readl(LS1X_INTC_INTPOL(n))
75 & ~(1 << bit), LS1X_INTC_INTPOL(n));
76 __raw_writel(__raw_readl(LS1X_INTC_INTEDGE(n))
77 & ~(1 << bit), LS1X_INTC_INTEDGE(n));
78 break;
79 case IRQ_TYPE_EDGE_RISING:
80 __raw_writel(__raw_readl(LS1X_INTC_INTPOL(n))
81 | (1 << bit), LS1X_INTC_INTPOL(n));
82 __raw_writel(__raw_readl(LS1X_INTC_INTEDGE(n))
83 | (1 << bit), LS1X_INTC_INTEDGE(n));
84 break;
85 case IRQ_TYPE_EDGE_FALLING:
86 __raw_writel(__raw_readl(LS1X_INTC_INTPOL(n))
87 & ~(1 << bit), LS1X_INTC_INTPOL(n));
88 __raw_writel(__raw_readl(LS1X_INTC_INTEDGE(n))
89 | (1 << bit), LS1X_INTC_INTEDGE(n));
90 break;
91 case IRQ_TYPE_EDGE_BOTH:
92 __raw_writel(__raw_readl(LS1X_INTC_INTPOL(n))
93 & ~(1 << bit), LS1X_INTC_INTPOL(n));
94 __raw_writel(__raw_readl(LS1X_INTC_INTEDGE(n))
95 | (1 << bit), LS1X_INTC_INTEDGE(n));
96 break;
97 case IRQ_TYPE_NONE:
98 break;
99 default:
100 return -EINVAL;
101 }
102
103 return 0;
104}
105
106static struct irq_chip ls1x_irq_chip = {
107 .name = "LS1X-INTC",
108 .irq_ack = ls1x_irq_ack,
109 .irq_mask = ls1x_irq_mask,
110 .irq_mask_ack = ls1x_irq_mask_ack,
111 .irq_unmask = ls1x_irq_unmask,
112 .irq_set_type = ls1x_irq_settype,
113};
114
115static void ls1x_irq_dispatch(int n)
116{
117 u32 int_status, irq;
118
119 /* Get pending sources, masked by current enables */
120 int_status = __raw_readl(LS1X_INTC_INTISR(n)) &
121 __raw_readl(LS1X_INTC_INTIEN(n));
122
123 if (int_status) {
124 irq = LS1X_IRQ(n, __ffs(int_status));
125 do_IRQ(irq);
126 }
127}
128
129asmlinkage void plat_irq_dispatch(void)
130{
131 unsigned int pending;
132
133 pending = read_c0_cause() & read_c0_status() & ST0_IM;
134
135 if (pending & CAUSEF_IP7)
136 do_IRQ(TIMER_IRQ);
137 else if (pending & CAUSEF_IP2)
138 ls1x_irq_dispatch(0); /* INT0 */
139 else if (pending & CAUSEF_IP3)
140 ls1x_irq_dispatch(1); /* INT1 */
141 else if (pending & CAUSEF_IP4)
142 ls1x_irq_dispatch(2); /* INT2 */
143 else if (pending & CAUSEF_IP5)
144 ls1x_irq_dispatch(3); /* INT3 */
145 else if (pending & CAUSEF_IP6)
146 ls1x_irq_dispatch(4); /* INT4 */
147 else
148 spurious_interrupt();
149
150}
151
152static void __init ls1x_irq_init(int base)
153{
154 int n;
155
156 /* Disable interrupts and clear pending,
157 * setup all IRQs as high level triggered
158 */
159 for (n = 0; n < INTN; n++) {
160 __raw_writel(0x0, LS1X_INTC_INTIEN(n));
161 __raw_writel(0xffffffff, LS1X_INTC_INTCLR(n));
162 __raw_writel(0xffffffff, LS1X_INTC_INTPOL(n));
163 /* set DMA0, DMA1 and DMA2 to edge trigger */
164 __raw_writel(n ? 0x0 : 0xe000, LS1X_INTC_INTEDGE(n));
165 }
166
167
168 for (n = base; n < NR_IRQS; n++) {
169 irq_set_chip_and_handler(n, &ls1x_irq_chip,
170 handle_level_irq);
171 }
172
173 if (request_irq(INT0_IRQ, no_action, IRQF_NO_THREAD, "cascade", NULL))
174 pr_err("Failed to request irq %d (cascade)\n", INT0_IRQ);
175 if (request_irq(INT1_IRQ, no_action, IRQF_NO_THREAD, "cascade", NULL))
176 pr_err("Failed to request irq %d (cascade)\n", INT1_IRQ);
177 if (request_irq(INT2_IRQ, no_action, IRQF_NO_THREAD, "cascade", NULL))
178 pr_err("Failed to request irq %d (cascade)\n", INT2_IRQ);
179 if (request_irq(INT3_IRQ, no_action, IRQF_NO_THREAD, "cascade", NULL))
180 pr_err("Failed to request irq %d (cascade)\n", INT3_IRQ);
181#if defined(CONFIG_LOONGSON1_LS1C)
182 if (request_irq(INT4_IRQ, no_action, IRQF_NO_THREAD, "cascade", NULL))
183 pr_err("Failed to request irq %d (cascade)\n", INT4_IRQ);
184#endif
185}
186
187void __init arch_init_irq(void)
188{
189 mips_cpu_irq_init();
190 ls1x_irq_init(LS1X_IRQ_BASE);
191}
diff --git a/arch/mips/loongson32/common/platform.c b/arch/mips/loongson32/common/platform.c
new file mode 100644
index 000000000..311dc1580
--- /dev/null
+++ b/arch/mips/loongson32/common/platform.c
@@ -0,0 +1,311 @@
1// SPDX-License-Identifier: GPL-2.0-or-later
2/*
3 * Copyright (c) 2011-2016 Zhang, Keguang <keguang.zhang@gmail.com>
4 */
5
6#include <linux/clk.h>
7#include <linux/dma-mapping.h>
8#include <linux/err.h>
9#include <linux/mtd/partitions.h>
10#include <linux/sizes.h>
11#include <linux/phy.h>
12#include <linux/serial_8250.h>
13#include <linux/stmmac.h>
14#include <linux/usb/ehci_pdriver.h>
15
16#include <platform.h>
17#include <loongson1.h>
18#include <cpufreq.h>
19#include <dma.h>
20#include <nand.h>
21
22/* 8250/16550 compatible UART */
23#define LS1X_UART(_id) \
24 { \
25 .mapbase = LS1X_UART ## _id ## _BASE, \
26 .irq = LS1X_UART ## _id ## _IRQ, \
27 .iotype = UPIO_MEM, \
28 .flags = UPF_IOREMAP | UPF_FIXED_TYPE, \
29 .type = PORT_16550A, \
30 }
31
32static struct plat_serial8250_port ls1x_serial8250_pdata[] = {
33 LS1X_UART(0),
34 LS1X_UART(1),
35 LS1X_UART(2),
36 LS1X_UART(3),
37 {},
38};
39
40struct platform_device ls1x_uart_pdev = {
41 .name = "serial8250",
42 .id = PLAT8250_DEV_PLATFORM,
43 .dev = {
44 .platform_data = ls1x_serial8250_pdata,
45 },
46};
47
48void __init ls1x_serial_set_uartclk(struct platform_device *pdev)
49{
50 struct clk *clk;
51 struct plat_serial8250_port *p;
52
53 clk = clk_get(&pdev->dev, pdev->name);
54 if (IS_ERR(clk)) {
55 pr_err("unable to get %s clock, err=%ld",
56 pdev->name, PTR_ERR(clk));
57 return;
58 }
59 clk_prepare_enable(clk);
60
61 for (p = pdev->dev.platform_data; p->flags != 0; ++p)
62 p->uartclk = clk_get_rate(clk);
63}
64
65/* CPUFreq */
66static struct plat_ls1x_cpufreq ls1x_cpufreq_pdata = {
67 .clk_name = "cpu_clk",
68 .osc_clk_name = "osc_clk",
69 .max_freq = 266 * 1000,
70 .min_freq = 33 * 1000,
71};
72
73struct platform_device ls1x_cpufreq_pdev = {
74 .name = "ls1x-cpufreq",
75 .dev = {
76 .platform_data = &ls1x_cpufreq_pdata,
77 },
78};
79
80/* Synopsys Ethernet GMAC */
81static struct stmmac_mdio_bus_data ls1x_mdio_bus_data = {
82 .phy_mask = 0,
83};
84
85static struct stmmac_dma_cfg ls1x_eth_dma_cfg = {
86 .pbl = 1,
87};
88
89int ls1x_eth_mux_init(struct platform_device *pdev, void *priv)
90{
91 struct plat_stmmacenet_data *plat_dat = NULL;
92 u32 val;
93
94 val = __raw_readl(LS1X_MUX_CTRL1);
95
96#if defined(CONFIG_LOONGSON1_LS1B)
97 plat_dat = dev_get_platdata(&pdev->dev);
98 if (plat_dat->bus_id) {
99 __raw_writel(__raw_readl(LS1X_MUX_CTRL0) | GMAC1_USE_UART1 |
100 GMAC1_USE_UART0, LS1X_MUX_CTRL0);
101 switch (plat_dat->phy_interface) {
102 case PHY_INTERFACE_MODE_RGMII:
103 val &= ~(GMAC1_USE_TXCLK | GMAC1_USE_PWM23);
104 break;
105 case PHY_INTERFACE_MODE_MII:
106 val |= (GMAC1_USE_TXCLK | GMAC1_USE_PWM23);
107 break;
108 default:
109 pr_err("unsupported mii mode %d\n",
110 plat_dat->phy_interface);
111 return -ENOTSUPP;
112 }
113 val &= ~GMAC1_SHUT;
114 } else {
115 switch (plat_dat->phy_interface) {
116 case PHY_INTERFACE_MODE_RGMII:
117 val &= ~(GMAC0_USE_TXCLK | GMAC0_USE_PWM01);
118 break;
119 case PHY_INTERFACE_MODE_MII:
120 val |= (GMAC0_USE_TXCLK | GMAC0_USE_PWM01);
121 break;
122 default:
123 pr_err("unsupported mii mode %d\n",
124 plat_dat->phy_interface);
125 return -ENOTSUPP;
126 }
127 val &= ~GMAC0_SHUT;
128 }
129 __raw_writel(val, LS1X_MUX_CTRL1);
130#elif defined(CONFIG_LOONGSON1_LS1C)
131 plat_dat = dev_get_platdata(&pdev->dev);
132
133 val &= ~PHY_INTF_SELI;
134 if (plat_dat->phy_interface == PHY_INTERFACE_MODE_RMII)
135 val |= 0x4 << PHY_INTF_SELI_SHIFT;
136 __raw_writel(val, LS1X_MUX_CTRL1);
137
138 val = __raw_readl(LS1X_MUX_CTRL0);
139 __raw_writel(val & (~GMAC_SHUT), LS1X_MUX_CTRL0);
140#endif
141
142 return 0;
143}
144
145static struct plat_stmmacenet_data ls1x_eth0_pdata = {
146 .bus_id = 0,
147 .phy_addr = -1,
148#if defined(CONFIG_LOONGSON1_LS1B)
149 .phy_interface = PHY_INTERFACE_MODE_MII,
150#elif defined(CONFIG_LOONGSON1_LS1C)
151 .phy_interface = PHY_INTERFACE_MODE_RMII,
152#endif
153 .mdio_bus_data = &ls1x_mdio_bus_data,
154 .dma_cfg = &ls1x_eth_dma_cfg,
155 .has_gmac = 1,
156 .tx_coe = 1,
157 .rx_queues_to_use = 1,
158 .tx_queues_to_use = 1,
159 .init = ls1x_eth_mux_init,
160};
161
162static struct resource ls1x_eth0_resources[] = {
163 [0] = {
164 .start = LS1X_GMAC0_BASE,
165 .end = LS1X_GMAC0_BASE + SZ_64K - 1,
166 .flags = IORESOURCE_MEM,
167 },
168 [1] = {
169 .name = "macirq",
170 .start = LS1X_GMAC0_IRQ,
171 .flags = IORESOURCE_IRQ,
172 },
173};
174
175struct platform_device ls1x_eth0_pdev = {
176 .name = "stmmaceth",
177 .id = 0,
178 .num_resources = ARRAY_SIZE(ls1x_eth0_resources),
179 .resource = ls1x_eth0_resources,
180 .dev = {
181 .platform_data = &ls1x_eth0_pdata,
182 },
183};
184
185#ifdef CONFIG_LOONGSON1_LS1B
186static struct plat_stmmacenet_data ls1x_eth1_pdata = {
187 .bus_id = 1,
188 .phy_addr = -1,
189 .phy_interface = PHY_INTERFACE_MODE_MII,
190 .mdio_bus_data = &ls1x_mdio_bus_data,
191 .dma_cfg = &ls1x_eth_dma_cfg,
192 .has_gmac = 1,
193 .tx_coe = 1,
194 .rx_queues_to_use = 1,
195 .tx_queues_to_use = 1,
196 .init = ls1x_eth_mux_init,
197};
198
199static struct resource ls1x_eth1_resources[] = {
200 [0] = {
201 .start = LS1X_GMAC1_BASE,
202 .end = LS1X_GMAC1_BASE + SZ_64K - 1,
203 .flags = IORESOURCE_MEM,
204 },
205 [1] = {
206 .name = "macirq",
207 .start = LS1X_GMAC1_IRQ,
208 .flags = IORESOURCE_IRQ,
209 },
210};
211
212struct platform_device ls1x_eth1_pdev = {
213 .name = "stmmaceth",
214 .id = 1,
215 .num_resources = ARRAY_SIZE(ls1x_eth1_resources),
216 .resource = ls1x_eth1_resources,
217 .dev = {
218 .platform_data = &ls1x_eth1_pdata,
219 },
220};
221#endif /* CONFIG_LOONGSON1_LS1B */
222
223/* GPIO */
224static struct resource ls1x_gpio0_resources[] = {
225 [0] = {
226 .start = LS1X_GPIO0_BASE,
227 .end = LS1X_GPIO0_BASE + SZ_4 - 1,
228 .flags = IORESOURCE_MEM,
229 },
230};
231
232struct platform_device ls1x_gpio0_pdev = {
233 .name = "ls1x-gpio",
234 .id = 0,
235 .num_resources = ARRAY_SIZE(ls1x_gpio0_resources),
236 .resource = ls1x_gpio0_resources,
237};
238
239static struct resource ls1x_gpio1_resources[] = {
240 [0] = {
241 .start = LS1X_GPIO1_BASE,
242 .end = LS1X_GPIO1_BASE + SZ_4 - 1,
243 .flags = IORESOURCE_MEM,
244 },
245};
246
247struct platform_device ls1x_gpio1_pdev = {
248 .name = "ls1x-gpio",
249 .id = 1,
250 .num_resources = ARRAY_SIZE(ls1x_gpio1_resources),
251 .resource = ls1x_gpio1_resources,
252};
253
254/* USB EHCI */
255static u64 ls1x_ehci_dmamask = DMA_BIT_MASK(32);
256
257static struct resource ls1x_ehci_resources[] = {
258 [0] = {
259 .start = LS1X_EHCI_BASE,
260 .end = LS1X_EHCI_BASE + SZ_32K - 1,
261 .flags = IORESOURCE_MEM,
262 },
263 [1] = {
264 .start = LS1X_EHCI_IRQ,
265 .flags = IORESOURCE_IRQ,
266 },
267};
268
269static struct usb_ehci_pdata ls1x_ehci_pdata = {
270};
271
272struct platform_device ls1x_ehci_pdev = {
273 .name = "ehci-platform",
274 .id = -1,
275 .num_resources = ARRAY_SIZE(ls1x_ehci_resources),
276 .resource = ls1x_ehci_resources,
277 .dev = {
278 .dma_mask = &ls1x_ehci_dmamask,
279 .platform_data = &ls1x_ehci_pdata,
280 },
281};
282
283/* Real Time Clock */
284void __init ls1x_rtc_set_extclk(struct platform_device *pdev)
285{
286 u32 val = __raw_readl(LS1X_RTC_CTRL);
287
288 if (!(val & RTC_EXTCLK_OK))
289 __raw_writel(val | RTC_EXTCLK_EN, LS1X_RTC_CTRL);
290}
291
292struct platform_device ls1x_rtc_pdev = {
293 .name = "ls1x-rtc",
294 .id = -1,
295};
296
297/* Watchdog */
298static struct resource ls1x_wdt_resources[] = {
299 {
300 .start = LS1X_WDT_BASE,
301 .end = LS1X_WDT_BASE + SZ_16 - 1,
302 .flags = IORESOURCE_MEM,
303 },
304};
305
306struct platform_device ls1x_wdt_pdev = {
307 .name = "ls1x-wdt",
308 .id = -1,
309 .num_resources = ARRAY_SIZE(ls1x_wdt_resources),
310 .resource = ls1x_wdt_resources,
311};
diff --git a/arch/mips/loongson32/common/prom.c b/arch/mips/loongson32/common/prom.c
new file mode 100644
index 000000000..c133b5adf
--- /dev/null
+++ b/arch/mips/loongson32/common/prom.c
@@ -0,0 +1,46 @@
1// SPDX-License-Identifier: GPL-2.0-or-later
2/*
3 * Copyright (c) 2011 Zhang, Keguang <keguang.zhang@gmail.com>
4 *
5 * Modified from arch/mips/pnx833x/common/prom.c.
6 */
7
8#include <linux/io.h>
9#include <linux/init.h>
10#include <linux/memblock.h>
11#include <linux/serial_reg.h>
12#include <asm/fw/fw.h>
13
14#include <loongson1.h>
15
16unsigned long memsize;
17
18void __init prom_init(void)
19{
20 void __iomem *uart_base;
21
22 fw_init_cmdline();
23
24 memsize = fw_getenvl("memsize");
25 if(!memsize)
26 memsize = DEFAULT_MEMSIZE;
27
28 if (strstr(arcs_cmdline, "console=ttyS3"))
29 uart_base = ioremap(LS1X_UART3_BASE, 0x0f);
30 else if (strstr(arcs_cmdline, "console=ttyS2"))
31 uart_base = ioremap(LS1X_UART2_BASE, 0x0f);
32 else if (strstr(arcs_cmdline, "console=ttyS1"))
33 uart_base = ioremap(LS1X_UART1_BASE, 0x0f);
34 else
35 uart_base = ioremap(LS1X_UART0_BASE, 0x0f);
36 setup_8250_early_printk_port((unsigned long)uart_base, 0, 0);
37}
38
39void __init prom_free_prom_memory(void)
40{
41}
42
43void __init plat_mem_setup(void)
44{
45 memblock_add(0x0, (memsize << 20));
46}
diff --git a/arch/mips/loongson32/common/reset.c b/arch/mips/loongson32/common/reset.c
new file mode 100644
index 000000000..0c7399b30
--- /dev/null
+++ b/arch/mips/loongson32/common/reset.c
@@ -0,0 +1,51 @@
1// SPDX-License-Identifier: GPL-2.0-or-later
2/*
3 * Copyright (c) 2011 Zhang, Keguang <keguang.zhang@gmail.com>
4 */
5
6#include <linux/io.h>
7#include <linux/pm.h>
8#include <linux/sizes.h>
9#include <asm/idle.h>
10#include <asm/reboot.h>
11
12#include <loongson1.h>
13
14static void __iomem *wdt_reg_base;
15
16static void ls1x_halt(void)
17{
18 while (1) {
19 if (cpu_wait)
20 cpu_wait();
21 }
22}
23
24static void ls1x_restart(char *command)
25{
26 __raw_writel(0x1, wdt_reg_base + WDT_EN);
27 __raw_writel(0x1, wdt_reg_base + WDT_TIMER);
28 __raw_writel(0x1, wdt_reg_base + WDT_SET);
29
30 ls1x_halt();
31}
32
33static void ls1x_power_off(void)
34{
35 ls1x_halt();
36}
37
38static int __init ls1x_reboot_setup(void)
39{
40 wdt_reg_base = ioremap(LS1X_WDT_BASE, (SZ_4 + SZ_8));
41 if (!wdt_reg_base)
42 panic("Failed to remap watchdog registers");
43
44 _machine_restart = ls1x_restart;
45 _machine_halt = ls1x_halt;
46 pm_power_off = ls1x_power_off;
47
48 return 0;
49}
50
51arch_initcall(ls1x_reboot_setup);
diff --git a/arch/mips/loongson32/common/setup.c b/arch/mips/loongson32/common/setup.c
new file mode 100644
index 000000000..4733fe037
--- /dev/null
+++ b/arch/mips/loongson32/common/setup.c
@@ -0,0 +1,26 @@
1// SPDX-License-Identifier: GPL-2.0-or-later
2/*
3 * Copyright (c) 2011 Zhang, Keguang <keguang.zhang@gmail.com>
4 */
5
6#include <linux/io.h>
7#include <linux/init.h>
8#include <linux/smp.h>
9#include <asm/cpu-info.h>
10#include <asm/bootinfo.h>
11
12const char *get_system_type(void)
13{
14 unsigned int processor_id = (&current_cpu_data)->processor_id;
15
16 switch (processor_id & PRID_REV_MASK) {
17 case PRID_REV_LOONGSON1B:
18#if defined(CONFIG_LOONGSON1_LS1B)
19 return "LOONGSON LS1B";
20#elif defined(CONFIG_LOONGSON1_LS1C)
21 return "LOONGSON LS1C";
22#endif
23 default:
24 return "LOONGSON (unknown)";
25 }
26}
diff --git a/arch/mips/loongson32/common/time.c b/arch/mips/loongson32/common/time.c
new file mode 100644
index 000000000..459b15c96
--- /dev/null
+++ b/arch/mips/loongson32/common/time.c
@@ -0,0 +1,232 @@
1// SPDX-License-Identifier: GPL-2.0-or-later
2/*
3 * Copyright (c) 2014 Zhang, Keguang <keguang.zhang@gmail.com>
4 */
5
6#include <linux/clk.h>
7#include <linux/interrupt.h>
8#include <linux/sizes.h>
9#include <asm/time.h>
10
11#include <loongson1.h>
12#include <platform.h>
13
14#ifdef CONFIG_CEVT_CSRC_LS1X
15
16#if defined(CONFIG_TIMER_USE_PWM1)
17#define LS1X_TIMER_BASE LS1X_PWM1_BASE
18#define LS1X_TIMER_IRQ LS1X_PWM1_IRQ
19
20#elif defined(CONFIG_TIMER_USE_PWM2)
21#define LS1X_TIMER_BASE LS1X_PWM2_BASE
22#define LS1X_TIMER_IRQ LS1X_PWM2_IRQ
23
24#elif defined(CONFIG_TIMER_USE_PWM3)
25#define LS1X_TIMER_BASE LS1X_PWM3_BASE
26#define LS1X_TIMER_IRQ LS1X_PWM3_IRQ
27
28#else
29#define LS1X_TIMER_BASE LS1X_PWM0_BASE
30#define LS1X_TIMER_IRQ LS1X_PWM0_IRQ
31#endif
32
33DEFINE_RAW_SPINLOCK(ls1x_timer_lock);
34
35static void __iomem *timer_reg_base;
36static uint32_t ls1x_jiffies_per_tick;
37
38static inline void ls1x_pwmtimer_set_period(uint32_t period)
39{
40 __raw_writel(period, timer_reg_base + PWM_HRC);
41 __raw_writel(period, timer_reg_base + PWM_LRC);
42}
43
44static inline void ls1x_pwmtimer_restart(void)
45{
46 __raw_writel(0x0, timer_reg_base + PWM_CNT);
47 __raw_writel(INT_EN | CNT_EN, timer_reg_base + PWM_CTRL);
48}
49
50void __init ls1x_pwmtimer_init(void)
51{
52 timer_reg_base = ioremap(LS1X_TIMER_BASE, SZ_16);
53 if (!timer_reg_base)
54 panic("Failed to remap timer registers");
55
56 ls1x_jiffies_per_tick = DIV_ROUND_CLOSEST(mips_hpt_frequency, HZ);
57
58 ls1x_pwmtimer_set_period(ls1x_jiffies_per_tick);
59 ls1x_pwmtimer_restart();
60}
61
62static u64 ls1x_clocksource_read(struct clocksource *cs)
63{
64 unsigned long flags;
65 int count;
66 u32 jifs;
67 static int old_count;
68 static u32 old_jifs;
69
70 raw_spin_lock_irqsave(&ls1x_timer_lock, flags);
71 /*
72 * Although our caller may have the read side of xtime_lock,
73 * this is now a seqlock, and we are cheating in this routine
74 * by having side effects on state that we cannot undo if
75 * there is a collision on the seqlock and our caller has to
76 * retry. (Namely, old_jifs and old_count.) So we must treat
77 * jiffies as volatile despite the lock. We read jiffies
78 * before latching the timer count to guarantee that although
79 * the jiffies value might be older than the count (that is,
80 * the counter may underflow between the last point where
81 * jiffies was incremented and the point where we latch the
82 * count), it cannot be newer.
83 */
84 jifs = jiffies;
85 /* read the count */
86 count = __raw_readl(timer_reg_base + PWM_CNT);
87
88 /*
89 * It's possible for count to appear to go the wrong way for this
90 * reason:
91 *
92 * The timer counter underflows, but we haven't handled the resulting
93 * interrupt and incremented jiffies yet.
94 *
95 * Previous attempts to handle these cases intelligently were buggy, so
96 * we just do the simple thing now.
97 */
98 if (count < old_count && jifs == old_jifs)
99 count = old_count;
100
101 old_count = count;
102 old_jifs = jifs;
103
104 raw_spin_unlock_irqrestore(&ls1x_timer_lock, flags);
105
106 return (u64) (jifs * ls1x_jiffies_per_tick) + count;
107}
108
109static struct clocksource ls1x_clocksource = {
110 .name = "ls1x-pwmtimer",
111 .read = ls1x_clocksource_read,
112 .mask = CLOCKSOURCE_MASK(24),
113 .flags = CLOCK_SOURCE_IS_CONTINUOUS,
114};
115
116static irqreturn_t ls1x_clockevent_isr(int irq, void *devid)
117{
118 struct clock_event_device *cd = devid;
119
120 ls1x_pwmtimer_restart();
121 cd->event_handler(cd);
122
123 return IRQ_HANDLED;
124}
125
126static int ls1x_clockevent_set_state_periodic(struct clock_event_device *cd)
127{
128 raw_spin_lock(&ls1x_timer_lock);
129 ls1x_pwmtimer_set_period(ls1x_jiffies_per_tick);
130 ls1x_pwmtimer_restart();
131 __raw_writel(INT_EN | CNT_EN, timer_reg_base + PWM_CTRL);
132 raw_spin_unlock(&ls1x_timer_lock);
133
134 return 0;
135}
136
137static int ls1x_clockevent_tick_resume(struct clock_event_device *cd)
138{
139 raw_spin_lock(&ls1x_timer_lock);
140 __raw_writel(INT_EN | CNT_EN, timer_reg_base + PWM_CTRL);
141 raw_spin_unlock(&ls1x_timer_lock);
142
143 return 0;
144}
145
146static int ls1x_clockevent_set_state_shutdown(struct clock_event_device *cd)
147{
148 raw_spin_lock(&ls1x_timer_lock);
149 __raw_writel(__raw_readl(timer_reg_base + PWM_CTRL) & ~CNT_EN,
150 timer_reg_base + PWM_CTRL);
151 raw_spin_unlock(&ls1x_timer_lock);
152
153 return 0;
154}
155
156static int ls1x_clockevent_set_next(unsigned long evt,
157 struct clock_event_device *cd)
158{
159 raw_spin_lock(&ls1x_timer_lock);
160 ls1x_pwmtimer_set_period(evt);
161 ls1x_pwmtimer_restart();
162 raw_spin_unlock(&ls1x_timer_lock);
163
164 return 0;
165}
166
167static struct clock_event_device ls1x_clockevent = {
168 .name = "ls1x-pwmtimer",
169 .features = CLOCK_EVT_FEAT_PERIODIC,
170 .rating = 300,
171 .irq = LS1X_TIMER_IRQ,
172 .set_next_event = ls1x_clockevent_set_next,
173 .set_state_shutdown = ls1x_clockevent_set_state_shutdown,
174 .set_state_periodic = ls1x_clockevent_set_state_periodic,
175 .set_state_oneshot = ls1x_clockevent_set_state_shutdown,
176 .tick_resume = ls1x_clockevent_tick_resume,
177};
178
179static void __init ls1x_time_init(void)
180{
181 struct clock_event_device *cd = &ls1x_clockevent;
182 int ret;
183
184 if (!mips_hpt_frequency)
185 panic("Invalid timer clock rate");
186
187 ls1x_pwmtimer_init();
188
189 clockevent_set_clock(cd, mips_hpt_frequency);
190 cd->max_delta_ns = clockevent_delta2ns(0xffffff, cd);
191 cd->max_delta_ticks = 0xffffff;
192 cd->min_delta_ns = clockevent_delta2ns(0x000300, cd);
193 cd->min_delta_ticks = 0x000300;
194 cd->cpumask = cpumask_of(smp_processor_id());
195 clockevents_register_device(cd);
196
197 ls1x_clocksource.rating = 200 + mips_hpt_frequency / 10000000;
198 ret = clocksource_register_hz(&ls1x_clocksource, mips_hpt_frequency);
199 if (ret)
200 panic(KERN_ERR "Failed to register clocksource: %d\n", ret);
201
202 if (request_irq(LS1X_TIMER_IRQ, ls1x_clockevent_isr,
203 IRQF_PERCPU | IRQF_TIMER, "ls1x-pwmtimer",
204 &ls1x_clockevent))
205 pr_err("Failed to register ls1x-pwmtimer interrupt\n");
206}
207#endif /* CONFIG_CEVT_CSRC_LS1X */
208
209void __init plat_time_init(void)
210{
211 struct clk *clk = NULL;
212
213 /* initialize LS1X clocks */
214 ls1x_clk_init();
215
216#ifdef CONFIG_CEVT_CSRC_LS1X
217 /* setup LS1X PWM timer */
218 clk = clk_get(NULL, "ls1x-pwmtimer");
219 if (IS_ERR(clk))
220 panic("unable to get timer clock, err=%ld", PTR_ERR(clk));
221
222 mips_hpt_frequency = clk_get_rate(clk);
223 ls1x_time_init();
224#else
225 /* setup mips r4k timer */
226 clk = clk_get(NULL, "cpu_clk");
227 if (IS_ERR(clk))
228 panic("unable to get cpu clock, err=%ld", PTR_ERR(clk));
229
230 mips_hpt_frequency = clk_get_rate(clk) / 2;
231#endif /* CONFIG_CEVT_CSRC_LS1X */
232}
diff --git a/arch/mips/loongson32/ls1b/Makefile b/arch/mips/loongson32/ls1b/Makefile
new file mode 100644
index 000000000..33c574dc0
--- /dev/null
+++ b/arch/mips/loongson32/ls1b/Makefile
@@ -0,0 +1,6 @@
1# SPDX-License-Identifier: GPL-2.0-only
2#
3# Makefile for loongson1B based machines.
4#
5
6obj-y += board.o
diff --git a/arch/mips/loongson32/ls1b/board.c b/arch/mips/loongson32/ls1b/board.c
new file mode 100644
index 000000000..727e06718
--- /dev/null
+++ b/arch/mips/loongson32/ls1b/board.c
@@ -0,0 +1,58 @@
1// SPDX-License-Identifier: GPL-2.0-or-later
2/*
3 * Copyright (c) 2011-2016 Zhang, Keguang <keguang.zhang@gmail.com>
4 */
5
6#include <linux/leds.h>
7#include <linux/mtd/partitions.h>
8#include <linux/sizes.h>
9
10#include <loongson1.h>
11#include <dma.h>
12#include <nand.h>
13#include <platform.h>
14
15static const struct gpio_led ls1x_gpio_leds[] __initconst = {
16 {
17 .name = "LED9",
18 .default_trigger = "heartbeat",
19 .gpio = 38,
20 .active_low = 1,
21 .default_state = LEDS_GPIO_DEFSTATE_OFF,
22 }, {
23 .name = "LED6",
24 .default_trigger = "nand-disk",
25 .gpio = 39,
26 .active_low = 1,
27 .default_state = LEDS_GPIO_DEFSTATE_OFF,
28 },
29};
30
31static const struct gpio_led_platform_data ls1x_led_pdata __initconst = {
32 .num_leds = ARRAY_SIZE(ls1x_gpio_leds),
33 .leds = ls1x_gpio_leds,
34};
35
36static struct platform_device *ls1b_platform_devices[] __initdata = {
37 &ls1x_uart_pdev,
38 &ls1x_cpufreq_pdev,
39 &ls1x_eth0_pdev,
40 &ls1x_eth1_pdev,
41 &ls1x_ehci_pdev,
42 &ls1x_gpio0_pdev,
43 &ls1x_gpio1_pdev,
44 &ls1x_rtc_pdev,
45 &ls1x_wdt_pdev,
46};
47
48static int __init ls1b_platform_init(void)
49{
50 ls1x_serial_set_uartclk(&ls1x_uart_pdev);
51
52 gpio_led_register_device(-1, &ls1x_led_pdata);
53
54 return platform_add_devices(ls1b_platform_devices,
55 ARRAY_SIZE(ls1b_platform_devices));
56}
57
58arch_initcall(ls1b_platform_init);
diff --git a/arch/mips/loongson32/ls1c/Makefile b/arch/mips/loongson32/ls1c/Makefile
new file mode 100644
index 000000000..1cf3aa264
--- /dev/null
+++ b/arch/mips/loongson32/ls1c/Makefile
@@ -0,0 +1,6 @@
1# SPDX-License-Identifier: GPL-2.0-only
2#
3# Makefile for loongson1C based machines.
4#
5
6obj-y += board.o
diff --git a/arch/mips/loongson32/ls1c/board.c b/arch/mips/loongson32/ls1c/board.c
new file mode 100644
index 000000000..9dcfe9de5
--- /dev/null
+++ b/arch/mips/loongson32/ls1c/board.c
@@ -0,0 +1,23 @@
1// SPDX-License-Identifier: GPL-2.0-or-later
2/*
3 * Copyright (c) 2016 Yang Ling <gnaygnil@gmail.com>
4 */
5
6#include <platform.h>
7
8static struct platform_device *ls1c_platform_devices[] __initdata = {
9 &ls1x_uart_pdev,
10 &ls1x_eth0_pdev,
11 &ls1x_rtc_pdev,
12 &ls1x_wdt_pdev,
13};
14
15static int __init ls1c_platform_init(void)
16{
17 ls1x_serial_set_uartclk(&ls1x_uart_pdev);
18
19 return platform_add_devices(ls1c_platform_devices,
20 ARRAY_SIZE(ls1c_platform_devices));
21}
22
23arch_initcall(ls1c_platform_init);