diff --git a/gdb/mips-linux-nat.c b/gdb/mips-linux-nat.c
index bfe9fcb..627c652 100644
--- a/gdb/mips-linux-nat.c
+++ b/gdb/mips-linux-nat.c
@@ -27,6 +27,7 @@
 #include "linux-nat.h"
 #include "mips-linux-tdep.h"
 #include "target-descriptions.h"
+#include "elf/common.h"
 
 #include "gdb_proc_service.h"
 #include "gregset.h"
@@ -46,10 +47,22 @@
 #define PTRACE_GET_THREAD_AREA 25
 #endif
 
+#ifndef PTRACE_GETREGSET
+#define PTRACE_GETREGSET	0x4204
+#endif
+
+#ifndef PTRACE_SETREGSET
+#define PTRACE_SETREGSET	0x4205
+#endif
+
 /* Assume that we have PTRACE_GETREGS et al. support.  If we do not,
    we'll clear this and use PTRACE_PEEKUSER instead.  */
 static int have_ptrace_regsets = 1;
 
+/* Does the current host support PTRACE_GETREGSET?  */
+static int have_ptrace_getregset_gp = 1;
+static int have_ptrace_getregset_fp = 1;
+
 /* Saved function pointers to fetch and store a single register using
    PTRACE_PEEKUSER and PTRACE_POKEUSER.  */
 
@@ -217,18 +230,25 @@ mips64_linux_regsets_fetch_registers (struct target_ops *ops,
 				      struct regcache *regcache, int regno)
 {
   struct gdbarch *gdbarch = get_regcache_arch (regcache);
+  int big_endian = (gdbarch_byte_order (gdbarch) == BFD_ENDIAN_BIG);
   int is_fp, is_dsp;
   int have_dsp;
   int regi;
   int tid;
 
-  if (regno >= mips_regnum (gdbarch)->fp0
-      && regno <= mips_regnum (gdbarch)->fp0 + 32)
+  /* FP registers can be obtained in several ways.
+     is_fp will be cleared once the registers have been obtained.  */
+  if (regno == -1)
+    is_fp = 1;
+  else if (regno >= mips_regnum (gdbarch)->fp0
+	   && regno <= mips_regnum (gdbarch)->fp0 + 32)
     is_fp = 1;
   else if (regno == mips_regnum (gdbarch)->fp_control_status)
     is_fp = 1;
   else if (regno == mips_regnum (gdbarch)->fp_implementation_revision)
     is_fp = 1;
+  else if (regno == mips_regnum (gdbarch)->config5)
+    is_fp = 1;
   else
     is_fp = 0;
 
@@ -266,23 +286,93 @@ mips64_linux_regsets_fetch_registers (struct target_ops *ops,
 			     (const mips64_elf_gregset_t *) &regs);
     }
 
