diff options
Diffstat (limited to 'arch/mips/loongson1/common')
-rw-r--r-- | arch/mips/loongson1/common/Makefile | 2 | ||||
-rw-r--r-- | arch/mips/loongson1/common/clock.c | 28 | ||||
-rw-r--r-- | arch/mips/loongson1/common/platform.c | 141 | ||||
-rw-r--r-- | arch/mips/loongson1/common/prom.c | 30 | ||||
-rw-r--r-- | arch/mips/loongson1/common/reset.c | 20 | ||||
-rw-r--r-- | arch/mips/loongson1/common/time.c | 226 |
6 files changed, 377 insertions, 70 deletions
diff --git a/arch/mips/loongson1/common/Makefile b/arch/mips/loongson1/common/Makefile index b2797709ef5b..723b4ce3b8f0 100644 --- a/arch/mips/loongson1/common/Makefile +++ b/arch/mips/loongson1/common/Makefile | |||
@@ -2,4 +2,4 @@ | |||
2 | # Makefile for common code of loongson1 based machines. | 2 | # Makefile for common code of loongson1 based machines. |
3 | # | 3 | # |
4 | 4 | ||
5 | obj-y += clock.o irq.o platform.o prom.o reset.o setup.o | 5 | obj-y += time.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 deleted file mode 100644 index b4437f19c3d9..000000000000 --- a/arch/mips/loongson1/common/clock.c +++ /dev/null | |||
@@ -1,28 +0,0 @@ | |||
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/err.h> | ||
12 | #include <asm/time.h> | ||
13 | #include <platform.h> | ||
14 | |||
15 | void __init plat_time_init(void) | ||
16 | { | ||
17 | struct clk *clk; | ||
18 | |||
19 | /* Initialize LS1X clocks */ | ||
20 | ls1x_clk_init(); | ||
21 | |||
22 | /* setup mips r4k timer */ | ||
23 | clk = clk_get(NULL, "cpu"); | ||
24 | if (IS_ERR(clk)) | ||
25 | panic("unable to get cpu clock, err=%ld", PTR_ERR(clk)); | ||
26 | |||
27 | mips_hpt_frequency = clk_get_rate(clk) / 2; | ||
28 | } | ||
diff --git a/arch/mips/loongson1/common/platform.c b/arch/mips/loongson1/common/platform.c index fdf8cb5987a4..ddf1d4cbf31e 100644 --- a/arch/mips/loongson1/common/platform.c +++ b/arch/mips/loongson1/common/platform.c | |||
@@ -16,8 +16,10 @@ | |||
16 | #include <linux/usb/ehci_pdriver.h> | 16 | #include <linux/usb/ehci_pdriver.h> |
17 | #include <asm-generic/sizes.h> | 17 | #include <asm-generic/sizes.h> |
18 | 18 | ||
19 | #include <cpufreq.h> | ||
19 | #include <loongson1.h> | 20 | #include <loongson1.h> |
20 | 21 | ||
22 | /* 8250/16550 compatible UART */ | ||
21 | #define LS1X_UART(_id) \ | 23 | #define LS1X_UART(_id) \ |
22 | { \ | 24 | { \ |
23 | .mapbase = LS1X_UART ## _id ## _BASE, \ | 25 | .mapbase = LS1X_UART ## _id ## _BASE, \ |
@@ -27,7 +29,7 @@ | |||
27 | .type = PORT_16550A, \ | 29 | .type = PORT_16550A, \ |
28 | } | 30 | } |
29 | 31 | ||
30 | static struct plat_serial8250_port ls1x_serial8250_port[] = { | 32 | static struct plat_serial8250_port ls1x_serial8250_pdata[] = { |
31 | LS1X_UART(0), | 33 | LS1X_UART(0), |
32 | LS1X_UART(1), | 34 | LS1X_UART(1), |
33 | LS1X_UART(2), | 35 | LS1X_UART(2), |
@@ -35,11 +37,11 @@ static struct plat_serial8250_port ls1x_serial8250_port[] = { | |||
35 | {}, | 37 | {}, |
36 | }; | 38 | }; |
37 | 39 | ||
38 | struct platform_device ls1x_uart_device = { | 40 | struct platform_device ls1x_uart_pdev = { |
39 | .name = "serial8250", | 41 | .name = "serial8250", |
40 | .id = PLAT8250_DEV_PLATFORM, | 42 | .id = PLAT8250_DEV_PLATFORM, |
41 | .dev = { | 43 | .dev = { |
42 | .platform_data = ls1x_serial8250_port, | 44 | .platform_data = ls1x_serial8250_pdata, |
43 | }, | 45 | }, |
44 | }; | 46 | }; |
45 | 47 | ||
@@ -48,16 +50,97 @@ void __init ls1x_serial_setup(struct platform_device *pdev) | |||
48 | struct clk *clk; | 50 | struct clk *clk; |
49 | struct plat_serial8250_port *p; | 51 | struct plat_serial8250_port *p; |
50 | 52 | ||
51 | clk = clk_get(NULL, pdev->name); | 53 | clk = clk_get(&pdev->dev, pdev->name); |
52 | if (IS_ERR(clk)) | 54 | if (IS_ERR(clk)) { |
53 | panic("unable to get %s clock, err=%ld", | 55 | pr_err("unable to get %s clock, err=%ld", |
54 | pdev->name, PTR_ERR(clk)); | 56 | pdev->name, PTR_ERR(clk)); |
57 | return; | ||
58 | } | ||
59 | clk_prepare_enable(clk); | ||
55 | 60 | ||
56 | for (p = pdev->dev.platform_data; p->flags != 0; ++p) | 61 | for (p = pdev->dev.platform_data; p->flags != 0; ++p) |
57 | p->uartclk = clk_get_rate(clk); | 62 | p->uartclk = clk_get_rate(clk); |
58 | } | 63 | } |
59 | 64 | ||
65 | /* CPUFreq */ | ||
66 | static struct plat_ls1x_cpufreq ls1x_cpufreq_pdata = { | ||
67 | .clk_name = "cpu_clk", | ||
68 | .osc_clk_name = "osc_33m_clk", | ||
69 | .max_freq = 266 * 1000, | ||
70 | .min_freq = 33 * 1000, | ||
71 | }; | ||
72 | |||
73 | struct platform_device ls1x_cpufreq_pdev = { | ||
74 | .name = "ls1x-cpufreq", | ||
75 | .dev = { | ||
76 | .platform_data = &ls1x_cpufreq_pdata, | ||
77 | }, | ||
78 | }; | ||
79 | |||
60 | /* Synopsys Ethernet GMAC */ | 80 | /* Synopsys Ethernet GMAC */ |
81 | static struct stmmac_mdio_bus_data ls1x_mdio_bus_data = { | ||
82 | .phy_mask = 0, | ||
83 | }; | ||
84 | |||
85 | static struct stmmac_dma_cfg ls1x_eth_dma_cfg = { | ||
86 | .pbl = 1, | ||
87 | }; | ||
88 | |||
89 | int 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 | plat_dat = dev_get_platdata(&pdev->dev); | ||
97 | if (plat_dat->bus_id) { | ||
98 | __raw_writel(__raw_readl(LS1X_MUX_CTRL0) | GMAC1_USE_UART1 | | ||
99 | GMAC1_USE_UART0, LS1X_MUX_CTRL0); | ||
100 | switch (plat_dat->interface) { | ||
101 | case PHY_INTERFACE_MODE_RGMII: | ||
102 | val &= ~(GMAC1_USE_TXCLK | GMAC1_USE_PWM23); | ||
103 | break; | ||
104 | case PHY_INTERFACE_MODE_MII: | ||
105 | val |= (GMAC1_USE_TXCLK | GMAC1_USE_PWM23); | ||
106 | break; | ||
107 | default: | ||
108 | pr_err("unsupported mii mode %d\n", | ||
109 | plat_dat->interface); | ||
110 | return -ENOTSUPP; | ||
111 | } | ||
112 | val &= ~GMAC1_SHUT; | ||
113 | } else { | ||
114 | switch (plat_dat->interface) { | ||
115 | case PHY_INTERFACE_MODE_RGMII: | ||
116 | val &= ~(GMAC0_USE_TXCLK | GMAC0_USE_PWM01); | ||
117 | break; | ||
118 | case PHY_INTERFACE_MODE_MII: | ||
119 | val |= (GMAC0_USE_TXCLK | GMAC0_USE_PWM01); | ||
120 | break; | ||
121 | default: | ||
122 | pr_err("unsupported mii mode %d\n", | ||
123 | plat_dat->interface); | ||
124 | return -ENOTSUPP; | ||
125 | } | ||
126 | val &= ~GMAC0_SHUT; | ||
127 | } | ||
128 | __raw_writel(val, LS1X_MUX_CTRL1); | ||
129 | |||
130 | return 0; | ||
131 | } | ||
132 | |||
133 | static struct plat_stmmacenet_data ls1x_eth0_pdata = { | ||
134 | .bus_id = 0, | ||
135 | .phy_addr = -1, | ||
136 | .interface = PHY_INTERFACE_MODE_MII, | ||
137 | .mdio_bus_data = &ls1x_mdio_bus_data, | ||
138 | .dma_cfg = &ls1x_eth_dma_cfg, | ||
139 | .has_gmac = 1, | ||
140 | .tx_coe = 1, | ||
141 | .init = ls1x_eth_mux_init, | ||
142 | }; | ||
143 | |||
61 | static struct resource ls1x_eth0_resources[] = { | 144 | static struct resource ls1x_eth0_resources[] = { |
62 | [0] = { | 145 | [0] = { |
63 | .start = LS1X_GMAC0_BASE, | 146 | .start = LS1X_GMAC0_BASE, |
@@ -71,25 +154,47 @@ static struct resource ls1x_eth0_resources[] = { | |||
71 | }, | 154 | }, |
72 | }; | 155 | }; |
73 | 156 | ||
74 | static struct stmmac_mdio_bus_data ls1x_mdio_bus_data = { | 157 | struct platform_device ls1x_eth0_pdev = { |
75 | .phy_mask = 0, | 158 | .name = "stmmaceth", |
159 | .id = 0, | ||
160 | .num_resources = ARRAY_SIZE(ls1x_eth0_resources), | ||
161 | .resource = ls1x_eth0_resources, | ||
162 | .dev = { | ||
163 | .platform_data = &ls1x_eth0_pdata, | ||
164 | }, | ||
76 | }; | 165 | }; |
77 | 166 | ||
78 | static struct plat_stmmacenet_data ls1x_eth_data = { | 167 | static struct plat_stmmacenet_data ls1x_eth1_pdata = { |
79 | .bus_id = 0, | 168 | .bus_id = 1, |
80 | .phy_addr = -1, | 169 | .phy_addr = -1, |
170 | .interface = PHY_INTERFACE_MODE_MII, | ||
81 | .mdio_bus_data = &ls1x_mdio_bus_data, | 171 | .mdio_bus_data = &ls1x_mdio_bus_data, |
172 | .dma_cfg = &ls1x_eth_dma_cfg, | ||
82 | .has_gmac = 1, | 173 | .has_gmac = 1, |
83 | .tx_coe = 1, | 174 | .tx_coe = 1, |
175 | .init = ls1x_eth_mux_init, | ||
84 | }; | 176 | }; |
85 | 177 | ||
86 | struct platform_device ls1x_eth0_device = { | 178 | static struct resource ls1x_eth1_resources[] = { |
179 | [0] = { | ||
180 | .start = LS1X_GMAC1_BASE, | ||
181 | .end = LS1X_GMAC1_BASE + SZ_64K - 1, | ||
182 | .flags = IORESOURCE_MEM, | ||
183 | }, | ||
184 | [1] = { | ||
185 | .name = "macirq", | ||
186 | .start = LS1X_GMAC1_IRQ, | ||
187 | .flags = IORESOURCE_IRQ, | ||
188 | }, | ||
189 | }; | ||
190 | |||
191 | struct platform_device ls1x_eth1_pdev = { | ||
87 | .name = "stmmaceth", | 192 | .name = "stmmaceth", |
88 | .id = 0, | 193 | .id = 1, |
89 | .num_resources = ARRAY_SIZE(ls1x_eth0_resources), | 194 | .num_resources = ARRAY_SIZE(ls1x_eth1_resources), |
90 | .resource = ls1x_eth0_resources, | 195 | .resource = ls1x_eth1_resources, |
91 | .dev = { | 196 | .dev = { |
92 | .platform_data = &ls1x_eth_data, | 197 | .platform_data = &ls1x_eth1_pdata, |
93 | }, | 198 | }, |
94 | }; | 199 | }; |
95 | 200 | ||
@@ -111,7 +216,7 @@ static struct resource ls1x_ehci_resources[] = { | |||
111 | static struct usb_ehci_pdata ls1x_ehci_pdata = { | 216 | static struct usb_ehci_pdata ls1x_ehci_pdata = { |
112 | }; | 217 | }; |
113 | 218 | ||
114 | struct platform_device ls1x_ehci_device = { | 219 | struct platform_device ls1x_ehci_pdev = { |
115 | .name = "ehci-platform", | 220 | .name = "ehci-platform", |
116 | .id = -1, | 221 | .id = -1, |
117 | .num_resources = ARRAY_SIZE(ls1x_ehci_resources), | 222 | .num_resources = ARRAY_SIZE(ls1x_ehci_resources), |
@@ -123,7 +228,7 @@ struct platform_device ls1x_ehci_device = { | |||
123 | }; | 228 | }; |
124 | 229 | ||
125 | /* Real Time Clock */ | 230 | /* Real Time Clock */ |
126 | struct platform_device ls1x_rtc_device = { | 231 | struct platform_device ls1x_rtc_pdev = { |
127 | .name = "ls1x-rtc", | 232 | .name = "ls1x-rtc", |
128 | .id = -1, | 233 | .id = -1, |
129 | }; | 234 | }; |
diff --git a/arch/mips/loongson1/common/prom.c b/arch/mips/loongson1/common/prom.c index 2a47af5a55c3..68600980ea49 100644 --- a/arch/mips/loongson1/common/prom.c +++ b/arch/mips/loongson1/common/prom.c | |||
@@ -27,7 +27,7 @@ char *prom_getenv(char *envname) | |||
27 | i = strlen(envname); | 27 | i = strlen(envname); |
28 | 28 | ||
29 | while (*env) { | 29 | while (*env) { |
30 | if (strncmp(envname, *env, i) == 0 && *(*env+i) == '=') | 30 | if (strncmp(envname, *env, i) == 0 && *(*env + i) == '=') |
31 | return *env + i + 1; | 31 | return *env + i + 1; |
32 | env++; | 32 | env++; |
33 | } | 33 | } |
@@ -49,7 +49,7 @@ void __init prom_init_cmdline(void) | |||
49 | for (i = 1; i < prom_argc; i++) { | 49 | for (i = 1; i < prom_argc; i++) { |
50 | strcpy(c, prom_argv[i]); | 50 | strcpy(c, prom_argv[i]); |
51 | c += strlen(prom_argv[i]); | 51 | c += strlen(prom_argv[i]); |
52 | if (i < prom_argc-1) | 52 | if (i < prom_argc - 1) |
53 | *c++ = ' '; | 53 | *c++ = ' '; |
54 | } | 54 | } |
55 | *c = 0; | 55 | *c = 0; |
@@ -57,6 +57,7 @@ void __init prom_init_cmdline(void) | |||
57 | 57 | ||
58 | void __init prom_init(void) | 58 | void __init prom_init(void) |
59 | { | 59 | { |
60 | void __iomem *uart_base; | ||
60 | prom_argc = fw_arg0; | 61 | prom_argc = fw_arg0; |
61 | prom_argv = (char **)fw_arg1; | 62 | prom_argv = (char **)fw_arg1; |
62 | prom_envp = (char **)fw_arg2; | 63 | prom_envp = (char **)fw_arg2; |
@@ -65,23 +66,18 @@ void __init prom_init(void) | |||
65 | 66 | ||
66 | memsize = env_or_default("memsize", DEFAULT_MEMSIZE); | 67 | memsize = env_or_default("memsize", DEFAULT_MEMSIZE); |
67 | highmemsize = env_or_default("highmemsize", 0x0); | 68 | highmemsize = env_or_default("highmemsize", 0x0); |
68 | } | ||
69 | 69 | ||
70 | void __init prom_free_prom_memory(void) | 70 | if (strstr(arcs_cmdline, "console=ttyS3")) |
71 | { | 71 | uart_base = ioremap_nocache(LS1X_UART3_BASE, 0x0f); |
72 | else if (strstr(arcs_cmdline, "console=ttyS2")) | ||
73 | uart_base = ioremap_nocache(LS1X_UART2_BASE, 0x0f); | ||
74 | else if (strstr(arcs_cmdline, "console=ttyS1")) | ||
75 | uart_base = ioremap_nocache(LS1X_UART1_BASE, 0x0f); | ||
76 | else | ||
77 | uart_base = ioremap_nocache(LS1X_UART0_BASE, 0x0f); | ||
78 | setup_8250_early_printk_port((unsigned long)uart_base, 0, 0); | ||
72 | } | 79 | } |
73 | 80 | ||
74 | #define PORT(offset) (u8 *)(KSEG1ADDR(LS1X_UART0_BASE + offset)) | 81 | void __init prom_free_prom_memory(void) |
75 | |||
76 | void prom_putchar(char c) | ||
77 | { | 82 | { |
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 | } | 83 | } |
diff --git a/arch/mips/loongson1/common/reset.c b/arch/mips/loongson1/common/reset.c index 547f34b69e4c..c41e4ca56ab4 100644 --- a/arch/mips/loongson1/common/reset.c +++ b/arch/mips/loongson1/common/reset.c | |||
@@ -14,12 +14,7 @@ | |||
14 | 14 | ||
15 | #include <loongson1.h> | 15 | #include <loongson1.h> |
16 | 16 | ||
17 | static void ls1x_restart(char *command) | 17 | static void __iomem *wdt_base; |
18 | { | ||
19 | __raw_writel(0x1, LS1X_WDT_EN); | ||
20 | __raw_writel(0x5000000, LS1X_WDT_TIMER); | ||
21 | __raw_writel(0x1, LS1X_WDT_SET); | ||
22 | } | ||
23 | 18 | ||
24 | static void ls1x_halt(void) | 19 | static void ls1x_halt(void) |
25 | { | 20 | { |
@@ -29,6 +24,15 @@ static void ls1x_halt(void) | |||
29 | } | 24 | } |
30 | } | 25 | } |
31 | 26 | ||
27 | static void ls1x_restart(char *command) | ||
28 | { | ||
29 | __raw_writel(0x1, wdt_base + WDT_EN); | ||
30 | __raw_writel(0x1, wdt_base + WDT_TIMER); | ||
31 | __raw_writel(0x1, wdt_base + WDT_SET); | ||
32 | |||
33 | ls1x_halt(); | ||
34 | } | ||
35 | |||
32 | static void ls1x_power_off(void) | 36 | static void ls1x_power_off(void) |
33 | { | 37 | { |
34 | ls1x_halt(); | 38 | ls1x_halt(); |
@@ -36,6 +40,10 @@ static void ls1x_power_off(void) | |||
36 | 40 | ||
37 | static int __init ls1x_reboot_setup(void) | 41 | static int __init ls1x_reboot_setup(void) |
38 | { | 42 | { |
43 | wdt_base = ioremap_nocache(LS1X_WDT_BASE, 0x0f); | ||
44 | if (!wdt_base) | ||
45 | panic("Failed to remap watchdog registers"); | ||
46 | |||
39 | _machine_restart = ls1x_restart; | 47 | _machine_restart = ls1x_restart; |
40 | _machine_halt = ls1x_halt; | 48 | _machine_halt = ls1x_halt; |
41 | pm_power_off = ls1x_power_off; | 49 | pm_power_off = ls1x_power_off; |
diff --git a/arch/mips/loongson1/common/time.c b/arch/mips/loongson1/common/time.c new file mode 100644 index 000000000000..df0f850d6a5f --- /dev/null +++ b/arch/mips/loongson1/common/time.c | |||
@@ -0,0 +1,226 @@ | |||
1 | /* | ||
2 | * Copyright (c) 2014 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/interrupt.h> | ||
12 | #include <asm/time.h> | ||
13 | |||
14 | #include <loongson1.h> | ||
15 | #include <platform.h> | ||
16 | |||
17 | #ifdef CONFIG_CEVT_CSRC_LS1X | ||
18 | |||
19 | #if defined(CONFIG_TIMER_USE_PWM1) | ||
20 | #define LS1X_TIMER_BASE LS1X_PWM1_BASE | ||
21 | #define LS1X_TIMER_IRQ LS1X_PWM1_IRQ | ||
22 | |||
23 | #elif defined(CONFIG_TIMER_USE_PWM2) | ||
24 | #define LS1X_TIMER_BASE LS1X_PWM2_BASE | ||
25 | #define LS1X_TIMER_IRQ LS1X_PWM2_IRQ | ||
26 | |||
27 | #elif defined(CONFIG_TIMER_USE_PWM3) | ||
28 | #define LS1X_TIMER_BASE LS1X_PWM3_BASE | ||
29 | #define LS1X_TIMER_IRQ LS1X_PWM3_IRQ | ||
30 | |||
31 | #else | ||
32 | #define LS1X_TIMER_BASE LS1X_PWM0_BASE | ||
33 | #define LS1X_TIMER_IRQ LS1X_PWM0_IRQ | ||
34 | #endif | ||
35 | |||
36 | DEFINE_RAW_SPINLOCK(ls1x_timer_lock); | ||
37 | |||
38 | static void __iomem *timer_base; | ||
39 | static uint32_t ls1x_jiffies_per_tick; | ||
40 | |||
41 | static inline void ls1x_pwmtimer_set_period(uint32_t period) | ||
42 | { | ||
43 | __raw_writel(period, timer_base + PWM_HRC); | ||
44 | __raw_writel(period, timer_base + PWM_LRC); | ||
45 | } | ||
46 | |||
47 | static inline void ls1x_pwmtimer_restart(void) | ||
48 | { | ||
49 | __raw_writel(0x0, timer_base + PWM_CNT); | ||
50 | __raw_writel(INT_EN | CNT_EN, timer_base + PWM_CTRL); | ||
51 | } | ||
52 | |||
53 | void __init ls1x_pwmtimer_init(void) | ||
54 | { | ||
55 | timer_base = ioremap(LS1X_TIMER_BASE, 0xf); | ||
56 | if (!timer_base) | ||
57 | panic("Failed to remap timer registers"); | ||
58 | |||
59 | ls1x_jiffies_per_tick = DIV_ROUND_CLOSEST(mips_hpt_frequency, HZ); | ||
60 | |||
61 | ls1x_pwmtimer_set_period(ls1x_jiffies_per_tick); | ||
62 | ls1x_pwmtimer_restart(); | ||
63 | } | ||
64 | |||
65 | static cycle_t ls1x_clocksource_read(struct clocksource *cs) | ||
66 | { | ||
67 | unsigned long flags; | ||
68 | int count; | ||
69 | u32 jifs; | ||
70 | static int old_count; | ||
71 | static u32 old_jifs; | ||
72 | |||
73 | raw_spin_lock_irqsave(&ls1x_timer_lock, flags); | ||
74 | /* | ||
75 | * Although our caller may have the read side of xtime_lock, | ||
76 | * this is now a seqlock, and we are cheating in this routine | ||
77 | * by having side effects on state that we cannot undo if | ||
78 | * there is a collision on the seqlock and our caller has to | ||
79 | * retry. (Namely, old_jifs and old_count.) So we must treat | ||
80 | * jiffies as volatile despite the lock. We read jiffies | ||
81 | * before latching the timer count to guarantee that although | ||
82 | * the jiffies value might be older than the count (that is, | ||
83 | * the counter may underflow between the last point where | ||
84 | * jiffies was incremented and the point where we latch the | ||
85 | * count), it cannot be newer. | ||
86 | */ | ||
87 | jifs = jiffies; | ||
88 | /* read the count */ | ||
89 | count = __raw_readl(timer_base + PWM_CNT); | ||
90 | |||
91 | /* | ||
92 | * It's possible for count to appear to go the wrong way for this | ||
93 | * reason: | ||
94 | * | ||
95 | * The timer counter underflows, but we haven't handled the resulting | ||
96 | * interrupt and incremented jiffies yet. | ||
97 | * | ||
98 | * Previous attempts to handle these cases intelligently were buggy, so | ||
99 | * we just do the simple thing now. | ||
100 | */ | ||
101 | if (count < old_count && jifs == old_jifs) | ||
102 | count = old_count; | ||
103 | |||
104 | old_count = count; | ||
105 | old_jifs = jifs; | ||
106 | |||
107 | raw_spin_unlock_irqrestore(&ls1x_timer_lock, flags); | ||
108 | |||
109 | return (cycle_t) (jifs * ls1x_jiffies_per_tick) + count; | ||
110 | } | ||
111 | |||
112 | static struct clocksource ls1x_clocksource = { | ||
113 | .name = "ls1x-pwmtimer", | ||
114 | .read = ls1x_clocksource_read, | ||
115 | .mask = CLOCKSOURCE_MASK(24), | ||
116 | .flags = CLOCK_SOURCE_IS_CONTINUOUS, | ||
117 | }; | ||
118 | |||
119 | static irqreturn_t ls1x_clockevent_isr(int irq, void *devid) | ||
120 | { | ||
121 | struct clock_event_device *cd = devid; | ||
122 | |||
123 | ls1x_pwmtimer_restart(); | ||
124 | cd->event_handler(cd); | ||
125 | |||
126 | return IRQ_HANDLED; | ||
127 | } | ||
128 | |||
129 | static void ls1x_clockevent_set_mode(enum clock_event_mode mode, | ||
130 | struct clock_event_device *cd) | ||
131 | { | ||
132 | raw_spin_lock(&ls1x_timer_lock); | ||
133 | switch (mode) { | ||
134 | case CLOCK_EVT_MODE_PERIODIC: | ||
135 | ls1x_pwmtimer_set_period(ls1x_jiffies_per_tick); | ||
136 | ls1x_pwmtimer_restart(); | ||
137 | case CLOCK_EVT_MODE_RESUME: | ||
138 | __raw_writel(INT_EN | CNT_EN, timer_base + PWM_CTRL); | ||
139 | break; | ||
140 | case CLOCK_EVT_MODE_ONESHOT: | ||
141 | case CLOCK_EVT_MODE_SHUTDOWN: | ||
142 | __raw_writel(__raw_readl(timer_base + PWM_CTRL) & ~CNT_EN, | ||
143 | timer_base + PWM_CTRL); | ||
144 | break; | ||
145 | default: | ||
146 | break; | ||
147 | } | ||
148 | raw_spin_unlock(&ls1x_timer_lock); | ||
149 | } | ||
150 | |||
151 | static int ls1x_clockevent_set_next(unsigned long evt, | ||
152 | struct clock_event_device *cd) | ||
153 | { | ||
154 | raw_spin_lock(&ls1x_timer_lock); | ||
155 | ls1x_pwmtimer_set_period(evt); | ||
156 | ls1x_pwmtimer_restart(); | ||
157 | raw_spin_unlock(&ls1x_timer_lock); | ||
158 | |||
159 | return 0; | ||
160 | } | ||
161 | |||
162 | static struct clock_event_device ls1x_clockevent = { | ||
163 | .name = "ls1x-pwmtimer", | ||
164 | .features = CLOCK_EVT_FEAT_PERIODIC, | ||
165 | .rating = 300, | ||
166 | .irq = LS1X_TIMER_IRQ, | ||
167 | .set_next_event = ls1x_clockevent_set_next, | ||
168 | .set_mode = ls1x_clockevent_set_mode, | ||
169 | }; | ||
170 | |||
171 | static struct irqaction ls1x_pwmtimer_irqaction = { | ||
172 | .name = "ls1x-pwmtimer", | ||
173 | .handler = ls1x_clockevent_isr, | ||
174 | .dev_id = &ls1x_clockevent, | ||
175 | .flags = IRQF_PERCPU | IRQF_TIMER, | ||
176 | }; | ||
177 | |||
178 | static void __init ls1x_time_init(void) | ||
179 | { | ||
180 | struct clock_event_device *cd = &ls1x_clockevent; | ||
181 | int ret; | ||
182 | |||
183 | if (!mips_hpt_frequency) | ||
184 | panic("Invalid timer clock rate"); | ||
185 | |||
186 | ls1x_pwmtimer_init(); | ||
187 | |||
188 | clockevent_set_clock(cd, mips_hpt_frequency); | ||
189 | cd->max_delta_ns = clockevent_delta2ns(0xffffff, cd); | ||
190 | cd->min_delta_ns = clockevent_delta2ns(0x000300, cd); | ||
191 | cd->cpumask = cpumask_of(smp_processor_id()); | ||
192 | clockevents_register_device(cd); | ||
193 | |||
194 | ls1x_clocksource.rating = 200 + mips_hpt_frequency / 10000000; | ||
195 | ret = clocksource_register_hz(&ls1x_clocksource, mips_hpt_frequency); | ||
196 | if (ret) | ||
197 | panic(KERN_ERR "Failed to register clocksource: %d\n", ret); | ||
198 | |||
199 | setup_irq(LS1X_TIMER_IRQ, &ls1x_pwmtimer_irqaction); | ||
200 | } | ||
201 | #endif /* CONFIG_CEVT_CSRC_LS1X */ | ||
202 | |||
203 | void __init plat_time_init(void) | ||
204 | { | ||
205 | struct clk *clk = NULL; | ||
206 | |||
207 | /* initialize LS1X clocks */ | ||
208 | ls1x_clk_init(); | ||
209 | |||
210 | #ifdef CONFIG_CEVT_CSRC_LS1X | ||
211 | /* setup LS1X PWM timer */ | ||
212 | clk = clk_get(NULL, "ls1x_pwmtimer"); | ||
213 | if (IS_ERR(clk)) | ||
214 | panic("unable to get timer clock, err=%ld", PTR_ERR(clk)); | ||
215 | |||
216 | mips_hpt_frequency = clk_get_rate(clk); | ||
217 | ls1x_time_init(); | ||
218 | #else | ||
219 | /* setup mips r4k timer */ | ||
220 | clk = clk_get(NULL, "cpu_clk"); | ||
221 | if (IS_ERR(clk)) | ||
222 | panic("unable to get cpu clock, err=%ld", PTR_ERR(clk)); | ||
223 | |||
224 | mips_hpt_frequency = clk_get_rate(clk) / 2; | ||
225 | #endif /* CONFIG_CEVT_CSRC_LS1X */ | ||
226 | } | ||