David Brazdil | 0f672f6 | 2019-12-10 10:32:29 +0000 | [diff] [blame] | 1 | /* SPDX-License-Identifier: GPL-2.0-only */ |
Andrew Scull | b4b6d4a | 2019-01-02 15:54:55 +0000 | [diff] [blame] | 2 | /* |
| 3 | * {read,write}{b,w,l,q} based on arch/arm64/include/asm/io.h |
| 4 | * which was based on arch/arm/include/io.h |
| 5 | * |
| 6 | * Copyright (C) 1996-2000 Russell King |
| 7 | * Copyright (C) 2012 ARM Ltd. |
| 8 | * Copyright (C) 2014 Regents of the University of California |
Andrew Scull | b4b6d4a | 2019-01-02 15:54:55 +0000 | [diff] [blame] | 9 | */ |
| 10 | |
| 11 | #ifndef _ASM_RISCV_IO_H |
| 12 | #define _ASM_RISCV_IO_H |
| 13 | |
| 14 | #include <linux/types.h> |
Olivier Deprez | 157378f | 2022-04-04 15:47:50 +0200 | [diff] [blame^] | 15 | #include <linux/pgtable.h> |
David Brazdil | 0f672f6 | 2019-12-10 10:32:29 +0000 | [diff] [blame] | 16 | #include <asm/mmiowb.h> |
Olivier Deprez | 157378f | 2022-04-04 15:47:50 +0200 | [diff] [blame^] | 17 | #include <asm/early_ioremap.h> |
Andrew Scull | b4b6d4a | 2019-01-02 15:54:55 +0000 | [diff] [blame] | 18 | |
| 19 | /* |
Olivier Deprez | 157378f | 2022-04-04 15:47:50 +0200 | [diff] [blame^] | 20 | * MMIO access functions are separated out to break dependency cycles |
| 21 | * when using {read,write}* fns in low-level headers |
Andrew Scull | b4b6d4a | 2019-01-02 15:54:55 +0000 | [diff] [blame] | 22 | */ |
Olivier Deprez | 157378f | 2022-04-04 15:47:50 +0200 | [diff] [blame^] | 23 | #include <asm/mmio.h> |
Andrew Scull | b4b6d4a | 2019-01-02 15:54:55 +0000 | [diff] [blame] | 24 | |
| 25 | /* |
David Brazdil | 0f672f6 | 2019-12-10 10:32:29 +0000 | [diff] [blame] | 26 | * I/O port access constants. |
| 27 | */ |
Olivier Deprez | 157378f | 2022-04-04 15:47:50 +0200 | [diff] [blame^] | 28 | #ifdef CONFIG_MMU |
David Brazdil | 0f672f6 | 2019-12-10 10:32:29 +0000 | [diff] [blame] | 29 | #define IO_SPACE_LIMIT (PCI_IO_SIZE - 1) |
| 30 | #define PCI_IOBASE ((void __iomem *)PCI_IO_START) |
Olivier Deprez | 157378f | 2022-04-04 15:47:50 +0200 | [diff] [blame^] | 31 | #endif /* CONFIG_MMU */ |
David Brazdil | 0f672f6 | 2019-12-10 10:32:29 +0000 | [diff] [blame] | 32 | |
| 33 | /* |
Andrew Scull | b4b6d4a | 2019-01-02 15:54:55 +0000 | [diff] [blame] | 34 | * Emulation routines for the port-mapped IO space used by some PCI drivers. |
| 35 | * These are defined as being "fully synchronous", but also "not guaranteed to |
| 36 | * be fully ordered with respect to other memory and I/O operations". We're |
| 37 | * going to be on the safe side here and just make them: |
| 38 | * - Fully ordered WRT each other, by bracketing them with two fences. The |
| 39 | * outer set contains both I/O so inX is ordered with outX, while the inner just |
| 40 | * needs the type of the access (I for inX and O for outX). |
| 41 | * - Ordered in the same manner as readX/writeX WRT memory by subsuming their |
| 42 | * fences. |
| 43 | * - Ordered WRT timer reads, so udelay and friends don't get elided by the |
| 44 | * implementation. |
| 45 | * Note that there is no way to actually enforce that outX is a non-posted |
| 46 | * operation on RISC-V, but hopefully the timer ordering constraint is |
| 47 | * sufficient to ensure this works sanely on controllers that support I/O |
| 48 | * writes. |
| 49 | */ |
| 50 | #define __io_pbr() __asm__ __volatile__ ("fence io,i" : : : "memory"); |
David Brazdil | 0f672f6 | 2019-12-10 10:32:29 +0000 | [diff] [blame] | 51 | #define __io_par(v) __asm__ __volatile__ ("fence i,ior" : : : "memory"); |
Andrew Scull | b4b6d4a | 2019-01-02 15:54:55 +0000 | [diff] [blame] | 52 | #define __io_pbw() __asm__ __volatile__ ("fence iow,o" : : : "memory"); |
| 53 | #define __io_paw() __asm__ __volatile__ ("fence o,io" : : : "memory"); |
| 54 | |
David Brazdil | 0f672f6 | 2019-12-10 10:32:29 +0000 | [diff] [blame] | 55 | #define inb(c) ({ u8 __v; __io_pbr(); __v = readb_cpu((void*)(PCI_IOBASE + (c))); __io_par(__v); __v; }) |
| 56 | #define inw(c) ({ u16 __v; __io_pbr(); __v = readw_cpu((void*)(PCI_IOBASE + (c))); __io_par(__v); __v; }) |
| 57 | #define inl(c) ({ u32 __v; __io_pbr(); __v = readl_cpu((void*)(PCI_IOBASE + (c))); __io_par(__v); __v; }) |
Andrew Scull | b4b6d4a | 2019-01-02 15:54:55 +0000 | [diff] [blame] | 58 | |
| 59 | #define outb(v,c) ({ __io_pbw(); writeb_cpu((v),(void*)(PCI_IOBASE + (c))); __io_paw(); }) |
| 60 | #define outw(v,c) ({ __io_pbw(); writew_cpu((v),(void*)(PCI_IOBASE + (c))); __io_paw(); }) |
| 61 | #define outl(v,c) ({ __io_pbw(); writel_cpu((v),(void*)(PCI_IOBASE + (c))); __io_paw(); }) |
| 62 | |
| 63 | #ifdef CONFIG_64BIT |
David Brazdil | 0f672f6 | 2019-12-10 10:32:29 +0000 | [diff] [blame] | 64 | #define inq(c) ({ u64 __v; __io_pbr(); __v = readq_cpu((void*)(c)); __io_par(__v); __v; }) |
Andrew Scull | b4b6d4a | 2019-01-02 15:54:55 +0000 | [diff] [blame] | 65 | #define outq(v,c) ({ __io_pbw(); writeq_cpu((v),(void*)(c)); __io_paw(); }) |
| 66 | #endif |
| 67 | |
| 68 | /* |
| 69 | * Accesses from a single hart to a single I/O address must be ordered. This |
| 70 | * allows us to use the raw read macros, but we still need to fence before and |
| 71 | * after the block to ensure ordering WRT other macros. These are defined to |
| 72 | * perform host-endian accesses so we use __raw instead of __cpu. |
| 73 | */ |
| 74 | #define __io_reads_ins(port, ctype, len, bfence, afence) \ |
| 75 | static inline void __ ## port ## len(const volatile void __iomem *addr, \ |
| 76 | void *buffer, \ |
| 77 | unsigned int count) \ |
| 78 | { \ |
| 79 | bfence; \ |
| 80 | if (count) { \ |
| 81 | ctype *buf = buffer; \ |
| 82 | \ |
| 83 | do { \ |
| 84 | ctype x = __raw_read ## len(addr); \ |
| 85 | *buf++ = x; \ |
| 86 | } while (--count); \ |
| 87 | } \ |
| 88 | afence; \ |
| 89 | } |
| 90 | |
| 91 | #define __io_writes_outs(port, ctype, len, bfence, afence) \ |
| 92 | static inline void __ ## port ## len(volatile void __iomem *addr, \ |
| 93 | const void *buffer, \ |
| 94 | unsigned int count) \ |
| 95 | { \ |
| 96 | bfence; \ |
| 97 | if (count) { \ |
| 98 | const ctype *buf = buffer; \ |
| 99 | \ |
| 100 | do { \ |
| 101 | __raw_write ## len(*buf++, addr); \ |
| 102 | } while (--count); \ |
| 103 | } \ |
| 104 | afence; \ |
| 105 | } |
| 106 | |
David Brazdil | 0f672f6 | 2019-12-10 10:32:29 +0000 | [diff] [blame] | 107 | __io_reads_ins(reads, u8, b, __io_br(), __io_ar(addr)) |
| 108 | __io_reads_ins(reads, u16, w, __io_br(), __io_ar(addr)) |
| 109 | __io_reads_ins(reads, u32, l, __io_br(), __io_ar(addr)) |
Andrew Scull | b4b6d4a | 2019-01-02 15:54:55 +0000 | [diff] [blame] | 110 | #define readsb(addr, buffer, count) __readsb(addr, buffer, count) |
| 111 | #define readsw(addr, buffer, count) __readsw(addr, buffer, count) |
| 112 | #define readsl(addr, buffer, count) __readsl(addr, buffer, count) |
| 113 | |
David Brazdil | 0f672f6 | 2019-12-10 10:32:29 +0000 | [diff] [blame] | 114 | __io_reads_ins(ins, u8, b, __io_pbr(), __io_par(addr)) |
| 115 | __io_reads_ins(ins, u16, w, __io_pbr(), __io_par(addr)) |
| 116 | __io_reads_ins(ins, u32, l, __io_pbr(), __io_par(addr)) |
Andrew Scull | b4b6d4a | 2019-01-02 15:54:55 +0000 | [diff] [blame] | 117 | #define insb(addr, buffer, count) __insb((void __iomem *)(long)addr, buffer, count) |
| 118 | #define insw(addr, buffer, count) __insw((void __iomem *)(long)addr, buffer, count) |
| 119 | #define insl(addr, buffer, count) __insl((void __iomem *)(long)addr, buffer, count) |
| 120 | |
| 121 | __io_writes_outs(writes, u8, b, __io_bw(), __io_aw()) |
| 122 | __io_writes_outs(writes, u16, w, __io_bw(), __io_aw()) |
| 123 | __io_writes_outs(writes, u32, l, __io_bw(), __io_aw()) |
| 124 | #define writesb(addr, buffer, count) __writesb(addr, buffer, count) |
| 125 | #define writesw(addr, buffer, count) __writesw(addr, buffer, count) |
| 126 | #define writesl(addr, buffer, count) __writesl(addr, buffer, count) |
| 127 | |
| 128 | __io_writes_outs(outs, u8, b, __io_pbw(), __io_paw()) |
| 129 | __io_writes_outs(outs, u16, w, __io_pbw(), __io_paw()) |
| 130 | __io_writes_outs(outs, u32, l, __io_pbw(), __io_paw()) |
| 131 | #define outsb(addr, buffer, count) __outsb((void __iomem *)(long)addr, buffer, count) |
| 132 | #define outsw(addr, buffer, count) __outsw((void __iomem *)(long)addr, buffer, count) |
| 133 | #define outsl(addr, buffer, count) __outsl((void __iomem *)(long)addr, buffer, count) |
| 134 | |
| 135 | #ifdef CONFIG_64BIT |
David Brazdil | 0f672f6 | 2019-12-10 10:32:29 +0000 | [diff] [blame] | 136 | __io_reads_ins(reads, u64, q, __io_br(), __io_ar(addr)) |
Andrew Scull | b4b6d4a | 2019-01-02 15:54:55 +0000 | [diff] [blame] | 137 | #define readsq(addr, buffer, count) __readsq(addr, buffer, count) |
| 138 | |
David Brazdil | 0f672f6 | 2019-12-10 10:32:29 +0000 | [diff] [blame] | 139 | __io_reads_ins(ins, u64, q, __io_pbr(), __io_par(addr)) |
Andrew Scull | b4b6d4a | 2019-01-02 15:54:55 +0000 | [diff] [blame] | 140 | #define insq(addr, buffer, count) __insq((void __iomem *)addr, buffer, count) |
| 141 | |
| 142 | __io_writes_outs(writes, u64, q, __io_bw(), __io_aw()) |
| 143 | #define writesq(addr, buffer, count) __writesq(addr, buffer, count) |
| 144 | |
| 145 | __io_writes_outs(outs, u64, q, __io_pbr(), __io_paw()) |
| 146 | #define outsq(addr, buffer, count) __outsq((void __iomem *)addr, buffer, count) |
| 147 | #endif |
| 148 | |
| 149 | #include <asm-generic/io.h> |
| 150 | |
| 151 | #endif /* _ASM_RISCV_IO_H */ |