-  if (regno == -1 || is_fp)
+  if (is_fp)
     {
-      mips64_elf_fpregset_t fp_regs;
+      const struct mips_regnum *rn = mips_regnum (gdbarch);
+      int float_regnum = rn->fp0;
 
-      if (ptrace (PTRACE_GETFPREGS, tid, 0L,
-		  (PTRACE_TYPE_ARG3) &fp_regs) == -1)
+      /* Try the FP regset next as it may contain Config5 */
+      if (have_ptrace_getregset_gp && have_ptrace_getregset_fp)
 	{
-	  if (errno == EIO)
+	  unsigned char fp_regs[34][8];
+	  struct iovec iovec;
+	  int ret;
+
+	  iovec.iov_base = &fp_regs;
+	  iovec.iov_len = sizeof (fp_regs);
+
+	  ret = ptrace (PTRACE_GETREGSET, tid, NT_FPREGSET, &iovec);
+	  if (ret < 0)
 	    {
-	      have_ptrace_regsets = 0;
-	      return;
+	      if (errno == EIO)
+		have_ptrace_getregset_gp = 0;
+	      else if (errno == EINVAL)
+		have_ptrace_getregset_fp = 0;
+	      else
+		perror_with_name (_("Unable to fetch FP registers."));
+	    }
+	  else
+	    {
+	      for (regi = 0; regi < 32; regi++)
+		{
+		  if (register_size (gdbarch, float_regnum + regi) == 8)
+		    {
+		      /* FR = 1
+			 copy entire double */
+		      regcache_raw_supply (regcache, float_regnum + regi,
+					   (char *) fp_regs[regi]);
+		    }
+		  else if (regi & 1)
+		    {
+		      /* FR = 0
+			 odd single from top of even double */
+		      regcache_raw_supply (regcache, float_regnum + regi,
+					   (char *) fp_regs[regi - 1] +
+					   4 - 4*big_endian);
+		    }
+		  else
+		    {
+		      /* FR = 0
+			 even single from bottom of even double */
+		      regcache_raw_supply (regcache, float_regnum + regi,
+					   (char *) fp_regs[regi] + 4 * big_endian);
+		    }
+		}
+
+	      if (iovec.iov_len >= 32*8 + 4)
+		regcache_raw_supply (regcache, rn->fp_control_status,
+				     (char *) (fp_regs + 32) + 0);
+	      if (iovec.iov_len >= 32*8 + 8)
+		regcache_raw_supply (regcache, rn->fp_implementation_revision,
+				     (char *) (fp_regs + 32) + 4);
+	      if (iovec.iov_len >= 33*8 + 4 && rn->config5 >= 0)
+		regcache_raw_supply (regcache, rn->config5,
+				     (char *) (fp_regs + 33) + 0);
+
+	      /* we've got fp registers now */
+	      is_fp = 0;
 	    }
-	  perror_with_name (_("Couldn't get FP registers"));
 	}
 
-      mips64_supply_fpregset (regcache,
-			      (const mips64_elf_fpregset_t *) &fp_regs);
+      /* Fall back to GETFPREGS. */
+      if (is_fp)
+	{
+	  mips64_elf_fpregset_t fp_regs;
+
+	  if (ptrace (PTRACE_GETFPREGS, tid, 0L,
+		      (PTRACE_TYPE_ARG3) &fp_regs) == -1)
+	    {
+	      if (errno == EIO)
+		{
+		  have_ptrace_regsets = 0;
+		  return;
+		}
+	      perror_with_name (_("Couldn't get FP registers"));
+	    }
+
+	  mips64_supply_fpregset (regcache,
+				  (const mips64_elf_fpregset_t *) &fp_regs);
+	}
     }
 
   if (is_dsp)
