diff options
author | Palmer Dabbelt <palmer@dabbelt.com> | 2017-07-10 21:05:09 -0400 |
---|---|---|
committer | Palmer Dabbelt <palmer@dabbelt.com> | 2017-09-26 18:26:47 -0400 |
commit | 6d60b6ee0c9777b92c47f6dc8aad1dd90612e4fa (patch) | |
tree | 759b45dbcebd1700f2f9df4deb0513ba54da36c7 | |
parent | 7db91e57a0acde126a162ababfb1e0ab190130cb (diff) |
RISC-V: Device, timer, IRQs, and the SBI
This patch contains code that interfaces with devices that are mandated
by the RISC-V supervisor specification and that don't have explicit
drivers anywhere else in the tree. This includes the staticly defined
interrupts, the CSR-mapped timer, and virtualized SBI devices.
Signed-off-by: Palmer Dabbelt <palmer@dabbelt.com>
-rw-r--r-- | arch/riscv/include/asm/delay.h | 28 | ||||
-rw-r--r-- | arch/riscv/include/asm/dma-mapping.h | 38 | ||||
-rw-r--r-- | arch/riscv/include/asm/irq.h | 28 | ||||
-rw-r--r-- | arch/riscv/include/asm/irqflags.h | 63 | ||||
-rw-r--r-- | arch/riscv/include/asm/pci.h | 48 | ||||
-rw-r--r-- | arch/riscv/include/asm/sbi.h | 100 | ||||
-rw-r--r-- | arch/riscv/include/asm/timex.h | 59 | ||||
-rw-r--r-- | arch/riscv/lib/delay.c | 110 | ||||
-rw-r--r-- | arch/riscv/mm/ioremap.c | 92 |
9 files changed, 566 insertions, 0 deletions
diff --git a/arch/riscv/include/asm/delay.h b/arch/riscv/include/asm/delay.h new file mode 100644 index 000000000000..cbb0c9eb96cb --- /dev/null +++ b/arch/riscv/include/asm/delay.h | |||
@@ -0,0 +1,28 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2009 Chen Liqin <liqin.chen@sunplusct.com> | ||
3 | * Copyright (C) 2016 Regents of the University of California | ||
4 | * | ||
5 | * This program is free software; you can redistribute it and/or | ||
6 | * modify it under the terms of the GNU General Public License | ||
7 | * as published by the Free Software Foundation, version 2. | ||
8 | * | ||
9 | * This program is distributed in the hope that it will be useful, | ||
10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
12 | * GNU General Public License for more details. | ||
13 | */ | ||
14 | |||
15 | #ifndef _ASM_RISCV_DELAY_H | ||
16 | #define _ASM_RISCV_DELAY_H | ||
17 | |||
18 | extern unsigned long riscv_timebase; | ||
19 | |||
20 | #define udelay udelay | ||
21 | extern void udelay(unsigned long usecs); | ||
22 | |||
23 | #define ndelay ndelay | ||
24 | extern void ndelay(unsigned long nsecs); | ||
25 | |||
26 | extern void __delay(unsigned long cycles); | ||
27 | |||
28 | #endif /* _ASM_RISCV_DELAY_H */ | ||
diff --git a/arch/riscv/include/asm/dma-mapping.h b/arch/riscv/include/asm/dma-mapping.h new file mode 100644 index 000000000000..3eec1000196d --- /dev/null +++ b/arch/riscv/include/asm/dma-mapping.h | |||
@@ -0,0 +1,38 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2003-2004 Hewlett-Packard Co | ||
3 | * David Mosberger-Tang <davidm@hpl.hp.com> | ||
4 | * Copyright (C) 2012 ARM Ltd. | ||
5 | * Copyright (C) 2016 SiFive, Inc. | ||
6 | * | ||
7 | * This program is free software; you can redistribute it and/or modify | ||
8 | * it under the terms of the GNU General Public License version 2 as | ||
9 | * published by the Free Software Foundation. | ||
10 | * | ||
11 | * This program is distributed in the hope that it will be useful, | ||
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
14 | * GNU General Public License for more details. | ||
15 | * | ||
16 | * You should have received a copy of the GNU General Public License | ||
17 | * along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
18 | */ | ||
19 | #ifndef __ASM_RISCV_DMA_MAPPING_H | ||
20 | #define __ASM_RISCV_DMA_MAPPING_H | ||
21 | |||
22 | /* Use ops->dma_mapping_error (if it exists) or assume success */ | ||
23 | // #undef DMA_ERROR_CODE | ||
24 | |||
25 | static inline const struct dma_map_ops *get_arch_dma_ops(struct bus_type *bus) | ||
26 | { | ||
27 | return &dma_noop_ops; | ||
28 | } | ||
29 | |||
30 | static inline bool dma_capable(struct device *dev, dma_addr_t addr, size_t size) | ||
31 | { | ||
32 | if (!dev->dma_mask) | ||
33 | return false; | ||
34 | |||
35 | return addr + size - 1 <= *dev->dma_mask; | ||
36 | } | ||
37 | |||
38 | #endif /* __ASM_RISCV_DMA_MAPPING_H */ | ||
diff --git a/arch/riscv/include/asm/irq.h b/arch/riscv/include/asm/irq.h new file mode 100644 index 000000000000..4dee9d4c13c0 --- /dev/null +++ b/arch/riscv/include/asm/irq.h | |||
@@ -0,0 +1,28 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2012 Regents of the University of California | ||
3 | * Copyright (C) 2017 SiFive | ||
4 | * | ||
5 | * This program is free software; you can redistribute it and/or | ||
6 | * modify it under the terms of the GNU General Public License | ||
7 | * as published by the Free Software Foundation, version 2. | ||
8 | * | ||
9 | * This program is distributed in the hope that it will be useful, | ||
10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
12 | * GNU General Public License for more details. | ||
13 | */ | ||
14 | |||
15 | #ifndef _ASM_RISCV_IRQ_H | ||
16 | #define _ASM_RISCV_IRQ_H | ||
17 | |||
18 | #define NR_IRQS 0 | ||
19 | |||
20 | #define INTERRUPT_CAUSE_SOFTWARE 1 | ||
21 | #define INTERRUPT_CAUSE_TIMER 5 | ||
22 | #define INTERRUPT_CAUSE_EXTERNAL 9 | ||
23 | |||
24 | void riscv_timer_interrupt(void); | ||
25 | |||
26 | #include <asm-generic/irq.h> | ||
27 | |||
28 | #endif /* _ASM_RISCV_IRQ_H */ | ||
diff --git a/arch/riscv/include/asm/irqflags.h b/arch/riscv/include/asm/irqflags.h new file mode 100644 index 000000000000..6fdc860d7f84 --- /dev/null +++ b/arch/riscv/include/asm/irqflags.h | |||
@@ -0,0 +1,63 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2012 Regents of the University of California | ||
3 | * | ||
4 | * This program is free software; you can redistribute it and/or | ||
5 | * modify it under the terms of the GNU General Public License | ||
6 | * as published by the Free Software Foundation, version 2. | ||
7 | * | ||
8 | * This program is distributed in the hope that it will be useful, | ||
9 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
11 | * GNU General Public License for more details. | ||
12 | */ | ||
13 | |||
14 | |||
15 | #ifndef _ASM_RISCV_IRQFLAGS_H | ||
16 | #define _ASM_RISCV_IRQFLAGS_H | ||
17 | |||
18 | #include <asm/processor.h> | ||
19 | #include <asm/csr.h> | ||
20 | |||
21 | /* read interrupt enabled status */ | ||
22 | static inline unsigned long arch_local_save_flags(void) | ||
23 | { | ||
24 | return csr_read(sstatus); | ||
25 | } | ||
26 | |||
27 | /* unconditionally enable interrupts */ | ||
28 | static inline void arch_local_irq_enable(void) | ||
29 | { | ||
30 | csr_set(sstatus, SR_IE); | ||
31 | } | ||
32 | |||
33 | /* unconditionally disable interrupts */ | ||
34 | static inline void arch_local_irq_disable(void) | ||
35 | { | ||
36 | csr_clear(sstatus, SR_IE); | ||
37 | } | ||
38 | |||
39 | /* get status and disable interrupts */ | ||
40 | static inline unsigned long arch_local_irq_save(void) | ||
41 | { | ||
42 | return csr_read_clear(sstatus, SR_IE); | ||
43 | } | ||
44 | |||
45 | /* test flags */ | ||
46 | static inline int arch_irqs_disabled_flags(unsigned long flags) | ||
47 | { | ||
48 | return !(flags & SR_IE); | ||
49 | } | ||
50 | |||
51 | /* test hardware interrupt enable bit */ | ||
52 | static inline int arch_irqs_disabled(void) | ||
53 | { | ||
54 | return arch_irqs_disabled_flags(arch_local_save_flags()); | ||
55 | } | ||
56 | |||
57 | /* set interrupt enabled status */ | ||
58 | static inline void arch_local_irq_restore(unsigned long flags) | ||
59 | { | ||
60 | csr_set(sstatus, flags & SR_IE); | ||
61 | } | ||
62 | |||
63 | #endif /* _ASM_RISCV_IRQFLAGS_H */ | ||
diff --git a/arch/riscv/include/asm/pci.h b/arch/riscv/include/asm/pci.h new file mode 100644 index 000000000000..0f2fc9ef20fc --- /dev/null +++ b/arch/riscv/include/asm/pci.h | |||
@@ -0,0 +1,48 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2016 SiFive | ||
3 | * | ||
4 | * This program is free software; you can redistribute it and/or | ||
5 | * modify it under the terms of the GNU General Public License | ||
6 | * as published by the Free Software Foundation, version 2. | ||
7 | * | ||
8 | * This program is distributed in the hope that it will be useful, | ||
9 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
11 | * GNU General Public License for more details. | ||
12 | */ | ||
13 | |||
14 | #ifndef __ASM_RISCV_PCI_H | ||
15 | #define __ASM_RISCV_PCI_H | ||
16 | |||
17 | #include <linux/types.h> | ||
18 | #include <linux/slab.h> | ||
19 | #include <linux/dma-mapping.h> | ||
20 | |||
21 | #include <asm/io.h> | ||
22 | |||
23 | #define PCIBIOS_MIN_IO 0 | ||
24 | #define PCIBIOS_MIN_MEM 0 | ||
25 | |||
26 | /* RISC-V shim does not initialize PCI bus */ | ||
27 | #define pcibios_assign_all_busses() 1 | ||
28 | |||
29 | /* We do not have an IOMMU */ | ||
30 | #define PCI_DMA_BUS_IS_PHYS 1 | ||
31 | |||
32 | extern int isa_dma_bridge_buggy; | ||
33 | |||
34 | #ifdef CONFIG_PCI | ||
35 | static inline int pci_get_legacy_ide_irq(struct pci_dev *dev, int channel) | ||
36 | { | ||
37 | /* no legacy IRQ on risc-v */ | ||
38 | return -ENODEV; | ||
39 | } | ||
40 | |||
41 | static inline int pci_proc_domain(struct pci_bus *bus) | ||
42 | { | ||
43 | /* always show the domain in /proc */ | ||
44 | return 1; | ||
45 | } | ||
46 | #endif /* CONFIG_PCI */ | ||
47 | |||
48 | #endif /* __ASM_PCI_H */ | ||
diff --git a/arch/riscv/include/asm/sbi.h b/arch/riscv/include/asm/sbi.h new file mode 100644 index 000000000000..b6bb10b92fe2 --- /dev/null +++ b/arch/riscv/include/asm/sbi.h | |||
@@ -0,0 +1,100 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2015 Regents of the University of California | ||
3 | * | ||
4 | * This program is free software; you can redistribute it and/or | ||
5 | * modify it under the terms of the GNU General Public License | ||
6 | * as published by the Free Software Foundation, version 2. | ||
7 | * | ||
8 | * This program is distributed in the hope that it will be useful, | ||
9 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
11 | * GNU General Public License for more details. | ||
12 | */ | ||
13 | |||
14 | #ifndef _ASM_RISCV_SBI_H | ||
15 | #define _ASM_RISCV_SBI_H | ||
16 | |||
17 | #include <linux/types.h> | ||
18 | |||
19 | #define SBI_SET_TIMER 0 | ||
20 | #define SBI_CONSOLE_PUTCHAR 1 | ||
21 | #define SBI_CONSOLE_GETCHAR 2 | ||
22 | #define SBI_CLEAR_IPI 3 | ||
23 | #define SBI_SEND_IPI 4 | ||
24 | #define SBI_REMOTE_FENCE_I 5 | ||
25 | #define SBI_REMOTE_SFENCE_VMA 6 | ||
26 | #define SBI_REMOTE_SFENCE_VMA_ASID 7 | ||
27 | #define SBI_SHUTDOWN 8 | ||
28 | |||
29 | #define SBI_CALL(which, arg0, arg1, arg2) ({ \ | ||
30 | register uintptr_t a0 asm ("a0") = (uintptr_t)(arg0); \ | ||
31 | register uintptr_t a1 asm ("a1") = (uintptr_t)(arg1); \ | ||
32 | register uintptr_t a2 asm ("a2") = (uintptr_t)(arg2); \ | ||
33 | register uintptr_t a7 asm ("a7") = (uintptr_t)(which); \ | ||
34 | asm volatile ("ecall" \ | ||
35 | : "+r" (a0) \ | ||
36 | : "r" (a1), "r" (a2), "r" (a7) \ | ||
37 | : "memory"); \ | ||
38 | a0; \ | ||
39 | }) | ||
40 | |||
41 | /* Lazy implementations until SBI is finalized */ | ||
42 | #define SBI_CALL_0(which) SBI_CALL(which, 0, 0, 0) | ||
43 | #define SBI_CALL_1(which, arg0) SBI_CALL(which, arg0, 0, 0) | ||
44 | #define SBI_CALL_2(which, arg0, arg1) SBI_CALL(which, arg0, arg1, 0) | ||
45 | |||
46 | static inline void sbi_console_putchar(int ch) | ||
47 | { | ||
48 | SBI_CALL_1(SBI_CONSOLE_PUTCHAR, ch); | ||
49 | } | ||
50 | |||
51 | static inline int sbi_console_getchar(void) | ||
52 | { | ||
53 | return SBI_CALL_0(SBI_CONSOLE_GETCHAR); | ||
54 | } | ||
55 | |||
56 | static inline void sbi_set_timer(uint64_t stime_value) | ||
57 | { | ||
58 | #if __riscv_xlen == 32 | ||
59 | SBI_CALL_2(SBI_SET_TIMER, stime_value, stime_value >> 32); | ||
60 | #else | ||
61 | SBI_CALL_1(SBI_SET_TIMER, stime_value); | ||
62 | #endif | ||
63 | } | ||
64 | |||
65 | static inline void sbi_shutdown(void) | ||
66 | { | ||
67 | SBI_CALL_0(SBI_SHUTDOWN); | ||
68 | } | ||
69 | |||
70 | static inline void sbi_clear_ipi(void) | ||
71 | { | ||
72 | SBI_CALL_0(SBI_CLEAR_IPI); | ||
73 | } | ||
74 | |||
75 | static inline void sbi_send_ipi(const unsigned long *hart_mask) | ||
76 | { | ||
77 | SBI_CALL_1(SBI_SEND_IPI, hart_mask); | ||
78 | } | ||
79 | |||
80 | static inline void sbi_remote_fence_i(const unsigned long *hart_mask) | ||
81 | { | ||
82 | SBI_CALL_1(SBI_REMOTE_FENCE_I, hart_mask); | ||
83 | } | ||
84 | |||
85 | static inline void sbi_remote_sfence_vma(const unsigned long *hart_mask, | ||
86 | unsigned long start, | ||
87 | unsigned long size) | ||
88 | { | ||
89 | SBI_CALL_1(SBI_REMOTE_SFENCE_VMA, hart_mask); | ||
90 | } | ||
91 | |||
92 | static inline void sbi_remote_sfence_vma_asid(const unsigned long *hart_mask, | ||
93 | unsigned long start, | ||
94 | unsigned long size, | ||
95 | unsigned long asid) | ||
96 | { | ||
97 | SBI_CALL_1(SBI_REMOTE_SFENCE_VMA_ASID, hart_mask); | ||
98 | } | ||
99 | |||
100 | #endif | ||
diff --git a/arch/riscv/include/asm/timex.h b/arch/riscv/include/asm/timex.h new file mode 100644 index 000000000000..3df4932d8964 --- /dev/null +++ b/arch/riscv/include/asm/timex.h | |||
@@ -0,0 +1,59 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2012 Regents of the University of California | ||
3 | * | ||
4 | * This program is free software; you can redistribute it and/or | ||
5 | * modify it under the terms of the GNU General Public License | ||
6 | * as published by the Free Software Foundation, version 2. | ||
7 | * | ||
8 | * This program is distributed in the hope that it will be useful, | ||
9 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
11 | * GNU General Public License for more details. | ||
12 | */ | ||
13 | |||
14 | #ifndef _ASM_RISCV_TIMEX_H | ||
15 | #define _ASM_RISCV_TIMEX_H | ||
16 | |||
17 | #include <asm/param.h> | ||
18 | |||
19 | typedef unsigned long cycles_t; | ||
20 | |||
21 | static inline cycles_t get_cycles(void) | ||
22 | { | ||
23 | cycles_t n; | ||
24 | |||
25 | __asm__ __volatile__ ( | ||
26 | "rdtime %0" | ||
27 | : "=r" (n)); | ||
28 | return n; | ||
29 | } | ||
30 | |||
31 | #ifdef CONFIG_64BIT | ||
32 | static inline uint64_t get_cycles64(void) | ||
33 | { | ||
34 | return get_cycles(); | ||
35 | } | ||
36 | #else | ||
37 | static inline uint64_t get_cycles64(void) | ||
38 | { | ||
39 | u32 lo, hi, tmp; | ||
40 | __asm__ __volatile__ ( | ||
41 | "1:\n" | ||
42 | "rdtimeh %0\n" | ||
43 | "rdtime %1\n" | ||
44 | "rdtimeh %2\n" | ||
45 | "bne %0, %2, 1b" | ||
46 | : "=&r" (hi), "=&r" (lo), "=&r" (tmp)); | ||
47 | return ((u64)hi << 32) | lo; | ||
48 | } | ||
49 | #endif | ||
50 | |||
51 | #define ARCH_HAS_READ_CURRENT_TIMER | ||
52 | |||
53 | static inline int read_current_timer(unsigned long *timer_val) | ||
54 | { | ||
55 | *timer_val = get_cycles(); | ||
56 | return 0; | ||
57 | } | ||
58 | |||
59 | #endif /* _ASM_RISCV_TIMEX_H */ | ||
diff --git a/arch/riscv/lib/delay.c b/arch/riscv/lib/delay.c new file mode 100644 index 000000000000..1cc4ac3964b4 --- /dev/null +++ b/arch/riscv/lib/delay.c | |||
@@ -0,0 +1,110 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2012 Regents of the University of California | ||
3 | * | ||
4 | * This program is free software; you can redistribute it and/or | ||
5 | * modify it under the terms of the GNU General Public License | ||
6 | * as published by the Free Software Foundation, version 2. | ||
7 | * | ||
8 | * This program is distributed in the hope that it will be useful, | ||
9 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
11 | * GNU General Public License for more details. | ||
12 | */ | ||
13 | |||
14 | #include <linux/delay.h> | ||
15 | #include <linux/param.h> | ||
16 | #include <linux/timex.h> | ||
17 | #include <linux/export.h> | ||
18 | |||
19 | /* | ||
20 | * This is copies from arch/arm/include/asm/delay.h | ||
21 | * | ||
22 | * Loop (or tick) based delay: | ||
23 | * | ||
24 | * loops = loops_per_jiffy * jiffies_per_sec * delay_us / us_per_sec | ||
25 | * | ||
26 | * where: | ||
27 | * | ||
28 | * jiffies_per_sec = HZ | ||
29 | * us_per_sec = 1000000 | ||
30 | * | ||
31 | * Therefore the constant part is HZ / 1000000 which is a small | ||
32 | * fractional number. To make this usable with integer math, we | ||
33 | * scale up this constant by 2^31, perform the actual multiplication, | ||
34 | * and scale the result back down by 2^31 with a simple shift: | ||
35 | * | ||
36 | * loops = (loops_per_jiffy * delay_us * UDELAY_MULT) >> 31 | ||
37 | * | ||
38 | * where: | ||
39 | * | ||
40 | * UDELAY_MULT = 2^31 * HZ / 1000000 | ||
41 | * = (2^31 / 1000000) * HZ | ||
42 | * = 2147.483648 * HZ | ||
43 | * = 2147 * HZ + 483648 * HZ / 1000000 | ||
44 | * | ||
45 | * 31 is the biggest scale shift value that won't overflow 32 bits for | ||
46 | * delay_us * UDELAY_MULT assuming HZ <= 1000 and delay_us <= 2000. | ||
47 | */ | ||
48 | #define MAX_UDELAY_US 2000 | ||
49 | #define MAX_UDELAY_HZ 1000 | ||
50 | #define UDELAY_MULT (2147UL * HZ + 483648UL * HZ / 1000000UL) | ||
51 | #define UDELAY_SHIFT 31 | ||
52 | |||
53 | #if HZ > MAX_UDELAY_HZ | ||
54 | #error "HZ > MAX_UDELAY_HZ" | ||
55 | #endif | ||
56 | |||
57 | /* | ||
58 | * RISC-V supports both UDELAY and NDELAY. This is largely the same as above, | ||
59 | * but with different constants. I added 10 bits to the shift to get this, but | ||
60 | * the result is that I need a 64-bit multiply, which is slow on 32-bit | ||
61 | * platforms. | ||
62 | * | ||
63 | * NDELAY_MULT = 2^41 * HZ / 1000000000 | ||
64 | * = (2^41 / 1000000000) * HZ | ||
65 | * = 2199.02325555 * HZ | ||
66 | * = 2199 * HZ + 23255550 * HZ / 1000000000 | ||
67 | * | ||
68 | * The maximum here is to avoid 64-bit overflow, but it isn't checked as it | ||
69 | * won't happen. | ||
70 | */ | ||
71 | #define MAX_NDELAY_NS (1ULL << 42) | ||
72 | #define MAX_NDELAY_HZ MAX_UDELAY_HZ | ||
73 | #define NDELAY_MULT ((unsigned long long)(2199ULL * HZ + 23255550ULL * HZ / 1000000000ULL)) | ||
74 | #define NDELAY_SHIFT 41 | ||
75 | |||
76 | #if HZ > MAX_NDELAY_HZ | ||
77 | #error "HZ > MAX_NDELAY_HZ" | ||
78 | #endif | ||
79 | |||
80 | void __delay(unsigned long cycles) | ||
81 | { | ||
82 | u64 t0 = get_cycles(); | ||
83 | |||
84 | while ((unsigned long)(get_cycles() - t0) < cycles) | ||
85 | cpu_relax(); | ||
86 | } | ||
87 | |||
88 | void udelay(unsigned long usecs) | ||
89 | { | ||
90 | unsigned long ucycles = usecs * lpj_fine * UDELAY_MULT; | ||
91 | |||
92 | if (unlikely(usecs > MAX_UDELAY_US)) { | ||
93 | __delay((u64)usecs * riscv_timebase / 1000000ULL); | ||
94 | return; | ||
95 | } | ||
96 | |||
97 | __delay(ucycles >> UDELAY_SHIFT); | ||
98 | } | ||
99 | EXPORT_SYMBOL(udelay); | ||
100 | |||
101 | void ndelay(unsigned long nsecs) | ||
102 | { | ||
103 | /* | ||
104 | * This doesn't bother checking for overflow, as it won't happen (it's | ||
105 | * an hour) of delay. | ||
106 | */ | ||
107 | unsigned long long ncycles = nsecs * lpj_fine * NDELAY_MULT; | ||
108 | __delay(ncycles >> NDELAY_SHIFT); | ||
109 | } | ||
110 | EXPORT_SYMBOL(ndelay); | ||
diff --git a/arch/riscv/mm/ioremap.c b/arch/riscv/mm/ioremap.c new file mode 100644 index 000000000000..e99194a4077e --- /dev/null +++ b/arch/riscv/mm/ioremap.c | |||
@@ -0,0 +1,92 @@ | |||
1 | /* | ||
2 | * (C) Copyright 1995 1996 Linus Torvalds | ||
3 | * (C) Copyright 2012 Regents of the University of California | ||
4 | * | ||
5 | * This program is free software; you can redistribute it and/or | ||
6 | * modify it under the terms of the GNU General Public License | ||
7 | * as published by the Free Software Foundation, version 2. | ||
8 | * | ||
9 | * This program is distributed in the hope that it will be useful, | ||
10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
12 | * GNU General Public License for more details. | ||
13 | */ | ||
14 | |||
15 | #include <linux/export.h> | ||
16 | #include <linux/mm.h> | ||
17 | #include <linux/vmalloc.h> | ||
18 | #include <linux/io.h> | ||
19 | |||
20 | #include <asm/pgtable.h> | ||
21 | |||
22 | /* | ||
23 | * Remap an arbitrary physical address space into the kernel virtual | ||
24 | * address space. Needed when the kernel wants to access high addresses | ||
25 | * directly. | ||
26 | * | ||
27 | * NOTE! We need to allow non-page-aligned mappings too: we will obviously | ||
28 | * have to convert them into an offset in a page-aligned mapping, but the | ||
29 | * caller shouldn't need to know that small detail. | ||
30 | */ | ||
31 | static void __iomem *__ioremap_caller(phys_addr_t addr, size_t size, | ||
32 | pgprot_t prot, void *caller) | ||
33 | { | ||
34 | phys_addr_t last_addr; | ||
35 | unsigned long offset, vaddr; | ||
36 | struct vm_struct *area; | ||
37 | |||
38 | /* Disallow wrap-around or zero size */ | ||
39 | last_addr = addr + size - 1; | ||
40 | if (!size || last_addr < addr) | ||
41 | return NULL; | ||
42 | |||
43 | /* Page-align mappings */ | ||
44 | offset = addr & (~PAGE_MASK); | ||
45 | addr &= PAGE_MASK; | ||
46 | size = PAGE_ALIGN(size + offset); | ||
47 | |||
48 | area = get_vm_area_caller(size, VM_IOREMAP, caller); | ||
49 | if (!area) | ||
50 | return NULL; | ||
51 | vaddr = (unsigned long)area->addr; | ||
52 | |||
53 | if (ioremap_page_range(vaddr, vaddr + size, addr, prot)) { | ||
54 | free_vm_area(area); | ||
55 | return NULL; | ||
56 | } | ||
57 | |||
58 | return (void __iomem *)(vaddr + offset); | ||
59 | } | ||
60 | |||
61 | /* | ||
62 | * ioremap - map bus memory into CPU space | ||
63 | * @offset: bus address of the memory | ||
64 | * @size: size of the resource to map | ||
65 | * | ||
66 | * ioremap performs a platform specific sequence of operations to | ||
67 | * make bus memory CPU accessible via the readb/readw/readl/writeb/ | ||
68 | * writew/writel functions and the other mmio helpers. The returned | ||
69 | * address is not guaranteed to be usable directly as a virtual | ||
70 | * address. | ||
71 | * | ||
72 | * Must be freed with iounmap. | ||
73 | */ | ||
74 | void __iomem *ioremap(phys_addr_t offset, unsigned long size) | ||
75 | { | ||
76 | return __ioremap_caller(offset, size, PAGE_KERNEL, | ||
77 | __builtin_return_address(0)); | ||
78 | } | ||
79 | EXPORT_SYMBOL(ioremap); | ||
80 | |||
81 | |||
82 | /** | ||
83 | * iounmap - Free a IO remapping | ||
84 | * @addr: virtual address from ioremap_* | ||
85 | * | ||
86 | * Caller must ensure there is only one unmapping for the same pointer. | ||
87 | */ | ||
88 | void iounmap(void __iomem *addr) | ||
89 | { | ||
90 | vunmap((void *)((unsigned long)addr & PAGE_MASK)); | ||
91 | } | ||
92 | EXPORT_SYMBOL(iounmap); | ||