hwtest.c 2.6 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495
  1. // SPDX-License-Identifier: GPL-2.0
  2. /* Tests for presence or absence of hardware registers.
  3. * This code was originally in atari/config.c, but I noticed
  4. * that it was also in drivers/nubus/nubus.c and I wanted to
  5. * use it in hp300/config.c, so it seemed sensible to pull it
  6. * out into its own file.
  7. *
  8. * The test is for use when trying to read a hardware register
  9. * that isn't present would cause a bus error. We set up a
  10. * temporary handler so that this doesn't kill the kernel.
  11. *
  12. * There is a test-by-reading and a test-by-writing; I present
  13. * them here complete with the comments from the original atari
  14. * config.c...
  15. * -- PMM <pmaydell@chiark.greenend.org.uk>, 05/1998
  16. */
  17. /* This function tests for the presence of an address, specially a
  18. * hardware register address. It is called very early in the kernel
  19. * initialization process, when the VBR register isn't set up yet. On
  20. * an Atari, it still points to address 0, which is unmapped. So a bus
  21. * error would cause another bus error while fetching the exception
  22. * vector, and the CPU would do nothing at all. So we needed to set up
  23. * a temporary VBR and a vector table for the duration of the test.
  24. */
  25. #include <linux/module.h>
  26. int hwreg_present(volatile void *regp)
  27. {
  28. int ret = 0;
  29. unsigned long flags;
  30. long save_sp, save_vbr;
  31. long tmp_vectors[3];
  32. local_irq_save(flags);
  33. __asm__ __volatile__ (
  34. "movec %/vbr,%2\n\t"
  35. "movel #Lberr1,%4@(8)\n\t"
  36. "movec %4,%/vbr\n\t"
  37. "movel %/sp,%1\n\t"
  38. "moveq #0,%0\n\t"
  39. "tstb %3@\n\t"
  40. "nop\n\t"
  41. "moveq #1,%0\n"
  42. "Lberr1:\n\t"
  43. "movel %1,%/sp\n\t"
  44. "movec %2,%/vbr"
  45. : "=&d" (ret), "=&r" (save_sp), "=&r" (save_vbr)
  46. : "a" (regp), "a" (tmp_vectors)
  47. );
  48. local_irq_restore(flags);
  49. return ret;
  50. }
  51. EXPORT_SYMBOL(hwreg_present);
  52. /* Basically the same, but writes a value into a word register, protected
  53. * by a bus error handler. Returns 1 if successful, 0 otherwise.
  54. */
  55. int hwreg_write(volatile void *regp, unsigned short val)
  56. {
  57. int ret;
  58. unsigned long flags;
  59. long save_sp, save_vbr;
  60. long tmp_vectors[3];
  61. local_irq_save(flags);
  62. __asm__ __volatile__ (
  63. "movec %/vbr,%2\n\t"
  64. "movel #Lberr2,%4@(8)\n\t"
  65. "movec %4,%/vbr\n\t"
  66. "movel %/sp,%1\n\t"
  67. "moveq #0,%0\n\t"
  68. "movew %5,%3@\n\t"
  69. "nop\n\t"
  70. /*
  71. * If this nop isn't present, 'ret' may already be loaded
  72. * with 1 at the time the bus error happens!
  73. */
  74. "moveq #1,%0\n"
  75. "Lberr2:\n\t"
  76. "movel %1,%/sp\n\t"
  77. "movec %2,%/vbr"
  78. : "=&d" (ret), "=&r" (save_sp), "=&r" (save_vbr)
  79. : "a" (regp), "a" (tmp_vectors), "g" (val)
  80. );
  81. local_irq_restore(flags);
  82. return ret;
  83. }
  84. EXPORT_SYMBOL(hwreg_write);