@@ -305,11 +395,16 @@ mips64_linux_regsets_store_registers (struct target_ops *ops,
 				      struct regcache *regcache, int regno)
 {
   struct gdbarch *gdbarch = get_regcache_arch (regcache);
+  int big_endian = (gdbarch_byte_order (gdbarch) == BFD_ENDIAN_BIG);
   int is_fp, is_dsp;
   int have_dsp;
   int regi;
   int tid;
 
+  /* FP registers can be written in several ways.
+     is_fp will be cleared once the registers have been written.  */
+  if (regno == -1)
+    is_fp = 1;
   if (regno >= mips_regnum (gdbarch)->fp0
       && regno <= mips_regnum (gdbarch)->fp0 + 32)
     is_fp = 1;
@@ -317,6 +412,8 @@ mips64_linux_regsets_store_registers (struct target_ops *ops,
     is_fp = 1;
   else if (regno == mips_regnum (gdbarch)->fp_implementation_revision)
     is_fp = 1;
+  else if (regno == mips_regnum (gdbarch)->config5)
+    is_fp = 1;
   else
     is_fp = 0;
 
@@ -349,19 +446,101 @@ mips64_linux_regsets_store_registers (struct target_ops *ops,
 	perror_with_name (_("Couldn't set registers"));
     }
 
-  if (regno == -1 || is_fp)
+  if (is_fp)
     {
-      mips64_elf_fpregset_t fp_regs;
+      const struct mips_regnum *rn = mips_regnum (gdbarch);
+      int float_regnum = rn->fp0;
+
+      /* Try the FP regset next as it may contain Config5 */
+      if (have_ptrace_getregset_gp && have_ptrace_getregset_fp)
+	{
+	  unsigned char fp_regs[34][8];
+	  struct iovec iovec;
+	  int ret;
+
+	  iovec.iov_base = &fp_regs;
+	  iovec.iov_len = sizeof (fp_regs);
 
-      if (ptrace (PTRACE_GETFPREGS, tid, 0L,
-		  (PTRACE_TYPE_ARG3) &fp_regs) == -1)
-	perror_with_name (_("Couldn't get FP registers"));
+	  ret = ptrace (PTRACE_GETREGSET, tid, NT_FPREGSET, &iovec);
+	  if (ret < 0)
+	    {
+	      if (errno == EIO)
+		have_ptrace_getregset_gp = 0;
+	      else if (errno == EINVAL)
+		have_ptrace_getregset_fp = 0;
+	      else
+		perror_with_name (_("Unable to fetch FP registers."));
+	    }
+	  else
+	    {
+	      for (regi = 0; regi < 32; regi++)
+		{
+		  if (register_size (gdbarch, float_regnum + regi) == 8)
+		    {
+		      /* FR = 1
+			 copy entire double */
+		      regcache_raw_collect (regcache, float_regnum + regi,
+					   (char *) fp_regs[regi]);
+		    }
+		  else if (regi & 1)
+		    {
+		      /* FR = 0
+			 odd single from top of even double */
+		      regcache_raw_collect (regcache, float_regnum + regi,
+					   (char *) fp_regs[regi - 1] +
+					   4 - 4*big_endian);
+		    }
+		  else
+		    {
+		      /* FR = 0
+			 even single from bottom of even double */
+		      regcache_raw_collect (regcache, float_regnum + regi,
+					   (char *) fp_regs[regi] + 4 * big_endian);
+		    }
+		}
+
+	      regcache_raw_collect (regcache, rn->fp_control_status,
+				   (char *) (fp_regs + 32) + 0);
+	      regcache_raw_collect (regcache, rn->fp_implementation_revision,
+				   (char *) (fp_regs + 32) + 4);
+	      if (rn->config5 >= 0)
+		regcache_raw_collect (regcache, rn->config5,
+				      (char *) (fp_regs + 33) + 0);
+
+	      /* don't modify iovec length from amount of data returned */
+	      ret = ptrace (PTRACE_SETREGSET, tid, NT_FPREGSET, &iovec);
+	      if (ret < 0)
+		{
+		  if (errno == EIO)
+		    have_ptrace_getregset_gp = 0;
+		  else if (errno == EINVAL)
+		    have_ptrace_getregset_fp = 0;
+		  else
+		    perror_with_name (_("Unable to store FP/MSA registers."));
+		}
+	      else
+		{
+		  /* we've got fp registers now */
+		  is_fp = 0;
+		}
+	    }
+	}
 
-      mips64_fill_fpregset (regcache, &fp_regs, regno);
+      /* Fall back to SETFPREGS. */
+      if (is_fp)
+	{
+	  mips64_elf_fpregset_t fp_regs;
+
+	  if (ptrace (PTRACE_GETFPREGS, tid, 0L,
+		      (PTRACE_TYPE_ARG3) &fp_regs) == -1)
+	    perror_with_name (_("Couldn't get FP registers"));
 
-      if (ptrace (PTRACE_SETFPREGS, tid, 0L,
-		  (PTRACE_TYPE_ARG3) &fp_regs) == -1)
-	perror_with_name (_("Couldn't set FP registers"));
+	  mips64_fill_fpregset (regcache, &fp_regs, regno);
+
+	  if (ptrace (PTRACE_SETFPREGS, tid, 0L,
+		      (PTRACE_TYPE_ARG3) &fp_regs) == -1)
+	    perror_with_name (_("Couldn't set FP registers"));
+	}
     }
 
   if (is_dsp)
diff --git a/gdb/mips-tdep.c b/gdb/mips-tdep.c
index ae4dc2e..28cfb57 100644
--- a/gdb/mips-tdep.c
+++ b/gdb/mips-tdep.c
@@ -993,6 +993,25 @@ mips_register_to_value (struct frame_info *frame, int regnum,
     }
 }
 
+static struct type *
+mips_config5_type (struct gdbarch *gdbarch)
+{
+  struct gdbarch_tdep *tdep = gdbarch_tdep (gdbarch);
+
+  if (tdep->config5_type == NULL)
+    {
+      struct type *t;
+      /* top half has flags */
+      t = arch_flags_type (gdbarch, "__gdb_builtin_type_config5", 4);
+      append_flags_type_flag (t, 8, "FRE");
+
+      TYPE_NAME (t) = "config5";
+      tdep->config5_type = t;
+    }
+
+  return tdep->config5_type;
+}
+
 static void
 mips_value_to_register (struct frame_info *frame, int regnum,
 			struct type *type, const gdb_byte *from)
@@ -1080,6 +1099,8 @@ mips_register_type (struct gdbarch *gdbarch, int regnum)
       else if (rawnum == mips_regnum (gdbarch)->fp_control_status
 	  || rawnum == mips_regnum (gdbarch)->fp_implementation_revision)
 	return builtin_type (gdbarch)->builtin_int32;
+      else if (rawnum == mips_regnum (gdbarch)->config5)
+	return mips_config5_type (gdbarch);
       else if (gdbarch_osabi (gdbarch) != GDB_OSABI_IRIX
 	       && gdbarch_osabi (gdbarch) != GDB_OSABI_LINUX
 	       && rawnum >= MIPS_FIRST_EMBED_REGNUM
@@ -1152,6 +1173,9 @@ mips_pseudo_register_type (struct gdbarch *gdbarch, int regnum)
 	      && rawnum < mips_regnum (gdbarch)->dspacc + 6)))
     return builtin_type (gdbarch)->builtin_int32;
 
