82b89152f0
I am about to commit: http://sourceware.org/ml/binutils/2010-10/msg00033.html that fixes a problem with the LD/SD macro currently implemented by GAS for the o32 ABI in an inconsistent way. This is best illustrated with a simple program, which I'm copying here from the message above for easier reference: $ cat ld.s ld $5,32767($4) ld $5,32768($4) This gets assebled into the following output: $ mips-linux-as -32 -mips3 -o ld.o ld.s $ mips-linux-objdump -d ld.o ld.o: file format elf32-tradbigmips Disassembly of section .text: 00000000 <.text>: 0: dc857fff ld a1,32767(a0) 4: 3c010001 lui at,0x1 8: 00810821 addu at,a0,at c: 8c258000 lw a1,-32768(at) 10: 8c268004 lw a2,-32764(at) ... Oops! The GAS fix makes the macro behave in a consistent way and pairs of LW/SW instructions to be output as appropriate regardless of the size of the offset associated with the address used. The machine instruction is still available, but to reach it macros have to be disabled first. This has a side effect of requiring the use of a machine-addressable memory operand. As some platforms require 64-bit operations for accesses to some I/O registers LD/SD instructions are used in a couple of places in Linux regardless of the ABI selected. Here's a fix for some pieces of code affected I've been able to track down. The fix should be backwards compatible with all supported binutils releases in existence and can be used as a reference for any other places or off-tree code. The use of the "R" constraint guarantees a machine-addressable operand. Signed-off-by: Maciej W. Rozycki <macro@linux-mips.org> Cc: linux-mips@linux-mips.org Patchwork: https://patchwork.linux-mips.org/patch/1680/ Signed-off-by: Ralf Baechle <ralf@linux-mips.org>
118 lines
2.5 KiB
C
118 lines
2.5 KiB
C
/*
|
|
* This file is subject to the terms and conditions of the GNU General Public
|
|
* License. See the file "COPYING" in the main directory of this archive
|
|
* for more details.
|
|
*
|
|
* Copyright (C) 2001, 2002, 2004 Ralf Baechle
|
|
*/
|
|
#include <linux/init.h>
|
|
#include <linux/console.h>
|
|
#include <linux/kdev_t.h>
|
|
#include <linux/major.h>
|
|
#include <linux/termios.h>
|
|
#include <linux/sched.h>
|
|
#include <linux/tty.h>
|
|
|
|
#include <linux/serial.h>
|
|
#include <linux/serial_core.h>
|
|
#include <asm/serial.h>
|
|
#include <asm/io.h>
|
|
|
|
/* SUPERIO uart register map */
|
|
struct yo_uartregs {
|
|
union {
|
|
volatile u8 rbr; /* read only, DLAB == 0 */
|
|
volatile u8 thr; /* write only, DLAB == 0 */
|
|
volatile u8 dll; /* DLAB == 1 */
|
|
} u1;
|
|
union {
|
|
volatile u8 ier; /* DLAB == 0 */
|
|
volatile u8 dlm; /* DLAB == 1 */
|
|
} u2;
|
|
union {
|
|
volatile u8 iir; /* read only */
|
|
volatile u8 fcr; /* write only */
|
|
} u3;
|
|
volatile u8 iu_lcr;
|
|
volatile u8 iu_mcr;
|
|
volatile u8 iu_lsr;
|
|
volatile u8 iu_msr;
|
|
volatile u8 iu_scr;
|
|
} yo_uregs_t;
|
|
|
|
#define iu_rbr u1.rbr
|
|
#define iu_thr u1.thr
|
|
#define iu_dll u1.dll
|
|
#define iu_ier u2.ier
|
|
#define iu_dlm u2.dlm
|
|
#define iu_iir u3.iir
|
|
#define iu_fcr u3.fcr
|
|
|
|
#define ssnop() __asm__ __volatile__("sll $0, $0, 1\n");
|
|
#define ssnop_4() do { ssnop(); ssnop(); ssnop(); ssnop(); } while (0)
|
|
|
|
#define IO_BASE_64 0x9000000000000000ULL
|
|
|
|
static unsigned char readb_outer_space(unsigned long long phys)
|
|
{
|
|
unsigned long long vaddr = IO_BASE_64 | phys;
|
|
unsigned char res;
|
|
unsigned int sr;
|
|
|
|
sr = read_c0_status();
|
|
write_c0_status((sr | ST0_KX) & ~ ST0_IE);
|
|
ssnop_4();
|
|
|
|
__asm__ __volatile__ (
|
|
" .set mips3 \n"
|
|
" .set push \n"
|
|
" .set noreorder \n"
|
|
" .set nomacro \n"
|
|
" ld %0, %1 \n"
|
|
" .set pop \n"
|
|
" lbu %0, (%0) \n"
|
|
" .set mips0 \n"
|
|
: "=r" (res)
|
|
: "R" (vaddr));
|
|
|
|
write_c0_status(sr);
|
|
ssnop_4();
|
|
|
|
return res;
|
|
}
|
|
|
|
static void writeb_outer_space(unsigned long long phys, unsigned char c)
|
|
{
|
|
unsigned long long vaddr = IO_BASE_64 | phys;
|
|
unsigned long tmp;
|
|
unsigned int sr;
|
|
|
|
sr = read_c0_status();
|
|
write_c0_status((sr | ST0_KX) & ~ ST0_IE);
|
|
ssnop_4();
|
|
|
|
__asm__ __volatile__ (
|
|
" .set mips3 \n"
|
|
" .set push \n"
|
|
" .set noreorder \n"
|
|
" .set nomacro \n"
|
|
" ld %0, %1 \n"
|
|
" .set pop \n"
|
|
" sb %2, (%0) \n"
|
|
" .set mips0 \n"
|
|
: "=&r" (tmp)
|
|
: "R" (vaddr), "r" (c));
|
|
|
|
write_c0_status(sr);
|
|
ssnop_4();
|
|
}
|
|
|
|
void prom_putchar(char c)
|
|
{
|
|
unsigned long lsr = 0xfd000008ULL + offsetof(struct yo_uartregs, iu_lsr);
|
|
unsigned long thr = 0xfd000008ULL + offsetof(struct yo_uartregs, iu_thr);
|
|
|
|
while ((readb_outer_space(lsr) & 0x20) == 0);
|
|
writeb_outer_space(thr, c);
|
|
}
|