+  if (rawnum == mips_regnum (gdbarch)->config5)
+    return mips_config5_type (gdbarch);
+
   if (gdbarch_osabi (gdbarch) != GDB_OSABI_IRIX
       && gdbarch_osabi (gdbarch) != GDB_OSABI_LINUX
       && rawnum >= MIPS_EMBED_FP0_REGNUM + 32
@@ -8236,6 +8260,7 @@ mips_gdbarch_init (struct gdbarch_info info, struct gdbarch_list *arches)
     info.tdep_info = &tdep_info;
 
   /* Fill in the OS dependent register numbers and names.  */
+  mips_regnum.config5 = -1;
   if (info.osabi == GDB_OSABI_IRIX)
     {
       mips_regnum.fp0 = 32;
@@ -8350,6 +8375,15 @@ mips_gdbarch_init (struct gdbarch_info info, struct gdbarch_list *arches)
       valid_p &= tdesc_numbered_register (feature, tdesc_data,
 					  mips_regnum.cause, "cause");
 
+      /* Optionally, Config5 contains FP mode bits */
+      if (tdesc_unnumbered_register (feature, "config5"))
+	{
+	  /* Allocate a new register.  */
+	  mips_regnum.config5 = num_regs++;
+	  tdesc_numbered_register (feature, tdesc_data,
+				   mips_regnum.config5, "config5");
+	}
+
       if (!valid_p)
 	{
 	  tdesc_data_cleanup (tdesc_data);
@@ -8666,6 +8700,7 @@ mips_gdbarch_init (struct gdbarch_info info, struct gdbarch_list *arches)
   tdep->register_size = 0;
   tdep->fp_register_size = info.tdep_info->fp_register_size;
   tdep->fp_register_size_fixed_p = 0;
+  tdep->config5_type = NULL;
 
   if (info.target_desc)
     {
diff --git a/gdb/mips-tdep.h b/gdb/mips-tdep.h
index 6c1d9ca..39af72b 100644
--- a/gdb/mips-tdep.h
+++ b/gdb/mips-tdep.h
@@ -63,6 +63,7 @@ struct mips_regnum
   int fp0;
   int fp_implementation_revision;
   int fp_control_status;
+  int config5;
   int badvaddr;		/* Bad vaddr for addressing exception.  */
   int cause;		/* Describes last exception.  */
   int hi;		/* Multiply/divide temp.  */
@@ -119,6 +120,9 @@ struct gdbarch_tdep
   int fp_register_size_fixed_p;
   int fp_register_size;
 
+  /* ISA-specific data types.  */
+  struct type *config5_type;
+
   /* Return the expected next PC if FRAME is stopped at a syscall
      instruction.  */
   CORE_ADDR (*syscall_next_pc) (struct frame_info *frame);
