Browse Source

Plan 9 from Bell Labs 2013-01-26

David du Colombier 11 years ago
parent
commit
bde1f7ed04
75 changed files with 12144 additions and 255 deletions
  1. 19 12
      sys/doc/port.ms
  2. 253 229
      sys/doc/port.ps
  3. 1 0
      sys/games/lib/fortunes
  4. 14 1
      sys/man/8/booting
  5. 1 0
      sys/src/9/bcm/arch.c
  6. 78 0
      sys/src/9/bcm/archbcm.c
  7. 273 0
      sys/src/9/bcm/arm.h
  8. 36 0
      sys/src/9/bcm/arm.s
  9. 30 0
      sys/src/9/bcm/boot.rc
  10. 204 0
      sys/src/9/bcm/clock.c
  11. 1 0
      sys/src/9/bcm/coproc.c
  12. 287 0
      sys/src/9/bcm/dat.h
  13. 163 0
      sys/src/9/bcm/devarch.c
  14. 1 0
      sys/src/9/bcm/devether.c
  15. 124 0
      sys/src/9/bcm/devfakertc.c
  16. 1487 0
      sys/src/9/bcm/devusb.c
  17. 221 0
      sys/src/9/bcm/dma.c
  18. 510 0
      sys/src/9/bcm/dwcotg.h
  19. 426 0
      sys/src/9/bcm/emmc.c
  20. 1 0
      sys/src/9/bcm/etherif.h
  21. 428 0
      sys/src/9/bcm/etherusb.c
  22. 119 0
      sys/src/9/bcm/fns.h
  23. 1 0
      sys/src/9/bcm/fpi.c
  24. 1 0
      sys/src/9/bcm/fpi.h
  25. 506 0
      sys/src/9/bcm/fpiarm.c
  26. 1 0
      sys/src/9/bcm/fpimem.c
  27. 1 0
      sys/src/9/bcm/init9.s
  28. 45 0
      sys/src/9/bcm/io.h
  29. 1 0
      sys/src/9/bcm/kbd.c
  30. 274 0
      sys/src/9/bcm/l.s
  31. 197 0
      sys/src/9/bcm/lexception.s
  32. 1 0
      sys/src/9/bcm/lproc.s
  33. 597 0
      sys/src/9/bcm/main.c
  34. 102 0
      sys/src/9/bcm/mem.h
  35. 137 0
      sys/src/9/bcm/mkfile
  36. 319 0
      sys/src/9/bcm/mmu.c
  37. 1 0
      sys/src/9/bcm/mouse.c
  38. 61 0
      sys/src/9/bcm/pi
  39. 61 0
      sys/src/9/bcm/picpu
  40. 57 0
      sys/src/9/bcm/pifat
  41. 1 0
      sys/src/9/bcm/random.c
  42. 93 0
      sys/src/9/bcm/rebootcode.s
  43. 544 0
      sys/src/9/bcm/screen.c
  44. 35 0
      sys/src/9/bcm/screen.h
  45. 1 0
      sys/src/9/bcm/softfpu.c
  46. 1 0
      sys/src/9/bcm/syscall.c
  47. 567 0
      sys/src/9/bcm/trap.c
  48. 423 0
      sys/src/9/bcm/uartmini.c
  49. 962 0
      sys/src/9/bcm/usbdwc.c
  50. 290 0
      sys/src/9/bcm/vcore.c
  51. 518 0
      sys/src/9/bcm/vfp3.c
  52. 149 0
      sys/src/9/bcm/words
  53. 1 0
      sys/src/9/mkfile
  54. 1 1
      sys/src/9/ppc/blast
  55. 2 0
      sys/src/9/ppc/blast.h
  56. 19 1
      sys/src/9/ppc/dat.h
  57. 191 0
      sys/src/9/ppc/devce.c
  58. 18 1
      sys/src/9/ppc/devirq.c
  59. 2 0
      sys/src/9/ppc/etherfcc.c
  60. 233 0
      sys/src/9/ppc/ethermii.c
  61. 117 0
      sys/src/9/ppc/ethermii.h
  62. 1 0
      sys/src/9/ppc/ethersaturn.c
  63. 2 0
      sys/src/9/ppc/fns.h
  64. 730 0
      sys/src/9/ppc/imm.h
  65. 16 0
      sys/src/9/ppc/l.s
  66. 19 2
      sys/src/9/ppc/m8260.c
  67. 3 0
      sys/src/9/ppc/mem.h
  68. 4 2
      sys/src/9/ppc/mkfile
  69. 16 1
      sys/src/9/ppc/saturntimer.c
  70. 69 0
      sys/src/9/ppc/trap.c
  71. 51 0
      sys/src/9/ppc/uartsmc.c
  72. 17 0
      sys/src/9/ppc/uartsmc.h
  73. 1 1
      sys/src/9/ppc/ucu
  74. 5 2
      sys/src/cmd/acme/fsys.c
  75. 2 2
      sys/src/libdraw/init.c

+ 19 - 12
sys/doc/port.ms

@@ -104,12 +104,14 @@ for the iPAQ, which uses a StrongArm SA1, and the Sheevaplug,
 Guruplug, Dreamplug, Gumstix Overo, Compulab Trimslice and others.
 The compiler supports the ARMv4 and later 32-bit architectures;
 it does not support the Thumb instruction sets.
-It has been used on ARM7500FE, ARM926 and Cortex-A8 processors
+It has been used on ARM7500FE, ARM926 and Cortex-A8 and -A9 processors
 and the Strongarm SA1 core machines.
 The compiler generates instructions for
-ARM 7500 FPA floating-point coprocessor 1,
-but probably should instead generate VFP 3+ instructions
-for coprocessors 10 and 11.
+ARM 7500 FPA floating-point coprocessor 1 by default,
+but
+.CW 5l
+.CW -f
+instead generates VFP instructions for coprocessors 10 and 11.
 .
 .SH
 The IBM PC operating system
@@ -237,7 +239,7 @@ for information about installing Plan 9 on the iPAQ.
 The Marvell Kirkwood operating system
 .PP
 This is an ARM kernel for the ARM926EJ-S processor
-and it emulates floating-point and
+and it emulates ARM 7500 floating-point and
 CAS (compare-and-swap) instructions.
 It is known to run on the Sheevaplug, Guruplug, Dreamplug
 and Openrd-client boards.
@@ -254,7 +256,7 @@ The Marvell PXA168 operating system
 .PP
 This is an ARM kernel for the ARM-v5-architecture processor in the
 Marvell PXA168 system-on-a-chip
-and it emulates floating-point and
+and it emulates ARM 7500 floating-point and
 CAS (compare-and-swap) instructions.
 It is known to run on the Guruplug Display.
 There are many features of the system-on-a-chip
@@ -267,7 +269,7 @@ we hope to add crypto acceleration, and a video driver.
 The TI OMAP35 operating system
 .PP
 This is an ARM kernel for the Cortex-A8 processor
-and it emulates pre-VFPv3 floating-point and
+and it emulates ARM 7500 floating-point and
 CAS (compare-and-swap) instructions.
 It is known to run on the IGEPv2 board and the Gumstix Overo,
 and might eventually run on the Beagleboard, once USB is working.
@@ -281,16 +283,21 @@ The Nvidia Tegra2 operating system
 .PP
 This is an ARM kernel for the dual Cortex-A9 processors
 in the Nvidia Tegra2 system-on-a-chip
-and it emulates pre-VFPv3 floating-point and
-CAS (compare-and-swap) instructions
-(the hardware has VFP3 floating-point but
-.CW 5l
-doesn't generate the new opcodes yet).
+and it emulates ARM 7500 floating-point and
+CAS (compare-and-swap) instructions, but
+the hardware includes VFP3 floating-point.
 It runs on the Compulab Trimslice.
 There are many features of the system-on-a-chip that it does not exploit.
 Initially, there are drivers for the Ethernet interface
 and the console serial port;
 we hope to add USB, flash memory and video drivers.
+.SH
+The Broadcom 2835 operating system
+.PP
+This is terminal and CPU kernels for the ARM1176 processor
+in the Broadcom 2835 system-on-a-chip.
+The hardware includes VFP2 floating-point.
+It runs on the Raspberry Pi Model B.
 .
 .ig
 .SH

+ 253 - 229
sys/doc/port.ps

@@ -4330,30 +4330,36 @@ mark
 (ARM7500FE,) 3990 2832 w
 (ARM926) 4633 2832 w
 (and) 720 2952 w
-(Cortex-A8) 939 2952 w
-(processors) 1493 2952 w
-(and) 2059 2952 w
-(the) 2278 2952 w
-(Strongarm) 2472 2952 w
-(SA1) 3017 2952 w
-(core) 3242 2952 w
-(machines.) 3489 2952 w
-(The) 4050 2952 w
-(compiler) 4269 2952 w
-(gener\255) 4730 2952 w
-(ates) 720 3072 w
-(instructions) 962 3072 w
-(for) 1578 3072 w
-(ARM) 1760 3072 w
-(7500) 2021 3072 w
-(FPA) 2316 3072 w
-(floating-point) 2538 3072 w
-(coprocessor) 3264 3072 w
-(1,) 3896 3072 w
-(but) 4035 3072 w
-(probably) 4241 3072 w
-(should) 4712 3072 w
-(instead generate VFP 3+ instructions for coprocessors 10 and 11.) 720 3192 w
+(Cortex-A8) 946 2952 w
+(and) 1507 2952 w
+(-A9) 1733 2952 w
+(processors) 1969 2952 w
+(and) 2542 2952 w
+(the) 2768 2952 w
+(Strongarm) 2969 2952 w
+(SA1) 3521 2952 w
+(core) 3753 2952 w
+(machines.) 4008 2952 w
+(The) 4576 2952 w
+(com\255) 4802 2952 w
+(piler) 720 3072 w
+(generates) 978 3072 w
+(instructions) 1494 3072 w
+(for) 2107 3072 w
+(ARM) 2286 3072 w
+(7500) 2545 3072 w
+(FPA) 2838 3072 w
+(floating-point) 3057 3072 w
+(coprocessor) 3780 3072 w
+(1) 4409 3072 w
+(by) 4513 3072 w
+(default,) 4669 3072 w
+(but) 720 3192 w
+10 /LucidaTypewriter f
+(5l) 914 3192 w
+(-f) 1090 3192 w
+10 /LucidaSansUnicode00 f
+(instead generates VFP instructions for coprocessors 10 and 11.) 1266 3192 w
 10 /LucidaSans-Demi f
 (The IBM PC operating system) 720 3432 w
 10 /LucidaSansUnicode00 f
@@ -5135,94 +5141,92 @@ mark
 (The Marvell Kirkwood operating system) 720 5328 w
 10 /LucidaSansUnicode00 f
 (This) 970 5484 w
-(is) 1208 5484 w
-(an) 1322 5484 w
-(ARM) 1473 5484 w
-(kernel) 1725 5484 w
-(for) 2061 5484 w
-(the) 2234 5484 w
-(ARM926EJ-S) 2423 5484 w
-(processor) 3061 5484 w
-(and) 3571 5484 w
-(it) 3785 5484 w
-(emulates) 3885 5484 w
-(floating-point) 4358 5484 w
-(and) 720 5604 w
-(CAS) 936 5604 w
-(\(compare-and-swap\)) 1164 5604 w
-(instructions.) 2228 5604 w
-(It) 2900 5604 w
-(is) 3001 5604 w
-(known) 3116 5604 w
-(to) 3471 5604 w
-(run) 3604 5604 w
-(on) 3804 5604 w
-(the) 3962 5604 w
-(Sheevaplug,) 4152 5604 w
-(Guru\255) 4770 5604 w
-(plug,) 720 5724 w
-(Dreamplug) 1009 5724 w
-(and) 1586 5724 w
-(Openrd-client) 1807 5724 w
-(boards.) 2533 5724 w
-(It) 2972 5724 w
-(is) 3080 5724 w
-(derived) 3202 5724 w
-(from) 3604 5724 w
-(a) 3878 5724 w
-(port) 3975 5724 w
-(of) 4219 5724 w
-(native) 4359 5724 w
-(Inferno) 4692 5724 w
-(to) 720 5844 w
-(the) 860 5844 w
-(Sheevaplug) 1057 5844 w
-(by) 1650 5844 w
-(Salva) 1807 5844 w
-(Peir) 2094 5844 w
- save 2275 5844 m
+(is) 1222 5484 w
+(an) 1349 5484 w
+(ARM) 1513 5484 w
+(kernel) 1778 5484 w
+(for) 2127 5484 w
+(the) 2314 5484 w
+(ARM926EJ-S) 2517 5484 w
+(processor) 3169 5484 w
+(and) 3693 5484 w
+(it) 3921 5484 w
+(emulates) 4035 5484 w
+(ARM) 4522 5484 w
+(7500) 4788 5484 w
+(floating-point) 720 5604 w
+(and) 1456 5604 w
+(CAS) 1690 5604 w
+(\(compare-and-swap\)) 1936 5604 w
+(instructions.) 3018 5604 w
+(It) 3709 5604 w
+(is) 3829 5604 w
+(known) 3963 5604 w
+(to) 4337 5604 w
+(run) 4489 5604 w
+(on) 4708 5604 w
+(the) 4885 5604 w
+(Sheevaplug,) 720 5724 w
+(Guruplug,) 1344 5724 w
+(Dreamplug) 1870 5724 w
+(and) 2447 5724 w
+(Openrd-client) 2668 5724 w
+(boards.) 3395 5724 w
+(It) 3835 5724 w
+(is) 3943 5724 w
+(derived) 4065 5724 w
+(from) 4467 5724 w
+(a) 4741 5724 w
+(port) 4838 5724 w
+(of) 720 5844 w
+(native) 854 5844 w
+(Inferno) 1181 5844 w
+(to) 1565 5844 w
+(the) 1699 5844 w
+(Sheevaplug) 1889 5844 w
+(by) 2475 5844 w
+(Salva) 2625 5844 w
+(Peir) 2905 5844 w
+ save 3086 5844 m
  currentpoint translate 10 10 scale ptsize dup scale
  16 24 true [16 0 0 -16 0 22]
  {<0000000000000000000000000000000004001c00380040003c006600c300c300c300c300c30066003c00000000000000>}
  imagemask restore
-(and) 2378 5844 w
-(Mechiel) 2600 5844 w
-(Lukkien.) 3011 5844 w
-(There) 3495 5844 w
-(are) 3814 5844 w
-(many) 4007 5844 w
-(features) 4310 5844 w
-(of) 4746 5844 w
-(the) 4885 5844 w
-(Kirkwood) 720 5964 w
-(system-on-a-chip) 1213 5964 w
-(that) 2148 5964 w
-(it) 2377 5964 w
-(does) 2481 5964 w
-(not) 2750 5964 w
-(exploit.) 2948 5964 w
-(There) 3387 5964 w
-(are) 3704 5964 w
-(currently) 3895 5964 w
-(drivers) 4365 5964 w
-(for) 4737 5964 w
-(up) 4915 5964 w
-(to) 720 6084 w
-(two) 872 6084 w
-(Gigabit) 1101 6084 w
-(Ethernet) 1502 6084 w
-(interfaces,) 1961 6084 w
-(USB) 2522 6084 w
-(and) 2757 6084 w
-(the) 2991 6084 w
-(console) 3200 6084 w
-(serial) 3625 6084 w
-(port;) 3940 6084 w
-(we) 4227 6084 w
-(hope) 4413 6084 w
-(to) 4708 6084 w
-(add) 4859 6084 w
-(crypto acceleration, and a video driver for the Openrd-client.) 720 6204 w
+(and) 3182 5844 w
+(Mechiel) 3397 5844 w
+(Lukkien.) 3801 5844 w
+(There) 4278 5844 w
+(are) 4591 5844 w
+(many) 4778 5844 w
+(features) 720 5964 w
+(of) 1149 5964 w
+(the) 1281 5964 w
+(Kirkwood) 1470 5964 w
+(system-on-a-chip) 1959 5964 w
+(that) 2890 5964 w
+(it) 3115 5964 w
+(does) 3215 5964 w
+(not) 3480 5964 w
+(exploit.) 3674 5964 w
+(There) 4109 5964 w
+(are) 4422 5964 w
+(currently) 4609 5964 w
+(drivers) 720 6084 w
+(for) 1102 6084 w
+(up) 1290 6084 w
+(to) 1464 6084 w
+(two) 1611 6084 w
+(Gigabit) 1834 6084 w
+(Ethernet) 2229 6084 w
+(interfaces,) 2682 6084 w
+(USB) 3237 6084 w
+(and) 3466 6084 w
+(the) 3694 6084 w
+(console) 3897 6084 w
+(serial) 4316 6084 w
+(port;) 4625 6084 w
+(we) 4907 6084 w
+(hope to add crypto acceleration, and a video driver for the Openrd-client.) 720 6204 w
 10 /LucidaSans-Demi f
 (The Marvell PXA168 operating system) 720 6444 w
 10 /LucidaSansUnicode00 f
@@ -5239,48 +5243,58 @@ mark
 (the) 4468 6600 w
 (Marvell) 4692 6600 w
 (PXA168) 720 6720 w
-(system-on-a-chip) 1131 6720 w
-(and) 2063 6720 w
-(it) 2278 6720 w
-(emulates) 2379 6720 w
-(floating-point) 2853 6720 w
-(and) 3570 6720 w
-(CAS) 3785 6720 w
-(\(compare-and-swap\)) 4012 6720 w
-(instructions.) 720 6840 w
-(It) 1401 6840 w
-(is) 1511 6840 w
-(known) 1635 6840 w
-(to) 1999 6840 w
-(run) 2141 6840 w
-(on) 2350 6840 w
-(the) 2517 6840 w
-(Guruplug) 2716 6840 w
-(Display.) 3213 6840 w
-(There) 3675 6840 w
-(are) 3998 6840 w
-(many) 4195 6840 w
-(features) 4502 6840 w
-(of) 4942 6840 w
-(the) 720 6960 w
-(system-on-a-chip that it does not exploit.) 908 6960 w
-(There are currently drivers for a Fast Eth\255) 3045 6960 w
-(ernet) 720 7080 w
-(interface,) 1015 7080 w
-(and) 1514 7080 w
-(the) 1737 7080 w
-(console) 1935 7080 w
-(serial) 2349 7080 w
-(port;) 2653 7080 w
-(we) 2930 7080 w
-(hope) 3106 7080 w
-(to) 3391 7080 w
-(add) 3532 7080 w
-(crypto) 3756 7080 w
-(acceleration,) 4104 7080 w
-(and) 4762 7080 w
-(a) 4985 7080 w
-(video driver.) 720 7200 w
+(system-on-a-chip) 1190 6720 w
+(and) 2180 6720 w
+(it) 2453 6720 w
+(emulates) 2612 6720 w
+(ARM) 3144 6720 w
+(7500) 3455 6720 w
+(floating-point) 3800 6720 w
+(and) 4575 6720 w
+(CAS) 4848 6720 w
+(\(compare-and-swap\)) 720 6840 w
+(instructions.) 1793 6840 w
+(It) 2475 6840 w
+(is) 2586 6840 w
+(known) 2711 6840 w
+(to) 3076 6840 w
+(run) 3219 6840 w
+(on) 3429 6840 w
+(the) 3598 6840 w
+(Guruplug) 3799 6840 w
+(Display.) 4298 6840 w
+(There) 4762 6840 w
+(are) 720 6960 w
+(many) 907 6960 w
+(features) 1204 6960 w
+(of) 1634 6960 w
+(the) 1767 6960 w
+(system-on-a-chip) 1957 6960 w
+(that) 2889 6960 w
+(it) 3115 6960 w
+(does) 3216 6960 w
+(not) 3482 6960 w
+(exploit.) 3677 6960 w
+(There) 4111 6960 w
+(are) 4423 6960 w
+(currently) 4609 6960 w
+(drivers) 720 7080 w
+(for) 1091 7080 w
+(a) 1268 7080 w
+(Fast) 1361 7080 w
+(Ethernet) 1596 7080 w
+(interface,) 2039 7080 w
+(and) 2533 7080 w
+(the) 2751 7080 w
+(console) 2944 7080 w
+(serial) 3353 7080 w
+(port;) 3652 7080 w
+(we) 3925 7080 w
+(hope) 4097 7080 w
+(to) 4378 7080 w
+(add) 4515 7080 w
+(crypto) 4735 7080 w
+(acceleration, and a video driver.) 720 7200 w
 cleartomark
 showpage
 saveobj restore
@@ -5295,18 +5309,19 @@ mark
 (The TI OMAP35 operating system) 720 840 w
 10 /LucidaSansUnicode00 f
 (This) 970 996 w
-(is) 1230 996 w
-(an) 1366 996 w
-(ARM) 1539 996 w
-(kernel) 1813 996 w
-(for) 2171 996 w
-(the) 2366 996 w
-(Cortex-A8) 2577 996 w
-(processor) 3148 996 w
-(and) 3680 996 w
-(it) 3916 996 w
-(emulates) 4038 996 w
-(pre-VFPv3) 4533 996 w
+(is) 1229 996 w
+(an) 1363 996 w
+(ARM) 1534 996 w
+(kernel) 1806 996 w
+(for) 2162 996 w
+(the) 2355 996 w
+(Cortex-A8) 2564 996 w
+(processor) 3134 996 w
+(and) 3665 996 w
+(it) 3900 996 w
+(emulates) 4021 996 w
+(ARM) 4515 996 w
+(7500) 4788 996 w
 (floating-point) 720 1116 w
 (and) 1456 1116 w
 (CAS) 1690 1116 w
@@ -5396,76 +5411,85 @@ mark
 (Nvidia) 4343 2112 w
 (Tegra2) 4700 2112 w
 (system-on-a-chip) 720 2232 w
-(and) 1671 2232 w
-(it) 1905 2232 w
-(emulates) 2025 2232 w
-(pre-VFPv3) 2518 2232 w
-(floating-point) 3078 2232 w
-(and) 3813 2232 w
-(CAS) 4046 2232 w
+(and) 1669 2232 w
+(it) 1901 2232 w
+(emulates) 2019 2232 w
+(ARM) 2510 2232 w
+(7500) 2780 2232 w
+(floating-point) 3084 2232 w
+(and) 3817 2232 w
+(CAS) 4048 2232 w
 (\(compare-and-) 4291 2232 w
 (swap\)) 720 2352 w
-(instructions) 1044 2352 w
-(\(the) 1662 2352 w
-(hardware) 1895 2352 w
-(has) 2390 2352 w
-(VFP3) 2603 2352 w
-(floating-point) 2885 2352 w
-(but) 3612 2352 w
-10 /LucidaTypewriter f
-(5l) 3819 2352 w
-10 /LucidaSansUnicode00 f
-(doesn) 4008 2352 w
-10 /LucidaSansUnicode20 f
-(\031) 4301 2352 w
+(instructions,) 1037 2352 w
+(but) 1681 2352 w
+(the) 1882 2352 w
+(hardware) 2076 2352 w
+(includes) 2565 2352 w
+(VFP3) 3007 2352 w
+(floating-point.) 3283 2352 w
+(It) 4068 2352 w
+(runs) 4173 2352 w
+(on) 4428 2352 w
+(the) 4590 2352 w
+(Com\255) 4784 2352 w
+(pulab) 720 2472 w
+(Trimslice.) 1047 2472 w
+(There) 1608 2472 w
+(are) 1941 2472 w
+(many) 2148 2472 w
+(features) 2465 2472 w
+(of) 2915 2472 w
+(the) 3068 2472 w
+(system-on-a-chip) 3278 2472 w
+(that) 4230 2472 w
+(it) 4475 2472 w
+(does) 4595 2472 w
+(not) 4880 2472 w
+(exploit.) 720 2592 w
+(Initially,) 1156 2592 w
+(there) 1575 2592 w
+(are) 1863 2592 w
+(drivers) 2051 2592 w
+(for) 2420 2592 w
+(the) 2596 2592 w
+(Ethernet) 2788 2592 w
+(interface) 3230 2592 w
+(and) 3691 2592 w
+(the) 3908 2592 w
+(console) 4100 2592 w
+(serial) 4508 2592 w
+(port;) 4806 2592 w
+(we hope to add USB, flash memory and video drivers.) 720 2712 w
+10 /LucidaSans-Demi f
+(The Broadcom 2835 operating system) 720 2952 w
 10 /LucidaSansUnicode00 f
-(t) 4333 2352 w
-(generate) 4415 2352 w
-(the) 4885 2352 w
-(new) 720 2472 w
-(opcodes) 957 2472 w
-(yet\).) 1405 2472 w
-(It) 1688 2472 w
-(runs) 1795 2472 w
-(on) 2052 2472 w
-(the) 2216 2472 w
-(Compulab) 2412 2472 w
-(Trimslice.) 2948 2472 w
-(There) 3495 2472 w
-(are) 3814 2472 w
-(many) 4007 2472 w
-(features) 4310 2472 w
-(of) 4746 2472 w
-(the) 4885 2472 w
-(system-on-a-chip) 720 2592 w
-(that) 1663 2592 w
-(it) 1900 2592 w
-(does) 2012 2592 w
-(not) 2289 2592 w
-(exploit.) 2495 2592 w
-(Initially,) 2941 2592 w
-(there) 3370 2592 w
-(are) 3668 2592 w
-(drivers) 3867 2592 w
-(for) 4247 2592 w
-(the) 4433 2592 w
-(Ethernet) 4635 2592 w
-(interface) 720 2712 w
-(and) 1199 2712 w
-(the) 1433 2712 w
-(console) 1642 2712 w
-(serial) 2067 2712 w
-(port;) 2382 2712 w
-(we) 2670 2712 w
-(hope) 2857 2712 w
-(to) 3153 2712 w
-(add) 3305 2712 w
-(USB,) 3540 2712 w
-(flash) 3807 2712 w
-(memory) 4095 2712 w
-(and) 4545 2712 w
-(video) 4779 2712 w
-(drivers.) 720 2832 w
+(This) 970 3108 w
+(is) 1208 3108 w
+(terminal) 1321 3108 w
+(and) 1756 3108 w
+(CPU) 1969 3108 w
+(kernels) 2195 3108 w
+(for) 2581 3108 w
+(the) 2754 3108 w
+(ARM1176) 2943 3108 w
+(processor) 3447 3108 w
+(in) 3957 3108 w
+(the) 4082 3108 w
+(Broadcom) 4271 3108 w
+(2835) 4788 3108 w
+(system-on-a-chip.) 720 3228 w
+(The) 1733 3228 w
+(hardware) 1966 3228 w
+(includes) 2468 3228 w
+(VFP2) 2923 3228 w
+(floating-point.) 3212 3228 w
+(It) 4010 3228 w
+(runs) 4128 3228 w
+(on) 4395 3228 w
+(the) 4569 3228 w
+(Rasp\255) 4775 3228 w
+(berry Pi Model B.) 720 3348 w
 cleartomark
 showpage
 saveobj restore

+ 1 - 0
sys/games/lib/fortunes

@@ -4304,3 +4304,4 @@ Sorry, Ubuntu 12.04 has experienced an internal error. If you notice further pro
 The real amount of storage available is a dangerously low 160 TB  - Argonne
 the rate at which apple fiddles with stuff is pretty amazing.  - quanstro
 **WARRANTY VOID IF BROKEN**
+Good to know: Git won't add an icon anywhere, it's not that sort of application.

+ 14 - 1
sys/man/8/booting

@@ -105,7 +105,7 @@ Once the kernel is loaded,
 it prompts for the Ethernet
 protocol to use to reach the root file server; request the default.
 .
-.SS ARM CPU Servers
+.SS ARM Systems
 All ARM systems are started by
 .I U-boot
 using similar commands.
@@ -128,6 +128,11 @@ for TI OMAP3 boards (IGEPv2 from ISEE, Gumstix Overo)
 .TP
 .L /arm/9ts
 for Trimslice systems, which contain the Nvidia Tegra 2
+.TP
+.L /arm/9pi
+.TP
+.L /arm/9picpu
+for Raspberry Pis
 .PD
 .LP
 In the following,
@@ -235,6 +240,14 @@ type this after
 \&'dhcp; dhcp; tftpboot 0x410000; tftpboot 0x400000 %C; go 0x410000'
 .EE
 .PP
+For Raspberry Pis,
+type this after
+.LR "setenv bootcmd" :
+.IP
+.EX
+\&'usb start; dhcp; tftp 100 %C; go 8000'
+.EE
+.PP
 Thereafter, the boards will automatically boot via BOOTP and TFTP
 when reset.
 .

+ 1 - 0
sys/src/9/bcm/arch.c

@@ -0,0 +1 @@
+#include "../omap/arch.c"

+ 78 - 0
sys/src/9/bcm/archbcm.c

@@ -0,0 +1,78 @@
+/*
+ * bcm2835 (e.g. raspberry pi) architecture-specific stuff
+ */
+
+#include "u.h"
+#include "../port/lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+#include "../port/error.h"
+#include "io.h"
+#include "arm.h"
+
+#include "../port/netif.h"
+#include "etherif.h"
+
+#define	POWERREGS	(VIRTIO+0x100000)
+
+enum {
+	Wdogfreq	= 65536,
+};
+
+/*
+ * Power management / watchdog registers
+ */
+enum {
+	Rstc		= 0x1c>>2,
+		Password	= 0x5A<<24,
+		CfgMask		= 0x03<<4,
+		CfgReset	= 0x02<<4,
+	Wdog		= 0x24>>2,
+};
+
+void
+archreset(void)
+{
+	fpon();
+}
+
+void
+archreboot(void)
+{
+	u32int *r;
+
+	r = (u32int*)POWERREGS;
+	r[Wdog] = Password | 1;
+	r[Rstc] = Password | (r[Rstc] & ~CfgMask) | CfgReset;
+	coherence();
+	for(;;)
+		;
+}
+
+void
+cpuidprint(void)
+{
+	print("cpu%d: %dMHz ARM1176JZF-S\n", m->machno, m->cpumhz);
+}
+
+void
+archbcmlink(void)
+{
+}
+
+int
+archether(unsigned ctlrno, Ether *ether)
+{
+	switch(ctlrno) {
+	case 0:
+		ether->type = "usb";
+		ether->ctlrno = ctlrno;
+		ether->irq = -1;
+		ether->nopt = 0;
+		ether->mbps = 100;
+		return 1;
+	}
+	return -1;
+}
+

+ 273 - 0
sys/src/9/bcm/arm.h

@@ -0,0 +1,273 @@
+/*
+ * arm-specific definitions for armv6
+ * these are used in C and assembler
+ */
+
+/*
+ * Program Status Registers
+ */
+#define PsrMusr		0x00000010		/* mode */
+#define PsrMfiq		0x00000011
+#define PsrMirq		0x00000012
+#define PsrMsvc		0x00000013	/* `protected mode for OS' */
+#define PsrMmon		0x00000016	/* `secure monitor' (trustzone hyper) */
+#define PsrMabt		0x00000017
+#define PsrMund		0x0000001B
+#define PsrMsys		0x0000001F	/* `privileged user mode for OS' (trustzone) */
+#define PsrMask		0x0000001F
+
+#define PsrDfiq		0x00000040		/* disable FIQ interrupts */
+#define PsrDirq		0x00000080		/* disable IRQ interrupts */
+
+#define PsrV		0x10000000		/* overflow */
+#define PsrC		0x20000000		/* carry/borrow/extend */
+#define PsrZ		0x40000000		/* zero */
+#define PsrN		0x80000000		/* negative/less than */
+
+/* instruction decoding */
+#define ISCPOP(op)	((op) == 0xE || ((op) & ~1) == 0xC)
+#define ISFPAOP(cp, op)	((cp) == CpOFPA && ISCPOP(op))
+#define ISVFPOP(cp, op)	(((cp) == CpDFP || (cp) == CpFP) && ISCPOP(op))
+
+/*
+ * Coprocessors
+ */
+#define CpOFPA		1			/* ancient 7500 FPA */
+#define CpFP		10			/* float FP, VFP cfg. */
+#define CpDFP		11			/* double FP */
+#define CpSC		15			/* System Control */
+
+/*
+ * Primary (CRn) CpSC registers.
+ */
+#define	CpID		0			/* ID and cache type */
+#define	CpCONTROL	1			/* miscellaneous control */
+#define	CpTTB		2			/* Translation Table Base(s) */
+#define	CpDAC		3			/* Domain Access Control */
+#define	CpFSR		5			/* Fault Status */
+#define	CpFAR		6			/* Fault Address */
+#define	CpCACHE		7			/* cache/write buffer control */
+#define	CpTLB		8			/* TLB control */
+#define	CpCLD		9			/* L2 Cache Lockdown, op1==1 */
+#define CpTLD		10			/* TLB Lockdown, with op2 */
+#define CpVECS		12			/* vector bases, op1==0, Crm==0, op2s (cortex) */
+#define	CpPID		13			/* Process ID */
+#define CpSPM		15			/* system performance monitor (arm1176) */
+
+/*
+ * CpTTB op1==0, Crm==0 opcode2 values.
+ */
+#define CpTTB0		0
+#define CpTTB1		1			/* cortex */
+#define CpTTBctl	2			/* cortex */
+
+/*
+ * CpFSR opcode2 values.
+ */
+#define	CpFSRdata	0			/* armv6, armv7 */
+#define	CpFSRinst	1			/* armv6, armv7 */
+
+/*
+ * CpID Secondary (CRm) registers.
+ */
+#define CpIDidct	0
+
+/*
+ * CpID op1==0 opcode2 fields.
+ * the cortex has more op1 codes for cache size, etc.
+ */
+#define CpIDid		0			/* main ID */
+#define CpIDct		1			/* cache type */
+#define CpIDtlb		3			/* tlb type (cortex) */
+#define CpIDmpid	5			/* multiprocessor id (cortex) */
+
+/* CpIDid op1 values */
+#define CpIDcsize	1			/* cache size (cortex) */
+#define CpIDcssel	2			/* cache size select (cortex) */
+
+/*
+ * CpCONTROL op2 codes, op1==0, Crm==0.
+ */
+#define CpMainctl	0
+#define CpAuxctl	1
+#define CpCPaccess	2
+
+/*
+ * CpCONTROL: op1==0, CRm==0, op2==CpMainctl.
+ * main control register.
+ * cortex/armv7 has more ops and CRm values.
+ */
+#define CpCmmu		0x00000001	/* M: MMU enable */
+#define CpCalign	0x00000002	/* A: alignment fault enable */
+#define CpCdcache	0x00000004	/* C: data cache on */
+#define CpCsbo (3<<22|1<<18|1<<16|017<<3)	/* must be 1 (armv7) */
+#define CpCsbz (CpCtre|1<<26|CpCve|1<<15|7<<7)	/* must be 0 (armv7) */
+#define CpCsw		(1<<10)		/* SW: SWP(B) enable (deprecated in v7) */
+#define CpCpredict	0x00000800	/* Z: branch prediction (armv7) */
+#define CpCicache	0x00001000	/* I: instruction cache on */
+#define CpChv		0x00002000	/* V: high vectors */
+#define CpCrr		(1<<14)	/* RR: round robin vs random cache replacement */
+#define CpCha		(1<<17)		/* HA: hw access flag enable */
+#define CpCdz		(1<<19)		/* DZ: divide by zero fault enable */
+#define CpCfi		(1<<21)		/* FI: fast intrs */
+#define CpCve		(1<<24)		/* VE: intr vectors enable */
+#define CpCee		(1<<25)		/* EE: exception endianness */
+#define CpCnmfi		(1<<27)		/* NMFI: non-maskable fast intrs. */
+#define CpCtre		(1<<28)		/* TRE: TEX remap enable */
+#define CpCafe		(1<<29)		/* AFE: access flag (ttb) enable */
+
+/*
+ * CpCONTROL: op1==0, CRm==0, op2==CpAuxctl.
+ * Auxiliary control register on cortex at least.
+ */
+#define CpACcachenopipe		(1<<20)	/* don't pipeline cache maint. */
+#define CpACcp15serial		(1<<18)	/* serialise CP1[45] ops. */
+#define CpACcp15waitidle	(1<<17)	/* CP1[45] wait-on-idle */
+#define CpACcp15pipeflush	(1<<16)	/* CP1[45] flush pipeline */
+#define CpACneonissue1		(1<<12)	/* neon single issue */
+#define CpACldstissue1		(1<<11)	/* force single issue ld, st */
+#define CpACissue1		(1<<10)	/* force single issue */
+#define CpACnobsm		(1<<7)	/* no branch size mispredicts */
+#define CpACibe			(1<<6)	/* cp15 invalidate & btb enable */
+#define CpACl1neon		(1<<5)	/* cache neon (FP) data in L1 cache */
+#define CpACasa			(1<<4)	/* enable speculative accesses */
+#define CpACl1pe		(1<<3)	/* l1 cache parity enable */
+#define CpACl2en		(1<<1)	/* l2 cache enable; default 1 */
+/*
+ * CpCONTROL Secondary (CRm) registers and opcode2 fields.
+ */
+#define CpCONTROLscr	1
+
+#define CpSCRscr	0
+
+/*
+ * CpCACHE Secondary (CRm) registers and opcode2 fields.  op1==0.
+ * In ARM-speak, 'flush' means invalidate and 'clean' means writeback.
+ */
+#define CpCACHEintr	0			/* interrupt (op2==4) */
+#define CpCACHEisi	1			/* inner-sharable I cache (v7) */
+#define CpCACHEpaddr	4			/* 0: phys. addr (cortex) */
+#define CpCACHEinvi	5			/* instruction, branch table */
+#define CpCACHEinvd	6			/* data or unified */
+#define CpCACHEinvu	7			/* unified (not on cortex) */
+#define CpCACHEva2pa	8			/* va -> pa translation (cortex) */
+#define CpCACHEwb	10			/* writeback */
+#define CpCACHEinvdse	11			/* data or unified by mva */
+#define CpCACHEwbi	14			/* writeback+invalidate */
+
+#define CpCACHEall	0			/* entire (not for invd nor wb(i) on cortex) */
+#define CpCACHEse	1			/* single entry */
+#define CpCACHEsi	2			/* set/index (set/way) */
+#define CpCACHEtest	3			/* test loop */
+#define CpCACHEwait	4			/* wait (prefetch flush on cortex) */
+#define CpCACHEdmbarr	5			/* wb only (cortex) */
+#define CpCACHEflushbtc	6			/* flush branch-target cache (cortex) */
+#define CpCACHEflushbtse 7			/* ⋯ or just one entry in it (cortex) */
+
+/*
+ * CpTLB Secondary (CRm) registers and opcode2 fields.
+ */
+#define CpTLBinvi	5			/* instruction */
+#define CpTLBinvd	6			/* data */
+#define CpTLBinvu	7			/* unified */
+
+#define CpTLBinv	0			/* invalidate all */
+#define CpTLBinvse	1			/* invalidate single entry */
+#define CpTBLasid	2			/* by ASID (cortex) */
+
+/*
+ * CpCLD Secondary (CRm) registers and opcode2 fields for op1==0. (cortex)
+ */
+#define CpCLDena	12			/* enables */
+#define CpCLDcyc	13			/* cycle counter */
+#define CpCLDuser	14			/* user enable */
+
+#define CpCLDenapmnc	0
+#define CpCLDenacyc	1
+
+/*
+ * CpCLD Secondary (CRm) registers and opcode2 fields for op1==1.
+ */
+#define CpCLDl2		0			/* l2 cache */
+
+#define CpCLDl2aux	2			/* auxiliary control */
+
+/*
+ * l2 cache aux. control
+ */
+#define CpCl2ecc	(1<<28)			/* use ecc, not parity */
+#define CpCl2noldforw	(1<<27)			/* no ld forwarding */
+#define CpCl2nowrcomb	(1<<25)			/* no write combining */
+#define CpCl2nowralldel	(1<<24)			/* no write allocate delay */
+#define CpCl2nowrallcomb (1<<23)		/* no write allocate combine */
+#define CpCl2nowralloc	(1<<22)			/* no write allocate */
+#define CpCl2eccparity	(1<<21)			/* enable ecc or parity */
+#define CpCl2inner	(1<<16)			/* inner cacheability */
+/* other bits are tag ram & data ram latencies */
+
+/*
+ * CpTLD Secondary (CRm) registers and opcode2 fields.
+ */
+#define CpTLDlock	0			/* TLB lockdown registers */
+#define CpTLDpreload	1			/* TLB preload */
+
+#define CpTLDi		0			/* TLB instr. lockdown reg. */
+#define CpTLDd		1			/* " data " " */
+
+/*
+ * CpVECS Secondary (CRm) registers and opcode2 fields.
+ */
+#define CpVECSbase	0
+
+#define CpVECSnorm	0			/* (non-)secure base addr */
+#define CpVECSmon	1			/* secure monitor base addr */
+
+/*
+ * CpSPM Secondary (CRm) registers and opcode2 fields.
+ */
+#define CpSPMperf	12			/* various counters */
+
+#define CpSPMctl	0			/* performance monitor control */
+#define	CpSPMcyc	1			/* cycle counter register */
+
+/*
+ * CpCACHERANGE opcode2 fields for MCRR instruction (armv6)
+ */
+#define	CpCACHERANGEinvi	5		/* invalidate instruction  */
+#define	CpCACHERANGEinvd	6		/* invalidate data */
+#define CpCACHERANGEdwb		12		/* writeback */
+#define CpCACHERANGEdwbi	14		/* writeback+invalidate */
+
+/*
+ * MMU page table entries.
+ * Mbz (0x10) bit is implementation-defined and must be 0 on the cortex.
+ */
+#define Mbz		(0<<4)
+#define Fault		0x00000000		/* L[12] pte: unmapped */
+
+#define Coarse		(Mbz|1)			/* L1 */
+#define Section		(Mbz|2)			/* L1 1MB */
+#define Fine		(Mbz|3)			/* L1 */
+
+#define Large		0x00000001		/* L2 64KB */
+#define Small		0x00000002		/* L2 4KB */
+#define Tiny		0x00000003		/* L2 1KB: not in v7 */
+#define Buffered	0x00000004		/* L[12]: write-back not -thru */
+#define Cached		0x00000008		/* L[12] */
+#define Dom0		0
+
+#define Noaccess	0			/* AP, DAC */
+#define Krw		1			/* AP */
+/* armv7 deprecates AP[2] == 1 & AP[1:0] == 2 (Uro), prefers 3 (new in v7) */
+#define Uro		2			/* AP */
+#define Urw		3			/* AP */
+#define Client		1			/* DAC */
+#define Manager		3			/* DAC */
+
+#define F(v, o, w)	(((v) & ((1<<(w))-1))<<(o))
+#define AP(n, v)	F((v), ((n)*2)+4, 2)
+#define L1AP(ap)	(AP(3, (ap)))
+#define L2AP(ap) (AP(3, (ap))|AP(2, (ap))|AP(1, (ap))|AP(0, (ap))) /* pre-armv7 */
+#define DAC(n, v)	F((v), (n)*2, 2)
+
+#define HVECTORS	0xffff0000

+ 36 - 0
sys/src/9/bcm/arm.s

@@ -0,0 +1,36 @@
+/*
+ * armv6 machine assist, definitions
+ *
+ * loader uses R11 as scratch.
+ */
+
+#include "mem.h"
+#include "arm.h"
+
+#define PADDR(va)	(PHYSDRAM | ((va) & ~KSEGM))
+
+#define L1X(va)		(((((va))>>20) & 0x0fff)<<2)
+
+#define PTEDRAM		(Dom0|L1AP(Krw)|Section|Cached|Buffered)
+
+/*
+ * new instructions
+ */
+
+#define ISB	\
+	MOVW	$0, R0; \
+	MCR	CpSC, 0, R0, C(CpCACHE), C(CpCACHEinvi), CpCACHEwait
+
+#define DSB \
+	MOVW	$0, R0; \
+	MCR	CpSC, 0, R0, C(CpCACHE), C(CpCACHEwb), CpCACHEwait
+
+#define	BARRIERS	ISB; DSB
+
+#define MCRR(coproc, op, rd, rn, crm) \
+	WORD $(0xec400000|(rn)<<16|(rd)<<12|(coproc)<<8|(op)<<4|(crm))
+
+#define OKAY \
+	MOVW	$0x7E200028,R2; \
+	MOVW	$0x10000,R3; \
+	MOVW	R3,(R2)

+ 30 - 0
sys/src/9/bcm/boot.rc

@@ -0,0 +1,30 @@
+#!/boot/rc -m /boot/rcmain
+
+/boot/echo -n boot...
+path=(/bin /boot)
+bind '#p' /proc
+bind '#d' /fd
+bind -a '#P' /dev
+bind -a '#t' /dev
+bind -a '#S' /dev
+bind -a '#I' /net
+echo -n rpi >/dev/hostowner
+echo -n fdisk...
+fdisk -p /dev/sdM0/data >/dev/sdM0/ctl
+dossrv -f/dev/sdM0/dos boot
+rootdir=/root/plan9
+rootspec=''
+mount -c /srv/boot /root
+bind -ac $rootdir /
+bind -ac $rootdir/mnt /mnt
+
+bind /$cputype/bin /bin
+bind -a /rc/bin /bin
+
+if (! ~ $#init 0)
+	exec `{echo $init}
+if (~ $service cpu)
+	exec /$cputype/init -c
+if not
+	exec /$cputype/init -t
+exec /boot/rc -m/boot/rcmain -i

+ 204 - 0
sys/src/9/bcm/clock.c

@@ -0,0 +1,204 @@
+/*
+ * bcm2835 timers
+ *	System timers run at 1MHz (timers 1 and 2 are used by GPU)
+ *	ARM timer usually runs at 250MHz (may be slower in low power modes)
+ *	Cycle counter runs at 700MHz (unless overclocked)
+ *    All are free-running up-counters
+ *
+ * Use system timer 3 (64 bits) for hzclock interrupts and fastticks
+ * Use ARM timer (32 bits) for perfticks
+ * Use ARM timer to force immediate interrupt
+ * Use cycle counter for cycles()
+ */
+
+#include "u.h"
+#include "../port/lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+#include "io.h"
+
+enum {
+	SYSTIMERS	= VIRTIO+0x3000,
+	ARMTIMER	= VIRTIO+0xB400,
+
+	SystimerFreq	= 1*Mhz,
+	MaxPeriod	= SystimerFreq / HZ,
+	MinPeriod	= SystimerFreq / (100*HZ),
+};
+
+typedef struct Systimers Systimers;
+typedef struct Armtimer Armtimer;
+
+struct Systimers {
+	u32int	cs;
+	u32int	clo;
+	u32int	chi;
+	u32int	c0;
+	u32int	c1;
+	u32int	c2;
+	u32int	c3;
+};
+
+struct Armtimer {
+	u32int	load;
+	u32int	val;
+	u32int	ctl;
+	u32int	irqack;
+	u32int	irq;
+	u32int	maskedirq;
+	u32int	reload;
+	u32int	predivider;
+	u32int	count;
+};
+
+enum {
+	CntPrescaleShift= 16,	/* freq is sys_clk/(prescale+1) */
+	CntPrescaleMask	= 0xFF,
+	CntEnable	= 1<<9,
+	TmrDbgHalt	= 1<<8,
+	TmrEnable	= 1<<7,
+	TmrIntEnable	= 1<<5,
+	TmrPrescale1	= 0x00<<2,
+	TmrPrescale16	= 0x01<<2,
+	TmrPrescale256	= 0x02<<2,
+	CntWidth16	= 0<<1,
+	CntWidth32	= 1<<1,
+};
+
+static void
+clockintr(Ureg *ureg, void *)
+{
+	Systimers *tn;
+
+	tn = (Systimers*)SYSTIMERS;
+	/* dismiss interrupt */
+	tn->cs = 1<<3;
+	timerintr(ureg, 0);
+}
+
+void
+clockshutdown(void)
+{
+	Armtimer *tm;
+
+	tm = (Armtimer*)ARMTIMER;
+	tm->ctl = 0;
+}
+
+void
+clockinit(void)
+{
+	Systimers *tn;
+	Armtimer *tm;
+	u32int t0, t1, tstart, tend;
+
+	tn = (Systimers*)SYSTIMERS;
+	tm = (Armtimer*)ARMTIMER;
+	tm->load = 0;
+	tm->ctl = TmrPrescale1|CntEnable|CntWidth32;
+	coherence();
+
+	tstart = tn->clo;
+	do{
+		t0 = lcycles();
+	}while(tn->clo == tstart);
+	tend = tstart + 10000;
+	do{
+		t1 = lcycles();
+	}while(tn->clo != tend);
+	t1 -= t0;
+	m->cpuhz = 100 * t1;
+	m->cpumhz = (m->cpuhz + Mhz/2 - 1) / Mhz;
+	m->cyclefreq = m->cpuhz;
+
+	tn->c3 = tn->clo - 1;
+	intrenable(IRQtimer3, clockintr, nil, 0, "clock");
+}
+
+void
+timerset(uvlong next)
+{
+	Systimers *tn;
+	vlong now, period;
+
+	tn = (Systimers*)SYSTIMERS;
+	now = fastticks(nil);
+	period = next - fastticks(nil);
+	if(period < MinPeriod)
+		next = now + MinPeriod;
+	else if(period > MaxPeriod)
+		next = now + MaxPeriod;
+	tn->c3 = (ulong)next;
+}
+
+uvlong
+fastticks(uvlong *hz)
+{
+	Systimers *tn;
+	ulong lo, hi;
+
+	tn = (Systimers*)SYSTIMERS;
+	if(hz)
+		*hz = SystimerFreq;
+	do{
+		hi = tn->chi;
+		lo = tn->clo;
+	}while(tn->chi != hi);
+	m->fastclock = (uvlong)hi<<32 | lo;
+	return m->fastclock;
+}
+
+ulong
+perfticks(void)
+{
+	Armtimer *tm;
+
+	tm = (Armtimer*)ARMTIMER;
+	return tm->count;
+}
+
+void
+armtimerset(int n)
+{
+	Armtimer *tm;
+
+	tm = (Armtimer*)ARMTIMER;
+	if(n > 0){
+		tm->ctl |= TmrEnable|TmrIntEnable;
+		tm->load = n;
+	}else{
+		tm->load = 0;
+		tm->ctl &= ~(TmrEnable|TmrIntEnable);
+		tm->irq = 1;
+	}
+	coherence();
+}
+
+ulong
+µs(void)
+{
+	if(SystimerFreq != 1*Mhz)
+		return fastticks2us(fastticks(nil));
+	return fastticks(nil);
+}
+
+void
+microdelay(int n)
+{
+	Systimers *tn;
+	u32int now, diff;
+
+	tn = (Systimers*)SYSTIMERS;
+	diff = n + 1;
+	now = tn->clo;
+	while(tn->clo - now < diff)
+		;
+}
+
+void
+delay(int n)
+{
+	while(--n >= 0)
+		microdelay(1000);
+}

+ 1 - 0
sys/src/9/bcm/coproc.c

@@ -0,0 +1 @@
+#include "../teg2/coproc.c"

+ 287 - 0
sys/src/9/bcm/dat.h

@@ -0,0 +1,287 @@
+/*
+ * Time.
+ *
+ * HZ should divide 1000 evenly, ideally.
+ * 100, 125, 200, 250 and 333 are okay.
+ */
+#define	HZ		100			/* clock frequency */
+#define	MS2HZ		(1000/HZ)		/* millisec per clock tick */
+#define	TK2SEC(t)	((t)/HZ)		/* ticks to seconds */
+
+enum {
+	Mhz	= 1000 * 1000,
+};
+
+typedef struct Conf	Conf;
+typedef struct Confmem	Confmem;
+typedef struct FPsave	FPsave;
+typedef struct ISAConf	ISAConf;
+typedef struct Label	Label;
+typedef struct Lock	Lock;
+typedef struct Memcache	Memcache;
+typedef struct MMMU	MMMU;
+typedef struct Mach	Mach;
+typedef struct Notsave	Notsave;
+typedef struct Page	Page;
+typedef struct PhysUart	PhysUart;
+typedef struct PMMU	PMMU;
+typedef struct Proc	Proc;
+typedef u32int		PTE;
+typedef struct Uart	Uart;
+typedef struct Ureg	Ureg;
+typedef uvlong		Tval;
+
+#pragma incomplete Ureg
+
+#define MAXSYSARG	5	/* for mount(fd, mpt, flag, arg, srv) */
+
+/*
+ *  parameters for sysproc.c
+ */
+#define AOUT_MAGIC	(E_MAGIC)
+
+struct Lock
+{
+	ulong	key;
+	u32int	sr;
+	uintptr	pc;
+	Proc*	p;
+	Mach*	m;
+	int	isilock;
+};
+
+struct Label
+{
+	uintptr	sp;
+	uintptr	pc;
+};
+
+enum {
+	Maxfpregs	= 32,	/* could be 16 or 32, see Mach.fpnregs */
+	Nfpctlregs	= 16,
+};
+
+/*
+ * emulated or vfp3 floating point
+ */
+struct FPsave
+{
+	ulong	status;
+	ulong	control;
+	/*
+	 * vfp3 with ieee fp regs; uvlong is sufficient for hardware but
+	 * each must be able to hold an Internal from fpi.h for sw emulation.
+	 */
+	ulong	regs[Maxfpregs][3];
+
+	int	fpstate;
+	uintptr	pc;		/* of failed fp instr. */
+};
+
+/*
+ * FPsave.fpstate
+ */
+enum
+{
+	FPinit,
+	FPactive,
+	FPinactive,
+	FPemu,
+
+	/* bits or'd with the state */
+	FPillegal= 0x100,
+};
+
+struct Confmem
+{
+	uintptr	base;
+	usize	npage;
+	uintptr	limit;
+	uintptr	kbase;
+	uintptr	klimit;
+};
+
+struct Conf
+{
+	ulong	nmach;		/* processors */
+	ulong	nproc;		/* processes */
+	Confmem	mem[1];		/* physical memory */
+	ulong	npage;		/* total physical pages of memory */
+	usize	upages;		/* user page pool */
+	ulong	copymode;	/* 0 is copy on write, 1 is copy on reference */
+	ulong	ialloc;		/* max interrupt time allocation in bytes */
+	ulong	pipeqsize;	/* size in bytes of pipe queues */
+	ulong	nimage;		/* number of page cache image headers */
+	ulong	nswap;		/* number of swap pages */
+	int	nswppo;		/* max # of pageouts per segment pass */
+	ulong	hz;		/* processor cycle freq */
+	ulong	mhz;
+	int	monitor;	/* flag */
+};
+
+/*
+ *  things saved in the Proc structure during a notify
+ */
+struct Notsave {
+	int	emptiness;
+};
+
+/*
+ *  MMU stuff in Mach.
+ */
+struct MMMU
+{
+	PTE*	mmul1;		/* l1 for this processor */
+	int	mmul1lo;
+	int	mmul1hi;
+	int	mmupid;
+};
+
+/*
+ *  MMU stuff in proc
+ */
+#define NCOLOR	1		/* 1 level cache, don't worry about VCE's */
+struct PMMU
+{
+	Page*	mmul2;
+	Page*	mmul2cache;	/* free mmu pages */
+};
+
+#include "../port/portdat.h"
+
+struct Mach
+{
+	int	machno;			/* physical id of processor */
+	uintptr	splpc;			/* pc of last caller to splhi */
+
+	Proc*	proc;			/* current process */
+
+	MMMU;
+	int	flushmmu;		/* flush current proc mmu state */
+
+	ulong	ticks;			/* of the clock since boot time */
+	Label	sched;			/* scheduler wakeup */
+	Lock	alarmlock;		/* access to alarm list */
+	void*	alarm;			/* alarms bound to this clock */
+
+	Proc*	readied;		/* for runproc */
+	ulong	schedticks;		/* next forced context switch */
+
+	int	cputype;
+	ulong	delayloop;
+
+	/* stats */
+	int	tlbfault;
+	int	tlbpurge;
+	int	pfault;
+	int	cs;
+	int	syscall;
+	int	load;
+	int	intr;
+	uvlong	fastclock;		/* last sampled value */
+	uvlong	inidle;			/* time spent in idlehands() */
+	ulong	spuriousintr;
+	int	lastintr;
+	int	ilockdepth;
+	Perf	perf;			/* performance counters */
+
+
+	int	cpumhz;
+	uvlong	cpuhz;			/* speed of cpu */
+	uvlong	cyclefreq;		/* Frequency of user readable cycle counter */
+
+	/* vfp2 or vfp3 fpu */
+	int	havefp;
+	int	havefpvalid;
+	int	fpon;
+	int	fpconfiged;
+	int	fpnregs;
+	ulong	fpscr;			/* sw copy */
+	int	fppid;			/* pid of last fault */
+	uintptr	fppc;			/* addr of last fault */
+	int	fpcnt;			/* how many consecutive at that addr */
+
+	/* save areas for exceptions, hold R0-R4 */
+	u32int	sfiq[5];
+	u32int	sirq[5];
+	u32int	sund[5];
+	u32int	sabt[5];
+	u32int	smon[5];		/* probably not needed */
+	u32int	ssys[5];
+
+	int	stack[1];
+};
+
+/*
+ * Fake kmap.
+ */
+typedef void		KMap;
+#define	VA(k)		((uintptr)(k))
+#define	kmap(p)		(KMap*)((p)->pa|kseg0)
+#define	kunmap(k)
+
+struct
+{
+	Lock;
+	int	machs;			/* bitmap of active CPUs */
+	int	exiting;		/* shutdown */
+	int	ispanic;		/* shutdown in response to a panic */
+}active;
+
+extern register Mach* m;			/* R10 */
+extern register Proc* up;			/* R9 */
+extern uintptr kseg0;
+extern Mach* machaddr[MAXMACH];
+extern ulong memsize;
+extern int normalprint;
+
+/*
+ *  a parsed plan9.ini line
+ */
+#define NISAOPT		8
+
+struct ISAConf {
+	char	*type;
+	ulong	port;
+	int	irq;
+	ulong	dma;
+	ulong	mem;
+	ulong	size;
+	ulong	freq;
+
+	int	nopt;
+	char	*opt[NISAOPT];
+};
+
+#define	MACHP(n)	(machaddr[n])
+
+/*
+ * Horrid. But the alternative is 'defined'.
+ */
+#ifdef _DBGC_
+#define DBGFLG		(dbgflg[_DBGC_])
+#else
+#define DBGFLG		(0)
+#endif /* _DBGC_ */
+
+int vflag;
+extern char dbgflg[256];
+
+#define dbgprint	print		/* for now */
+
+/*
+ *  hardware info about a device
+ */
+typedef struct {
+	ulong	port;
+	int	size;
+} Devport;
+
+struct DevConf
+{
+	ulong	intnum;			/* interrupt number */
+	char	*type;			/* card type, malloced */
+	int	nports;			/* Number of ports */
+	Devport	*ports;			/* The ports themselves */
+};
+

+ 163 - 0
sys/src/9/bcm/devarch.c

@@ -0,0 +1,163 @@
+#include "u.h"
+#include "../port/lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+#include "../port/error.h"
+#include "io.h"
+
+enum {
+	Qdir = 0,
+	Qbase,
+
+	Qmax = 16,
+};
+
+typedef long Rdwrfn(Chan*, void*, long, vlong);
+
+static Rdwrfn *readfn[Qmax];
+static Rdwrfn *writefn[Qmax];
+
+static Dirtab archdir[Qmax] = {
+	".",		{ Qdir, 0, QTDIR },	0,	0555,
+};
+
+Lock archwlock;	/* the lock is only for changing archdir */
+int narchdir = Qbase;
+
+/*
+ * Add a file to the #P listing.  Once added, you can't delete it.
+ * You can't add a file with the same name as one already there,
+ * and you get a pointer to the Dirtab entry so you can do things
+ * like change the Qid version.  Changing the Qid path is disallowed.
+ */
+Dirtab*
+addarchfile(char *name, int perm, Rdwrfn *rdfn, Rdwrfn *wrfn)
+{
+	int i;
+	Dirtab d;
+	Dirtab *dp;
+
+	memset(&d, 0, sizeof d);
+	strcpy(d.name, name);
+	d.perm = perm;
+
+	lock(&archwlock);
+	if(narchdir >= Qmax){
+		unlock(&archwlock);
+		return nil;
+	}
+
+	for(i=0; i<narchdir; i++)
+		if(strcmp(archdir[i].name, name) == 0){
+			unlock(&archwlock);
+			return nil;
+		}
+
+	d.qid.path = narchdir;
+	archdir[narchdir] = d;
+	readfn[narchdir] = rdfn;
+	writefn[narchdir] = wrfn;
+	dp = &archdir[narchdir++];
+	unlock(&archwlock);
+
+	return dp;
+}
+
+static Chan*
+archattach(char* spec)
+{
+	return devattach('P', spec);
+}
+
+Walkqid*
+archwalk(Chan* c, Chan *nc, char** name, int nname)
+{
+	return devwalk(c, nc, name, nname, archdir, narchdir, devgen);
+}
+
+static int
+archstat(Chan* c, uchar* dp, int n)
+{
+	return devstat(c, dp, n, archdir, narchdir, devgen);
+}
+
+static Chan*
+archopen(Chan* c, int omode)
+{
+	return devopen(c, omode, archdir, narchdir, devgen);
+}
+
+static void
+archclose(Chan*)
+{
+}
+
+static long
+archread(Chan *c, void *a, long n, vlong offset)
+{
+	Rdwrfn *fn;
+
+	switch((ulong)c->qid.path){
+	case Qdir:
+		return devdirread(c, a, n, archdir, narchdir, devgen);
+
+	default:
+		if(c->qid.path < narchdir && (fn = readfn[c->qid.path]))
+			return fn(c, a, n, offset);
+		error(Eperm);
+		break;
+	}
+
+	return 0;
+}
+
+static long
+archwrite(Chan *c, void *a, long n, vlong offset)
+{
+	Rdwrfn *fn;
+
+	if(c->qid.path < narchdir && (fn = writefn[c->qid.path]))
+		return fn(c, a, n, offset);
+	error(Eperm);
+
+	return 0;
+}
+
+void archinit(void);
+
+Dev archdevtab = {
+	'P',
+	"arch",
+
+	devreset,
+	archinit,
+	devshutdown,
+	archattach,
+	archwalk,
+	archstat,
+	archopen,
+	devcreate,
+	archclose,
+	archread,
+	devbread,
+	archwrite,
+	devbwrite,
+	devremove,
+	devwstat,
+};
+
+static long
+cputyperead(Chan*, void *a, long n, vlong offset)
+{
+	char str[128];
+
+	snprint(str, sizeof str, "ARM11 %d\n", m->cpumhz);
+	return readstr(offset, a, n, str);
+}
+
+void
+archinit(void)
+{
+	addarchfile("cputype", 0444, cputyperead, nil);
+}

+ 1 - 0
sys/src/9/bcm/devether.c

@@ -0,0 +1 @@
+#include "../omap/devether.c"

+ 124 - 0
sys/src/9/bcm/devfakertc.c

@@ -0,0 +1,124 @@
+/*
+ * raspberry pi doesn't have a realtime clock
+ * fake a crude approximation from the kernel build time
+ */
+
+#include	"u.h"
+#include	"../port/lib.h"
+#include	"mem.h"
+#include	"dat.h"
+#include	"fns.h"
+#include	"../port/error.h"
+
+enum{
+	Qdir = 0,
+	Qrtc,
+};
+
+Dirtab rtcdir[]={
+	".",	{Qdir, 0, QTDIR},	0,	0555,
+	"rtc",		{Qrtc, 0},	0,	0664,
+};
+
+extern ulong kerndate;
+
+static ulong rtcsecs;
+
+static void
+rtctick(void)
+{
+	rtcsecs++;
+}
+
+static void
+rtcinit(void)
+{
+	rtcsecs = kerndate;
+	addclock0link(rtctick, 1000);
+}
+
+static long
+rtcread(Chan *c, void *a, long n, vlong offset)
+{
+	if(c->qid.type & QTDIR)
+		return devdirread(c, a, n, rtcdir, nelem(rtcdir), devgen);
+
+	switch((ulong)c->qid.path){
+	case Qrtc:
+		return readnum((ulong)offset, a, n, rtcsecs, 12);
+	}
+	error(Ebadarg);
+	return 0;
+}
+
+static long
+rtcwrite(Chan*c, void *a, long n, vlong)
+{
+	char b[13];
+	ulong i;
+
+	switch((ulong)c->qid.path){
+	case Qrtc:
+		if(n >= sizeof(b))
+			error(Ebadarg);
+		strncpy(b, (char*)a, n);
+		i = strtol(b, 0, 0);
+		if(i <= 0)
+			error(Ebadarg);
+		rtcsecs = i;
+		return n;
+	}
+	error(Eperm);
+	return 0;
+}
+
+static Chan*
+rtcattach(char* spec)
+{
+	return devattach('r', spec);
+}
+
+static Walkqid*	 
+rtcwalk(Chan* c, Chan *nc, char** name, int nname)
+{
+	return devwalk(c, nc, name, nname, rtcdir, nelem(rtcdir), devgen);
+}
+
+static int	 
+rtcstat(Chan* c, uchar* dp, int n)
+{
+	return devstat(c, dp, n, rtcdir, nelem(rtcdir), devgen);
+}
+
+static Chan*
+rtcopen(Chan* c, int omode)
+{
+	return devopen(c, omode, rtcdir, nelem(rtcdir), devgen);
+}
+
+static void	 
+rtcclose(Chan*)
+{
+}
+
+Dev fakertcdevtab = {
+	'r',
+	"rtc",
+
+	devreset,
+	rtcinit,
+	devshutdown,
+	rtcattach,
+	rtcwalk,
+	rtcstat,
+	rtcopen,
+	devcreate,
+	rtcclose,
+	rtcread,
+	devbread,
+	rtcwrite,
+	devbwrite,
+	devremove,
+	devwstat,
+};
+

+ 1487 - 0
sys/src/9/bcm/devusb.c

@@ -0,0 +1,1487 @@
+/*
+ * USB device driver framework.
+ *
+ * This is in charge of providing access to actual HCIs
+ * and providing I/O to the various endpoints of devices.
+ * A separate user program (usbd) is in charge of
+ * enumerating the bus, setting up endpoints and
+ * starting devices (also user programs).
+ *
+ * The interface provided is a violation of the standard:
+ * you're welcome.
+ *
+ * The interface consists of a root directory with several files
+ * plus a directory (epN.M) with two files per endpoint.
+ * A device is represented by its first endpoint, which
+ * is a control endpoint automatically allocated for each device.
+ * Device control endpoints may be used to create new endpoints.
+ * Devices corresponding to hubs may also allocate new devices,
+ * perhaps also hubs. Initially, a hub device is allocated for
+ * each controller present, to represent its root hub. Those can
+ * never be removed.
+ *
+ * All endpoints refer to the first endpoint (epN.0) of the device,
+ * which keeps per-device information, and also to the HCI used
+ * to reach them. Although all endpoints cache that information.
+ *
+ * epN.M/data files permit I/O and are considered DMEXCL.
+ * epN.M/ctl files provide status info and accept control requests.
+ *
+ * Endpoints may be given file names to be listed also at #u,
+ * for those drivers that have nothing to do after configuring the
+ * device and its endpoints.
+ *
+ * Drivers for different controllers are kept at usb[oue]hci.c
+ * It's likely we could factor out much from controllers into
+ * a generic controller driver, the problem is that details
+ * regarding how to handle toggles, tokens, Tds, etc. will
+ * get in the way. Thus, code is probably easier the way it is.
+ */
+
+#include	"u.h"
+#include	"../port/lib.h"
+#include	"mem.h"
+#include	"dat.h"
+#include	"fns.h"
+#include	"io.h"
+#include	"../port/error.h"
+#include	"../port/usb.h"
+
+typedef struct Hcitype Hcitype;
+
+enum
+{
+	/* Qid numbers */
+	Qdir = 0,		/* #u */
+	Qusbdir,		/* #u/usb */
+	Qctl,			/* #u/usb/ctl - control requests */
+
+	Qep0dir,		/* #u/usb/ep0.0 - endpoint 0 dir */
+	Qep0io,			/* #u/usb/ep0.0/data - endpoint 0 I/O */
+	Qep0ctl,		/* #u/usb/ep0.0/ctl - endpoint 0 ctl. */
+	Qep0dummy,		/* give 4 qids to each endpoint */
+
+	Qepdir = 0,		/* (qid-qep0dir)&3 is one of these */
+	Qepio,			/* to identify which file for the endpoint */
+	Qepctl,
+
+	/* ... */
+
+	/* Usb ctls. */
+	CMdebug = 0,		/* debug on|off */
+	CMdump,			/* dump (data structures for debug) */
+
+	/* Ep. ctls */
+	CMnew = 0,		/* new nb ctl|bulk|intr|iso r|w|rw (endpoint) */
+	CMnewdev,		/* newdev full|low|high portnb (allocate new devices) */
+	CMhub,			/* hub (set the device as a hub) */
+	CMspeed,		/* speed full|low|high|no */
+	CMmaxpkt,		/* maxpkt size */
+	CMntds,			/* ntds nb (max nb. of tds per µframe) */
+	CMclrhalt,		/* clrhalt (halt was cleared on endpoint) */
+	CMpollival,		/* pollival interval (interrupt/iso) */
+	CMhz,			/* hz n (samples/sec; iso) */
+	CMsamplesz,		/* samplesz n (sample size; iso) */
+	CMinfo,			/* info infostr (ke.ep info for humans) */
+	CMdetach,		/* detach (abort I/O forever on this ep). */
+	CMaddress,		/* address (address is assigned) */
+	CMdebugep,		/* debug n (set/clear debug for this ep) */
+	CMname,			/* name str (show up as #u/name as well) */
+	CMtmout,		/* timeout n (activate timeouts for ep) */
+	CMpreset,		/* reset the port */
+
+	/* Hub feature selectors */
+	Rportenable	= 1,
+	Rportreset	= 4,
+
+};
+
+struct Hcitype
+{
+	char*	type;
+	int	(*reset)(Hci*);
+};
+
+#define QID(q)	((int)(q).path)
+
+static char Edetach[] = "device is detached";
+static char Enotconf[] = "endpoint not configured";
+char Estalled[] = "endpoint stalled";
+
+static Cmdtab usbctls[] =
+{
+	{CMdebug,	"debug",	2},
+	{CMdump,	"dump",		1},
+};
+
+static Cmdtab epctls[] =
+{
+	{CMnew,		"new",		4},
+	{CMnewdev,	"newdev",	3},
+	{CMhub,		"hub",		1},
+	{CMspeed,	"speed",	2},
+	{CMmaxpkt,	"maxpkt",	2},
+	{CMntds,	"ntds",		2},
+	{CMpollival,	"pollival",	2},
+	{CMsamplesz,	"samplesz",	2},
+	{CMhz,		"hz",		2},
+	{CMinfo,	"info",		0},
+	{CMdetach,	"detach",	1},
+	{CMaddress,	"address",	1},
+	{CMdebugep,	"debug",	2},
+	{CMclrhalt,	"clrhalt",	1},
+	{CMname,	"name",		2},
+	{CMtmout,	"timeout",	2},
+	{CMpreset,	"reset",	1},
+};
+
+static Dirtab usbdir[] =
+{
+	"ctl",		{Qctl},		0,	0666,
+};
+
+char *usbmodename[] =
+{
+	[OREAD]	"r",
+	[OWRITE] "w",
+	[ORDWR]	"rw",
+};
+
+static char *ttname[] =
+{
+	[Tnone]	"none",
+	[Tctl]	"control",
+	[Tiso]	"iso",
+	[Tintr]	"interrupt",
+	[Tbulk]	"bulk",
+};
+
+static char *spname[] =
+{
+	[Fullspeed]	"full",
+	[Lowspeed]	"low",
+	[Highspeed]	"high",
+	[Nospeed]	"no",
+};
+
+static int	debug;
+static Hcitype	hcitypes[Nhcis];
+static Hci*	hcis[Nhcis];
+static QLock	epslck;		/* add, del, lookup endpoints */
+static Ep*	eps[Neps];	/* all endpoints known */
+static int	epmax;		/* 1 + last endpoint index used  */
+static int	usbidgen;	/* device address generator */
+
+/*
+ * Is there something like this in a library? should it be?
+ */
+char*
+seprintdata(char *s, char *se, uchar *d, int n)
+{
+	int i, l;
+
+	s = seprint(s, se, " %#p[%d]: ", d, n);
+	l = n;
+	if(l > 10)
+		l = 10;
+	for(i=0; i<l; i++)
+		s = seprint(s, se, " %2.2ux", d[i]);
+	if(l < n)
+		s = seprint(s, se, "...");
+	return s;
+}
+
+static int
+name2speed(char *name)
+{
+	int i;
+
+	for(i = 0; i < nelem(spname); i++)
+		if(strcmp(name, spname[i]) == 0)
+			return i;
+	return Nospeed;
+}
+
+static int
+name2ttype(char *name)
+{
+	int i;
+
+	for(i = 0; i < nelem(ttname); i++)
+		if(strcmp(name, ttname[i]) == 0)
+			return i;
+	/* may be a std. USB ep. type */
+	i = strtol(name, nil, 0);
+	switch(i+1){
+	case Tctl:
+	case Tiso:
+	case Tbulk:
+	case Tintr:
+		return i+1;
+	default:
+		return Tnone;
+	}
+}
+
+static int
+name2mode(char *mode)
+{
+	int i;
+
+	for(i = 0; i < nelem(usbmodename); i++)
+		if(strcmp(mode, usbmodename[i]) == 0)
+			return i;
+	return -1;
+}
+
+static int
+qid2epidx(int q)
+{
+	q = (q - Qep0dir) / 4;
+	if(q < 0 || q >= epmax || eps[q] == nil)
+		return -1;
+	return q;
+}
+
+static int
+isqtype(int q, int type)
+{
+	if(q < Qep0dir)
+		return 0;
+	q -= Qep0dir;
+	return (q & 3) == type;
+}
+
+void
+addhcitype(char* t, int (*r)(Hci*))
+{
+	static int ntype;
+
+	if(ntype == Nhcis)
+		panic("too many USB host interface types");
+	hcitypes[ntype].type = t;
+	hcitypes[ntype].reset = r;
+	ntype++;
+}
+
+static char*
+seprintep(char *s, char *se, Ep *ep, int all)
+{
+	Udev *d;
+	int i, di;
+	static char *dsnames[] = { "config", "enabled", "detached", "reset" };
+
+	d = ep->dev;
+
+	qlock(ep);
+	if(waserror()){
+		qunlock(ep);
+		nexterror();
+	}
+	di = ep->dev->nb;
+	if(all)
+		s = seprint(s, se, "dev %d ep %d ", di, ep->nb);
+	s = seprint(s, se, "%s", dsnames[ep->dev->state]);
+	s = seprint(s, se, " %s", ttname[ep->ttype]);
+	assert(ep->mode == OREAD || ep->mode == OWRITE || ep->mode == ORDWR);
+	s = seprint(s, se, " %s", usbmodename[ep->mode]);
+	s = seprint(s, se, " speed %s", spname[d->speed]);
+	s = seprint(s, se, " maxpkt %ld", ep->maxpkt);
+	s = seprint(s, se, " pollival %ld", ep->pollival);
+	s = seprint(s, se, " samplesz %ld", ep->samplesz);
+	s = seprint(s, se, " hz %ld", ep->hz);
+	s = seprint(s, se, " hub %d", ep->dev->hub);
+	s = seprint(s, se, " port %d", ep->dev->port);
+	if(ep->inuse)
+		s = seprint(s, se, " busy");
+	else
+		s = seprint(s, se, " idle");
+	if(all){
+		s = seprint(s, se, " load %uld", ep->load);
+		s = seprint(s, se, " ref %ld addr %#p", ep->ref, ep);
+		s = seprint(s, se, " idx %d", ep->idx);
+		if(ep->name != nil)
+			s = seprint(s, se, " name '%s'", ep->name);
+		if(ep->tmout != 0)
+			s = seprint(s, se, " tmout");
+		if(ep == ep->ep0){
+			s = seprint(s, se, " ctlrno %#x", ep->hp->ctlrno);
+			s = seprint(s, se, " eps:");
+			for(i = 0; i < nelem(d->eps); i++)
+				if(d->eps[i] != nil)
+					s = seprint(s, se, " ep%d.%d", di, i);
+		}
+	}
+	if(ep->info != nil)
+		s = seprint(s, se, "\n%s %s\n", ep->info, ep->hp->type);
+	else
+		s = seprint(s, se, "\n");
+	qunlock(ep);
+	poperror();
+	return s;
+}
+
+static Ep*
+epalloc(Hci *hp)
+{
+	Ep *ep;
+	int i;
+
+	ep = smalloc(sizeof(Ep));
+	ep->ref = 1;
+	qlock(&epslck);
+	for(i = 0; i < Neps; i++)
+		if(eps[i] == nil)
+			break;
+	if(i == Neps){
+		qunlock(&epslck);
+		free(ep);
+		print("usb: bug: too few endpoints.\n");
+		return nil;
+	}
+	ep->idx = i;
+	if(epmax <= i)
+		epmax = i+1;
+	eps[i] = ep;
+	ep->hp = hp;
+	ep->maxpkt = 8;
+	ep->ntds = 1;
+	ep->samplesz = ep->pollival = ep->hz = 0;	/* make them void */
+	qunlock(&epslck);
+	return ep;
+}
+
+static Ep*
+getep(int i)
+{
+	Ep *ep;
+
+	if(i < 0 || i >= epmax || eps[i] == nil)
+		return nil;
+	qlock(&epslck);
+	ep = eps[i];
+	if(ep != nil)
+		incref(ep);
+	qunlock(&epslck);
+	return ep;
+}
+
+static void
+putep(Ep *ep)
+{
+	Udev *d;
+
+	if(ep != nil && decref(ep) == 0){
+		d = ep->dev;
+		deprint("usb: ep%d.%d %#p released\n", d->nb, ep->nb, ep);
+		qlock(&epslck);
+		eps[ep->idx] = nil;
+		if(ep->idx == epmax-1)
+			epmax--;
+		if(ep == ep->ep0 && ep->dev != nil && ep->dev->nb == usbidgen)
+			usbidgen--;
+		qunlock(&epslck);
+		if(d != nil){
+			qlock(ep->ep0);
+			d->eps[ep->nb] = nil;
+			qunlock(ep->ep0);
+		}
+		if(ep->ep0 != ep){
+			putep(ep->ep0);
+			ep->ep0 = nil;
+		}
+		free(ep->info);
+		free(ep->name);
+		free(ep);
+	}
+}
+
+static void
+dumpeps(void)
+{
+	int i;
+	char *s, *e;
+	Ep *ep;
+	static char buf[512];
+
+	print("usb dump eps: epmax %d Neps %d (ref=1+ for dump):\n", epmax, Neps);
+	for(i = 0; i < epmax; i++){
+		s = buf;
+		e = buf+sizeof(buf);
+		ep = getep(i);
+		if(ep != nil){
+			if(waserror()){
+				putep(ep);
+				nexterror();
+			}
+			s = seprint(s, e, "ep%d.%d ", ep->dev->nb, ep->nb);
+			seprintep(s, e, ep, 1);
+			print("%s", buf);
+			ep->hp->seprintep(buf, e, ep);
+			print("%s", buf);
+			poperror();
+			putep(ep);
+		}
+	}
+	print("usb dump hcis:\n");
+	for(i = 0; i < Nhcis; i++)
+		if(hcis[i] != nil)
+			hcis[i]->dump(hcis[i]);
+}
+
+static int
+newusbid(Hci *)
+{
+	int id;
+
+	qlock(&epslck);
+	id = ++usbidgen;
+	if(id >= 0x7F)
+		print("#u: too many device addresses; reuse them more\n");
+	qunlock(&epslck);
+	return id;
+}
+
+/*
+ * Create endpoint 0 for a new device
+ */
+static Ep*
+newdev(Hci *hp, int ishub, int isroot)
+{
+	Ep *ep;
+	Udev *d;
+
+	ep = epalloc(hp);
+	d = ep->dev = smalloc(sizeof(Udev));
+	d->nb = newusbid(hp);
+	d->eps[0] = ep;
+	ep->nb = 0;
+	ep->toggle[0] = ep->toggle[1] = 0;
+	d->ishub = ishub;
+	d->isroot = isroot;
+	if(hp->highspeed != 0)
+		d->speed = Highspeed;
+	else
+		d->speed = Fullspeed;
+	d->state = Dconfig;		/* address not yet set */
+	ep->dev = d;
+	ep->ep0 = ep;			/* no ref counted here */
+	ep->ttype = Tctl;
+	ep->tmout = Xfertmout;
+	ep->mode = ORDWR;
+	dprint("newdev %#p ep%d.%d %#p\n", d, d->nb, ep->nb, ep);
+	return ep;
+}
+
+/*
+ * Create a new endpoint for the device
+ * accessed via the given endpoint 0.
+ */
+static Ep*
+newdevep(Ep *ep, int i, int tt, int mode)
+{
+	Ep *nep;
+	Udev *d;
+
+	d = ep->dev;
+	if(d->eps[i] != nil)
+		error("endpoint already in use");
+	nep = epalloc(ep->hp);
+	incref(ep);
+	d->eps[i] = nep;
+	nep->nb = i;
+	nep->toggle[0] = nep->toggle[1] = 0;
+	nep->ep0 = ep;
+	nep->dev = ep->dev;
+	nep->mode = mode;
+	nep->ttype = tt;
+	nep->debug = ep->debug;
+	/* set defaults */
+	switch(tt){
+	case Tctl:
+		nep->tmout = Xfertmout;
+		break;
+	case Tintr:
+		nep->pollival = 10;
+		break;
+	case Tiso:
+		nep->tmout = Xfertmout;
+		nep->pollival = 10;
+		nep->samplesz = 4;
+		nep->hz = 44100;
+		break;
+	}
+	deprint("newdevep ep%d.%d %#p\n", d->nb, nep->nb, nep);
+	return ep;
+}
+
+static int
+epdataperm(int mode)
+{
+	switch(mode){
+	case OREAD:
+		return 0440|DMEXCL;
+		break;
+	case OWRITE:
+		return 0220|DMEXCL;
+		break;
+	default:
+		return 0660|DMEXCL;
+	}
+}
+
+static int
+usbgen(Chan *c, char *, Dirtab*, int, int s, Dir *dp)
+{
+	int mode, nb, perm;
+	char *se;
+	Dirtab *dir;
+	Ep *ep;
+	Qid q;
+
+	if(0)ddprint("usbgen q %#x s %d...", QID(c->qid), s);
+	if(s == DEVDOTDOT){
+		if(QID(c->qid) <= Qusbdir){
+			mkqid(&q, Qdir, 0, QTDIR);
+			devdir(c, q, "#u", 0, eve, 0555, dp);
+		}else{
+			mkqid(&q, Qusbdir, 0, QTDIR);
+			devdir(c, q, "usb", 0, eve, 0555, dp);
+		}
+		if(0)ddprint("ok\n");
+		return 1;
+	}
+
+	switch(QID(c->qid)){
+	case Qdir:				/* list #u */
+		if(s == 0){
+			mkqid(&q, Qusbdir, 0, QTDIR);
+			devdir(c, q, "usb", 0, eve, 0555, dp);
+			if(0)ddprint("ok\n");
+			return 1;
+		}
+		s--;
+		if(s < 0 || s >= epmax)
+			goto Fail;
+		ep = getep(s);
+		if(ep == nil || ep->name == nil){
+			if(ep != nil)
+				putep(ep);
+			if(0)ddprint("skip\n");
+			return 0;
+		}
+		if(waserror()){
+			putep(ep);
+			nexterror();
+		}
+		mkqid(&q, Qep0io+s*4, 0, QTFILE);
+		devdir(c, q, ep->name, 0, eve, epdataperm(ep->mode), dp);
+		putep(ep);
+		poperror();
+		if(0)ddprint("ok\n");
+		return 1;
+
+	case Qusbdir:				/* list #u/usb */
+	Usbdir:
+		if(s < nelem(usbdir)){
+			dir = &usbdir[s];
+			mkqid(&q, dir->qid.path, 0, QTFILE);
+			devdir(c, q, dir->name, dir->length, eve, dir->perm, dp);
+			if(0)ddprint("ok\n");
+			return 1;
+		}
+		s -= nelem(usbdir);
+		if(s < 0 || s >= epmax)
+			goto Fail;
+		ep = getep(s);
+		if(ep == nil){
+			if(0)ddprint("skip\n");
+			return 0;
+		}
+		if(waserror()){
+			putep(ep);
+			nexterror();
+		}
+		se = up->genbuf+sizeof(up->genbuf);
+		seprint(up->genbuf, se, "ep%d.%d", ep->dev->nb, ep->nb);
+		mkqid(&q, Qep0dir+4*s, 0, QTDIR);
+		putep(ep);
+		poperror();
+		devdir(c, q, up->genbuf, 0, eve, 0755, dp);
+		if(0)ddprint("ok\n");
+		return 1;
+
+	case Qctl:
+		s = 0;
+		goto Usbdir;
+
+	default:				/* list #u/usb/epN.M */
+		nb = qid2epidx(QID(c->qid));
+		ep = getep(nb);
+		if(ep == nil)
+			goto Fail;
+		mode = ep->mode;
+		putep(ep);
+		if(isqtype(QID(c->qid), Qepdir)){
+		Epdir:
+			switch(s){
+			case 0:
+				mkqid(&q, Qep0io+nb*4, 0, QTFILE);
+				perm = epdataperm(mode);
+				devdir(c, q, "data", 0, eve, perm, dp);
+				break;
+			case 1:
+				mkqid(&q, Qep0ctl+nb*4, 0, QTFILE);
+				devdir(c, q, "ctl", 0, eve, 0664, dp);
+				break;
+			default:
+				goto Fail;
+			}
+		}else if(isqtype(QID(c->qid), Qepctl)){
+			s = 1;
+			goto Epdir;
+		}else{
+			s = 0;
+			goto Epdir;
+		}
+		if(0)ddprint("ok\n");
+		return 1;
+	}
+Fail:
+	if(0)ddprint("fail\n");
+	return -1;
+}
+
+static Hci*
+hciprobe(int cardno, int ctlrno)
+{
+	Hci *hp;
+	char *type;
+	char name[64];
+	static int epnb = 1;	/* guess the endpoint nb. for the controller */
+
+	ddprint("hciprobe %d %d\n", cardno, ctlrno);
+	hp = smalloc(sizeof(Hci));
+	hp->ctlrno = ctlrno;
+
+	if(cardno < 0)
+		for(cardno = 0; cardno < Nhcis; cardno++){
+			if(hcitypes[cardno].type == nil)
+				break;
+			type = hp->type;
+			if(type==nil || *type==0)
+				type = "uhci";
+			if(cistrcmp(hcitypes[cardno].type, type) == 0)
+				break;
+		}
+
+	if(cardno >= Nhcis || hcitypes[cardno].type == nil){
+		free(hp);
+		return nil;
+	}
+	dprint("%s...", hcitypes[cardno].type);
+	if(hcitypes[cardno].reset(hp) < 0){
+		free(hp);
+		return nil;
+	}
+
+	snprint(name, sizeof(name), "usb%s", hcitypes[cardno].type);
+	intrenable(hp->irq, hp->interrupt, hp, UNKNOWN, name);
+
+	print("#u/usb/ep%d.0: %s: port %#luX irq %d\n",
+		epnb, hcitypes[cardno].type, hp->port, hp->irq);
+	epnb++;
+
+	return hp;
+}
+
+static void
+usbreset(void)
+{
+	int cardno, ctlrno;
+	Hci *hp;
+
+	dprint("usbreset\n");
+
+	for(ctlrno = 0; ctlrno < Nhcis; ctlrno++)
+		if((hp = hciprobe(-1, ctlrno)) != nil)
+			hcis[ctlrno] = hp;
+	cardno = ctlrno = 0;
+	while(cardno < Nhcis && ctlrno < Nhcis && hcitypes[cardno].type != nil)
+		if(hcis[ctlrno] != nil)
+			ctlrno++;
+		else{
+			hp = hciprobe(cardno, ctlrno);
+			if(hp == nil)
+				cardno++;
+			hcis[ctlrno++] = hp;
+		}
+	if(hcis[Nhcis-1] != nil)
+		print("usbreset: bug: Nhcis too small\n");
+}
+
+static void
+usbinit(void)
+{
+	Hci *hp;
+	int ctlrno;
+	Ep *d;
+	char info[40];
+
+	dprint("usbinit\n");
+	for(ctlrno = 0; ctlrno < Nhcis; ctlrno++){
+		hp = hcis[ctlrno];
+		if(hp != nil){
+			if(hp->init != nil)
+				hp->init(hp);
+			d = newdev(hp, 1, 1);		/* new root hub */
+			d->dev->state = Denabled;	/* although addr == 0 */
+			d->maxpkt = 64;
+			snprint(info, sizeof(info), "ports %d", hp->nports);
+			kstrdup(&d->info, info);
+		}
+	}
+}
+
+static Chan*
+usbattach(char *spec)
+{
+	return devattach(L'u', spec);
+}
+
+static Walkqid*
+usbwalk(Chan *c, Chan *nc, char **name, int nname)
+{
+	return devwalk(c, nc, name, nname, nil, 0, usbgen);
+}
+
+static int
+usbstat(Chan *c, uchar *db, int n)
+{
+	return devstat(c, db, n, nil, 0, usbgen);
+}
+
+/*
+ * µs for the given transfer, for bandwidth allocation.
+ * This is a very rough worst case for what 5.11.3
+ * of the usb 2.0 spec says.
+ * Also, we are using maxpkt and not actual transfer sizes.
+ * Only when we are sure we
+ * are not exceeding b/w might we consider adjusting it.
+ */
+static ulong
+usbload(int speed, int maxpkt)
+{
+	enum{ Hostns = 1000, Hubns = 333 };
+	ulong l, bs;
+
+	l = 0;
+	bs = 10UL * maxpkt;
+	switch(speed){
+	case Highspeed:
+		l = 55*8*2 + 2 * (3 + bs) + Hostns;
+		break;
+	case Fullspeed:
+		l = 9107 + 84 * (4 + bs) + Hostns;
+		break;
+	case Lowspeed:
+		l = 64107 + 2 * Hubns + 667 * (3 + bs) + Hostns;
+		break;
+	default:
+		print("usbload: bad speed %d\n", speed);
+		/* let it run */
+	}
+	return l / 1000UL;		/* in µs */
+}
+
+static Chan*
+usbopen(Chan *c, int omode)
+{
+	int q, mode;
+	Ep *ep;
+
+	mode = openmode(omode);
+	q = QID(c->qid);
+
+	if(q >= Qep0dir && qid2epidx(q) < 0)
+		error(Eio);
+	if(q < Qep0dir || isqtype(q, Qepctl) || isqtype(q, Qepdir))
+		return devopen(c, omode, nil, 0, usbgen);
+
+	ep = getep(qid2epidx(q));
+	if(ep == nil)
+		error(Eio);
+	deprint("usbopen q %#x fid %d omode %d\n", q, c->fid, mode);
+	if(waserror()){
+		putep(ep);
+		nexterror();
+	}
+	qlock(ep);
+	if(ep->inuse){
+		qunlock(ep);
+		error(Einuse);
+	}
+	ep->inuse = 1;
+	qunlock(ep);
+	if(waserror()){
+		ep->inuse = 0;
+		nexterror();
+	}
+	if(mode != OREAD && ep->mode == OREAD)
+		error(Eperm);
+	if(mode != OWRITE && ep->mode == OWRITE)
+		error(Eperm);
+	if(ep->ttype == Tnone)
+		error(Enotconf);
+	ep->clrhalt = 0;
+	ep->rhrepl = -1;
+	if(ep->load == 0)
+		ep->load = usbload(ep->dev->speed, ep->maxpkt);
+	ep->hp->epopen(ep);
+
+	poperror();	/* ep->inuse */
+	poperror();	/* don't putep(): ref kept for fid using the ep. */
+
+	c->mode = mode;
+	c->flag |= COPEN;
+	c->offset = 0;
+	c->aux = nil;		/* paranoia */
+	return c;
+}
+
+static void
+epclose(Ep *ep)
+{
+	qlock(ep);
+	if(waserror()){
+		qunlock(ep);
+		nexterror();
+	}
+	if(ep->inuse){
+		ep->hp->epclose(ep);
+		ep->inuse = 0;
+	}
+	qunlock(ep);
+	poperror();
+}
+
+static void
+usbclose(Chan *c)
+{
+	int q;
+	Ep *ep;
+
+	q = QID(c->qid);
+	if(q < Qep0dir || isqtype(q, Qepctl) || isqtype(q, Qepdir))
+		return;
+
+	ep = getep(qid2epidx(q));
+	if(ep == nil)
+		return;
+	deprint("usbclose q %#x fid %d ref %ld\n", q, c->fid, ep->ref);
+	if(waserror()){
+		putep(ep);
+		nexterror();
+	}
+	if(c->flag & COPEN){
+		free(c->aux);
+		c->aux = nil;
+		epclose(ep);
+		putep(ep);	/* release ref kept since usbopen */
+		c->flag &= ~COPEN;
+	}
+	poperror();
+	putep(ep);
+}
+
+static long
+ctlread(Chan *c, void *a, long n, vlong offset)
+{
+	int i, q;
+	char *s, *se, *us;
+	Ep *ep;
+
+	q = QID(c->qid);
+	us = s = smalloc(READSTR);
+	se = s + READSTR;
+	if(waserror()){
+		free(us);
+		nexterror();
+	}
+	if(q == Qctl)
+		for(i = 0; i < epmax; i++){
+			ep = getep(i);
+			if(ep != nil){
+				if(waserror()){
+					putep(ep);
+					nexterror();
+				}
+				s = seprint(s, se, "ep%d.%d ",
+					ep->dev->nb, ep->nb);
+				s = seprintep(s, se, ep, 0);
+				poperror();
+			}
+			putep(ep);
+		}
+	else{
+		ep = getep(qid2epidx(q));
+		if(ep == nil)
+			error(Eio);
+		if(waserror()){
+			putep(ep);
+			nexterror();
+		}
+		if(c->aux != nil){
+			/*
+			 * After a new endpoint request we read
+			 * the new endpoint name back.
+			 */
+			strecpy(s, se, c->aux);
+			free(c->aux);
+			c->aux = nil;
+		}else
+			seprintep(s, se, ep, 0);
+		poperror();
+		putep(ep);
+	}
+	n = readstr(offset, a, n, us);
+	poperror();
+	free(us);
+	return n;
+}
+
+/*
+ * Fake root hub emulation.
+ */
+static long
+rhubread(Ep *ep, void *a, long n)
+{
+	char *b;
+
+	if(ep->dev->isroot == 0 || ep->nb != 0 || n < 2)
+		return -1;
+	if(ep->rhrepl < 0)
+		return -1;
+
+	b = a;
+	memset(b, 0, n);
+	PUT2(b, ep->rhrepl);
+	ep->rhrepl = -1;
+	return n;
+}
+
+static long
+rhubwrite(Ep *ep, void *a, long n)
+{
+	int cmd, feature, port;
+	uchar *s;
+	Hci *hp;
+
+	if(ep->dev == nil || ep->dev->isroot == 0 || ep->nb != 0)
+		return -1;
+	if(n != Rsetuplen)
+		error("root hub is a toy hub");
+	ep->rhrepl = -1;
+	s = a;
+	if(s[Rtype] != (Rh2d|Rclass|Rother) && s[Rtype] != (Rd2h|Rclass|Rother))
+		error("root hub is a toy hub");
+	hp = ep->hp;
+	cmd = s[Rreq];
+	feature = GET2(s+Rvalue);
+	port = GET2(s+Rindex);
+	if(port < 1 || port > hp->nports)
+		error("bad hub port number");
+	switch(feature){
+	case Rportenable:
+		ep->rhrepl = hp->portenable(hp, port, cmd == Rsetfeature);
+		break;
+	case Rportreset:
+		ep->rhrepl = hp->portreset(hp, port, cmd == Rsetfeature);
+		break;
+	case Rgetstatus:
+		ep->rhrepl = hp->portstatus(hp, port);
+		break;
+	default:
+		ep->rhrepl = 0;
+		break;
+	}
+	return n;
+}
+
+static long
+usbread(Chan *c, void *a, long n, vlong offset)
+{
+	int q, nr;
+	Ep *ep;
+
+	q = QID(c->qid);
+
+	if(c->qid.type == QTDIR)
+		return devdirread(c, a, n, nil, 0, usbgen);
+
+	if(q == Qctl || isqtype(q, Qepctl))
+		return ctlread(c, a, n, offset);
+
+	ep = getep(qid2epidx(q));
+	if(ep == nil)
+		error(Eio);
+	if(waserror()){
+		putep(ep);
+		nexterror();
+	}
+	if(ep->dev->state == Ddetach)
+		error(Edetach);
+	if(ep->mode == OWRITE || ep->inuse == 0)
+		error(Ebadusefd);
+	switch(ep->ttype){
+	case Tnone:
+		error("endpoint not configured");
+	case Tctl:
+		nr = rhubread(ep, a, n);
+		if(nr >= 0){
+			n = nr;
+			break;
+		}
+		/* else fall through */
+	default:
+		ddeprint("\nusbread q %#x fid %d cnt %ld off %lld\n",
+			q, c->fid, n, offset);
+		n = ep->hp->epread(ep, a, n);
+		break;
+	}
+	poperror();
+	putep(ep);
+	return n;
+}
+
+static long
+pow2(int n)
+{
+	return 1 << n;
+}
+
+static void
+setmaxpkt(Ep *ep, char* s)
+{
+	long spp;			/* samples per packet */
+
+	if(ep->dev->speed == Highspeed)
+		spp = (ep->hz * ep->pollival * ep->ntds + 7999) / 8000;
+	else
+		spp = (ep->hz * ep->pollival + 999) / 1000;
+	ep->maxpkt = spp * ep->samplesz;
+	deprint("usb: %s: setmaxpkt: hz %ld poll %ld"
+		" ntds %d %s speed -> spp %ld maxpkt %ld\n", s,
+		ep->hz, ep->pollival, ep->ntds, spname[ep->dev->speed],
+		spp, ep->maxpkt);
+	if(ep->maxpkt > 1024){
+		print("usb: %s: maxpkt %ld > 1024. truncating\n", s, ep->maxpkt);
+		ep->maxpkt = 1024;
+	}
+}
+
+/*
+ * Many endpoint ctls.  Simply update the portable representation
+ * of the endpoint. The actual controller driver will look
+ * at them to setup the endpoints as dictated.
+ */
+static long
+epctl(Ep *ep, Chan *c, void *a, long n)
+{
+	int i, l, mode, nb, tt;
+	char *b, *s;
+	Cmdbuf *cb;
+	Cmdtab *ct;
+	Ep *nep;
+	Udev *d;
+	static char *Info = "info ";
+
+	d = ep->dev;
+
+	cb = parsecmd(a, n);
+	if(waserror()){
+		free(cb);
+		nexterror();
+	}
+	ct = lookupcmd(cb, epctls, nelem(epctls));
+	if(ct == nil)
+		error(Ebadctl);
+	i = ct->index;
+	if(i == CMnew || i == CMspeed || i == CMhub || i == CMpreset)
+		if(ep != ep->ep0)
+			error("allowed only on a setup endpoint");
+	if(i != CMclrhalt && i != CMdetach && i != CMdebugep && i != CMname)
+		if(ep != ep->ep0 && ep->inuse != 0)
+			error("must configure before using");
+	switch(i){
+	case CMnew:
+		deprint("usb epctl %s\n", cb->f[0]);
+		nb = strtol(cb->f[1], nil, 0);
+		if(nb < 0 || nb >= Ndeveps)
+			error("bad endpoint number");
+		tt = name2ttype(cb->f[2]);
+		if(tt == Tnone)
+			error("unknown endpoint type");
+		mode = name2mode(cb->f[3]);
+		if(mode < 0)
+			error("unknown i/o mode");
+		newdevep(ep, nb, tt, mode);
+		break;
+	case CMnewdev:
+		deprint("usb epctl %s\n", cb->f[0]);
+		if(ep != ep->ep0 || d->ishub == 0)
+			error("not a hub setup endpoint");
+		l = name2speed(cb->f[1]);
+		if(l == Nospeed)
+			error("speed must be full|low|high");
+		nep = newdev(ep->hp, 0, 0);
+		nep->dev->speed = l;
+		if(nep->dev->speed  != Lowspeed)
+			nep->maxpkt = 64;	/* assume full speed */
+		nep->dev->hub = d->nb;
+		nep->dev->port = atoi(cb->f[2]);
+		/*
+		 * next read request will read
+		 * the name for the new endpoint
+		 */
+		l = sizeof(up->genbuf);
+		snprint(up->genbuf, l, "ep%d.%d", nep->dev->nb, nep->nb);
+		kstrdup(&c->aux, up->genbuf);
+		break;
+	case CMhub:
+		deprint("usb epctl %s\n", cb->f[0]);
+		d->ishub = 1;
+		break;
+	case CMspeed:
+		l = name2speed(cb->f[1]);
+		deprint("usb epctl %s %d\n", cb->f[0], l);
+		if(l == Nospeed)
+			error("speed must be full|low|high");
+		qlock(ep->ep0);
+		d->speed = l;
+		qunlock(ep->ep0);
+		break;
+	case CMmaxpkt:
+		l = strtoul(cb->f[1], nil, 0);
+		deprint("usb epctl %s %d\n", cb->f[0], l);
+		if(l < 1 || l > 1024)
+			error("maxpkt not in [1:1024]");
+		qlock(ep);
+		ep->maxpkt = l;
+		qunlock(ep);
+		break;
+	case CMntds:
+		l = strtoul(cb->f[1], nil, 0);
+		deprint("usb epctl %s %d\n", cb->f[0], l);
+		if(l < 1 || l > 3)
+			error("ntds not in [1:3]");
+		qlock(ep);
+		ep->ntds = l;
+		qunlock(ep);
+		break;
+	case CMpollival:
+		if(ep->ttype != Tintr && ep->ttype != Tiso)
+			error("not an intr or iso endpoint");
+		l = strtoul(cb->f[1], nil, 0);
+		deprint("usb epctl %s %d\n", cb->f[0], l);
+		if(ep->ttype == Tiso ||
+		   (ep->ttype == Tintr && ep->dev->speed == Highspeed)){
+			if(l < 1 || l > 16)
+				error("pollival power not in [1:16]");
+			l = pow2(l-1);
+		}else
+			if(l < 1 || l > 255)
+				error("pollival not in [1:255]");
+		qlock(ep);
+		ep->pollival = l;
+		if(ep->ttype == Tiso)
+			setmaxpkt(ep, "pollival");
+		qunlock(ep);
+		break;
+	case CMsamplesz:
+		if(ep->ttype != Tiso)
+			error("not an iso endpoint");
+		l = strtoul(cb->f[1], nil, 0);
+		deprint("usb epctl %s %d\n", cb->f[0], l);
+		if(l <= 0 || l > 8)
+			error("samplesz not in [1:8]");
+		qlock(ep);
+		ep->samplesz = l;
+		setmaxpkt(ep, "samplesz");
+		qunlock(ep);
+		break;
+	case CMhz:
+		if(ep->ttype != Tiso)
+			error("not an iso endpoint");
+		l = strtoul(cb->f[1], nil, 0);
+		deprint("usb epctl %s %d\n", cb->f[0], l);
+		if(l <= 0 || l > 100000)
+			error("hz not in [1:100000]");
+		qlock(ep);
+		ep->hz = l;
+		setmaxpkt(ep, "hz");
+		qunlock(ep);
+		break;
+	case CMclrhalt:
+		qlock(ep);
+		deprint("usb epctl %s\n", cb->f[0]);
+		ep->clrhalt = 1;
+		qunlock(ep);
+		break;
+	case CMinfo:
+		deprint("usb epctl %s\n", cb->f[0]);
+		l = strlen(Info);
+		s = a;
+		if(n < l+2 || strncmp(Info, s, l) != 0)
+			error(Ebadctl);
+		if(n > 1024)
+			n = 1024;
+		b = smalloc(n);
+		memmove(b, s+l, n-l);
+		b[n-l] = 0;
+		if(b[n-l-1] == '\n')
+			b[n-l-1] = 0;
+		qlock(ep);
+		free(ep->info);
+		ep->info = b;
+		qunlock(ep);
+		break;
+	case CMaddress:
+		deprint("usb epctl %s\n", cb->f[0]);
+		ep->dev->state = Denabled;
+		break;
+	case CMdetach:
+		if(ep->dev->isroot != 0)
+			error("can't detach a root hub");
+		deprint("usb epctl %s ep%d.%d\n",
+			cb->f[0], ep->dev->nb, ep->nb);
+		ep->dev->state = Ddetach;
+		/* Release file system ref. for its endpoints */
+		for(i = 0; i < nelem(ep->dev->eps); i++)
+			putep(ep->dev->eps[i]);
+		break;
+	case CMdebugep:
+		if(strcmp(cb->f[1], "on") == 0)
+			ep->debug = 1;
+		else if(strcmp(cb->f[1], "off") == 0)
+			ep->debug = 0;
+		else
+			ep->debug = strtoul(cb->f[1], nil, 0);
+		print("usb: ep%d.%d debug %d\n",
+			ep->dev->nb, ep->nb, ep->debug);
+		break;
+	case CMname:
+		deprint("usb epctl %s %s\n", cb->f[0], cb->f[1]);
+		validname(cb->f[1], 0);
+		kstrdup(&ep->name, cb->f[1]);
+		break;
+	case CMtmout:
+		deprint("usb epctl %s\n", cb->f[0]);
+		if(ep->ttype == Tiso || ep->ttype == Tctl)
+			error("ctl ignored for this endpoint type");
+		ep->tmout = strtoul(cb->f[1], nil, 0);
+		if(ep->tmout != 0 && ep->tmout < Xfertmout)
+			ep->tmout = Xfertmout;
+		break;
+	case CMpreset:
+		deprint("usb epctl %s\n", cb->f[0]);
+		if(ep->ttype != Tctl)
+			error("not a control endpoint");
+		if(ep->dev->state != Denabled)
+			error("forbidden on devices not enabled");
+		ep->dev->state = Dreset;
+		break;
+	default:
+		panic("usb: unknown epctl %d", ct->index);
+	}
+	free(cb);
+	poperror();
+	return n;
+}
+
+static long
+usbctl(void *a, long n)
+{
+	Cmdtab *ct;
+	Cmdbuf *cb;
+	Ep *ep;
+	int i;
+
+	cb = parsecmd(a, n);
+	if(waserror()){
+		free(cb);
+		nexterror();
+	}
+	ct = lookupcmd(cb, usbctls, nelem(usbctls));
+	dprint("usb ctl %s\n", cb->f[0]);
+	switch(ct->index){
+	case CMdebug:
+		if(strcmp(cb->f[1], "on") == 0)
+			debug = 1;
+		else if(strcmp(cb->f[1], "off") == 0)
+			debug = 0;
+		else
+			debug = strtol(cb->f[1], nil, 0);
+		print("usb: debug %d\n", debug);
+		for(i = 0; i < epmax; i++)
+			if((ep = getep(i)) != nil){
+				ep->hp->debug(ep->hp, debug);
+				putep(ep);
+			}
+		break;
+	case CMdump:
+		dumpeps();
+		break;
+	}
+	free(cb);
+	poperror();
+	return n;
+}
+
+static long
+ctlwrite(Chan *c, void *a, long n)
+{
+	int q;
+	Ep *ep;
+
+	q = QID(c->qid);
+	if(q == Qctl)
+		return usbctl(a, n);
+
+	ep = getep(qid2epidx(q));
+	if(ep == nil)
+		error(Eio);
+	if(waserror()){
+		putep(ep);
+		nexterror();
+	}
+	if(ep->dev->state == Ddetach)
+		error(Edetach);
+	if(isqtype(q, Qepctl) && c->aux != nil){
+		/* Be sure we don't keep a cloned ep name */
+		free(c->aux);
+		c->aux = nil;
+		error("read, not write, expected");
+	}
+	n = epctl(ep, c, a, n);
+	putep(ep);
+	poperror();
+	return n;
+}
+
+static long
+usbwrite(Chan *c, void *a, long n, vlong off)
+{
+	int nr, q;
+	Ep *ep;
+
+	if(c->qid.type == QTDIR)
+		error(Eisdir);
+
+	q = QID(c->qid);
+
+	if(q == Qctl || isqtype(q, Qepctl))
+		return ctlwrite(c, a, n);
+
+	ep = getep(qid2epidx(q));
+	if(ep == nil)
+		error(Eio);
+	if(waserror()){
+		putep(ep);
+		nexterror();
+	}
+	if(ep->dev->state == Ddetach)
+		error(Edetach);
+	if(ep->mode == OREAD || ep->inuse == 0)
+		error(Ebadusefd);
+
+	switch(ep->ttype){
+	case Tnone:
+		error("endpoint not configured");
+	case Tctl:
+		nr = rhubwrite(ep, a, n);
+		if(nr >= 0){
+			n = nr;
+			break;
+		}
+		/* else fall through */
+	default:
+		ddeprint("\nusbwrite q %#x fid %d cnt %ld off %lld\n",
+			q, c->fid, n, off);
+		ep->hp->epwrite(ep, a, n);
+	}
+	putep(ep);
+	poperror();
+	return n;
+}
+
+Block*
+usbbread(Chan *c, long n, ulong offset)
+{
+	Block *bp;
+
+	bp = allocb(n);
+	if(bp == 0)
+		error(Enomem);
+	if(waserror()) {
+		freeb(bp);
+		nexterror();
+	}
+	bp->wp += usbread(c, bp->wp, n, offset);
+	poperror();
+	return bp;
+}
+
+long
+usbbwrite(Chan *c, Block *bp, ulong offset)
+{
+	long n;
+
+	if(waserror()) {
+		freeb(bp);
+		nexterror();
+	}
+	n = usbwrite(c, bp->rp, BLEN(bp), offset);
+	poperror();
+	freeb(bp);
+
+	return n;
+}
+
+void
+usbshutdown(void)
+{
+	Hci *hp;
+	int i;
+
+	for(i = 0; i < Nhcis; i++){
+		hp = hcis[i];
+		if(hp == nil)
+			continue;
+		if(hp->shutdown == nil)
+			print("#u: no shutdown function for %s\n", hp->type);
+		else
+			hp->shutdown(hp);
+	}
+}
+
+Dev usbdevtab = {
+	L'u',
+	"usb",
+
+	usbreset,
+	usbinit,
+	usbshutdown,
+	usbattach,
+	usbwalk,
+	usbstat,
+	usbopen,
+	devcreate,
+	usbclose,
+	usbread,
+	usbbread,
+	usbwrite,
+	usbbwrite,
+	devremove,
+	devwstat,
+};

+ 221 - 0
sys/src/9/bcm/dma.c

@@ -0,0 +1,221 @@
+/*
+ * bcm2835 dma controller
+ *
+ * simplest to use only channels 0-6
+ *	channels 7-14 have reduced functionality
+ *	channel 15 is at a weird address
+ *	channels 0 and 15 have an "external 128 bit 8 word read FIFO"
+ *	  for memory to memory transfers
+ *
+ * Experiments show that only channels 2-5,11-12 work with mmc
+ */
+
+#include "u.h"
+#include "../port/lib.h"
+#include "../port/error.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+#include "io.h"
+
+#define DMAREGS	(VIRTIO+0x7000)
+
+#define DBG	if(Dbg)
+
+enum {
+	Nchan		= 7,		/* number of dma channels */
+	Regsize		= 0x100,	/* size of regs for each chan */
+	Cbalign		= 32,		/* control block byte alignment */
+	Dbg		= 0,
+	
+	/* registers for each dma controller */
+	Cs		= 0x00>>2,
+	Conblkad	= 0x04>>2,
+	Ti		= 0x08>>2,
+	Sourcead	= 0x0c>>2,
+	Destad		= 0x10>>2,
+	Txfrlen		= 0x14>>2,
+	Stride		= 0x18>>2,
+	Nextconbk	= 0x1c>>2,
+	Debug		= 0x20>>2,
+
+	/* collective registers */
+	Intstatus	= 0xfe0>>2,
+	Enable		= 0xff0>>2,
+
+	/* Cs */
+	Reset		= 1<<31,
+	Abort		= 1<<30,
+	Error		= 1<<8,
+	Waitwrite	= 1<<6,
+	Waitdreq	= 1<<5,
+	Paused		= 1<<4,
+	Dreq		= 1<<3,
+	Int		= 1<<2,
+	End		= 1<<1,
+	Active		= 1<<0,
+
+	/* Ti */
+	Permapshift= 16,
+	Srcignore	= 1<<11,
+	Srcdreq		= 1<<10,
+	Srcwidth128	= 1<<9,
+	Srcinc		= 1<<8,
+	Destignore	= 1<<7,
+	Destdreq	= 1<<6,
+	Destwidth128	= 1<<5,
+	Destinc		= 1<<4,
+	Waitresp	= 1<<3,
+	Tdmode		= 1<<1,
+	Inten		= 1<<0,
+
+	/* Debug */
+	Lite		= 1<<28,
+	Clrerrors	= 7<<0,
+};
+
+typedef struct Ctlr Ctlr;
+typedef struct Cb Cb;
+
+struct Ctlr {
+	u32int	*regs;
+	Cb	*cb;
+	Rendez	r;
+	int	dmadone;
+};
+
+struct Cb {
+	u32int	ti;
+	u32int	sourcead;
+	u32int	destad;
+	u32int	txfrlen;
+	u32int	stride;
+	u32int	nextconbk;
+	u32int	reserved[2];
+};
+
+static Ctlr dma[Nchan];
+static u32int *dmaregs = (u32int*)DMAREGS;
+
+static void
+dump(char *msg, uchar *p, int n)
+{
+	print("%s", msg);
+	while(n-- > 0)
+		print(" %2.2x", *p++);
+	print("\n");
+}
+
+static void
+dumpdregs(char *msg, u32int *r)
+{
+	int i;
+
+	print("%s: %#p =", msg, r);
+	for(i = 0; i < 9; i++)
+		print(" %8.8uX", r[i]);
+	print("\n");
+}
+
+static int
+dmadone(void *a)
+{
+	return ((Ctlr*)a)->dmadone;
+}
+
+static void
+dmainterrupt(Ureg*, void *a)
+{
+	Ctlr *ctlr;
+
+	ctlr = a;
+	ctlr->regs[Cs] = Int;
+	ctlr->dmadone = 1;
+	wakeup(&ctlr->r);
+}
+
+void
+dmastart(int chan, int dev, int dir, void *src, void *dst, int len)
+{
+	Ctlr *ctlr;
+	Cb *cb;
+	int ti;
+
+	ctlr = &dma[chan];
+	if(ctlr->regs == nil){
+		ctlr->regs = (u32int*)(DMAREGS + chan*Regsize);
+		ctlr->cb = xspanalloc(sizeof(Cb), Cbalign, 0);
+		assert(ctlr->cb != nil);
+		dmaregs[Enable] |= 1 << chan;
+		ctlr->regs[Cs] = Reset;
+		while(ctlr->regs[Cs] & Reset)
+			;
+		intrenable(IRQDMA(chan), dmainterrupt, ctlr, 0, "dma");
+	}
+	cb = ctlr->cb;
+	ti = 0;
+	switch(dir){
+	case DmaD2M:
+		cachedwbinvse(dst, len);
+		ti = Srcdreq | Destinc;
+		cb->sourcead = DMAIO(src);
+		cb->destad = DMAADDR(dst);
+		break;
+	case DmaM2D:
+		cachedwbse(src, len);
+		ti = Destdreq | Srcinc;
+		cb->sourcead = DMAADDR(src);
+		cb->destad = DMAIO(dst);
+		break;
+	case DmaM2M:
+		cachedwbse(src, len);
+		cachedwbinvse(dst, len);
+		ti = Srcinc | Destinc;
+		cb->sourcead = DMAADDR(src);
+		cb->destad = DMAADDR(dst);
+		break;
+	}
+	cb->ti = ti | dev << Permapshift | Inten;
+	cb->txfrlen = len;
+	cb->stride = 0;
+	cb->nextconbk = 0;
+	cachedwbse(cb, sizeof(Cb));
+	ctlr->regs[Cs] = 0;
+	microdelay(1);
+	ctlr->regs[Conblkad] = DMAADDR(cb);
+	DBG print("dma start: %ux %ux %ux %ux %ux %ux\n",
+		cb->ti, cb->sourcead, cb->destad, cb->txfrlen,
+		cb->stride, cb->nextconbk);
+	DBG print("intstatus %ux\n", dmaregs[Intstatus]);
+	dmaregs[Intstatus] = 0;
+	ctlr->regs[Cs] = Int;
+	microdelay(1);
+	coherence();
+	DBG dumpdregs("before Active", ctlr->regs);
+	ctlr->regs[Cs] = Active;
+	DBG dumpdregs("after Active", ctlr->regs);
+}
+
+int
+dmawait(int chan)
+{
+	Ctlr *ctlr;
+	u32int *r;
+	int s;
+
+	ctlr = &dma[chan];
+	tsleep(&ctlr->r, dmadone, ctlr, 3000);
+	ctlr->dmadone = 0;
+	r = ctlr->regs;
+	DBG dumpdregs("after sleep", r);
+	s = r[Cs];
+	if((s & (Active|End|Error)) != End){
+		print("dma chan %d %s Cs %ux Debug %ux\n", chan,
+			(s&End)? "error" : "timeout", s, r[Debug]);
+		r[Cs] = Reset;
+		r[Debug] = Clrerrors;
+		return -1;
+	}
+	r[Cs] = Int|End;
+	return 0;
+}

+ 510 - 0
sys/src/9/bcm/dwcotg.h

@@ -0,0 +1,510 @@
+/*
+ * USB host driver for BCM2835
+ *	Synopsis DesignWare Core USB 2.0 OTG controller
+ *
+ * Device register definitions
+ */
+
+typedef unsigned int Reg;
+typedef struct Dwcregs Dwcregs;
+typedef struct Hostchan Hostchan;
+
+enum {
+	Maxchans	= 16,	/* actual number of channels in ghwcfg2 */
+};
+
+struct Dwcregs {
+	/* Core global registers 0x000-0x140 */
+	Reg	gotgctl;	/* OTG Control and Status */
+	Reg	gotgint;	/* OTG Interrupt */
+	Reg	gahbcfg;	/* Core AHB Configuration */
+	Reg	gusbcfg;	/* Core USB Configuration */
+	Reg	grstctl;	/* Core Reset */
+	Reg	gintsts;	/* Core Interrupt */
+	Reg	gintmsk;	/* Core Interrupt Mask */
+	Reg	grxstsr;	/* Receive Status Queue Read (RO) */
+	Reg	grxstsp;	/* Receive Status Queue Read & POP (RO) */
+	Reg	grxfsiz;	/* Receive FIFO Size */
+	Reg	gnptxfsiz;	/* Non Periodic Transmit FIFO Size */
+	Reg	gnptxsts;	/* Non Periodic Transmit FIFO/Queue Status (RO) */
+	Reg	gi2cctl;	/* I2C Access */
+	Reg	gpvndctl;	/* PHY Vendor Control */
+	Reg	ggpio;		/* General Purpose Input/Output */
+	Reg	guid;		/* User ID */
+	Reg	gsnpsid;	/* Synopsys ID (RO) */
+	Reg	ghwcfg1;	/* User HW Config1 (RO) (DEVICE) */
+	Reg	ghwcfg2;	/* User HW Config2 (RO) */
+	Reg	ghwcfg3;	/* User HW Config3 (RO) */
+	Reg	ghwcfg4;	/* User HW Config4 (RO)*/
+	Reg	glpmcfg;	/* Core LPM Configuration */
+	Reg	gpwrdn;		/* Global PowerDn */
+	Reg	gdfifocfg;	/* Global DFIFO SW Config (DEVICE?) */
+	Reg	adpctl;		/* ADP Control */
+	Reg	reserved0[39];
+	Reg	hptxfsiz;	/* Host Periodic Transmit FIFO Size */
+	Reg	dtxfsiz[15];	/* Device Periodic Transmit FIFOs (DEVICE) */
+	char	pad0[0x400-0x140];
+
+	/* Host global registers 0x400-0x420 */
+	Reg	hcfg;		/* Configuration */
+	Reg	hfir;		/* Frame Interval */
+	Reg	hfnum;		/* Frame Number / Frame Remaining (RO) */
+	Reg	reserved1;
+	Reg	hptxsts;	/* Periodic Transmit FIFO / Queue Status */
+	Reg	haint;		/* All Channels Interrupt */
+	Reg	haintmsk;	/* All Channels Interrupt Mask */
+	Reg	hflbaddr;	/* Frame List Base Address */
+	char	pad1[0x440-0x420];
+
+	/* Host port register 0x440 */
+	Reg	hport0;		/* Host Port 0 Control and Status */
+	char	pad2[0x500-0x444];
+
+	/* Host channel specific registers 0x500-0x700 */
+	struct	Hostchan {
+		Reg	hcchar;	/* Characteristic */
+		Reg	hcsplt;	/* Split Control */
+		Reg	hcint;	/* Interrupt */
+		Reg	hcintmsk; /* Interrupt Mask */
+		Reg	hctsiz;	/* Transfer Size */
+		Reg	hcdma;	/* DMA Address */
+		Reg	reserved;
+		Reg	hcdmab;	/* DMA Buffer Address */
+	} hchan[Maxchans];
+	char	pad3[0xE00-0x700];
+
+	/* Power & clock gating control register 0xE00 */
+	Reg	pcgcctl;
+};
+
+enum {
+	/* gotgctl */
+	Sesreqscs	= 1<<0,
+	Sesreq		= 1<<1,
+	Vbvalidoven	= 1<<2,
+	Vbvalidovval	= 1<<3,
+	Avalidoven	= 1<<4,
+	Avalidovval	= 1<<5,
+	Bvalidoven	= 1<<6,
+	Bvalidovval	= 1<<7,
+	Hstnegscs	= 1<<8,
+	Hnpreq		= 1<<9,
+	Hstsethnpen	= 1<<10,
+	Devhnpen	= 1<<11,
+	Conidsts	= 1<<16,
+	Dbnctime	= 1<<17,
+	Asesvld		= 1<<18,
+	Bsesvld		= 1<<19,
+	Otgver		= 1<<20,
+	Multvalidbc	= 0x1F<<22,
+	Chirpen		= 1<<27,
+
+	/* gotgint */
+	Sesenddet	= 1<<2,
+	Sesreqsucstschng= 1<<8,
+	Hstnegsucstschng= 1<<9,
+	Hstnegdet	= 1<<17,
+	Adevtoutchng	= 1<<18,
+	Debdone		= 1<<19,
+	Mvic		= 1<<20,
+
+	/* gahbcfg */
+	Glblintrmsk	= 1<<0,
+	/* bits 1:4 redefined for BCM2835 */
+	Axiburstlen	= 0x3<<1,
+		BURST1		= 3<<1,
+		BURST2		= 2<<1,
+		BURST3		= 1<<1,
+		BURST4		= 0<<1,
+	Axiwaitwrites	= 1<<4,
+	Dmaenable	= 1<<5,
+	Nptxfemplvl	= 1<<7,
+		NPTX_HALFEMPTY	= 0<<7,
+		NPTX_EMPTY	= 1<<7,
+	Ptxfemplvl	= 1<<8,
+		PTX_HALFEMPTY	= 0<<8,
+		PTX_EMPTY	= 1<<8,
+	Remmemsupp	= 1<<21,
+	Notialldmawrit	= 1<<22,
+	Ahbsingle	= 1<<23,
+
+	/* gusbcfg */
+	Toutcal		= 0x7<<0,
+	Phyif		= 1<<3,
+	Ulpi_utmi_sel	= 1<<4,
+	Fsintf		= 1<<5,
+		FsUnidir	= 0<<5,
+		FsBidir		= 1<<5,
+	Physel		= 1<<6,
+		PhyHighspeed	= 0<<6,
+		PhyFullspeed	= 1<<6,
+	Ddrsel		= 1<<7,
+	Srpcap		= 1<<8,
+	Hnpcap		= 1<<9,
+	Usbtrdtim	= 0xf<<10,
+		OUsbtrdtim		= 10,
+	Phylpwrclksel	= 1<<15,
+	Otgutmifssel	= 1<<16,
+	Ulpi_fsls	= 1<<17,
+	Ulpi_auto_res	= 1<<18,
+	Ulpi_clk_sus_m	= 1<<19,
+	Ulpi_ext_vbus_drv= 1<<20,
+	Ulpi_int_vbus_indicator= 1<<21,
+	Term_sel_dl_pulse= 1<<22,
+	Indicator_complement= 1<<23,
+	Indicator_pass_through= 1<<24,
+	Ulpi_int_prot_dis= 1<<25,
+	Ic_usb_cap	= 1<<26,
+	Ic_traffic_pull_remove= 1<<27,
+	Tx_end_delay	= 1<<28,
+	Force_host_mode	= 1<<29,
+	Force_dev_mode	= 1<<30,
+
+	/* grstctl */
+	Csftrst		= 1<<0,
+	Hsftrst		= 1<<1,
+	Hstfrm		= 1<<2,
+	Intknqflsh	= 1<<3,
+	Rxfflsh		= 1<<4,
+	Txfflsh		= 1<<5,
+	Txfnum		= 0x1f<<6,
+		TXF_ALL		= 0x10<<6,
+	Dmareq		= 1<<30,
+	Ahbidle		= 1<<31,
+
+	/* gintsts, gintmsk */
+	Curmode		= 1<<0,
+		HOSTMODE	= 1<<0,
+		DEVMODE		= 0<<0,
+	Modemismatch	= 1<<1,
+	Otgintr		= 1<<2,
+	Sofintr		= 1<<3,
+	Rxstsqlvl	= 1<<4,
+	Nptxfempty	= 1<<5,
+	Ginnakeff	= 1<<6,
+	Goutnakeff	= 1<<7,
+	Ulpickint	= 1<<8,
+	I2cintr		= 1<<9,
+	Erlysuspend	= 1<<10,
+	Usbsuspend	= 1<<11,
+	Usbreset	= 1<<12,
+	Enumdone	= 1<<13,
+	Isooutdrop	= 1<<14,
+	Eopframe	= 1<<15,
+	Restoredone	= 1<<16,
+	Epmismatch	= 1<<17,
+	Inepintr	= 1<<18,
+	Outepintr	= 1<<19,
+	Incomplisoin	= 1<<20,
+	Incomplisoout	= 1<<21,
+	Fetsusp		= 1<<22,
+	Resetdet	= 1<<23,
+	Portintr	= 1<<24,
+	Hcintr		= 1<<25,
+	Ptxfempty	= 1<<26,
+	Lpmtranrcvd	= 1<<27,
+	Conidstschng	= 1<<28,
+	Disconnect	= 1<<29,
+	Sessreqintr	= 1<<30,
+	Wkupintr	= 1<<31,
+
+	/* grxsts[rp] */
+	Chnum		= 0xf<<0,
+	Bcnt		= 0x7ff<<4,
+	Dpid		= 0x3<<15,
+	Pktsts		= 0xf<<17,
+		PKTSTS_IN		= 2<<17,
+		PKTSTS_IN_XFER_COMP	= 3<<17,
+		PKTSTS_DATA_TOGGLE_ERR	= 5<<17,
+		PKTSTS_CH_HALTED	= 7<<17,
+
+	/* hptxfsiz, gnptxfsiz */
+	Startaddr	= 0xffff<<0,
+	Depth		= 0xffff<<16,
+		ODepth		= 16,
+
+	/* gnptxsts */
+	Nptxfspcavail	= 0xffff<<0,
+	Nptxqspcavail	= 0xff<<16,
+	Nptxqtop_terminate= 1<<24,
+	Nptxqtop_token	= 0x3<<25,
+	Nptxqtop_chnep	= 0xf<<27,
+
+	/* gpvndctl */
+	Regdata		= 0xff<<0,
+	Vctrl		= 0xff<<8,
+	Regaddr16_21	= 0x3f<<16,
+	Regwr		= 1<<22,
+	Newregreq	= 1<<25,
+	Vstsbsy		= 1<<26,
+	Vstsdone	= 1<<27,
+	Disulpidrvr	= 1<<31,
+
+	/* ggpio */
+	Gpi		= 0xffff<<0,
+	Gpo		= 0xffff<<16,
+
+	/* ghwcfg2 */
+	Op_mode		= 0x7<<0,
+		HNP_SRP_CAPABLE_OTG	= 0<<0,
+		SRP_ONLY_CAPABLE_OTG	= 1<<0,
+		NO_HNP_SRP_CAPABLE	= 2<<0,
+		SRP_CAPABLE_DEVICE	= 3<<0,
+		NO_SRP_CAPABLE_DEVICE	= 4<<0,
+		SRP_CAPABLE_HOST	= 5<<0,
+		NO_SRP_CAPABLE_HOST	= 6<<0,
+	Architecture	= 0x3<<3,
+		SLAVE_ONLY		= 0<<3,
+		EXT_DMA			= 1<<3,
+		INT_DMA			= 2<<3,
+	Point2point	= 1<<5,
+	Hs_phy_type	= 0x3<<6,
+		PHY_NOT_SUPPORTED	= 0<<6,
+		PHY_UTMI		= 1<<6,
+		PHY_ULPI		= 2<<6,
+		PHY_UTMI_ULPI		= 3<<6,
+	Fs_phy_type	= 0x3<<8,
+	Num_dev_ep	= 0xf<<10,
+	Num_host_chan	= 0xf<<14,
+		ONum_host_chan		= 14,
+	Perio_ep_supported= 1<<18,
+	Dynamic_fifo	= 1<<19,
+	Nonperio_tx_q_depth= 0x3<<22,
+	Host_perio_tx_q_depth= 0x3<<24,
+	Dev_token_q_depth= 0x1f<<26,
+	Otg_enable_ic_usb= 1<<31,
+
+	/* ghwcfg3 */
+	Xfer_size_cntr_width	= 0xf<<0,
+	Packet_size_cntr_width	= 0x7<<4,
+	Otg_func		= 1<<7,
+	I2c			= 1<<8,
+	Vendor_ctrl_if		= 1<<9,
+	Optional_features	= 1<<10,
+	Synch_reset_type	= 1<<11,
+	Adp_supp		= 1<<12,
+	Otg_enable_hsic		= 1<<13,
+	Bc_support		= 1<<14,
+	Otg_lpm_en		= 1<<15,
+	Dfifo_depth		= 0xffff<<16,
+		ODfifo_depth		= 16,
+
+	/* ghwcfg4 */
+	Num_dev_perio_in_ep	= 0xf<<0,
+	Power_optimiz		= 1<<4,
+	Min_ahb_freq		= 1<<5,
+	Hiber			= 1<<6,
+	Xhiber			= 1<<7,
+	Utmi_phy_data_width	= 0x3<<14,
+	Num_dev_mode_ctrl_ep	= 0xf<<16,
+	Iddig_filt_en		= 1<<20,
+	Vbus_valid_filt_en	= 1<<21,
+	A_valid_filt_en		= 1<<22,
+	B_valid_filt_en		= 1<<23,
+	Session_end_filt_en	= 1<<24,
+	Ded_fifo_en		= 1<<25,
+	Num_in_eps		= 0xf<<26,
+	Desc_dma		= 1<<30,
+	Desc_dma_dyn		= 1<<31,
+
+	/* glpmcfg */
+	Lpm_cap_en	= 1<<0,
+	Appl_resp	= 1<<1,
+	Hird		= 0xf<<2,
+	Rem_wkup_en	= 1<<6,
+	En_utmi_sleep	= 1<<7,
+	Hird_thres	= 0x1f<<8,
+	Lpm_resp	= 0x3<<13,
+	Prt_sleep_sts	= 1<<15,
+	Sleep_state_resumeok= 1<<16,
+	Lpm_chan_index	= 0xf<<17,
+	Retry_count	= 0x7<<21,
+	Send_lpm	= 1<<24,
+	Retry_count_sts	= 0x7<<25,
+	Hsic_connect	= 1<<30,
+	Inv_sel_hsic	= 1<<31,
+
+	/* gpwrdn */
+	Pmuintsel	= 1<<0,
+	Pmuactv		= 1<<1,
+	Restore		= 1<<2,
+	Pwrdnclmp	= 1<<3,
+	Pwrdnrstn	= 1<<4,
+	Pwrdnswtch	= 1<<5,
+	Dis_vbus	= 1<<6,
+	Lnstschng	= 1<<7,
+	Lnstchng_msk	= 1<<8,
+	Rst_det		= 1<<9,
+	Rst_det_msk	= 1<<10,
+	Disconn_det	= 1<<11,
+	Disconn_det_msk	= 1<<12,
+	Connect_det	= 1<<13,
+	Connect_det_msk	= 1<<14,
+	Srp_det		= 1<<15,
+	Srp_det_msk	= 1<<16,
+	Sts_chngint	= 1<<17,
+	Sts_chngint_msk	= 1<<18,
+	Linestate	= 0x3<<19,
+	Idsts		= 1<<21,
+	Bsessvld	= 1<<22,
+	Adp_int		= 1<<23,
+	Mult_val_id_bc	= 0x1f<<24,
+
+	/* gdfifocfg */
+	Gdfifocfg	= 0xffff<<0,
+	Epinfobase	= 0xffff<<16,
+
+	/* adpctl */
+	Prb_dschg	= 0x3<<0,
+	Prb_delta	= 0x3<<2,
+	Prb_per		= 0x3<<4,
+	Rtim		= 0x7ff<<6,
+	Enaprb		= 1<<17,
+	Enasns		= 1<<18,
+	Adpres		= 1<<19,
+	Adpen		= 1<<20,
+	Adp_prb_int	= 1<<21,
+	Adp_sns_int	= 1<<22,
+	Adp_tmout_int	= 1<<23,
+	Adp_prb_int_msk	= 1<<24,
+	Adp_sns_int_msk	= 1<<25,
+	Adp_tmout_int_msk= 1<<26,
+	Ar		= 0x3<<27,
+
+	/* hcfg */
+	Fslspclksel	= 0x3<<0,
+		HCFG_30_60_MHZ	= 0<<0,
+		HCFG_48_MHZ	= 1<<0,
+		HCFG_6_MHZ	= 2<<0,
+	Fslssupp	= 1<<2,
+	Ena32khzs	= 1<<7,
+	Resvalid	= 0xff<<8,
+	Descdma		= 1<<23,
+	Frlisten	= 0x3<<24,
+	Modechtimen	= 1<<31,
+
+	/* hfir */
+	Frint		= 0xffff<<0,
+	Hfirrldctrl	= 1<<16,
+
+	/* hfnum */
+	Frnum		= 0xffff<<0,
+		MAX_FRNUM 	= 0x3FFF<<0,
+	Frrem		= 0xffff<<16,
+
+	/* hptxsts */
+	Ptxfspcavail	= 0xffff<<0,
+	Ptxqspcavail	= 0xff<<16,
+	Ptxqtop_terminate= 1<<24,
+	Ptxqtop_token	= 0x3<<25,
+	Ptxqtop_chnum	= 0xf<<27,
+	Ptxqtop_odd	= 1<<31,
+
+	/* haint, haintmsk */
+#define CHANINT(n)	(1<<(n))
+
+	/* hport0 */
+	Prtconnsts	= 1<<0,		/* connect status (RO) */
+	Prtconndet	= 1<<1,		/* connect detected R/W1C) */
+	Prtena		= 1<<2,		/* enable (R/W1C) */
+	Prtenchng	= 1<<3,		/* enable/disable change (R/W1C) */
+	Prtovrcurract	= 1<<4,		/* overcurrent active (RO) */
+	Prtovrcurrchng	= 1<<5,		/* overcurrent change (R/W1C) */
+	Prtres		= 1<<6,		/* resume */
+	Prtsusp		= 1<<7,		/* suspend */
+	Prtrst		= 1<<8,		/* reset */
+	Prtlnsts	= 0x3<<10,	/* line state {D+,D-} (RO) */
+	Prtpwr		= 1<<12,	/* power on */
+	Prttstctl	= 0xf<<13,	/* test */
+	Prtspd		= 0x3<<17,	/* speed (RO) */
+		HIGHSPEED	= 0<<17,
+		FULLSPEED	= 1<<17,
+		LOWSPEED	= 2<<17,
+
+	/* hcchar */
+	Mps		= 0x7ff<<0,	/* endpoint maximum packet size */
+	Epnum		= 0xf<<11,	/* endpoint number */
+		OEpnum		= 11,
+	Epdir		= 1<<15,	/* endpoint direction */
+		Epout		= 0<<15,
+		Epin		= 1<<15,
+	Lspddev		= 1<<17,	/* device is lowspeed */
+	Eptype		= 0x3<<18,	/* endpoint type */
+		Epctl		= 0<<18,
+		Episo		= 1<<18,
+		Epbulk		= 2<<18,
+		Epintr		= 3<<18,
+	Multicnt	= 0x3<<20,	/* transactions per μframe or retries */
+					/* per periodic split */
+		OMulticnt	= 20,
+	Devaddr		= 0x7f<<22,	/* device address */
+		ODevaddr	= 22,
+	Oddfrm		= 1<<29,	/* xfer in odd frame (iso/interrupt) */
+	Chdis		= 1<<30,	/* channel disable (write 1 only) */
+	Chen		= 1<<31,	/* channel enable (write 1 only) */
+
+	/* hcsplt */
+	Prtaddr		= 0x7f<<0,	/* port address of recipient */
+					/* transaction translator */
+	Hubaddr		= 0x7f<<7,	/* dev address of transaction */
+					/* translator's hub */
+		OHubaddr	= 7,
+	Xactpos		= 0x3<<14,	/* payload's position within transaction */
+		POS_MID		= 0<<14,		
+		POS_END		= 1<<14,
+		POS_BEGIN	= 2<<14,
+		POS_ALL		= 3<<14, /* all of data (<= 188 bytes) */
+	Compsplt	= 1<<16,	/* do complete split */
+	Spltena		= 1<<31,	/* channel enabled to do splits */
+
+	/* hcint, hcintmsk */
+	Xfercomp	= 1<<0,		/* transfer completed without error */
+	Chhltd		= 1<<1,		/* channel halted */
+	Ahberr		= 1<<2,		/* AHB dma error */
+	Stall		= 1<<3,
+	Nak		= 1<<4,
+	Ack		= 1<<5,
+	Nyet		= 1<<6,
+	Xacterr		= 1<<7,	/* transaction error (crc, t/o, bit stuff, eop) */
+	Bblerr		= 1<<8,
+	Frmovrun	= 1<<9,
+	Datatglerr	= 1<<10,
+	Bna		= 1<<11,
+	Xcs_xact	= 1<<12,
+	Frm_list_roll	= 1<<13,
+
+	/* hctsiz */
+	Xfersize	= 0x7ffff<<0,	/* expected total bytes */
+	Pktcnt		= 0x3ff<<19,	/* expected number of packets */
+		OPktcnt		= 19,
+	Pid		= 0x3<<29,	/* packet id for initial transaction */
+		DATA0		= 0<<29,
+		DATA1		= 2<<29,	/* sic */
+		DATA2		= 1<<29,	/* sic */
+		MDATA		= 3<<29,	/* (non-ctl ep) */
+		SETUP		= 3<<29,	/* (ctl ep) */
+	Dopng		= 1<<31,	/* do PING protocol */
+
+	/* pcgcctl */
+	Stoppclk		= 1<<0,
+	Gatehclk		= 1<<1,
+	Pwrclmp			= 1<<2,
+	Rstpdwnmodule		= 1<<3,
+	Enbl_sleep_gating	= 1<<5,
+	Phy_in_sleep		= 1<<6,
+	Deep_sleep		= 1<<7,
+	Resetaftsusp		= 1<<8,
+	Restoremode		= 1<<9,
+	Enbl_extnd_hiber	= 1<<10,
+	Extnd_hiber_pwrclmp	= 1<<11,
+	Extnd_hiber_switch	= 1<<12,
+	Ess_reg_restored	= 1<<13,
+	Prt_clk_sel		= 0x3<<14,
+	Port_power		= 1<<16,
+	Max_xcvrselect		= 0x3<<17,
+	Max_termsel		= 1<<19,
+	Mac_dev_addr		= 0x7f<<20,
+	P2hd_dev_enum_spd	= 0x3<<27,
+	P2hd_prt_spd		= 0x3<<29,
+	If_dev_mode		= 1<<31,
+};

+ 426 - 0
sys/src/9/bcm/emmc.c

@@ -0,0 +1,426 @@
+/*
+ * bcm2835 external mass media controller (mmc / sd host interface)
+ *
+ * Copyright © 2012 Richard Miller <r.miller@acm.org>
+ */
+
+#include "u.h"
+#include "../port/lib.h"
+#include "../port/error.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+#include "io.h"
+#include "../port/sd.h"
+
+#define EMMCREGS	(VIRTIO+0x300000)
+
+enum {
+	Extfreq		= 100*Mhz,	/* guess external clock frequency if */
+					/* not available from vcore */
+	Initfreq	= 400000,	/* initialisation frequency for MMC */
+	SDfreq		= 25*Mhz,	/* standard SD frequency */
+	DTO		= 14,		/* data timeout exponent (guesswork) */
+
+	MMCSelect	= 7,		/* mmc/sd card select command */
+	Setbuswidth	= 6,		/* mmc/sd set bus width command */
+};
+
+enum {
+	/* Controller registers */
+	Arg2			= 0x00>>2,
+	Blksizecnt		= 0x04>>2,
+	Arg1			= 0x08>>2,
+	Cmdtm			= 0x0c>>2,
+	Resp0			= 0x10>>2,
+	Resp1			= 0x14>>2,
+	Resp2			= 0x18>>2,
+	Resp3			= 0x1c>>2,
+	Data			= 0x20>>2,
+	Status			= 0x24>>2,
+	Control0		= 0x28>>2,
+	Control1		= 0x2c>>2,
+	Interrupt		= 0x30>>2,
+	Irptmask		= 0x34>>2,
+	Irpten			= 0x38>>2,
+	Control2		= 0x3c>>2,
+	Forceirpt		= 0x50>>2,
+	Boottimeout		= 0x70>>2,
+	Dbgsel			= 0x74>>2,
+	Exrdfifocfg		= 0x80>>2,
+	Exrdfifoen		= 0x84>>2,
+	Tunestep		= 0x88>>2,
+	Tunestepsstd		= 0x8c>>2,
+	Tunestepsddr		= 0x90>>2,
+	Spiintspt		= 0xf0>>2,
+	Slotisrver		= 0xfc>>2,
+
+	/* Control0 */
+	Dwidth4			= 1<<1,
+	Dwidth1			= 0<<1,
+
+	/* Control1 */
+	Srstdata		= 1<<26,	/* reset data circuit */
+	Srstcmd			= 1<<25,	/* reset command circuit */
+	Srsthc			= 1<<24,	/* reset complete host controller */
+	Datatoshift		= 16,		/* data timeout unit exponent */
+	Datatomask		= 0xF0000,
+	Clkfreq8shift		= 8,		/* SD clock base divider LSBs */
+	Clkfreq8mask		= 0xFF00,
+	Clkfreqms2shift		= 6,		/* SD clock base divider MSBs */
+	Clkfreqms2mask		= 0xC0,
+	Clkgendiv		= 0<<5,		/* SD clock divided */
+	Clkgenprog		= 1<<5,		/* SD clock programmable */
+	Clken			= 1<<2,		/* SD clock enable */
+	Clkstable		= 1<<1,	
+	Clkintlen		= 1<<0,		/* enable internal EMMC clocks */
+
+	/* Cmdtm */
+	Indexshift		= 24,
+	Suspend			= 1<<22,
+	Resume			= 2<<22,
+	Abort			= 3<<22,
+	Isdata			= 1<<21,
+	Ixchken			= 1<<20,
+	Crcchken		= 1<<19,
+	Respmask		= 3<<16,
+	Respnone		= 0<<16,
+	Resp136			= 1<<16,
+	Resp48			= 2<<16,
+	Resp48busy		= 3<<16,
+	Multiblock		= 1<<5,
+	Host2card		= 0<<4,
+	Card2host		= 1<<4,
+	Autocmd12		= 1<<2,
+	Autocmd23		= 2<<2,
+	Blkcnten		= 1<<1,
+
+	/* Interrupt */
+	Acmderr		= 1<<24,
+	Denderr		= 1<<22,
+	Dcrcerr		= 1<<21,
+	Dtoerr		= 1<<20,
+	Cbaderr		= 1<<19,
+	Cenderr		= 1<<18,
+	Ccrcerr		= 1<<17,
+	Ctoerr		= 1<<16,
+	Err		= 1<<15,
+	Cardintr	= 1<<8,		/* not in Broadcom datasheet */
+	Cardinsert	= 1<<6,		/* not in Broadcom datasheet */
+	Readrdy		= 1<<5,
+	Writerdy	= 1<<4,
+	Datadone	= 1<<1,
+	Cmddone		= 1<<0,
+
+	/* Status */
+	Bufread		= 1<<11,	/* not in Broadcom datasheet */
+	Bufwrite	= 1<<10,	/* not in Broadcom datasheet */
+	Readtrans	= 1<<9,
+	Writetrans	= 1<<8,
+	Datactive	= 1<<2,
+	Datinhibit	= 1<<1,
+	Cmdinhibit	= 1<<0,
+};
+
+int cmdinfo[64] = {
+[0]  Ixchken,
+[2]  Resp136,
+[3]  Resp48 | Ixchken | Crcchken,
+[6]  Resp48 | Ixchken | Crcchken,
+[7]  Resp48busy | Ixchken | Crcchken,
+[8]  Resp48 | Ixchken | Crcchken,
+[9]  Resp136,
+[12] Resp48busy | Ixchken | Crcchken,
+[13] Resp48 | Ixchken | Crcchken,
+[16] Resp48,
+[17] Resp48 | Isdata | Card2host | Ixchken | Crcchken,
+[18] Resp48 | Isdata | Card2host | Multiblock | Blkcnten | Ixchken | Crcchken,
+[24] Resp48 | Isdata | Host2card | Ixchken | Crcchken,
+[25] Resp48 | Isdata | Host2card | Multiblock | Blkcnten | Ixchken | Crcchken,
+[41] Resp48,
+[55] Resp48 | Ixchken | Crcchken,
+};
+
+typedef struct Ctlr Ctlr;
+
+struct Ctlr {
+	Rendez	r;
+	int	datadone;
+	int	fastclock;
+	ulong	extclk;
+};
+
+static Ctlr emmc;
+
+static void mmcinterrupt(Ureg*, void*);
+
+static void
+WR(int reg, u32int val)
+{
+	u32int *r = (u32int*)EMMCREGS;
+
+	if(0)print("WR %2.2ux %ux\n", reg<<2, val);
+	microdelay(emmc.fastclock? 2: 20);
+	r[reg] = val;
+}
+
+static uint
+clkdiv(uint d)
+{
+	uint v;
+
+	assert(d < 1<<10);
+	v = (d << Clkfreq8shift) & Clkfreq8mask;
+	v |= ((d >> 8) << Clkfreqms2shift) & Clkfreqms2mask;
+	return v;
+}
+
+static int
+datadone(void*)
+{
+	return emmc.datadone;
+}
+
+static int
+emmcinit(void)
+{
+	u32int *r;
+	ulong clk;
+	char *s;
+
+	clk = getclkrate(ClkEmmc);
+	s = "";
+	if(clk == 0){
+		s = "Assuming ";
+		clk = Extfreq;
+	}
+	emmc.extclk = clk;
+	print("%seMMC external clock %lud Mhz\n", s, clk/1000000);
+	r = (u32int*)EMMCREGS;
+	if(0)print("emmc control %8.8ux %8.8ux %8.8ux\n",
+		r[Control0], r[Control1], r[Control2]);
+	WR(Control1, Srsthc);
+	delay(10);
+	while(r[Control1] & Srsthc)
+		;
+	return 0;
+}
+
+static int
+emmcinquiry(char *inquiry, int inqlen)
+{
+	u32int *r;
+	uint ver;
+
+	r = (u32int*)EMMCREGS;
+	ver = r[Slotisrver] >> 16;
+	return snprint(inquiry, inqlen,
+		"Arasan eMMC SD Host Controller %2.2x Version %2.2x",
+		ver&0xFF, ver>>8);
+}
+
+static void
+emmcenable(void)
+{
+	u32int *r;
+	int i;
+
+	r = (u32int*)EMMCREGS;
+	WR(Control1, clkdiv(emmc.extclk / Initfreq - 1) | DTO << Datatoshift |
+		Clkgendiv | Clken | Clkintlen);
+	for(i = 0; i < 1000; i++){
+		delay(1);
+		if(r[Control1] & Clkstable)
+			break;
+	}
+	if(i == 1000)
+		print("SD clock won't initialise!\n");
+	WR(Irptmask, ~(Dtoerr|Cardintr));
+	intrenable(IRQmmc, mmcinterrupt, nil, 0, "mmc");
+}
+
+static int
+emmccmd(u32int cmd, u32int arg, u32int *resp)
+{
+	u32int *r;
+	u32int c;
+	int i;
+	ulong now;
+
+	r = (u32int*)EMMCREGS;
+	assert(cmd < nelem(cmdinfo) && cmdinfo[cmd] != 0);
+	c = (cmd << Indexshift) | cmdinfo[cmd];
+	if(r[Status] & Cmdinhibit){
+		print("emmccmd: need to reset Cmdinhibit intr %ux stat %ux\n",
+			r[Interrupt], r[Status]);
+		WR(Control1, r[Control1] | Srstcmd);
+		while(r[Control1] & Srstcmd)
+			;
+		while(r[Status] & Cmdinhibit)
+			;
+	}
+	if((c & Isdata || (c & Respmask) == Resp48busy) &&
+	    r[Status] & Datinhibit){
+		print("emmccmd: need to reset Datinhibit intr %ux stat %ux\n",
+			r[Interrupt], r[Status]);
+		WR(Control1, r[Control1] | Srstdata);
+		while(r[Control1] & Srstdata)
+			;
+		while(r[Status] & Datinhibit)
+			;
+	}
+	WR(Arg1, arg);
+	if((i = r[Interrupt]) != 0){
+		if(i != Cardinsert)
+			print("emmc: before command, intr was %ux\n", i);
+		WR(Interrupt, i);
+	}
+	WR(Cmdtm, c);
+	now = m->ticks;
+	while(((i=r[Interrupt])&(Cmddone|Err)) == 0)
+		if(m->ticks-now > HZ)
+			break;
+	if((i&(Cmddone|Err)) != Cmddone){
+		if((i&~Err) != Ctoerr)
+			print("emmc: cmd %ux error intr %ux stat %ux\n", c, i, r[Status]);
+		WR(Interrupt, i);
+		if(r[Status]&Cmdinhibit){
+			WR(Control1, r[Control1]|Srstcmd);
+			while(r[Control1]&Srstcmd)
+				;
+		}
+		error(Eio);
+	}
+	WR(Interrupt, i & ~(Datadone|Readrdy|Writerdy));
+	switch(c & Respmask){
+	case Resp136:
+		resp[0] = r[Resp0]<<8;
+		resp[1] = r[Resp0]>>24 | r[Resp1]<<8;
+		resp[2] = r[Resp1]>>24 | r[Resp2]<<8;
+		resp[3] = r[Resp2]>>24 | r[Resp3]<<8;
+		break;
+	case Resp48:
+	case Resp48busy:
+		resp[0] = r[Resp0];
+		break;
+	case Respnone:
+		resp[0] = 0;
+		break;
+	}
+	if((c & Respmask) == Resp48busy){
+		WR(Irpten, Datadone|Err);
+		tsleep(&emmc.r, datadone, 0, 3000);
+		i = emmc.datadone;
+		emmc.datadone = 0;
+		WR(Irpten, 0);
+		if((i & Datadone) == 0)
+			print("emmcio: no Datadone after CMD%d\n", cmd);
+		if(i & Err)
+			print("emmcio: CMD%d error interrupt %ux\n",
+				cmd, r[Interrupt]);
+		WR(Interrupt, i);
+	}
+	/*
+	 * Once card is selected, use faster clock
+	 */
+	if(cmd == MMCSelect){
+		delay(10);
+		WR(Control1, clkdiv(emmc.extclk / SDfreq - 1) |
+			DTO << Datatoshift | Clkgendiv | Clken | Clkintlen);
+		for(i = 0; i < 1000; i++){
+			delay(1);
+			if(r[Control1] & Clkstable)
+				break;
+		}
+		delay(10);
+		emmc.fastclock = 1;
+	}
+	/*
+	 * If card bus width changes, change host bus width
+	 */
+	if(cmd == Setbuswidth)
+		switch(arg){
+		case 0:
+			WR(Control0, r[Control0] & ~Dwidth4);
+			break;
+		case 2:
+			WR(Control0, r[Control0] | Dwidth4);
+			break;
+		}
+	return 0;
+}
+
+void
+emmciosetup(int write, void *buf, int bsize, int bcount)
+{
+	USED(write);
+	USED(buf);
+	WR(Blksizecnt, bcount<<16 | bsize);
+}
+
+static void
+emmcio(int write, uchar *buf, int len)
+{
+	u32int *r;
+	int i;
+
+	r = (u32int*)EMMCREGS;
+	assert((len&3) == 0);
+	okay(1);
+	if(waserror()){
+		okay(0);
+		nexterror();
+	}
+	if(write)
+		dmastart(DmaChanEmmc, DmaDevEmmc, DmaM2D,
+			buf, &r[Data], len);
+	else
+		dmastart(DmaChanEmmc, DmaDevEmmc, DmaD2M,
+			&r[Data], buf, len);
+	if(dmawait(DmaChanEmmc) < 0)
+		error(Eio);
+	WR(Irpten, Datadone|Err);
+	tsleep(&emmc.r, datadone, 0, 3000);
+	i = emmc.datadone;
+	emmc.datadone = 0;
+	WR(Irpten, 0);
+	if((i & Datadone) == 0){
+		print("emmcio: %d timeout intr %ux stat %ux\n",
+			write, i, r[Status]);
+		WR(Interrupt, i);
+		error(Eio);
+	}
+	if(i & Err){
+		print("emmcio: %d error intr %ux stat %ux\n",
+			write, r[Interrupt], r[Status]);
+		WR(Interrupt, i);
+		error(Eio);
+	}
+	if(i)
+		WR(Interrupt, i);
+	poperror();
+	okay(0);
+}
+
+static void
+mmcinterrupt(Ureg*, void*)
+{	
+	u32int *r;
+	int i;
+
+	r = (u32int*)EMMCREGS;
+	i = r[Interrupt];
+	r[Interrupt] = i & (Datadone|Err);
+	emmc.datadone = i;
+	wakeup(&emmc.r);
+}
+
+SDio sdio = {
+	"emmc",
+	emmcinit,
+	emmcenable,
+	emmcinquiry,
+	emmccmd,
+	emmciosetup,
+	emmcio,
+};

+ 1 - 0
sys/src/9/bcm/etherif.h

@@ -0,0 +1 @@
+#include "../omap/etherif.h"

+ 428 - 0
sys/src/9/bcm/etherusb.c

@@ -0,0 +1,428 @@
+/*
+ * Kernel proxy for usb ethernet device
+ */
+
+#include "u.h"
+#include "../port/lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+#include "io.h"
+#include "../port/error.h"
+#include "../port/netif.h"
+
+#include "etherif.h"
+#include "../ip/ip.h"
+
+#define	GET4(p)		((p)[3]<<24 | (p)[2]<<16 | (p)[1]<<8  | (p)[0])
+#define	PUT4(p, v)	((p)[0] = (v), (p)[1] = (v)>>8, \
+			 (p)[2] = (v)>>16, (p)[3] = (v)>>24)
+#define	dprint	if(debug) print
+#define ddump	if(0) dump
+
+static int debug = 0;
+
+enum {
+	Bind	= 0,
+	Unbind,
+
+	SmscRxerror	= 0x8000,
+	SmscTxfirst	= 0x2000,
+	SmscTxlast	= 0x1000,
+};
+
+typedef struct Ctlr Ctlr;
+typedef struct Udev Udev;
+
+typedef int (Unpackfn)(Ether*, Block*);
+typedef void (Transmitfn)(Ctlr*, Block*);
+
+struct Ctlr {
+	Ether*	edev;
+	Udev*	udev;
+	Chan*	inchan;
+	Chan*	outchan;
+	char*	buf;
+	int	bufsize;
+	int	maxpkt;
+	uint	rxbuf;
+	uint	rxpkt;
+	uint	txbuf;
+	uint	txpkt;
+	QLock;
+};
+
+struct Udev {
+	char	*name;
+	Unpackfn *unpack;
+	Transmitfn *transmit;
+};
+	
+static Cmdtab cmds[] = {
+	{ Bind,		"bind",		7, },
+	{ Unbind,	"unbind",	0, },
+};
+
+static Unpackfn unpackcdc, unpackasix, unpacksmsc;
+static Transmitfn transmitcdc, transmitasix, transmitsmsc;
+
+static Udev udevtab[] = {
+	{ "cdc",	unpackcdc,	transmitcdc, },
+	{ "asix",	unpackasix,	transmitasix, },
+	{ "smsc",	unpacksmsc,	transmitsmsc, },
+	{ nil },
+};
+
+static void
+dump(int c, Block *b)
+{
+	int s, i;
+
+	s = splhi();
+	print("%c%ld:", c, BLEN(b));
+	for(i = 0; i < 32; i++)
+		print(" %2.2ux", b->rp[i]);
+	print("\n");
+	splx(s);
+}
+
+static int
+unpack(Ether *edev, Block *b, int m)
+{
+	Block *nb;
+	Ctlr *ctlr;
+
+	ctlr = edev->ctlr;
+	ddump('?', b);
+	if(m == BLEN(b)){
+		etheriq(edev, b, 1);
+		ctlr->rxpkt++;
+		return 1;
+	}
+	nb = iallocb(m);
+	if(nb != nil){
+		memmove(nb->wp, b->rp, m);
+		nb->wp += m;
+		etheriq(edev, nb, 1);
+		ctlr->rxpkt++;
+	}else
+		edev->soverflows++;
+	b->rp += m;
+	return 0;
+}
+
+static int
+unpackcdc(Ether *edev, Block *b)
+{
+	int m;
+
+	m = BLEN(b);
+	if(m < 6)
+		return -1;
+	return unpack(edev, b, m);
+}
+
+static int
+unpackasix(Ether *edev, Block *b)
+{
+	ulong hd;
+	int m;
+	uchar *wp;
+
+	if(BLEN(b) < 4)
+		return -1;
+	hd = GET4(b->rp);
+	b->rp += 4;
+	m = hd & 0xFFFF;
+	hd >>= 16;
+	if(m != (~hd & 0xFFFF))
+		return -1;
+	m = ROUND(m, 2);
+	if(m < 6 || m > BLEN(b))
+		return -1;
+	if((wp = b->rp + m) != b->wp && b->wp - wp < 4)
+		b->wp = wp;
+	return unpack(edev, b, m);
+}
+
+static int
+unpacksmsc(Ether *edev, Block *b)
+{
+	ulong hd;
+	int m;
+	
+	ddump('@', b);
+	if(BLEN(b) < 4)
+		return -1;
+	hd = GET4(b->rp);
+	b->rp += 4;
+	m = hd >> 16;
+	if(m < 6 || m > BLEN(b))
+		return -1;
+	if(BLEN(b) - m < 4)
+		b->wp = b->rp + m;
+	if(hd & SmscRxerror){
+		edev->frames++;
+		b->rp += m;
+		if(BLEN(b) == 0){
+			freeb(b);
+			return 1;
+		}
+	}else if(unpack(edev, b, m) == 1)
+		return 1;
+	if((m &= 3) != 0)
+		b->rp += 4 - m;
+	return 0;
+}
+
+static void
+transmit(Ctlr *ctlr, Block *b)
+{
+	Chan *c;
+
+	ddump('!', b);
+	c = ctlr->outchan;
+	devtab[c->type]->bwrite(c, b, 0);
+}
+
+static void
+transmitcdc(Ctlr *ctlr, Block *b)
+{
+	transmit(ctlr, b);
+}
+
+static void
+transmitasix(Ctlr *ctlr, Block *b)
+{
+	int n;
+
+	n = BLEN(b) & 0xFFFF;
+	n = 0xFFFF0000 & ~(n << 16) | n;
+	padblock(b, 4);
+	PUT4(b->rp, n);
+	if(BLEN(b) % ctlr->maxpkt == 0){
+		padblock(b, -4);
+		PUT4(b->wp, 0xFFFF0000);
+		b->wp += 4;
+	}
+	transmit(ctlr, b);
+}
+
+static void
+transmitsmsc(Ctlr *ctlr, Block *b)
+{
+	int n;
+
+	n = BLEN(b) & 0x7FF;
+	padblock(b, 8);
+	PUT4(b->rp, n | SmscTxfirst | SmscTxlast);
+	PUT4(b->rp+4, n);
+	transmit(ctlr, b);
+}
+
+static void
+etherusbproc(void *a)
+{
+	Ether *edev;
+	Ctlr *ctlr;
+	Chan *c;
+	Block *b;
+
+	edev = a;
+	ctlr = edev->ctlr;
+	c = ctlr->inchan;
+	b = nil;
+	if(waserror()){
+		print("etherusbproc: error exit %s\n", up->errstr);
+		pexit(up->errstr, 1);
+		return;
+	}
+	for(;;){
+		if(b == nil){
+			b = devtab[c->type]->bread(c, ctlr->bufsize, 0);
+			ctlr->rxbuf++;
+		}
+		switch(ctlr->udev->unpack(edev, b)){
+		case -1:
+			edev->buffs++;
+			freeb(b);
+			/* fall through */
+		case 1:
+			b = nil;
+			break;
+		}
+	}
+}
+
+/*
+ * bind type indev outdev mac bufsize maxpkt
+ */
+static void
+bind(Ctlr *ctlr, Udev *udev, Cmdbuf *cb)
+{
+	Chan *inchan, *outchan;
+	char *buf;
+	uint bufsize, maxpkt;
+
+	qlock(ctlr);
+	inchan = outchan = nil;
+	buf = nil;
+	if(waserror()){
+		free(buf);
+		if(inchan)
+			cclose(inchan);
+		if(outchan)
+			cclose(outchan);
+		qunlock(ctlr);
+		nexterror();
+	}
+	if(ctlr->buf != nil)
+		cmderror(cb, "already bound to a device");
+	maxpkt = strtol(cb->f[6], 0, 0);
+	if(maxpkt < 8 || maxpkt > 512)
+		cmderror(cb, "bad maxpkt");
+	bufsize = strtol(cb->f[5], 0, 0);
+	if(bufsize < maxpkt || bufsize > 32*1024)
+		cmderror(cb, "bad bufsize");
+	buf = smalloc(bufsize);
+	inchan = namec(cb->f[2], Aopen, OREAD, 0);
+	outchan = namec(cb->f[3], Aopen, OWRITE, 0);
+	assert(inchan != nil && outchan != nil);
+	if(parsemac(ctlr->edev->ea, cb->f[4], Eaddrlen) != Eaddrlen)
+		cmderror(cb, "bad etheraddr");
+	memmove(ctlr->edev->addr, ctlr->edev->ea, Eaddrlen);
+	print("\netherusb %s: %E\n", udev->name, ctlr->edev->addr);
+	ctlr->buf = buf;
+	ctlr->inchan = inchan;
+	ctlr->outchan = outchan;
+	ctlr->bufsize = bufsize;
+	ctlr->maxpkt = maxpkt;
+	ctlr->udev = udev;
+	kproc("etherusb", etherusbproc, ctlr->edev);
+	poperror();
+	qunlock(ctlr);
+}
+
+static void
+unbind(Ctlr *ctlr)
+{
+	qlock(ctlr);
+	if(ctlr->buf != nil){
+		free(ctlr->buf);
+		ctlr->buf = nil;
+		if(ctlr->inchan)
+			cclose(ctlr->inchan);
+		if(ctlr->outchan)
+			cclose(ctlr->outchan);
+		ctlr->inchan = ctlr->outchan = nil;
+	}
+	qunlock(ctlr);
+}
+
+static long
+etherusbifstat(Ether* edev, void* a, long n, ulong offset)
+{
+	Ctlr *ctlr;
+	char *p;
+	int l;
+
+	ctlr = edev->ctlr;
+	p = malloc(READSTR);
+	l = 0;
+
+	l += snprint(p+l, READSTR-l, "rxbuf: %ud\n", ctlr->rxbuf);
+	l += snprint(p+l, READSTR-l, "rxpkt: %ud\n", ctlr->rxpkt);
+	l += snprint(p+l, READSTR-l, "txbuf: %ud\n", ctlr->txbuf);
+	l += snprint(p+l, READSTR-l, "txpkt: %ud\n", ctlr->txpkt);
+	USED(l);
+
+	n = readstr(offset, a, n, p);
+	free(p);
+	return n;
+}
+
+static void
+etherusbtransmit(Ether *edev)
+{
+	Ctlr *ctlr;
+	Block *b;
+	
+	ctlr = edev->ctlr;
+	while((b = qget(edev->oq)) != nil){
+		ctlr->txpkt++;
+		if(ctlr->buf == nil)
+			freeb(b);
+		else{
+			ctlr->udev->transmit(ctlr, b);
+			ctlr->txbuf++;
+		}
+	}
+}
+
+static long
+etherusbctl(Ether* edev, void* buf, long n)
+{
+	Ctlr *ctlr;
+	Cmdbuf *cb;
+	Cmdtab *ct;
+	Udev *udev;
+
+	if((ctlr = edev->ctlr) == nil)
+		error(Enonexist);
+
+	cb = parsecmd(buf, n);
+	if(waserror()){
+		free(cb);
+		nexterror();
+	}
+	ct = lookupcmd(cb, cmds, nelem(cmds));
+	switch(ct->index){
+	case Bind:
+		for(udev = udevtab; udev->name; udev++)
+			if(strcmp(cb->f[1], udev->name) == 0)
+				break;
+		if(udev->name == nil)
+			cmderror(cb, "unknown etherusb type");
+		bind(ctlr, udev, cb);
+		break;
+	case Unbind:
+		unbind(ctlr);
+		break;
+	default:
+		cmderror(cb, "unknown etherusb control message");
+	}
+	poperror();
+	free(cb);
+	return n;
+}
+
+static void
+etherusbattach(Ether* edev)
+{
+	Ctlr *ctlr;
+
+	ctlr = edev->ctlr;
+	ctlr->edev = edev;
+}
+
+static int
+etherusbpnp(Ether* edev)
+{
+	Ctlr *ctlr;
+
+	ctlr = malloc(sizeof(Ctlr));
+	edev->ctlr = ctlr;
+	edev->irq = -1;
+	edev->mbps = 100;	/* TODO: get this from usbether */
+	edev->attach = etherusbattach;
+	edev->transmit = etherusbtransmit;
+	edev->ifstat = etherusbifstat;
+	edev->ctl = etherusbctl;
+	return 0;
+}
+
+void
+etherusblink(void)
+{
+	addethercard("usb", etherusbpnp);
+}

+ 119 - 0
sys/src/9/bcm/fns.h

@@ -0,0 +1,119 @@
+#include "../port/portfns.h"
+
+Dirtab*	addarchfile(char*, int, long(*)(Chan*, void*, long, vlong), 
+	long(*)(Chan*, void*, long, vlong));
+extern void archreboot(void);
+extern void archreset(void);
+extern void armtimerset(int);
+extern void cachedwbinv(void);
+extern void cachedwbse(void*, int);
+extern void cachedwbinvse(void*, int);
+extern void cacheiinv(void);
+extern void cacheuwbinv(void);
+extern uintptr cankaddr(uintptr pa);
+extern int cas32(void*, u32int, u32int);
+extern void checkmmu(uintptr, uintptr);
+extern void clockinit(void);
+extern void clockshutdown(void);
+extern int cmpswap(long*, long, long);
+extern void coherence(void);
+extern ulong cprd(int cp, int op1, int crn, int crm, int op2);
+extern ulong cprdsc(int op1, int crn, int crm, int op2);
+extern void cpuidprint(void);
+extern void cpwr(int cp, int op1, int crn, int crm, int op2, ulong val);
+extern void cpwrsc(int op1, int crn, int crm, int op2, ulong val);
+#define cycles(ip) *(ip) = lcycles()
+extern void dmastart(int, int, int, void*, void*, int);
+extern int dmawait(int);
+extern int fbblank(int);
+extern void* fbinit(int, int*, int*, int*);
+extern u32int farget(void);
+extern void fpon(void);
+extern ulong fprd(int fpreg);
+extern void fprestreg(int fpreg, uvlong val);
+extern void fpsave(FPsave *);
+extern ulong fpsavereg(int fpreg, uvlong *fpp);
+extern void fpwr(int fpreg, ulong val);
+extern u32int fsrget(void);
+extern ulong getclkrate(int);
+extern char* getconf(char*);
+extern char *getethermac(void);
+extern uint getfirmware(void);
+extern int getpower(int);
+extern void getramsize(Confmem*);
+extern u32int ifsrget(void);
+extern void irqenable(int, void (*)(Ureg*, void*), void*);
+#define intrenable(i, f, a, b, n) irqenable((i), (f), (a))
+extern void intrsoff(void);
+extern int isaconfig(char*, int, ISAConf*);
+extern void links(void);
+extern void mmuinit(void);
+extern void mmuinit1(void);
+extern void mmuinvalidate(void);
+extern void mmuinvalidateaddr(u32int);
+extern uintptr mmukmap(uintptr, uintptr, usize);
+extern void okay(int);
+extern void procrestore(Proc *);
+extern void procsave(Proc*);
+extern void procsetup(Proc*);
+extern void screeninit(void);
+#define sdfree(p) free(p)
+#define sdmalloc(n)	mallocalign(n, CACHELINESZ, 0, 0)
+extern void setpower(int, int);
+extern void setr13(int, u32int*);
+extern int splfhi(void);
+extern int splflo(void);
+extern void swcursorinit(void);
+extern void syscallfmt(int syscallno, ulong pc, va_list list);
+extern void sysretfmt(int syscallno, va_list list, long ret, uvlong start, uvlong stop);
+extern int tas(void *);
+extern void touser(uintptr);
+extern void trapinit(void);
+extern void uartconsinit(void);
+extern int userureg(Ureg*);
+extern void vectors(void);
+extern void vtable(void);
+
+/*
+ * floating point emulation
+ */
+extern int fpiarm(Ureg*);
+extern int fpudevprocio(Proc*, void*, long, uintptr, int);
+extern void fpuinit(void);
+extern void fpunoted(void);
+extern void fpunotify(Ureg*);
+extern void fpuprocrestore(Proc*);
+extern void fpuprocsave(Proc*);
+extern void fpusysprocsetup(Proc*);
+extern void fpusysrfork(Ureg*);
+extern void fpusysrforkchild(Proc*, Ureg*, Proc*);
+extern int fpuemu(Ureg*);
+/*
+ * Things called from port.
+ */
+extern void delay(int);				/* only scheddump() */
+extern int islo(void);
+extern void microdelay(int);			/* only edf.c */
+extern void evenaddr(uintptr);
+extern void idlehands(void);
+extern void setkernur(Ureg*, Proc*);		/* only devproc.c */
+extern void* sysexecregs(uintptr, ulong, int);
+extern void sysprocsetup(Proc*);
+
+extern void kexit(Ureg*);
+
+#define	getpgcolor(a)	0
+#define	kmapinval()
+#define countpagerefs(a, b)
+
+#define PTR2UINT(p)	((uintptr)(p))
+#define UINT2PTR(i)	((void*)(i))
+
+#define	waserror()	(up->nerrlab++, setlabel(&up->errlab[up->nerrlab-1]))
+
+#define KADDR(pa)	UINT2PTR(KZERO    | ((uintptr)(pa) & ~KSEGM))
+#define PADDR(va)	PTR2UINT(PHYSDRAM | ((uintptr)(va) & ~KSEGM))
+#define DMAADDR(va)	PTR2UINT(BUSDRAM  | ((uintptr)(va) & ~KSEGM))
+#define DMAIO(va)	PTR2UINT(BUSIO    | ((uintptr)(va) & ~VIRTIO))
+
+#define MASK(v)	((1UL << (v)) - 1)	/* mask `v' bits wide */

+ 1 - 0
sys/src/9/bcm/fpi.c

@@ -0,0 +1 @@
+#include "../omap/fpi.c"

+ 1 - 0
sys/src/9/bcm/fpi.h

@@ -0,0 +1 @@
+#include "../omap/fpi.h"

+ 506 - 0
sys/src/9/bcm/fpiarm.c

@@ -0,0 +1,506 @@
+/*
+ * this doesn't attempt to implement ARM floating-point properties
+ * that aren't visible in the Inferno environment.
+ * all arithmetic is done in double precision.
+ * the FP trap status isn't updated.
+ */
+#include	"u.h"
+#include	"../port/lib.h"
+#include	"mem.h"
+#include	"dat.h"
+#include	"fns.h"
+
+#include	"ureg.h"
+
+#include	"arm.h"
+#include	"fpi.h"
+
+#define ARM7500			/* emulate old pre-VFP opcodes */
+
+/* undef this if correct kernel r13 isn't in Ureg;
+ * check calculation in fpiarm below
+ */
+
+#define	REG(ur, x) (*(long*)(((char*)(ur))+roff[(x)]))
+#ifdef ARM7500
+#define	FR(ufp, x) (*(Internal*)(ufp)->regs[(x)&7])
+#else
+#define	FR(ufp, x) (*(Internal*)(ufp)->regs[(x)&(Nfpregs - 1)])
+#endif
+
+typedef struct FP2 FP2;
+typedef struct FP1 FP1;
+
+struct FP2 {
+	char*	name;
+	void	(*f)(Internal, Internal, Internal*);
+};
+
+struct FP1 {
+	char*	name;
+	void	(*f)(Internal*, Internal*);
+};
+
+enum {
+	N = 1<<31,
+	Z = 1<<30,
+	C = 1<<29,
+	V = 1<<28,
+	REGPC = 15,
+};
+
+enum {
+	fpemudebug = 0,
+};
+
+#undef OFR
+#define	OFR(X)	((ulong)&((Ureg*)0)->X)
+
+static	int	roff[] = {
+	OFR(r0), OFR(r1), OFR(r2), OFR(r3),
+	OFR(r4), OFR(r5), OFR(r6), OFR(r7),
+	OFR(r8), OFR(r9), OFR(r10), OFR(r11),
+	OFR(r12), OFR(r13), OFR(r14), OFR(pc),
+};
+
+static Internal fpconst[8] = {		/* indexed by op&7 (ARM 7500 FPA) */
+	/* s, e, l, h */
+	{0, 0x1, 0x00000000, 0x00000000}, /* 0.0 */
+	{0, 0x3FF, 0x00000000, 0x08000000},	/* 1.0 */
+	{0, 0x400, 0x00000000, 0x08000000},	/* 2.0 */
+	{0, 0x400, 0x00000000, 0x0C000000},	/* 3.0 */
+	{0, 0x401, 0x00000000, 0x08000000},	/* 4.0 */
+	{0, 0x401, 0x00000000, 0x0A000000},	/* 5.0 */
+	{0, 0x3FE, 0x00000000, 0x08000000},	/* 0.5 */
+	{0, 0x402, 0x00000000, 0x0A000000},	/* 10.0 */
+};
+
+/*
+ * arm binary operations
+ */
+
+static void
+fadd(Internal m, Internal n, Internal *d)
+{
+	(m.s == n.s? fpiadd: fpisub)(&m, &n, d);
+}
+
+static void
+fsub(Internal m, Internal n, Internal *d)
+{
+	m.s ^= 1;
+	(m.s == n.s? fpiadd: fpisub)(&m, &n, d);
+}
+
+static void
+fsubr(Internal m, Internal n, Internal *d)
+{
+	n.s ^= 1;
+	(n.s == m.s? fpiadd: fpisub)(&n, &m, d);
+}
+
+static void
+fmul(Internal m, Internal n, Internal *d)
+{
+	fpimul(&m, &n, d);
+}
+
+static void
+fdiv(Internal m, Internal n, Internal *d)
+{
+	fpidiv(&m, &n, d);
+}
+
+static void
+fdivr(Internal m, Internal n, Internal *d)
+{
+	fpidiv(&n, &m, d);
+}
+
+/*
+ * arm unary operations
+ */
+
+static void
+fmov(Internal *m, Internal *d)
+{
+	*d = *m;
+}
+
+static void
+fmovn(Internal *m, Internal *d)
+{
+	*d = *m;
+	d->s ^= 1;
+}
+
+static void
+fabsf(Internal *m, Internal *d)
+{
+	*d = *m;
+	d->s = 0;
+}
+
+static void
+frnd(Internal *m, Internal *d)
+{
+	short e;
+
+	(m->s? fsub: fadd)(fpconst[6], *m, d);
+	if(IsWeird(d))
+		return;
+	fpiround(d);
+	e = (d->e - ExpBias) + 1;
+	if(e <= 0)
+		SetZero(d);
+	else if(e > FractBits){
+		if(e < 2*FractBits)
+			d->l &= ~((1<<(2*FractBits - e))-1);
+	}else{
+		d->l = 0;
+		if(e < FractBits)
+			d->h &= ~((1<<(FractBits-e))-1);
+	}
+}
+
+/*
+ * ARM 7500 FPA opcodes
+ */
+
+static	FP1	optab1[16] = {	/* Fd := OP Fm */
+[0]	{"MOVF",	fmov},
+[1]	{"NEGF",	fmovn},
+[2]	{"ABSF",	fabsf},
+[3]	{"RNDF",	frnd},
+[4]	{"SQTF",	/*fsqt*/0},
+/* LOG, LGN, EXP, SIN, COS, TAN, ASN, ACS, ATN all `deprecated' */
+/* URD and NRM aren't implemented */
+};
+
+static	FP2	optab2[16] = {	/* Fd := Fn OP Fm */
+[0]	{"ADDF",	fadd},
+[1]	{"MULF",	fmul},
+[2]	{"SUBF",	fsub},
+[3]	{"RSUBF",	fsubr},
+[4]	{"DIVF",	fdiv},
+[5]	{"RDIVF",	fdivr},
+/* POW, RPW deprecated */
+[8]	{"REMF",	/*frem*/0},
+[9]	{"FMF",	fmul},	/* fast multiply */
+[10]	{"FDV",	fdiv},	/* fast divide */
+[11]	{"FRD",	fdivr},	/* fast reverse divide */
+/* POL deprecated */
+};
+
+static ulong
+fcmp(Internal *n, Internal *m)
+{
+	int i;
+	Internal rm, rn;
+
+	if(IsWeird(m) || IsWeird(n)){
+		/* BUG: should trap if not masked */
+		return V|C;
+	}
+	rn = *n;
+	rm = *m;
+	fpiround(&rn);
+	fpiround(&rm);
+	i = fpicmp(&rn, &rm);
+	if(i > 0)
+		return C;
+	else if(i == 0)
+		return C|Z;
+	else
+		return N;
+}
+
+static void
+fld(void (*f)(Internal*, void*), int d, ulong ea, int n, FPsave *ufp)
+{
+	void *mem;
+
+	mem = (void*)ea;
+	(*f)(&FR(ufp, d), mem);
+	if(fpemudebug)
+		print("MOV%c #%lux, F%d\n", n==8? 'D': 'F', ea, d);
+}
+
+static void
+fst(void (*f)(void*, Internal*), ulong ea, int s, int n, FPsave *ufp)
+{
+	Internal tmp;
+	void *mem;
+
+	mem = (void*)ea;
+	tmp = FR(ufp, s);
+	if(fpemudebug)
+		print("MOV%c	F%d,#%lux\n", n==8? 'D': 'F', s, ea);
+	(*f)(mem, &tmp);
+}
+
+static int
+condok(int cc, int c)
+{
+	switch(c){
+	case 0:	/* Z set */
+		return cc&Z;
+	case 1:	/* Z clear */
+		return (cc&Z) == 0;
+	case 2:	/* C set */
+		return cc&C;
+	case 3:	/* C clear */
+		return (cc&C) == 0;
+	case 4:	/* N set */
+		return cc&N;
+	case 5:	/* N clear */
+		return (cc&N) == 0;
+	case 6:	/* V set */
+		return cc&V;
+	case 7:	/* V clear */
+		return (cc&V) == 0;
+	case 8:	/* C set and Z clear */
+		return cc&C && (cc&Z) == 0;
+	case 9:	/* C clear or Z set */
+		return (cc&C) == 0 || cc&Z;
+	case 10:	/* N set and V set, or N clear and V clear */
+		return (~cc&(N|V))==0 || (cc&(N|V)) == 0;
+	case 11:	/* N set and V clear, or N clear and V set */
+		return (cc&(N|V))==N || (cc&(N|V))==V;
+	case 12:	/* Z clear, and either N set and V set or N clear and V clear */
+		return (cc&Z) == 0 && ((~cc&(N|V))==0 || (cc&(N|V))==0);
+	case 13:	/* Z set, or N set and V clear or N clear and V set */
+		return (cc&Z) || (cc&(N|V))==N || (cc&(N|V))==V;
+	case 14:	/* always */
+		return 1;
+	case 15:	/* never (reserved) */
+		return 0;
+	}
+	return 0;	/* not reached */
+}
+
+static void
+unimp(ulong pc, ulong op)
+{
+	char buf[60];
+
+	snprint(buf, sizeof(buf), "sys: fp: pc=%lux unimp fp 0x%.8lux", pc, op);
+	if(fpemudebug)
+		print("FPE: %s\n", buf);
+	error(buf);
+	/* no return */
+}
+
+static void
+fpemu(ulong pc, ulong op, Ureg *ur, FPsave *ufp)
+{
+	int rn, rd, tag, o;
+	long off;
+	ulong ea;
+	Internal tmp, *fm, *fn;
+
+	/* note: would update fault status here if we noted numeric exceptions */
+
+	/*
+	 * LDF, STF; 10.1.1
+	 */
+	if(((op>>25)&7) == 6){
+		if(op & (1<<22))
+			unimp(pc, op);	/* packed or extended */
+		rn = (op>>16)&0xF;
+		off = (op&0xFF)<<2;
+		if((op & (1<<23)) == 0)
+			off = -off;
+		ea = REG(ur, rn);
+		if(rn == REGPC)
+			ea += 8;
+		if(op & (1<<24))
+			ea += off;
+		rd = (op>>12)&7;
+		if(op & (1<<20)){
+			if(op & (1<<15))
+				fld(fpid2i, rd, ea, 8, ufp);
+			else
+				fld(fpis2i, rd, ea, 4, ufp);
+		}else{
+			if(op & (1<<15))
+				fst(fpii2d, ea, rd, 8, ufp);
+			else
+				fst(fpii2s, ea, rd, 4, ufp);
+		}
+		if((op & (1<<24)) == 0)
+			ea += off;
+		if(op & (1<<21))
+			REG(ur, rn) = ea;
+		return;
+	}
+
+	/*
+	 * CPRT/transfer, 10.3
+	 */
+	if(op & (1<<4)){
+		rd = (op>>12) & 0xF;
+
+		/*
+		 * compare, 10.3.1
+		 */
+		if(rd == 15 && op & (1<<20)){
+			rn = (op>>16)&7;
+			fn = &FR(ufp, rn);
+			if(op & (1<<3)){
+				fm = &fpconst[op&7];
+				if(fpemudebug)
+					tag = 'C';
+			}else{
+				fm = &FR(ufp, op&7);
+				if(fpemudebug)
+					tag = 'F';
+			}
+			switch((op>>21)&7){
+			default:
+				unimp(pc, op);
+			case 4:	/* CMF: Fn :: Fm */
+			case 6:	/* CMFE: Fn :: Fm (with exception) */
+				ur->psr &= ~(N|C|Z|V);
+				ur->psr |= fcmp(fn, fm);
+				break;
+			case 5:	/* CNF: Fn :: -Fm */
+			case 7:	/* CNFE: Fn :: -Fm (with exception) */
+				tmp = *fm;
+				tmp.s ^= 1;
+				ur->psr &= ~(N|C|Z|V);
+				ur->psr |= fcmp(fn, &tmp);
+				break;
+			}
+			if(fpemudebug)
+				print("CMPF	%c%d,F%ld =%#lux\n",
+					tag, rn, op&7, ur->psr>>28);
+			return;
+		}
+
+		/*
+		 * other transfer, 10.3
+		 */
+		switch((op>>20)&0xF){
+		default:
+			unimp(pc, op);
+		case 0:	/* FLT */
+			rn = (op>>16) & 7;
+			fpiw2i(&FR(ufp, rn), &REG(ur, rd));
+			if(fpemudebug)
+				print("MOVW[FD]	R%d, F%d\n", rd, rn);
+			break;
+		case 1:	/* FIX */
+			if(op & (1<<3))
+				unimp(pc, op);
+			rn = op & 7;
+			tmp = FR(ufp, rn);
+			fpii2w(&REG(ur, rd), &tmp);
+			if(fpemudebug)
+				print("MOV[FD]W	F%d, R%d =%ld\n", rn, rd, REG(ur, rd));
+			break;
+		case 2:	/* FPSR := Rd */
+			ufp->status = REG(ur, rd);
+			if(fpemudebug)
+				print("MOVW	R%d, FPSR\n", rd);
+			break;
+		case 3:	/* Rd := FPSR */
+			REG(ur, rd) = ufp->status;
+			if(fpemudebug)
+				print("MOVW	FPSR, R%d\n", rd);
+			break;
+		case 4:	/* FPCR := Rd */
+			ufp->control = REG(ur, rd);
+			if(fpemudebug)
+				print("MOVW	R%d, FPCR\n", rd);
+			break;
+		case 5:	/* Rd := FPCR */
+			REG(ur, rd) = ufp->control;
+			if(fpemudebug)
+				print("MOVW	FPCR, R%d\n", rd);
+			break;
+		}
+		return;
+	}
+
+	/*
+	 * arithmetic
+	 */
+
+	if(op & (1<<3)){	/* constant */
+		fm = &fpconst[op&7];
+		if(fpemudebug)
+			tag = 'C';
+	}else{
+		fm = &FR(ufp, op&7);
+		if(fpemudebug)
+			tag = 'F';
+	}
+	rd = (op>>12)&7;
+	o = (op>>20)&0xF;
+	if(op & (1<<15)){	/* monadic */
+		FP1 *fp;
+		fp = &optab1[o];
+		if(fp->f == nil)
+			unimp(pc, op);
+		if(fpemudebug)
+			print("%s	%c%ld,F%d\n", fp->name, tag, op&7, rd);
+		(*fp->f)(fm, &FR(ufp, rd));
+	} else {
+		FP2 *fp;
+		fp = &optab2[o];
+		if(fp->f == nil)
+			unimp(pc, op);
+		rn = (op>>16)&7;
+		if(fpemudebug)
+			print("%s	%c%ld,F%d,F%d\n", fp->name, tag, op&7, rn, rd);
+		(*fp->f)(*fm, FR(ufp, rn), &FR(ufp, rd));
+	}
+}
+
+/*
+ * returns the number of FP instructions emulated
+ */
+int
+fpiarm(Ureg *ur)
+{
+	ulong op, o, cp;
+	FPsave *ufp;
+	int n;
+
+	if(up == nil)
+		panic("fpiarm not in a process");
+	ufp = &up->fpsave;
+	/*
+	 * because all the emulated fp state is in the proc structure,
+	 * it need not be saved/restored
+	 */
+	switch(up->fpstate){
+	case FPactive:
+	case FPinactive:
+		error("illegal instruction: emulated fpu opcode in VFP mode");
+	case FPinit:
+		assert(sizeof(Internal) <= sizeof(ufp->regs[0]));
+		up->fpstate = FPemu;
+		ufp->control = 0;
+		ufp->status = (0x01<<28)|(1<<12); /* sw emulation, alt. C flag */
+		for(n = 0; n < 8; n++)
+			FR(ufp, n) = fpconst[0];
+	}
+	for(n=0; ;n++){
+		validaddr(ur->pc, 4, 0);
+		op = *(ulong*)(ur->pc);
+		if(fpemudebug)
+			print("%#lux: %#8.8lux ", ur->pc, op);
+		o = (op>>24) & 0xF;
+		cp = (op>>8) & 0xF;
+		if(!ISFPAOP(cp, o))
+			break;
+		if(condok(ur->psr, op>>28))
+			fpemu(ur->pc, op, ur, ufp);
+		ur->pc += 4;		/* pretend cpu executed the instr */
+	}
+	if(fpemudebug)
+		print("\n");
+	return n;
+}

+ 1 - 0
sys/src/9/bcm/fpimem.c

@@ -0,0 +1 @@
+#include "../omap/fpimem.c"

+ 1 - 0
sys/src/9/bcm/init9.s

@@ -0,0 +1 @@
+#include "../omap/init9.s"

+ 45 - 0
sys/src/9/bcm/io.h

@@ -0,0 +1,45 @@
+enum {
+	IRQtimer0	= 0,
+	IRQtimer1	= 1,
+	IRQtimer2	= 2,
+	IRQtimer3	= 3,
+	IRQclock	= IRQtimer3,
+	IRQusb		= 9,
+	IRQdma0		= 16,
+#define IRQDMA(chan)	(IRQdma0+(chan))
+	IRQaux		= 29,
+	IRQmmc		= 62,
+
+	IRQbasic	= 64,
+	IRQtimerArm	= IRQbasic + 0,
+
+	IRQfiq		= IRQusb,	/* only one source can be FIQ */
+
+	DmaD2M		= 0,		/* device to memory */
+	DmaM2D		= 1,		/* memory to device */
+	DmaM2M		= 2,		/* memory to memory */
+
+	DmaChanEmmc	= 4,		/* can only use 2-5, maybe 0 */
+	DmaDevEmmc	= 11,
+
+	PowerSd		= 0,
+	PowerUart0,
+	PowerUart1,
+	PowerUsb,
+	PowerI2c0,
+	PowerI2c1,
+	PowerI2c2,
+	PowerSpi,
+	PowerCcp2tx,
+
+	ClkEmmc		= 1,
+	ClkUart,
+	ClkArm,
+	ClkCore,
+	ClkV3d,
+	ClkH264,
+	ClkIsp,
+	ClkSdram,
+	ClkPixel,
+	ClkPwm,
+};

+ 1 - 0
sys/src/9/bcm/kbd.c

@@ -0,0 +1 @@
+#include "../omap/kbd.c"

+ 274 - 0
sys/src/9/bcm/l.s

@@ -0,0 +1,274 @@
+/*
+ * Broadcom bcm2835 SoC, as used in Raspberry Pi
+ * arm1176jzf-s processor (armv6)
+ */
+
+#include "arm.s"
+
+TEXT _start(SB), 1, $-4
+	/*
+	 * load physical base for SB addressing while mmu is off
+	 * keep a handy zero in R0 until first function call
+	 */
+	MOVW	$setR12(SB), R12
+	SUB	$KZERO, R12
+	ADD	$PHYSDRAM, R12
+	MOVW	$0, R0
+
+	/*
+	 * SVC mode, interrupts disabled
+	 */
+	MOVW	$(PsrDirq|PsrDfiq|PsrMsvc), R1
+	MOVW	R1, CPSR
+
+	/*
+	 * disable the mmu and L1 caches
+	 * invalidate caches and tlb
+	 */
+	MRC	CpSC, 0, R1, C(CpCONTROL), C(0), CpMainctl
+	BIC	$(CpCdcache|CpCicache|CpCpredict|CpCmmu), R1
+	MCR	CpSC, 0, R1, C(CpCONTROL), C(0), CpMainctl
+	MCR	CpSC, 0, R0, C(CpCACHE), C(CpCACHEinvu), CpCACHEall
+	MCR	CpSC, 0, R0, C(CpTLB), C(CpTLBinvu), CpTLBinv
+	ISB
+
+	/*
+	 * clear mach and page tables
+	 */
+	MOVW	$PADDR(MACHADDR), R1
+	MOVW	$PADDR(KTZERO), R2
+_ramZ:
+	MOVW	R0, (R1)
+	ADD	$4, R1
+	CMP	R1, R2
+	BNE	_ramZ
+
+	/*
+	 * start stack at top of mach (physical addr)
+	 * set up page tables for kernel
+	 */
+	MOVW	$PADDR(MACHADDR+MACHSIZE-4), R13
+	BL	,mmuinit(SB)
+
+	/*
+	 * set up domain access control and page table base
+	 */
+	MOVW	$Client, R1
+	MCR	CpSC, 0, R1, C(CpDAC), C(0)
+	MOVW	$PADDR(L1), R1
+	MCR	CpSC, 0, R1, C(CpTTB), C(0)
+
+	/*
+	 * enable caches, mmu, and high vectors
+	 */
+	MRC	CpSC, 0, R0, C(CpCONTROL), C(0), CpMainctl
+	ORR	$(CpChv|CpCdcache|CpCicache|CpCmmu), R0
+	MCR	CpSC, 0, R0, C(CpCONTROL), C(0), CpMainctl
+	ISB
+
+	/*
+	 * switch SB, SP, and PC into KZERO space
+	 */
+	MOVW	$setR12(SB), R12
+	MOVW	$(MACHADDR+MACHSIZE-4), R13
+	MOVW	$_startpg(SB), R15
+
+TEXT _startpg(SB), 1, $-4
+
+	/*
+	 * enable cycle counter
+	 */
+	MOVW	$1, R1
+	MCR	CpSC, 0, R1, C(CpSPM), C(CpSPMperf), CpSPMctl
+
+	/*
+	 * call main and loop forever if it returns
+	 */
+	BL	,main(SB)
+	B	,0(PC)
+
+	BL	_div(SB)		/* hack to load _div, etc. */
+
+TEXT fsrget(SB), 1, $-4				/* data fault status */
+	MRC	CpSC, 0, R0, C(CpFSR), C(0), CpFSRdata
+	RET
+
+TEXT ifsrget(SB), 1, $-4			/* instruction fault status */
+	MRC	CpSC, 0, R0, C(CpFSR), C(0), CpFSRinst
+	RET
+
+TEXT farget(SB), 1, $-4				/* fault address */
+	MRC	CpSC, 0, R0, C(CpFAR), C(0x0)
+	RET
+
+TEXT lcycles(SB), 1, $-4
+	MRC	CpSC, 0, R0, C(CpSPM), C(CpSPMperf), CpSPMcyc
+	RET
+
+TEXT splhi(SB), 1, $-4
+	MOVW	$(MACHADDR+4), R2		/* save caller pc in Mach */
+	MOVW	R14, 0(R2)
+
+	MOVW	CPSR, R0			/* turn off irqs (but not fiqs) */
+	ORR	$(PsrDirq), R0, R1
+	MOVW	R1, CPSR
+	RET
+
+TEXT splfhi(SB), 1, $-4
+	MOVW	$(MACHADDR+4), R2		/* save caller pc in Mach */
+	MOVW	R14, 0(R2)
+
+	MOVW	CPSR, R0			/* turn off irqs and fiqs */
+	ORR	$(PsrDirq|PsrDfiq), R0, R1
+	MOVW	R1, CPSR
+	RET
+
+TEXT splflo(SB), 1, $-4
+	MOVW	CPSR, R0			/* turn on fiqs */
+	BIC	$(PsrDfiq), R0, R1
+	MOVW	R1, CPSR
+	RET
+
+TEXT spllo(SB), 1, $-4
+	MOVW	CPSR, R0			/* turn on irqs and fiqs */
+	BIC	$(PsrDirq|PsrDfiq), R0, R1
+	MOVW	R1, CPSR
+	RET
+
+TEXT splx(SB), 1, $-4
+	MOVW	$(MACHADDR+0x04), R2		/* save caller pc in Mach */
+	MOVW	R14, 0(R2)
+
+	MOVW	R0, R1				/* reset interrupt level */
+	MOVW	CPSR, R0
+	MOVW	R1, CPSR
+	RET
+
+TEXT spldone(SB), 1, $0				/* end marker for devkprof.c */
+	RET
+
+TEXT islo(SB), 1, $-4
+	MOVW	CPSR, R0
+	AND	$(PsrDirq), R0
+	EOR	$(PsrDirq), R0
+	RET
+
+TEXT	tas(SB), $-4
+TEXT	_tas(SB), $-4
+	MOVW	R0,R1
+	MOVW	$1,R0
+	SWPW	R0,(R1)			/* fix: deprecated in armv6 */
+	RET
+
+TEXT setlabel(SB), 1, $-4
+	MOVW	R13, 0(R0)		/* sp */
+	MOVW	R14, 4(R0)		/* pc */
+	MOVW	$0, R0
+	RET
+
+TEXT gotolabel(SB), 1, $-4
+	MOVW	0(R0), R13		/* sp */
+	MOVW	4(R0), R14		/* pc */
+	MOVW	$1, R0
+	RET
+
+TEXT getcallerpc(SB), 1, $-4
+	MOVW	0(R13), R0
+	RET
+
+TEXT idlehands(SB), $-4
+	BARRIERS
+	MOVW	CPSR, R3
+	BIC	$(PsrDirq|PsrDfiq), R3, R1		/* spllo */
+	MOVW	R1, CPSR
+
+	MOVW	$0, R0				/* wait for interrupt */
+	MCR	CpSC, 0, R0, C(CpCACHE), C(CpCACHEintr), CpCACHEwait
+	ISB
+
+	MOVW	R3, CPSR			/* splx */
+	RET
+
+
+TEXT coherence(SB), $-4
+	BARRIERS
+	RET
+
+/*
+ * invalidate tlb
+ */
+TEXT mmuinvalidate(SB), 1, $-4
+	MOVW	$0, R0
+	MCR	CpSC, 0, R0, C(CpTLB), C(CpTLBinvu), CpTLBinv
+	BARRIERS
+	RET
+
+/*
+ * mmuinvalidateaddr(va)
+ *   invalidate tlb entry for virtual page address va, ASID 0
+ */
+TEXT mmuinvalidateaddr(SB), 1, $-4
+	MCR	CpSC, 0, R0, C(CpTLB), C(CpTLBinvu), CpTLBinvse
+	BARRIERS
+	RET
+
+/*
+ * drain write buffer
+ * writeback and invalidate data cache
+ */
+TEXT cachedwbinv(SB), 1, $-4
+	DSB
+	MOVW	$0, R0
+	MCR	CpSC, 0, R0, C(CpCACHE), C(CpCACHEwbi), CpCACHEall
+	RET
+
+/*
+ * cachedwbinvse(va, n)
+ *   drain write buffer
+ *   writeback and invalidate data cache range [va, va+n)
+ */
+TEXT cachedwbinvse(SB), 1, $-4
+	MOVW	R0, R1		/* DSB clears R0 */
+	DSB
+	MOVW	n+4(FP), R2
+	ADD	R1, R2
+	SUB	$1, R2
+	BIC	$(CACHELINESZ-1), R1
+	BIC	$(CACHELINESZ-1), R2
+	MCRR(CpSC, 0, 2, 1, CpCACHERANGEdwbi)
+	RET
+
+/*
+ * cachedwbse(va, n)
+ *   drain write buffer
+ *   writeback data cache range [va, va+n)
+ */
+TEXT cachedwbse(SB), 1, $-4
+	MOVW	R0, R1		/* DSB clears R0 */
+	DSB
+	MOVW	n+4(FP), R2
+	ADD	R1, R2
+	BIC	$(CACHELINESZ-1), R1
+	BIC	$(CACHELINESZ-1), R2
+	MCRR(CpSC, 0, 2, 1, CpCACHERANGEdwb)
+	RET
+
+/*
+ * drain write buffer and prefetch buffer
+ * writeback and invalidate data cache
+ * invalidate instruction cache
+ */
+TEXT cacheuwbinv(SB), 1, $-4
+	BARRIERS
+	MOVW	$0, R0
+	MCR	CpSC, 0, R0, C(CpCACHE), C(CpCACHEwbi), CpCACHEall
+	MCR	CpSC, 0, R0, C(CpCACHE), C(CpCACHEinvi), CpCACHEall
+	RET
+
+/*
+ * invalidate instruction cache
+ */
+TEXT cacheiinv(SB), 1, $-4
+	MOVW	$0, R0
+	MCR	CpSC, 0, R0, C(CpCACHE), C(CpCACHEinvi), CpCACHEall
+	RET

+ 197 - 0
sys/src/9/bcm/lexception.s

@@ -0,0 +1,197 @@
+/*
+ * arm exception handlers
+ */
+#include "arm.s"
+
+/*
+ *  exception vectors, copied by trapinit() to somewhere useful
+ */
+TEXT vectors(SB), 1, $-4
+	MOVW	0x18(R15), R15		/* reset */
+	MOVW	0x18(R15), R15		/* undefined instr. */
+	MOVW	0x18(R15), R15		/* SWI & SMC */
+	MOVW	0x18(R15), R15		/* prefetch abort */
+	MOVW	0x18(R15), R15		/* data abort */
+	MOVW	0x18(R15), R15		/* reserved */
+	MOVW	0x18(R15), R15		/* IRQ */
+	MOVW	0x18(R15), R15		/* FIQ */
+
+TEXT vtable(SB), 1, $-4
+	WORD	$_vsvc(SB)		/* reset, in svc mode already */
+	WORD	$_vund(SB)		/* undefined, switch to svc mode */
+	WORD	$_vsvc(SB)		/* swi, in svc mode already */
+	WORD	$_vpabt(SB)		/* prefetch abort, switch to svc mode */
+	WORD	$_vdabt(SB)		/* data abort, switch to svc mode */
+	WORD	$_vsvc(SB)		/* reserved */
+	WORD	$_virq(SB)		/* IRQ, switch to svc mode */
+	WORD	$_vfiq(SB)		/* FIQ, switch to svc mode */
+
+TEXT _vsvc(SB), 1, $-4			/* SWI */
+	MOVW.W	R14, -4(R13)		/* ureg->pc = interrupted PC */
+	MOVW	SPSR, R14		/* ureg->psr = SPSR */
+	MOVW.W	R14, -4(R13)		/* ... */
+	MOVW	$PsrMsvc, R14		/* ureg->type = PsrMsvc */
+	MOVW.W	R14, -4(R13)		/* ... */
+
+	/* avoid the ambiguity described in notes/movm.w. */
+	MOVM.DB.S [R0-R14], (R13)	/* save user level registers */
+	SUB	$(15*4), R13		/* r13 now points to ureg */
+
+	MOVW	$setR12(SB), R12	/* Make sure we've got the kernel's SB loaded */
+
+//	MOVW	$(KSEG0+16*KiB-MACHSIZE), R10	/* m */
+	MOVW	$(MACHADDR), R10	/* m */
+	MOVW	8(R10), R9		/* up */
+
+	MOVW	R13, R0			/* first arg is pointer to ureg */
+	SUB	$8, R13			/* space for argument+link */
+
+	BL	syscall(SB)
+
+	ADD	$(8+4*15), R13		/* make r13 point to ureg->type */
+	MOVW	8(R13), R14		/* restore link */
+	MOVW	4(R13), R0		/* restore SPSR */
+	MOVW	R0, SPSR		/* ... */
+	MOVM.DB.S (R13), [R0-R14]	/* restore registers */
+	ADD	$8, R13			/* pop past ureg->{type+psr} */
+	RFE				/* MOVM.IA.S.W (R13), [R15] */
+
+TEXT _vund(SB), 1, $-4			/* undefined */
+	MOVM.IA	[R0-R4], (R13)		/* free some working space */
+	MOVW	$PsrMund, R0
+	B	_vswitch
+
+TEXT _vpabt(SB), 1, $-4			/* prefetch abort */
+	MOVM.IA	[R0-R4], (R13)		/* free some working space */
+	MOVW	$PsrMabt, R0		/* r0 = type */
+	B	_vswitch
+
+TEXT _vdabt(SB), 1, $-4			/* data abort */
+	MOVM.IA	[R0-R4], (R13)		/* free some working space */
+	MOVW	$(PsrMabt+1), R0	/* r0 = type */
+	B	_vswitch
+
+TEXT _virq(SB), 1, $-4			/* IRQ */
+	MOVM.IA	[R0-R4], (R13)		/* free some working space */
+	MOVW	$PsrMirq, R0		/* r0 = type */
+	B	_vswitch
+
+	/*
+	 *  come here with type in R0 and R13 pointing above saved [r0-r4].
+	 *  we'll switch to SVC mode and then call trap.
+	 */
+_vswitch:
+	MOVW	SPSR, R1		/* save SPSR for ureg */
+	MOVW	R14, R2			/* save interrupted pc for ureg */
+	MOVW	R13, R3			/* save pointer to where the original [R0-R4] are */
+
+	/*
+	 * switch processor to svc mode.  this switches the banked registers
+	 * (r13 [sp] and r14 [link]) to those of svc mode.
+	 */
+	MOVW	CPSR, R14
+	BIC	$PsrMask, R14
+	ORR	$(PsrDirq|PsrMsvc), R14
+	MOVW	R14, CPSR		/* switch! */
+
+	AND.S	$0xf, R1, R4		/* interrupted code kernel or user? */
+	BEQ	_userexcep
+
+	/* here for trap from SVC mode */
+	MOVM.DB.W [R0-R2], (R13)	/* set ureg->{type, psr, pc}; r13 points to ureg->type  */
+	MOVM.IA	  (R3), [R0-R4]		/* restore [R0-R4] from previous mode's stack */
+
+	/*
+	 * avoid the ambiguity described in notes/movm.w.
+	 * In order to get a predictable value in R13 after the stores,
+	 * separate the store-multiple from the stack-pointer adjustment.
+	 * We'll assume that the old value of R13 should be stored on the stack.
+	 */
+	/* save kernel level registers, at end r13 points to ureg */
+	MOVM.DB	[R0-R14], (R13)
+	SUB	$(15*4), R13		/* SP now points to saved R0 */
+
+	MOVW	$setR12(SB), R12	/* Make sure we've got the kernel's SB loaded */
+
+	MOVW	R13, R0			/* first arg is pointer to ureg */
+	SUB	$(4*2), R13		/* space for argument+link (for debugger) */
+	MOVW	$0xdeaddead, R11	/* marker */
+
+	BL	trap(SB)
+
+	ADD	$(4*2+4*15), R13	/* make r13 point to ureg->type */
+	MOVW	8(R13), R14		/* restore link */
+	MOVW	4(R13), R0		/* restore SPSR */
+	MOVW	R0, SPSR		/* ... */
+
+	MOVM.DB (R13), [R0-R14]		/* restore registers */
+
+	ADD	$(4*2), R13		/* pop past ureg->{type+psr} to pc */
+	RFE				/* MOVM.IA.S.W (R13), [R15] */
+
+	/* here for trap from USER mode */
+_userexcep:
+	MOVM.DB.W [R0-R2], (R13)	/* set ureg->{type, psr, pc}; r13 points to ureg->type  */
+	MOVM.IA	  (R3), [R0-R4]		/* restore [R0-R4] from previous mode's stack */
+
+	/* avoid the ambiguity described in notes/movm.w. */
+	MOVM.DB.S [R0-R14], (R13)	/* save kernel level registers */
+	SUB	$(15*4), R13		/* r13 now points to ureg */
+
+	MOVW	$setR12(SB), R12	/* Make sure we've got the kernel's SB loaded */
+
+//	MOVW	$(KSEG0+16*KiB-MACHSIZE), R10	/* m */
+	MOVW	$(MACHADDR), R10	/* m */
+	MOVW	8(R10), R9		/* up */
+
+	MOVW	R13, R0			/* first arg is pointer to ureg */
+	SUB	$(4*2), R13		/* space for argument+link (for debugger) */
+
+	BL	trap(SB)
+
+	ADD	$(4*2+4*15), R13	/* make r13 point to ureg->type */
+	MOVW	8(R13), R14		/* restore link */
+	MOVW	4(R13), R0		/* restore SPSR */
+	MOVW	R0, SPSR		/* ... */
+	MOVM.DB.S (R13), [R0-R14]	/* restore registers */
+	ADD	$(4*2), R13		/* pop past ureg->{type+psr} */
+	RFE				/* MOVM.IA.S.W (R13), [R15] */
+
+TEXT _vfiq(SB), 1, $-4			/* FIQ */
+	MOVW	$PsrMfiq, R8		/* trap type */
+	MOVW	SPSR, R9		/* interrupted psr */
+	MOVW	R14, R10		/* interrupted pc */
+	MOVM.DB.W [R8-R10], (R13)	/* save in ureg */
+	MOVM.DB.W.S [R0-R14], (R13)	/* save interrupted regs */
+	MOVW	$setR12(SB), R12	/* Make sure we've got the kernel's SB loaded */
+	MOVW	$(MACHADDR), R10	/* m */
+	MOVW	8(R10), R9		/* up */
+	MOVW	R13, R0			/* first arg is pointer to ureg */
+	SUB	$(4*2), R13		/* space for argument+link (for debugger) */
+
+	BL	fiq(SB)
+
+	ADD	$(8+4*15), R13		/* make r13 point to ureg->type */
+	MOVW	8(R13), R14		/* restore link */
+	MOVW	4(R13), R0		/* restore SPSR */
+	MOVW	R0, SPSR		/* ... */
+	MOVM.DB.S (R13), [R0-R14]	/* restore registers */
+	ADD	$8, R13			/* pop past ureg->{type+psr} */
+	RFE				/* MOVM.IA.S.W (R13), [R15] */
+
+/*
+ *  set the stack value for the mode passed in R0
+ */
+TEXT setr13(SB), 1, $-4
+	MOVW	4(FP), R1
+
+	MOVW	CPSR, R2
+	BIC	$PsrMask, R2, R3
+	ORR	R0, R3
+	MOVW	R3, CPSR		/* switch to new mode */
+
+	MOVW	R13, R0			/* return old sp */
+	MOVW	R1, R13			/* install new one */
+
+	MOVW	R2, CPSR		/* switch back to old mode */
+	RET

+ 1 - 0
sys/src/9/bcm/lproc.s

@@ -0,0 +1 @@
+#include "../omap/lproc.s"

+ 597 - 0
sys/src/9/bcm/main.c

@@ -0,0 +1,597 @@
+#include "u.h"
+#include "tos.h"
+#include "../port/lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+
+#include "init.h"
+#include <pool.h>
+
+#include "reboot.h"
+
+/* Firmware compatibility */
+#define	Minfirmrev	326770
+#define	Minfirmdate	"22 Jul 2012"
+
+/*
+ * Where configuration info is left for the loaded programme.
+ */
+#define BOOTARGS	((char*)CONFADDR)
+#define	BOOTARGSLEN	(MACHADDR-CONFADDR)
+#define	MAXCONF		64
+#define MAXCONFLINE	160
+
+uintptr kseg0 = KZERO;
+Mach*	machaddr[MAXMACH];
+Conf	conf;
+ulong	memsize = 128*1024*1024;
+
+/*
+ * Option arguments from the command line.
+ * oargv[0] is the boot file.
+ */
+static int oargc;
+static char* oargv[20];
+static char oargb[128];
+static int oargblen;
+
+static uintptr sp;		/* XXX - must go - user stack of init proc */
+
+/* store plan9.ini contents here at least until we stash them in #ec */
+static char confname[MAXCONF][KNAMELEN];
+static char confval[MAXCONF][MAXCONFLINE];
+static int nconf;
+
+typedef struct Atag Atag;
+struct Atag {
+	u32int	size;	/* size of atag in words, including this header */
+	u32int	tag;	/* atag type */
+	union {
+		u32int	data[1];	/* actually [size-2] */
+		/* AtagMem */
+		struct {
+			u32int	size;
+			u32int	base;
+		} mem;
+		/* AtagCmdLine */
+		char	cmdline[1];	/* actually [4*(size-2)] */
+	};
+};
+
+enum {
+	AtagNone	= 0x00000000,
+	AtagCore	= 0x54410001,
+	AtagMem		= 0x54410002,
+	AtagCmdline	= 0x54410009,
+};
+
+static int
+findconf(char *name)
+{
+	int i;
+
+	for(i = 0; i < nconf; i++)
+		if(cistrcmp(confname[i], name) == 0)
+			return i;
+	return -1;
+}
+
+char*
+getconf(char *name)
+{
+	int i;
+
+	i = findconf(name);
+	if(i >= 0)
+		return confval[i];
+	return nil;
+}
+
+void
+addconf(char *name, char *val)
+{
+	int i;
+
+	i = findconf(name);
+	if(i < 0){
+		if(val == nil || nconf >= MAXCONF)
+			return;
+		i = nconf++;
+		strecpy(confname[i], confname[i]+sizeof(confname[i]), name);
+	}
+	strecpy(confval[i], confval[i]+sizeof(confval[i]), val);
+}
+
+static void
+writeconf(void)
+{
+	char *p, *q;
+	int n;
+
+	p = getconfenv();
+
+	if(waserror()) {
+		free(p);
+		nexterror();
+	}
+
+	/* convert to name=value\n format */
+	for(q=p; *q; q++) {
+		q += strlen(q);
+		*q = '=';
+		q += strlen(q);
+		*q = '\n';
+	}
+	n = q - p + 1;
+	if(n >= BOOTARGSLEN)
+		error("kernel configuration too large");
+	memmove(BOOTARGS, p, n);
+	memset(BOOTARGS + n, '\n', BOOTARGSLEN - n);
+	poperror();
+	free(p);
+}
+
+static void
+plan9iniinit(char *s, int cmdline)
+{
+	char *toks[MAXCONF];
+	int i, c, n;
+	char *v;
+
+	if((c = *s) < ' ' || c >= 0x80)
+		return;
+	if(cmdline)
+		n = tokenize(s, toks, MAXCONF);
+	else
+		n = getfields(s, toks, MAXCONF, 1, "\n");
+	for(i = 0; i < n; i++){
+		if(toks[i][0] == '#')
+			continue;
+		v = strchr(toks[i], '=');
+		if(v == nil)
+			continue;
+		*v++ = '\0';
+		addconf(toks[i], v);
+	}
+}
+
+static void
+ataginit(Atag *a)
+{
+	int n;
+
+	if(a->tag != AtagCore){
+		plan9iniinit((char*)a, 0);
+		return;
+	}
+	while(a->tag != AtagNone){
+		switch(a->tag){
+		case AtagMem:
+			/* use only first bank */
+			if(conf.mem[0].limit == 0 && a->mem.size != 0){
+				memsize = a->mem.size;
+				conf.mem[0].base = a->mem.base;
+				conf.mem[0].limit = a->mem.base + memsize;
+			}
+			break;
+		case AtagCmdline:
+			n = (a->size * sizeof(u32int)) - offsetof(Atag, cmdline[0]);
+			if(a->cmdline + n < BOOTARGS + BOOTARGSLEN)
+				a->cmdline[n] = 0;
+			else
+				BOOTARGS[BOOTARGSLEN-1] = 0;
+			plan9iniinit(a->cmdline, 1);
+			break;
+		}
+		a = (Atag*)((u32int*)a + a->size);
+	}
+}
+
+void
+machinit(void)
+{
+	m->machno = 0;
+	machaddr[m->machno] = m;
+
+	m->ticks = 1;
+	m->perf.period = 1;
+
+	conf.nmach = 1;
+
+	active.machs = 1;
+	active.exiting = 0;
+
+	up = nil;
+}
+
+static void
+optionsinit(char* s)
+{
+	strecpy(oargb, oargb+sizeof(oargb), s);
+
+	oargblen = strlen(oargb);
+	oargc = tokenize(oargb, oargv, nelem(oargv)-1);
+	oargv[oargc] = nil;
+}
+
+void
+main(void)
+{
+	extern char edata[], end[];
+	uint rev;
+
+	okay(1);
+	m = (Mach*)MACHADDR;
+	memset(edata, 0, end - edata);	/* clear bss */
+	machinit();
+	mmuinit1();
+
+	optionsinit("/boot/boot boot");
+	quotefmtinstall();
+	
+	ataginit((Atag*)BOOTARGS);
+	confinit();		/* figures out amount of memory */
+	xinit();
+	uartconsinit();
+	screeninit();
+
+	print("\nPlan 9 from Bell Labs\n");
+	rev = getfirmware();
+	print("firmware: rev %d\n", rev);
+	if(rev < Minfirmrev){
+		print("Sorry, firmware (start.elf) must be at least rev %d (%s)\n",
+			Minfirmrev, Minfirmdate);
+		for(;;)
+			;
+	}
+	trapinit();
+	clockinit();
+	printinit();
+	timersinit();
+	if(conf.monitor)
+		swcursorinit();
+	cpuidprint();
+	archreset();
+
+	procinit0();
+	initseg();
+	links();
+	chandevreset();			/* most devices are discovered here */
+	pageinit();
+	swapinit();
+	userinit();
+	schedinit();
+	assert(0);			/* shouldn't have returned */
+}
+
+/*
+ *  starting place for first process
+ */
+void
+init0(void)
+{
+	int i;
+	char buf[2*KNAMELEN];
+
+	up->nerrlab = 0;
+	coherence();
+	spllo();
+
+	/*
+	 * These are o.k. because rootinit is null.
+	 * Then early kproc's will have a root and dot.
+	 */
+	up->slash = namec("#/", Atodir, 0, 0);
+	pathclose(up->slash->path);
+	up->slash->path = newpath("/");
+	up->dot = cclone(up->slash);
+
+	chandevinit();
+
+	if(!waserror()){
+		snprint(buf, sizeof(buf), "%s %s", "ARM", conffile);
+		ksetenv("terminal", buf, 0);
+		ksetenv("cputype", "arm", 0);
+		if(cpuserver)
+			ksetenv("service", "cpu", 0);
+		else
+			ksetenv("service", "terminal", 0);
+		snprint(buf, sizeof(buf), "-a %s", getethermac());
+		ksetenv("etherargs", buf, 0);
+
+		/* convert plan9.ini variables to #e and #ec */
+		for(i = 0; i < nconf; i++) {
+			ksetenv(confname[i], confval[i], 0);
+			ksetenv(confname[i], confval[i], 1);
+		}
+		poperror();
+	}
+	kproc("alarm", alarmkproc, 0);
+	touser(sp);
+	assert(0);			/* shouldn't have returned */
+}
+
+static void
+bootargs(uintptr base)
+{
+	int i;
+	ulong ssize;
+	char **av, *p;
+
+	/*
+	 * Push the boot args onto the stack.
+	 * The initial value of the user stack must be such
+	 * that the total used is larger than the maximum size
+	 * of the argument list checked in syscall.
+	 */
+	i = oargblen+1;
+	p = UINT2PTR(STACKALIGN(base + BY2PG - sizeof(Tos) - i));
+	memmove(p, oargb, i);
+
+	/*
+	 * Now push the argv pointers.
+	 * The code jumped to by touser in lproc.s expects arguments
+	 *	main(char* argv0, ...)
+	 * and calls
+	 * 	startboot("/boot/boot", &argv0)
+	 * not the usual (int argc, char* argv[])
+	 */
+	av = (char**)(p - (oargc+1)*sizeof(char*));
+	ssize = base + BY2PG - PTR2UINT(av);
+	for(i = 0; i < oargc; i++)
+		*av++ = (oargv[i] - oargb) + (p - base) + (USTKTOP - BY2PG);
+	*av = nil;
+	sp = USTKTOP - ssize;
+}
+
+/*
+ *  create the first process
+ */
+void
+userinit(void)
+{
+	Proc *p;
+	Segment *s;
+	KMap *k;
+	Page *pg;
+
+	/* no processes yet */
+	up = nil;
+
+	p = newproc();
+	p->pgrp = newpgrp();
+	p->egrp = smalloc(sizeof(Egrp));
+	p->egrp->ref = 1;
+	p->fgrp = dupfgrp(nil);
+	p->rgrp = newrgrp();
+	p->procmode = 0640;
+
+	kstrdup(&eve, "");
+	kstrdup(&p->text, "*init*");
+	kstrdup(&p->user, eve);
+
+	/*
+	 * Kernel Stack
+	 */
+	p->sched.pc = PTR2UINT(init0);
+	p->sched.sp = PTR2UINT(p->kstack+KSTACK-sizeof(up->s.args)-sizeof(uintptr));
+	p->sched.sp = STACKALIGN(p->sched.sp);
+
+	/*
+	 * User Stack
+	 *
+	 * Technically, newpage can't be called here because it
+	 * should only be called when in a user context as it may
+	 * try to sleep if there are no pages available, but that
+	 * shouldn't be the case here.
+	 */
+	s = newseg(SG_STACK, USTKTOP-USTKSIZE, USTKSIZE/BY2PG);
+	s->flushme++;
+	p->seg[SSEG] = s;
+	pg = newpage(1, 0, USTKTOP-BY2PG);
+	segpage(s, pg);
+	k = kmap(pg);
+	bootargs(VA(k));
+	kunmap(k);
+
+	/*
+	 * Text
+	 */
+	s = newseg(SG_TEXT, UTZERO, 1);
+	p->seg[TSEG] = s;
+	pg = newpage(1, 0, UTZERO);
+	memset(pg->cachectl, PG_TXTFLUSH, sizeof(pg->cachectl));
+	segpage(s, pg);
+	k = kmap(s->map[0]->pages[0]);
+	memmove(UINT2PTR(VA(k)), initcode, sizeof initcode);
+	kunmap(k);
+
+	ready(p);
+}
+
+void
+confinit(void)
+{
+	int i;
+	ulong kpages;
+	uintptr pa;
+	char *p;
+
+	if(0 && (p = getconf("service")) != nil){
+		if(strcmp(p, "cpu") == 0)
+			cpuserver = 1;
+		else if(strcmp(p,"terminal") == 0)
+			cpuserver = 0;
+	}
+	if((p = getconf("*maxmem")) != nil){
+		memsize = strtoul(p, 0, 0) - PHYSDRAM;
+		if (memsize < 16*MB)		/* sanity */
+			memsize = 16*MB;
+	}
+
+	getramsize(&conf.mem[0]);
+	if(conf.mem[0].limit == 0){
+		conf.mem[0].base = PHYSDRAM;
+		conf.mem[0].limit = PHYSDRAM + memsize;
+	}else if(p != nil)
+		conf.mem[0].limit = conf.mem[0].base + memsize;
+
+	conf.npage = 0;
+	pa = PADDR(PGROUND(PTR2UINT(end)));
+
+	/*
+	 *  we assume that the kernel is at the beginning of one of the
+	 *  contiguous chunks of memory and fits therein.
+	 */
+	for(i=0; i<nelem(conf.mem); i++){
+		/* take kernel out of allocatable space */
+		if(pa > conf.mem[i].base && pa < conf.mem[i].limit)
+			conf.mem[i].base = pa;
+
+		conf.mem[i].npage = (conf.mem[i].limit - conf.mem[i].base)/BY2PG;
+		conf.npage += conf.mem[i].npage;
+	}
+
+	conf.upages = (conf.npage*80)/100;
+	conf.ialloc = ((conf.npage-conf.upages)/2)*BY2PG;
+
+	/* only one processor */
+	conf.nmach = 1;
+
+	/* set up other configuration parameters */
+	conf.nproc = 100 + ((conf.npage*BY2PG)/MB)*5;
+	if(cpuserver)
+		conf.nproc *= 3;
+	if(conf.nproc > 2000)
+		conf.nproc = 2000;
+	conf.nswap = conf.npage*3;
+	conf.nswppo = 4096;
+	conf.nimage = 200;
+
+	conf.copymode = 0;		/* copy on write */
+
+	/*
+	 * Guess how much is taken by the large permanent
+	 * datastructures. Mntcache and Mntrpc are not accounted for
+	 * (probably ~300KB).
+	 */
+	kpages = conf.npage - conf.upages;
+	kpages *= BY2PG;
+	kpages -= conf.upages*sizeof(Page)
+		+ conf.nproc*sizeof(Proc)
+		+ conf.nimage*sizeof(Image)
+		+ conf.nswap
+		+ conf.nswppo*sizeof(Page);
+	mainmem->maxsize = kpages;
+	if(!cpuserver)
+		/*
+		 * give terminals lots of image memory, too; the dynamic
+		 * allocation will balance the load properly, hopefully.
+		 * be careful with 32-bit overflow.
+		 */
+		imagmem->maxsize = kpages;
+
+}
+
+static void
+shutdown(int ispanic)
+{
+	int ms, once;
+
+	lock(&active);
+	if(ispanic)
+		active.ispanic = ispanic;
+	else if(m->machno == 0 && (active.machs & (1<<m->machno)) == 0)
+		active.ispanic = 0;
+	once = active.machs & (1<<m->machno);
+	active.machs &= ~(1<<m->machno);
+	active.exiting = 1;
+	unlock(&active);
+
+	if(once)
+		iprint("cpu%d: exiting\n", m->machno);
+	spllo();
+	for(ms = 5*1000; ms > 0; ms -= TK2MS(2)){
+		delay(TK2MS(2));
+		if(active.machs == 0 && consactive() == 0)
+			break;
+	}
+	delay(1000);
+}
+
+/*
+ *  exit kernel either on a panic or user request
+ */
+void
+exit(int code)
+{
+	shutdown(code);
+	splfhi();
+	archreboot();
+}
+
+/*
+ * stub for ../omap/devether.c
+ */
+int
+isaconfig(char *class, int ctlrno, ISAConf *isa)
+{
+	USED(ctlrno);
+	USED(isa);
+	return strcmp(class, "ether") == 0;
+}
+
+/*
+ * the new kernel is already loaded at address `code'
+ * of size `size' and entry point `entry'.
+ */
+void
+reboot(void *entry, void *code, ulong size)
+{
+	void (*f)(ulong, ulong, ulong);
+
+	print("starting reboot...");
+	writeconf();
+	shutdown(0);
+
+	/*
+	 * should be the only processor running now
+	 */
+
+	print("reboot entry %#lux code %#lux size %ld\n",
+		PADDR(entry), PADDR(code), size);
+	delay(100);
+
+	/* turn off buffered serial console */
+	serialoq = nil;
+	kprintoq = nil;
+	screenputs = nil;
+
+	/* shutdown devices */
+	chandevshutdown();
+
+	/* stop the clock (and watchdog if any) */
+	clockshutdown();
+
+	splfhi();
+	intrsoff();
+
+	/* setup reboot trampoline function */
+	f = (void*)REBOOTADDR;
+	memmove(f, rebootcode, sizeof(rebootcode));
+	cacheuwbinv();
+
+	/* off we go - never to return */
+	(*f)(PADDR(entry), PADDR(code), size);
+
+	iprint("loaded kernel returned!\n");
+	delay(1000);
+	archreboot();
+}
+
+int
+cmpswap(long *addr, long old, long new)
+{
+	return cas32(addr, old, new);
+}

+ 102 - 0
sys/src/9/bcm/mem.h

@@ -0,0 +1,102 @@
+/*
+ * Memory and machine-specific definitions.  Used in C and assembler.
+ */
+#define KiB		1024u			/* Kibi 0x0000000000000400 */
+#define MiB		1048576u		/* Mebi 0x0000000000100000 */
+#define GiB		1073741824u		/* Gibi 000000000040000000 */
+
+#define HOWMANY(x, y)	(((x)+((y)-1))/(y))
+#define ROUNDUP(x, y)	(HOWMANY((x), (y))*(y))	/* ceiling */
+#define ROUNDDN(x, y)	(((x)/(y))*(y))		/* floor */
+#define MIN(a, b)	((a) < (b)? (a): (b))
+#define MAX(a, b)	((a) > (b)? (a): (b))
+
+/*
+ * Sizes
+ */
+#define	BY2PG		(4*KiB)			/* bytes per page */
+#define	PGSHIFT		12			/* log(BY2PG) */
+#define	PGROUND(s)	ROUNDUP(s, BY2PG)
+#define	ROUND(s, sz)	(((s)+(sz-1))&~(sz-1))
+
+#define	MAXMACH		1			/* max # cpus system can run */
+#define	MACHSIZE	BY2PG
+
+#define KSTKSIZE	(8*KiB)
+#define STACKALIGN(sp)	((sp) & ~3)		/* bug: assure with alloc */
+
+/*
+ * Address spaces.
+ * KTZERO is used by kprof and dumpstack (if any).
+ *
+ * KZERO is mapped to physical 0 (start of ram).
+ *
+ * vectors are at 0, plan9.ini is at KZERO+256 and is limited to 16K by
+ * devenv.
+ */
+
+#define	KSEG0		0x80000000		/* kernel segment */
+/* mask to check segment; good for 512MB dram */
+#define	KSEGM		0xE0000000
+#define	KZERO		KSEG0			/* kernel address space */
+#define CONFADDR	(KZERO+0x100)		/* unparsed plan9.ini */
+#define	MACHADDR	(KZERO+0x2000)		/* Mach structure */
+#define	L2		(KZERO+0x3000)		/* L2 ptes for vectors etc */
+#define	VCBUFFER	(KZERO+0x3400)		/* videocore mailbox buffer */
+#define	FIQSTKTOP	(KZERO+0x4000)		/* FIQ stack */
+#define	L1		(KZERO+0x4000)		/* tt ptes: 16KiB aligned */
+#define	KTZERO		(KZERO+0x8000)		/* kernel text start */
+#define VIRTIO		0x7E000000		/* i/o registers */
+#define	FRAMEBUFFER	0xA0000000		/* video framebuffer */
+
+#define	UZERO		0			/* user segment */
+#define	UTZERO		(UZERO+BY2PG)		/* user text start */
+#define	USTKTOP		0x20000000		/* user segment end +1 */
+#define	USTKSIZE	(8*1024*1024)		/* user stack size */
+#define	TSTKTOP		(USTKTOP-USTKSIZE)	/* sysexec temporary stack */
+#define	TSTKSIZ	 	256
+
+/* address at which to copy and execute rebootcode */
+#define	REBOOTADDR	(KZERO+0x3400)
+
+/*
+ * Legacy...
+ */
+#define BLOCKALIGN	32			/* only used in allocb.c */
+#define KSTACK		KSTKSIZE
+
+/*
+ * Sizes
+ */
+#define BI2BY		8			/* bits per byte */
+#define BY2SE		4
+#define BY2WD		4
+#define BY2V		8			/* only used in xalloc.c */
+
+#define CACHELINESZ	32
+#define	PTEMAPMEM	(1024*1024)
+#define	PTEPERTAB	(PTEMAPMEM/BY2PG)
+#define	SEGMAPSIZE	1984
+#define	SSEGMAPSIZE	16
+#define	PPN(x)		((x)&~(BY2PG-1))
+
+/*
+ * With a little work these move to port.
+ */
+#define	PTEVALID	(1<<0)
+#define	PTERONLY	0
+#define	PTEWRITE	(1<<1)
+#define	PTEUNCACHED	(1<<2)
+#define PTEKERNEL	(1<<3)
+
+/*
+ * Physical machine information from here on.
+ *	PHYS addresses as seen from the arm cpu.
+ *	BUS  addresses as seen from the videocore gpu.
+ */
+#define	PHYSDRAM	0
+#define BUSDRAM		0x40000000
+#define	DRAMSIZE	(512*MiB)
+#define	PHYSIO		0x20000000
+#define	BUSIO		0x7E000000
+#define	IOSIZE		(16*MiB)

+ 137 - 0
sys/src/9/bcm/mkfile

@@ -0,0 +1,137 @@
+CONF=pi
+CONFLIST=pi picpu pifat
+EXTRACOPIES=piestand lookout boundary # bovril
+
+loadaddr=0x80008000
+
+objtype=arm
+</$objtype/mkfile
+p=9
+
+DEVS=`{rc ../port/mkdevlist $CONF}
+
+PORT=\
+	alarm.$O\
+	alloc.$O\
+	allocb.$O\
+	auth.$O\
+	cache.$O\
+	chan.$O\
+	dev.$O\
+	edf.$O\
+	fault.$O\
+	mul64fract.$O\
+	page.$O\
+	parse.$O\
+	pgrp.$O\
+	portclock.$O\
+	print.$O\
+	proc.$O\
+	qio.$O\
+	qlock.$O\
+	rdb.$O\
+	rebootcmd.$O\
+	segment.$O\
+	swap.$O\
+	syscallfmt.$O\
+	sysfile.$O\
+	sysproc.$O\
+	taslock.$O\
+	tod.$O\
+	xalloc.$O\
+
+OBJ=\
+	l.$O\
+	lexception.$O\
+	lproc.$O\
+	arch.$O\
+	clock.$O\
+	fpi.$O\
+	fpiarm.$O\
+	fpimem.$O\
+	main.$O\
+	mmu.$O\
+	random.$O\
+	syscall.$O\
+	trap.$O\
+	$CONF.root.$O\
+	$CONF.rootc.$O\
+	$DEVS\
+	$PORT\
+
+# HFILES=
+
+LIB=\
+	/$objtype/lib/libmemlayer.a\
+	/$objtype/lib/libmemdraw.a\
+	/$objtype/lib/libdraw.a\
+	/$objtype/lib/libip.a\
+	/$objtype/lib/libsec.a\
+	/$objtype/lib/libmp.a\
+	/$objtype/lib/libc.a\
+
+9:V: $p$CONF s$p$CONF
+
+$p$CONF:DQ:	$CONF.c $OBJ $LIB mkfile
+	$CC $CFLAGS '-DKERNDATE='`{date -n} $CONF.c
+	echo '# linking raw kernel'	# H6: no headers, data segment aligned
+	$LD -l -o $target -H6 -R4096 -T$loadaddr $OBJ $CONF.$O $LIB
+
+s$p$CONF:DQ:	$CONF.$O $OBJ $LIB
+	echo '# linking kernel with symbols'
+	$LD -l -o $target -R4096 -T$loadaddr $OBJ $CONF.$O $LIB
+	size $target
+
+$p$CONF.gz:D:	$p$CONF
+	gzip -9 <$p$CONF >$target
+
+$OBJ: $HFILES
+
+install:V: /$objtype/$p$CONF
+
+/$objtype/$p$CONF:D: $p$CONF s$p$CONF
+	cp -x $p$CONF s$p$CONF /$objtype/ &
+	for(i in $EXTRACOPIES)
+		{ 9fs $i && cp $p$CONF s$p$CONF /n/$i/$objtype && echo -n $i... & }
+	wait
+	echo
+	touch $target
+
+<../boot/bootmkfile
+<../port/portmkfile
+<|../port/mkbootrules $CONF
+
+arch.$O clock.$O fpiarm.$O main.$O mmu.$O screen.$O syscall.$O trap.$O: \
+	/$objtype/include/ureg.h
+
+archbcm.$O devether.$0: etherif.h ../port/netif.h
+archbcm.$O: ../port/flashif.h
+fpi.$O fpiarm.$O fpimem.$O: fpi.h
+l.$O lexception.$O lproc.$O mmu.$O: arm.s mem.h
+main.$O: errstr.h init.h reboot.h
+devmouse.$O mouse.$O screen.$O: screen.h
+devusb.$O: ../port/usb.h
+usbehci.$O usbohci.$O usbuhci.$O: ../port/usb.h usbehci.h uncached.h
+
+init.h:D:	../port/initcode.c init9.s
+	$CC ../port/initcode.c
+	$AS init9.s
+	$LD -l -R1 -s -o init.out init9.$O initcode.$O /$objtype/lib/libc.a
+	{echo 'uchar initcode[]={'
+	 xd -1x <init.out |
+		sed -e 's/^[0-9a-f]+ //' -e 's/ ([0-9a-f][0-9a-f])/0x\1,/g'
+	 echo '};'} > init.h
+
+reboot.h:D:	rebootcode.s arm.s arm.h mem.h
+	$AS rebootcode.s
+	# -lc is only for memmove.  -T arg is PADDR(REBOOTADDR)
+	$LD -l -s -T0x3400 -R4 -o reboot.out rebootcode.$O -lc
+	{echo 'uchar rebootcode[]={'
+	 xd -1x reboot.out |
+		sed -e '1,2d' -e 's/^[0-9a-f]+ //' -e 's/ ([0-9a-f][0-9a-f])/0x\1,/g'
+	 echo '};'} > reboot.h
+errstr.h:D:	../port/mkerrstr ../port/error.h
+	rc ../port/mkerrstr > errstr.h
+
+$CONF.clean:
+	rm -rf $p$CONF s$p$CONF errstr.h reboot.h $CONF.c boot$CONF.c

+ 319 - 0
sys/src/9/bcm/mmu.c

@@ -0,0 +1,319 @@
+#include "u.h"
+#include "../port/lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+
+#include "arm.h"
+
+#define FEXT(d, o, w)	(((d)>>(o)) & ((1<<(w))-1))
+#define L1X(va)		FEXT((va), 20, 12)
+#define L2X(va)		FEXT((va), 12, 8)
+
+enum {
+	L1lo		= UZERO/MiB,		/* L1X(UZERO)? */
+	L1hi		= (USTKTOP+MiB-1)/MiB,	/* L1X(USTKTOP+MiB-1)? */
+};
+
+void
+mmuinit(void)
+{
+	PTE *l1, *l2;
+	uintptr pa, va;
+
+	l1 = (PTE*)PADDR(L1);
+	l2 = (PTE*)PADDR(L2);
+
+	/*
+	 * map all of ram at KZERO
+	 */
+	va = KZERO;
+	for(pa = PHYSDRAM; pa < PHYSDRAM+DRAMSIZE; pa += MiB){
+		l1[L1X(va)] = pa|Dom0|L1AP(Krw)|Section|Cached|Buffered;
+		va += MiB;
+	}
+
+	/*
+	 * identity map first MB of ram so mmu can be enabled
+	 */
+	l1[L1X(PHYSDRAM)] = PHYSDRAM|Dom0|L1AP(Krw)|Section|Cached|Buffered;
+
+	/*
+	 * map i/o registers 
+	 */
+	va = VIRTIO;
+	for(pa = PHYSIO; pa < PHYSIO+IOSIZE; pa += MiB){
+		l1[L1X(va)] = pa|Dom0|L1AP(Krw)|Section;
+		va += MiB;
+	}
+
+	/*
+	 * double map exception vectors at top of virtual memory
+	 */
+	va = HVECTORS;
+	l1[L1X(va)] = (uintptr)l2|Dom0|Coarse;
+	l2[L2X(va)] = PHYSDRAM|L2AP(Krw)|Small;
+}
+
+void
+mmuinit1(void)
+{
+	PTE *l1;
+
+	l1 = (PTE*)L1;
+	m->mmul1 = l1;
+
+	/*
+	 * undo identity map of first MB of ram
+	 */
+	l1[L1X(PHYSDRAM)] = 0;
+	cachedwbse(&l1[L1X(PHYSDRAM)], sizeof(PTE));
+	mmuinvalidate();
+}
+
+static void
+mmul2empty(Proc* proc, int clear)
+{
+	PTE *l1;
+	Page **l2, *page;
+
+	l1 = m->mmul1;
+	l2 = &proc->mmul2;
+	for(page = *l2; page != nil; page = page->next){
+		if(clear)
+			memset(UINT2PTR(page->va), 0, BY2PG);
+		l1[page->daddr] = Fault;
+		l2 = &page->next;
+	}
+	*l2 = proc->mmul2cache;
+	proc->mmul2cache = proc->mmul2;
+	proc->mmul2 = nil;
+}
+
+static void
+mmul1empty(void)
+{
+#ifdef notdef
+/* there's a bug in here */
+	PTE *l1;
+
+	/* clean out any user mappings still in l1 */
+	if(m->mmul1lo > L1lo){
+		if(m->mmul1lo == 1)
+			m->mmul1[L1lo] = Fault;
+		else
+			memset(&m->mmul1[L1lo], 0, m->mmul1lo*sizeof(PTE));
+		m->mmul1lo = L1lo;
+	}
+	if(m->mmul1hi < L1hi){
+		l1 = &m->mmul1[m->mmul1hi];
+		if((L1hi - m->mmul1hi) == 1)
+			*l1 = Fault;
+		else
+			memset(l1, 0, (L1hi - m->mmul1hi)*sizeof(PTE));
+		m->mmul1hi = L1hi;
+	}
+#else
+	memset(&m->mmul1[L1lo], 0, (L1hi - L1lo)*sizeof(PTE));
+#endif /* notdef */
+}
+
+void
+mmuswitch(Proc* proc)
+{
+	int x;
+	PTE *l1;
+	Page *page;
+
+	/* do kprocs get here and if so, do they need to? */
+	if(m->mmupid == proc->pid && !proc->newtlb)
+		return;
+	m->mmupid = proc->pid;
+
+	/* write back dirty and invalidate l1 caches */
+	cacheuwbinv();
+
+	if(proc->newtlb){
+		mmul2empty(proc, 1);
+		proc->newtlb = 0;
+	}
+
+	mmul1empty();
+
+	/* move in new map */
+	l1 = m->mmul1;
+	for(page = proc->mmul2; page != nil; page = page->next){
+		x = page->daddr;
+		l1[x] = PPN(page->pa)|Dom0|Coarse;
+		/* know here that L1lo < x < L1hi */
+		if(x+1 - m->mmul1lo < m->mmul1hi - x)
+			m->mmul1lo = x+1;
+		else
+			m->mmul1hi = x;
+	}
+
+	/* make sure map is in memory */
+	/* could be smarter about how much? */
+	cachedwbse(&l1[L1X(UZERO)], (L1hi - L1lo)*sizeof(PTE));
+
+	/* lose any possible stale tlb entries */
+	mmuinvalidate();
+}
+
+void
+flushmmu(void)
+{
+	int s;
+
+	s = splhi();
+	up->newtlb = 1;
+	mmuswitch(up);
+	splx(s);
+}
+
+void
+mmurelease(Proc* proc)
+{
+	Page *page, *next;
+
+	/* write back dirty and invalidate l1 caches */
+	cacheuwbinv();
+
+	mmul2empty(proc, 0);
+	for(page = proc->mmul2cache; page != nil; page = next){
+		next = page->next;
+		if(--page->ref)
+			panic("mmurelease: page->ref %d", page->ref);
+		pagechainhead(page);
+	}
+	if(proc->mmul2cache && palloc.r.p)
+		wakeup(&palloc.r);
+	proc->mmul2cache = nil;
+
+	mmul1empty();
+
+	/* make sure map is in memory */
+	/* could be smarter about how much? */
+	cachedwbse(&m->mmul1[L1X(UZERO)], (L1hi - L1lo)*sizeof(PTE));
+
+	/* lose any possible stale tlb entries */
+	mmuinvalidate();
+}
+
+void
+putmmu(uintptr va, uintptr pa, Page* page)
+{
+	int x;
+	Page *pg;
+	PTE *l1, *pte;
+
+	x = L1X(va);
+	l1 = &m->mmul1[x];
+	if(*l1 == Fault){
+		/* wasteful - l2 pages only have 256 entries - fix */
+		if(up->mmul2cache == nil){
+			/* auxpg since we don't need much? memset if so */
+			pg = newpage(1, 0, 0);
+			pg->va = VA(kmap(pg));
+		}
+		else{
+			pg = up->mmul2cache;
+			up->mmul2cache = pg->next;
+			memset(UINT2PTR(pg->va), 0, BY2PG);
+		}
+		pg->daddr = x;
+		pg->next = up->mmul2;
+		up->mmul2 = pg;
+
+		/* force l2 page to memory */
+		cachedwbse((void *)pg->va, BY2PG);
+
+		*l1 = PPN(pg->pa)|Dom0|Coarse;
+		cachedwbse(l1, sizeof *l1);
+
+		if(x >= m->mmul1lo && x < m->mmul1hi){
+			if(x+1 - m->mmul1lo < m->mmul1hi - x)
+				m->mmul1lo = x+1;
+			else
+				m->mmul1hi = x;
+		}
+	}
+	pte = UINT2PTR(KADDR(PPN(*l1)));
+
+	/* protection bits are
+	 *	PTERONLY|PTEVALID;
+	 *	PTEWRITE|PTEVALID;
+	 *	PTEWRITE|PTEUNCACHED|PTEVALID;
+	 */
+	x = Small;
+	if(!(pa & PTEUNCACHED))
+		x |= Cached|Buffered;
+	if(pa & PTEWRITE)
+		x |= L2AP(Urw);
+	else
+		x |= L2AP(Uro);
+	pte[L2X(va)] = PPN(pa)|x;
+	cachedwbse(&pte[L2X(va)], sizeof pte[0]);
+
+	/* clear out the current entry */
+	mmuinvalidateaddr(PPN(va));
+
+	/*  write back dirty entries - we need this because the pio() in
+	 *  fault.c is writing via a different virt addr and won't clean
+	 *  its changes out of the dcache.  Page coloring doesn't work
+	 *  on this mmu because the virtual cache is set associative
+	 *  rather than direct mapped.
+	 */
+	cachedwbinv();
+	if(page->cachectl[0] == PG_TXTFLUSH){
+		/* pio() sets PG_TXTFLUSH whenever a text pg has been written */
+		cacheiinv();
+		page->cachectl[0] = PG_NOFLUSH;
+	}
+	checkmmu(va, PPN(pa));
+}
+
+/*
+ * Return the number of bytes that can be accessed via KADDR(pa).
+ * If pa is not a valid argument to KADDR, return 0.
+ */
+uintptr
+cankaddr(uintptr pa)
+{
+	if(pa < PHYSDRAM + memsize)		/* assumes PHYSDRAM is 0 */
+		return PHYSDRAM + memsize - pa;
+	return 0;
+}
+
+uintptr
+mmukmap(uintptr va, uintptr pa, usize size)
+{
+	int o;
+	usize n;
+	PTE *pte, *pte0;
+
+	assert((va & (MiB-1)) == 0);
+	o = pa & (MiB-1);
+	pa -= o;
+	size += o;
+	pte = pte0 = &m->mmul1[L1X(va)];
+	for(n = 0; n < size; n += MiB)
+		if(*pte++ != Fault)
+			return 0;
+	pte = pte0;
+	for(n = 0; n < size; n += MiB){
+		*pte++ = (pa+n)|Dom0|L1AP(Krw)|Section;
+		mmuinvalidateaddr(va+n);
+	}
+	cachedwbse(pte0, pte - pte0);
+	return va + o;
+}
+
+
+void
+checkmmu(uintptr va, uintptr pa)
+{
+	USED(va);
+	USED(pa);
+}
+

+ 1 - 0
sys/src/9/bcm/mouse.c

@@ -0,0 +1 @@
+#include "../omap/mouse.c"

+ 61 - 0
sys/src/9/bcm/pi

@@ -0,0 +1,61 @@
+dev
+	root
+	cons
+	env
+	pipe
+	proc
+	mnt
+	srv
+	dup
+	arch
+	ssl
+	tls
+	cap
+	fs
+	ip		arp chandial ip ipv6 ipaux iproute netlog nullmedium pktmedium ptclbsum inferno
+	draw	screen
+	mouse	mouse
+	kbmap
+	kbin	kbd latin1
+	uart
+
+	fakertc
+	sd
+	usb
+	ether	netif
+
+link
+	archbcm
+	loopbackmedium
+	ethermedium
+	usbdwc
+	etherusb
+
+ip
+	tcp
+	udp
+	ipifc
+	icmp
+	icmp6
+	ipmux
+
+misc
+	uartmini
+	sdmmc	emmc
+	dma
+	vcore
+	vfp3	coproc
+
+port
+	int cpuserver = 0;
+
+boot boot #S/sdM0/
+	local
+	tcp
+
+bootdir
+	boot$CONF.out	boot
+	/arm/bin/ip/ipconfig
+	/arm/bin/auth/factotum
+	/arm/bin/fossil/fossil
+	/arm/bin/usb/usbd

+ 61 - 0
sys/src/9/bcm/picpu

@@ -0,0 +1,61 @@
+dev
+	root
+	cons
+	env
+	pipe
+	proc
+	mnt
+	srv
+	dup
+	arch
+	ssl
+	tls
+	cap
+	fs
+	ip		arp chandial ip ipv6 ipaux iproute netlog nullmedium pktmedium ptclbsum inferno
+	draw	screen
+	mouse	mouse
+	kbmap
+	kbin	kbd latin1
+	uart
+
+	fakertc
+	sd
+	usb
+	ether	netif
+
+link
+	archbcm
+	loopbackmedium
+	ethermedium
+	usbdwc
+	etherusb
+
+ip
+	tcp
+	udp
+	ipifc
+	icmp
+	icmp6
+	ipmux
+
+misc
+	uartmini
+	sdmmc	emmc
+	dma
+	vcore
+	vfp3	coproc
+
+port
+	int cpuserver = 1;
+
+boot cpu boot #S/sdM0/
+	local
+	tcp
+
+bootdir
+	boot$CONF.out	boot
+	/arm/bin/ip/ipconfig
+	/arm/bin/auth/factotum
+	/arm/bin/fossil/fossil
+	/arm/bin/usb/usbd

+ 57 - 0
sys/src/9/bcm/pifat

@@ -0,0 +1,57 @@
+dev
+	root
+	cons
+	env
+	pipe
+	proc
+	mnt
+	srv
+	dup
+	arch
+	ip		arp chandial ip ipv6 ipaux iproute netlog nullmedium pktmedium ptclbsum inferno
+	draw	screen vcore
+	mouse	mouse
+	kbin	kbd latin1
+	uart
+
+	fakertc
+	sd
+	usb
+
+link
+	archbcm
+	loopbackmedium
+	ethermedium
+	usbdwc
+
+ip
+	tcp
+	udp
+	ipifc
+	icmp
+	icmp6
+	ipmux
+
+misc
+	uartmini
+	sdmmc	emmc
+	dma
+	vfp3	coproc
+
+port
+	int cpuserver = 0;
+
+bootdir
+	boot.rc			boot
+	/arm/bin/rc
+	/rc/lib/rcmain
+	/arm/bin/mount
+	/arm/bin/bind
+	/arm/bin/echo
+	/arm/bin/disk/fdisk
+	/arm/bin/dossrv
+	/arm/bin/ls
+	/arm/bin/cat
+	/arm/bin/usb/usbd
+
+

+ 1 - 0
sys/src/9/bcm/random.c

@@ -0,0 +1 @@
+#include "../omap/random.c"

+ 93 - 0
sys/src/9/bcm/rebootcode.s

@@ -0,0 +1,93 @@
+/*
+ * armv6 reboot code
+ */
+#include "arm.s"
+
+/*
+ * Turn off MMU, then copy the new kernel to its correct location
+ * in physical memory.  Then jump to the start of the kernel.
+ */
+
+/* main(PADDR(entry), PADDR(code), size); */
+TEXT	main(SB), 1, $-4
+	MOVW	$setR12(SB), R12
+
+	/* copy in arguments before stack gets unmapped */
+	MOVW	R0, R8			/* entry point */
+	MOVW	p2+4(FP), R9		/* source */
+	MOVW	n+8(FP), R10		/* byte count */
+
+	/* SVC mode, interrupts disabled */
+	MOVW	$(PsrDirq|PsrDfiq|PsrMsvc), R1
+	MOVW	R1, CPSR
+
+	/* prepare to turn off mmu  */
+	BL	cachesoff(SB)
+
+	/* turn off mmu */
+	MRC	CpSC, 0, R1, C(CpCONTROL), C(0), CpMainctl
+	BIC	$CpCmmu, R1
+	MCR	CpSC, 0, R1, C(CpCONTROL), C(0), CpMainctl
+
+	/* set up a tiny stack for local vars and memmove args */
+	MOVW	R8, SP			/* stack top just before kernel dest */
+	SUB	$20, SP			/* allocate stack frame */
+
+	/* copy the kernel to final destination */
+	MOVW	R8, 16(SP)		/* save dest (entry point) */
+	MOVW	R8, R0			/* first arg is dest */
+	MOVW	R9, 8(SP)		/* push src */
+	MOVW	R10, 12(SP)		/* push size */
+	BL	memmove(SB)
+	MOVW	16(SP), R8		/* restore entry point */
+
+	/* jump to kernel physical entry point */
+	B	(R8)
+	B	0(PC)
+
+/*
+ * turn the caches off, double map PHYSDRAM & KZERO, invalidate TLBs, revert
+ * to tiny addresses.  upon return, it will be safe to turn off the mmu.
+ * clobbers R0-R2, and returns with SP invalid.
+ */
+TEXT cachesoff(SB), 1, $-4
+
+	/* write back and invalidate caches */
+	BARRIERS
+	MOVW	$0, R0
+	MCR	CpSC, 0, R0, C(CpCACHE), C(CpCACHEwbi), CpCACHEall
+	MCR	CpSC, 0, R0, C(CpCACHE), C(CpCACHEinvi), CpCACHEall
+
+	/* turn caches off */
+	MRC	CpSC, 0, R1, C(CpCONTROL), C(0), CpMainctl
+	BIC	$(CpCdcache|CpCicache|CpCpredict), R1
+	MCR	CpSC, 0, R1, C(CpCONTROL), C(0), CpMainctl
+
+	/* invalidate stale TLBs before changing them */
+	BARRIERS
+	MOVW	$KZERO, R0			/* some valid virtual address */
+	MCR	CpSC, 0, R0, C(CpTLB), C(CpTLBinvu), CpTLBinv
+	BARRIERS
+
+	/* from here on, R0 is base of physical memory */
+	MOVW	$PHYSDRAM, R0
+
+	/* redo double map of first MiB PHYSDRAM = KZERO */
+	MOVW	$(L1+L1X(PHYSDRAM)), R2		/* address of PHYSDRAM's PTE */
+	MOVW	$PTEDRAM, R1			/* PTE bits */
+	ORR	R0, R1				/* dram base */
+	MOVW	R1, (R2)
+
+	/* invalidate stale TLBs again */
+	BARRIERS
+	MCR	CpSC, 0, R0, C(CpTLB), C(CpTLBinvu), CpTLBinv
+	BARRIERS
+
+	/* relocate SB and return address to PHYSDRAM addressing */
+	MOVW	$KSEGM, R1		/* clear segment bits */
+	BIC	R1, R12			/* adjust SB */
+	ORR	R0, R12
+	BIC	R1, R14			/* adjust return address */
+	ORR	R0, R14
+
+	RET

+ 544 - 0
sys/src/9/bcm/screen.c

@@ -0,0 +1,544 @@
+/*
+ * bcm2385 framebuffer
+ */
+
+#include "u.h"
+#include "../port/lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+
+#define	Image	IMAGE
+#include <draw.h>
+#include <memdraw.h>
+#include <cursor.h>
+#include "screen.h"
+
+enum {
+	Tabstop		= 4,
+	Scroll		= 8,
+	Wid		= 1024,
+	Ht		= 768,
+	Depth		= 16,
+};
+
+Cursor	arrow = {
+	{ -1, -1 },
+	{ 0xFF, 0xFF, 0x80, 0x01, 0x80, 0x02, 0x80, 0x0C,
+	  0x80, 0x10, 0x80, 0x10, 0x80, 0x08, 0x80, 0x04,
+	  0x80, 0x02, 0x80, 0x01, 0x80, 0x02, 0x8C, 0x04,
+	  0x92, 0x08, 0x91, 0x10, 0xA0, 0xA0, 0xC0, 0x40,
+	},
+	{ 0x00, 0x00, 0x7F, 0xFE, 0x7F, 0xFC, 0x7F, 0xF0,
+	  0x7F, 0xE0, 0x7F, 0xE0, 0x7F, 0xF0, 0x7F, 0xF8,
+	  0x7F, 0xFC, 0x7F, 0xFE, 0x7F, 0xFC, 0x73, 0xF8,
+	  0x61, 0xF0, 0x60, 0xE0, 0x40, 0x40, 0x00, 0x00,
+	},
+};
+
+Memimage *gscreen;
+
+static Memdata xgdata;
+
+static Memimage xgscreen =
+{
+	{ 0, 0, Wid, Ht },	/* r */
+	{ 0, 0, Wid, Ht },	/* clipr */
+	Depth,			/* depth */
+	3,			/* nchan */
+	RGB16,			/* chan */
+	nil,			/* cmap */
+	&xgdata,		/* data */
+	0,			/* zero */
+	0, 			/* width in words of a single scan line */
+	0,			/* layer */
+	0,			/* flags */
+};
+
+static Memimage *conscol;
+static Memimage *back;
+static Memsubfont *memdefont;
+
+static Lock screenlock;
+
+static Point	curpos;
+static int	h, w;
+static Rectangle window;
+
+static void myscreenputs(char *s, int n);
+static void screenputc(char *buf);
+static void screenwin(void);
+
+/*
+ * Software cursor. 
+ */
+static int	swvisible;	/* is the cursor visible? */
+static int	swenabled;	/* is the cursor supposed to be on the screen? */
+static Memimage *swback;	/* screen under cursor */
+static Memimage *swimg;		/* cursor image */
+static Memimage *swmask;	/* cursor mask */
+static Memimage *swimg1;
+static Memimage *swmask1;
+
+static Point	swoffset;
+static Rectangle swrect;	/* screen rectangle in swback */
+static Point	swpt;		/* desired cursor location */
+static Point	swvispt;	/* actual cursor location */
+static int	swvers;		/* incremented each time cursor image changes */
+static int	swvisvers;	/* the version on the screen */
+
+/*
+ * called with drawlock locked for us, most of the time.
+ * kernel prints at inopportune times might mean we don't
+ * hold the lock, but memimagedraw is now reentrant so
+ * that should be okay: worst case we get cursor droppings.
+ */
+static void
+swcursorhide(void)
+{
+	if(swvisible == 0)
+		return;
+	if(swback == nil)
+		return;
+	swvisible = 0;
+	memimagedraw(gscreen, swrect, swback, ZP, memopaque, ZP, S);
+	flushmemscreen(swrect);
+}
+
+static void
+swcursoravoid(Rectangle r)
+{
+	if(swvisible && rectXrect(r, swrect))
+		swcursorhide();
+}
+
+static void
+swcursordraw(void)
+{
+	int dounlock;
+
+	if(swvisible)
+		return;
+	if(swenabled == 0)
+		return;
+	if(swback == nil || swimg1 == nil || swmask1 == nil)
+		return;
+	dounlock = canqlock(&drawlock);
+	swvispt = swpt;
+	swvisvers = swvers;
+	swrect = rectaddpt(Rect(0,0,16,16), swvispt);
+	memimagedraw(swback, swback->r, gscreen, swpt, memopaque, ZP, S);
+	memimagedraw(gscreen, swrect, swimg1, ZP, swmask1, ZP, SoverD);
+	flushmemscreen(swrect);
+	swvisible = 1;
+	if(dounlock)
+		qunlock(&drawlock);
+}
+
+int
+cursoron(int dolock)
+{
+	int retry;
+
+	if (dolock)
+		lock(&cursor);
+	if (canqlock(&drawlock)) {
+		retry = 0;
+		swcursorhide();
+		swcursordraw();
+		qunlock(&drawlock);
+	} else
+		retry = 1;
+	if (dolock)
+		unlock(&cursor);
+	return retry;
+}
+
+void
+cursoroff(int dolock)
+{
+	if (dolock)
+		lock(&cursor);
+	swcursorhide();
+	if (dolock)
+		unlock(&cursor);
+}
+
+static void
+swload(Cursor *curs)
+{
+	uchar *ip, *mp;
+	int i, j, set, clr;
+
+	if(!swimg || !swmask || !swimg1 || !swmask1)
+		return;
+	/*
+	 * Build cursor image and mask.
+	 * Image is just the usual cursor image
+	 * but mask is a transparent alpha mask.
+	 * 
+	 * The 16x16x8 memimages do not have
+	 * padding at the end of their scan lines.
+	 */
+	ip = byteaddr(swimg, ZP);
+	mp = byteaddr(swmask, ZP);
+	for(i=0; i<32; i++){
+		set = curs->set[i];
+		clr = curs->clr[i];
+		for(j=0x80; j; j>>=1){
+			*ip++ = set&j ? 0x00 : 0xFF;
+			*mp++ = (clr|set)&j ? 0xFF : 0x00;
+		}
+	}
+	swoffset = curs->offset;
+	swvers++;
+	memimagedraw(swimg1,  swimg1->r,  swimg,  ZP, memopaque, ZP, S);
+	memimagedraw(swmask1, swmask1->r, swmask, ZP, memopaque, ZP, S);
+}
+
+/* called from devmouse */
+void
+setcursor(Cursor* curs)
+{
+	cursoroff(0);
+	swload(curs);
+	cursoron(0);
+}
+
+static int
+swmove(Point p)
+{
+	swpt = addpt(p, swoffset);
+	return 0;
+}
+
+static void
+swcursorclock(void)
+{
+	int x;
+
+	if(!swenabled)
+		return;
+	swmove(mousexy());
+	if(swvisible && eqpt(swpt, swvispt) && swvers==swvisvers)
+		return;
+
+	x = splhi();
+	if(swenabled)
+	if(!swvisible || !eqpt(swpt, swvispt) || swvers!=swvisvers)
+	if(canqlock(&drawlock)){
+		swcursorhide();
+		swcursordraw();
+		qunlock(&drawlock);
+	}
+	splx(x);
+}
+
+void
+swcursorinit(void)
+{
+	static int init;
+
+	if(!init){
+		init = 1;
+		addclock0link(swcursorclock, 10);
+		swenabled = 1;
+	}
+	if(swback){
+		freememimage(swback);
+		freememimage(swmask);
+		freememimage(swmask1);
+		freememimage(swimg);
+		freememimage(swimg1);
+	}
+
+	swback  = allocmemimage(Rect(0,0,32,32), gscreen->chan);
+	swmask  = allocmemimage(Rect(0,0,16,16), GREY8);
+	swmask1 = allocmemimage(Rect(0,0,16,16), GREY1);
+	swimg   = allocmemimage(Rect(0,0,16,16), GREY8);
+	swimg1  = allocmemimage(Rect(0,0,16,16), GREY1);
+	if(swback==nil || swmask==nil || swmask1==nil || swimg==nil || swimg1 == nil){
+		print("software cursor: allocmemimage fails\n");
+		return;
+	}
+
+	memfillcolor(swmask, DOpaque);
+	memfillcolor(swmask1, DOpaque);
+	memfillcolor(swimg, DBlack);
+	memfillcolor(swimg1, DBlack);
+}
+
+int
+hwdraw(Memdrawparam *par)
+{
+	Memimage *dst, *src, *mask;
+
+	if((dst=par->dst) == nil || dst->data == nil)
+		return 0;
+	if((src=par->src) == nil || src->data == nil)
+		return 0;
+	if((mask=par->mask) == nil || mask->data == nil)
+		return 0;
+
+	if(dst->data->bdata == xgdata.bdata)
+		swcursoravoid(par->r);
+	if(src->data->bdata == xgdata.bdata)
+		swcursoravoid(par->sr);
+	if(mask->data->bdata == xgdata.bdata)
+		swcursoravoid(par->mr);
+
+	return 0;
+}
+
+static int
+screensize(void)
+{
+	char *p;
+	char *f[3];
+	int width, height, depth;
+
+	p = getconf("vgasize");
+	if(p == nil || getfields(p, f, nelem(f), 0, "x") != nelem(f) ||
+	    (width = atoi(f[0])) < 16 || (height = atoi(f[1])) <= 0 ||
+	    (depth = atoi(f[2])) <= 0)
+		return -1;
+	xgscreen.r.max = Pt(width, height);
+	xgscreen.depth = depth;
+	return 0;
+}
+
+void
+screeninit(void)
+{
+	uchar *fb;
+	int set;
+	ulong chan;
+
+	set = screensize() == 0;
+	fb = fbinit(set, &xgscreen.r.max.x, &xgscreen.r.max.y, &xgscreen.depth);
+	if(fb == nil){
+		print("can't initialise %dx%dx%d framebuffer \n",
+			xgscreen.r.max.x, xgscreen.r.max.y, xgscreen.depth);
+		return;
+	}
+	xgscreen.clipr = xgscreen.r;
+	switch(xgscreen.depth){
+	default:
+		print("unsupported screen depth %d\n", xgscreen.depth);
+		xgscreen.depth = 16;
+		/* fall through */
+	case 16:
+		chan = RGB16;
+		break;
+	case 24:
+		chan = BGR24;
+		break;
+	case 32:
+		chan = ARGB32;
+		break;
+	}
+	memsetchan(&xgscreen, chan);
+	conf.monitor = 1;
+	xgdata.bdata = fb;
+	xgdata.ref = 1;
+	gscreen = &xgscreen;
+	gscreen->width = wordsperline(gscreen->r, gscreen->depth);
+
+	memimageinit();
+	memdefont = getmemdefont();
+	screenwin();
+	screenputs = myscreenputs;
+}
+
+void
+flushmemscreen(Rectangle)
+{
+}
+
+uchar*
+attachscreen(Rectangle *r, ulong *chan, int* d, int *width, int *softscreen)
+{
+	*r = gscreen->r;
+	*d = gscreen->depth;
+	*chan = gscreen->chan;
+	*width = gscreen->width;
+	*softscreen = 0;
+
+	return gscreen->data->bdata;
+}
+
+void
+getcolor(ulong p, ulong *pr, ulong *pg, ulong *pb)
+{
+	USED(p, pr, pg, pb);
+}
+
+int
+setcolor(ulong p, ulong r, ulong g, ulong b)
+{
+	USED(p, r, g, b);
+	return 0;
+}
+
+void
+blankscreen(int blank)
+{
+	fbblank(blank);
+}
+
+static void
+myscreenputs(char *s, int n)
+{
+	int i;
+	Rune r;
+	char buf[4];
+
+	if(!islo()) {
+		/* don't deadlock trying to print in interrupt */
+		if(!canlock(&screenlock))
+			return;	
+	}
+	else
+		lock(&screenlock);
+
+	while(n > 0){
+		i = chartorune(&r, s);
+		if(i == 0){
+			s++;
+			--n;
+			continue;
+		}
+		memmove(buf, s, i);
+		buf[i] = 0;
+		n -= i;
+		s += i;
+		screenputc(buf);
+	}
+	unlock(&screenlock);
+}
+
+static void
+screenwin(void)
+{
+	char *greet;
+	Memimage *orange;
+	Point p, q;
+	Rectangle r;
+
+	back = memwhite;
+	conscol = memblack;
+
+	orange = allocmemimage(Rect(0, 0, 1, 1), RGB16);
+	orange->flags |= Frepl;
+	orange->clipr = gscreen->r;
+	orange->data->bdata[0] = 0x40;		/* magic: colour? */
+	orange->data->bdata[1] = 0xfd;		/* magic: colour? */
+
+	w = memdefont->info[' '].width;
+	h = memdefont->height;
+
+	r = insetrect(gscreen->r, 4);
+
+	memimagedraw(gscreen, r, memblack, ZP, memopaque, ZP, S);
+	window = insetrect(r, 4);
+	memimagedraw(gscreen, window, memwhite, ZP, memopaque, ZP, S);
+
+	memimagedraw(gscreen, Rect(window.min.x, window.min.y,
+		window.max.x, window.min.y + h + 5 + 6), orange, ZP, nil, ZP, S);
+	freememimage(orange);
+	window = insetrect(window, 5);
+
+	greet = " Plan 9 Console ";
+	p = addpt(window.min, Pt(10, 0));
+	q = memsubfontwidth(memdefont, greet);
+	memimagestring(gscreen, p, conscol, ZP, memdefont, greet);
+	flushmemscreen(r);
+	window.min.y += h + 6;
+	curpos = window.min;
+	window.max.y = window.min.y + ((window.max.y - window.min.y) / h) * h;
+}
+
+static void
+scroll(void)
+{
+	int o;
+	Point p;
+	Rectangle r;
+
+	o = Scroll*h;
+	r = Rpt(window.min, Pt(window.max.x, window.max.y-o));
+	p = Pt(window.min.x, window.min.y+o);
+	memimagedraw(gscreen, r, gscreen, p, nil, p, S);
+	flushmemscreen(r);
+	r = Rpt(Pt(window.min.x, window.max.y-o), window.max);
+	memimagedraw(gscreen, r, back, ZP, nil, ZP, S);
+	flushmemscreen(r);
+
+	curpos.y -= o;
+}
+
+static void
+screenputc(char *buf)
+{
+	int w;
+	uint pos;
+	Point p;
+	Rectangle r;
+	static int *xp;
+	static int xbuf[256];
+
+	if (xp < xbuf || xp >= &xbuf[sizeof(xbuf)])
+		xp = xbuf;
+
+	switch (buf[0]) {
+	case '\n':
+		if (curpos.y + h >= window.max.y)
+			scroll();
+		curpos.y += h;
+		screenputc("\r");
+		break;
+	case '\r':
+		xp = xbuf;
+		curpos.x = window.min.x;
+		break;
+	case '\t':
+		p = memsubfontwidth(memdefont, " ");
+		w = p.x;
+		if (curpos.x >= window.max.x - Tabstop * w)
+			screenputc("\n");
+
+		pos = (curpos.x - window.min.x) / w;
+		pos = Tabstop - pos % Tabstop;
+		*xp++ = curpos.x;
+		r = Rect(curpos.x, curpos.y, curpos.x + pos * w, curpos.y + h);
+		memimagedraw(gscreen, r, back, back->r.min, nil, back->r.min, S);
+		flushmemscreen(r);
+		curpos.x += pos * w;
+		break;
+	case '\b':
+		if (xp <= xbuf)
+			break;
+		xp--;
+		r = Rect(*xp, curpos.y, curpos.x, curpos.y + h);
+		memimagedraw(gscreen, r, back, back->r.min, nil, back->r.min, S);
+		flushmemscreen(r);
+		curpos.x = *xp;
+		break;
+	case '\0':
+		break;
+	default:
+		p = memsubfontwidth(memdefont, buf);
+		w = p.x;
+
+		if (curpos.x >= window.max.x - w)
+			screenputc("\n");
+
+		*xp++ = curpos.x;
+		r = Rect(curpos.x, curpos.y, curpos.x + w, curpos.y + h);
+		memimagedraw(gscreen, r, back, back->r.min, nil, back->r.min, S);
+		memimagestring(gscreen, curpos, conscol, ZP, memdefont, buf);
+		flushmemscreen(r);
+		curpos.x += w;
+		break;
+	}
+}

+ 35 - 0
sys/src/9/bcm/screen.h

@@ -0,0 +1,35 @@
+typedef struct Cursor Cursor;
+typedef struct Cursorinfo Cursorinfo;
+struct Cursorinfo {
+	Cursor;
+	Lock;
+};
+
+/* devmouse.c */
+extern void mousetrack(int, int, int, int);
+extern Point mousexy(void);
+
+extern void mouseaccelerate(int);
+extern int m3mouseputc(Queue*, int);
+extern int m5mouseputc(Queue*, int);
+extern int mouseputc(Queue*, int);
+
+extern Cursorinfo cursor;
+extern Cursor arrow;
+
+/* mouse.c */
+extern void mousectl(Cmdbuf*);
+extern void mouseresize(void);
+
+/* screen.c */
+extern void	blankscreen(int);
+extern void	flushmemscreen(Rectangle);
+extern uchar*	attachscreen(Rectangle*, ulong*, int*, int*, int*);
+extern int	cursoron(int);
+extern void	cursoroff(int);
+extern void	setcursor(Cursor*);
+
+/* devdraw.c */
+extern QLock	drawlock;
+
+#define ishwimage(i)	1		/* for ../port/devdraw.c */

+ 1 - 0
sys/src/9/bcm/softfpu.c

@@ -0,0 +1 @@
+#include "../teg2/softfpu.c"

+ 1 - 0
sys/src/9/bcm/syscall.c

@@ -0,0 +1 @@
+#include "../kw/syscall.c"

+ 567 - 0
sys/src/9/bcm/trap.c

@@ -0,0 +1,567 @@
+/*
+ * traps, exceptions, interrupts, system calls.
+ */
+#include "u.h"
+#include "../port/lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+#include "io.h"
+#include "ureg.h"
+#include "../port/error.h"
+
+#include "arm.h"
+
+#define INTREGS		(VIRTIO+0xB200)
+
+typedef struct Intregs Intregs;
+typedef struct Vctl Vctl;
+
+enum {
+	Nvec = 8,		/* # of vectors at start of lexception.s */
+	Fiqenable = 1<<7,
+};
+
+/*
+ *   Layout at virtual address KZERO (double mapped at HVECTORS).
+ */
+typedef struct Vpage0 {
+	void	(*vectors[Nvec])(void);
+	u32int	vtable[Nvec];
+} Vpage0;
+
+/*
+ * interrupt control registers
+ */
+struct Intregs {
+	u32int	ARMpending;
+	u32int	GPUpending[2];
+	u32int	FIQctl;
+	u32int	GPUenable[2];
+	u32int	ARMenable;
+	u32int	GPUdisable[2];
+	u32int	ARMdisable;
+};
+
+struct Vctl {
+	Vctl	*next;
+	int	irq;
+	u32int	*reg;
+	u32int	mask;
+	void	(*f)(Ureg*, void*);
+	void	*a;
+};
+
+static Vctl *vctl, *vfiq;
+
+static char *trapnames[PsrMask+1] = {
+	[ PsrMusr ] "user mode",
+	[ PsrMfiq ] "fiq interrupt",
+	[ PsrMirq ] "irq interrupt",
+	[ PsrMsvc ] "svc/swi exception",
+	[ PsrMabt ] "prefetch abort/data abort",
+	[ PsrMabt+1 ] "data abort",
+	[ PsrMund ] "undefined instruction",
+	[ PsrMsys ] "sys trap",
+};
+
+extern int notify(Ureg*);
+
+/*
+ *  set up for exceptions
+ */
+void
+trapinit(void)
+{
+	Vpage0 *vpage0;
+
+	/* disable everything */
+	intrsoff();
+
+	/* set up the exception vectors */
+	vpage0 = (Vpage0*)HVECTORS;
+	memmove(vpage0->vectors, vectors, sizeof(vpage0->vectors));
+	memmove(vpage0->vtable, vtable, sizeof(vpage0->vtable));
+	cacheuwbinv();
+
+	/* set up the stacks for the interrupt modes */
+	setr13(PsrMfiq, (u32int*)(FIQSTKTOP));
+	setr13(PsrMirq, m->sirq);
+	setr13(PsrMabt, m->sabt);
+	setr13(PsrMund, m->sund);
+	setr13(PsrMsys, m->ssys);
+
+	coherence();
+}
+
+void
+intrsoff(void)
+{
+	Intregs *ip;
+	int disable;
+
+	ip = (Intregs*)INTREGS;
+	disable = ~0;
+	ip->GPUdisable[0] = disable;
+	ip->GPUdisable[1] = disable;
+	ip->ARMdisable = disable;
+	ip->FIQctl = 0;
+}
+
+/*
+ *  called by trap to handle irq interrupts.
+ *  returns true iff a clock interrupt, thus maybe reschedule.
+ */
+static int
+irq(Ureg* ureg)
+{
+	Vctl *v;
+	int clockintr;
+
+	clockintr = 0;
+	for(v = vctl; v; v = v->next)
+		if(*v->reg & v->mask){
+			coherence();
+			v->f(ureg, v->a);
+			coherence();
+			if(v->irq == IRQclock)
+				clockintr = 1;
+		}
+	return clockintr;
+}
+
+/*
+ * called direct from lexception.s to handle fiq interrupt.
+ */
+void
+fiq(Ureg *ureg)
+{
+	Vctl *v;
+
+	v = vfiq;
+	if(v == nil)
+		panic("unexpected item in bagging area");
+	m->intr++;
+	ureg->pc -= 4;
+	coherence();
+	v->f(ureg, v->a);
+	coherence();
+}
+
+void
+irqenable(int irq, void (*f)(Ureg*, void*), void* a)
+{
+	Vctl *v;
+	Intregs *ip;
+	u32int *enable;
+
+	ip = (Intregs*)INTREGS;
+	v = (Vctl*)malloc(sizeof(Vctl));
+	if(v == nil)
+		panic("irqenable: no mem");
+	v->irq = irq;
+	if(irq >= IRQbasic){
+		enable = &ip->ARMenable;
+		v->reg = &ip->ARMpending;
+		v->mask = 1 << (irq - IRQbasic);
+	}else{
+		enable = &ip->GPUenable[irq/32];
+		v->reg = &ip->GPUpending[irq/32];
+		v->mask = 1 << (irq % 32);
+	}
+	v->f = f;
+	v->a = a;
+	if(irq == IRQfiq){
+		assert((ip->FIQctl & Fiqenable) == 0);
+		assert((*enable & v->mask) == 0);
+		vfiq = v;
+		ip->FIQctl = Fiqenable | irq;
+	}else{
+		v->next = vctl;
+		vctl = v;
+		*enable = v->mask;
+	}
+}
+
+static char *
+trapname(int psr)
+{
+	char *s;
+
+	s = trapnames[psr & PsrMask];
+	if(s == nil)
+		s = "unknown trap number in psr";
+	return s;
+}
+
+/*
+ *  called by trap to handle access faults
+ */
+static void
+faultarm(Ureg *ureg, uintptr va, int user, int read)
+{
+	int n, insyscall;
+	char buf[ERRMAX];
+	static int cnt, lastpid;
+	static ulong lastva;
+
+	if(up == nil) {
+		dumpregs(ureg);
+		panic("fault: nil up in faultarm, accessing %#p", va);
+	}
+	insyscall = up->insyscall;
+	up->insyscall = 1;
+	/* this is quite helpful during mmu and cache debugging */
+	if(va == lastva && up->pid == lastpid) {
+		++cnt;
+		if (cnt >= 2)
+			/* fault() isn't fixing the underlying cause */
+			panic("fault: %d consecutive faults for va %#lux",
+				cnt+1, va);
+	} else {
+		cnt = 0;
+		lastva = va;
+		lastpid = up->pid;
+	}
+
+	n = fault(va, read);
+	if(n < 0){
+		if(!user){
+			dumpregs(ureg);
+			panic("fault: kernel accessing %#p", va);
+		}
+		/* don't dump registers; programs suicide all the time */
+		snprint(buf, sizeof buf, "sys: trap: fault %s va=%#p",
+			read? "read": "write", va);
+		postnote(up, 1, buf, NDebug);
+	}
+	up->insyscall = insyscall;
+}
+
+/*
+ *  returns 1 if the instruction writes memory, 0 otherwise
+ */
+int
+writetomem(ulong inst)
+{
+	/* swap always write memory */
+	if((inst & 0x0FC00000) == 0x01000000)
+		return 1;
+
+	/* loads and stores are distinguished by bit 20 */
+	if(inst & (1<<20))
+		return 0;
+
+	return 1;
+}
+
+/*
+ *  here on all exceptions other than syscall (SWI) and fiq
+ */
+void
+trap(Ureg *ureg)
+{
+	int clockintr, user, x, rv, rem;
+	ulong inst, fsr;
+	uintptr va;
+	char buf[ERRMAX];
+
+	assert(!islo());
+	if(up != nil)
+		rem = ((char*)ureg)-up->kstack;
+	else
+		rem = ((char*)ureg)-((char*)m+sizeof(Mach));
+	if(rem < 256) {
+		iprint("trap: %d stack bytes left, up %#p ureg %#p at pc %#lux\n",
+			rem, up, ureg, ureg->pc);
+		delay(1000);
+		dumpstack();
+		panic("trap: %d stack bytes left, up %#p ureg %#p at pc %#lux",
+			rem, up, ureg, ureg->pc);
+	}
+
+	user = (ureg->psr & PsrMask) == PsrMusr;
+	if(user){
+		up->dbgreg = ureg;
+		cycles(&up->kentry);
+	}
+
+	/*
+	 * All interrupts/exceptions should be resumed at ureg->pc-4,
+	 * except for Data Abort which resumes at ureg->pc-8.
+	 */
+	if(ureg->type == (PsrMabt+1))
+		ureg->pc -= 8;
+	else
+		ureg->pc -= 4;
+
+	clockintr = 0;		/* if set, may call sched() before return */
+	switch(ureg->type){
+	default:
+		panic("unknown trap; type %#lux, psr mode %#lux", ureg->type,
+			ureg->psr & PsrMask);
+		break;
+	case PsrMirq:
+		clockintr = irq(ureg);
+		m->intr++;
+		break;
+	case PsrMabt:			/* prefetch fault */
+		x = ifsrget();
+		fsr = (x>>7) & 0x8 | x & 0x7;
+		switch(fsr){
+		case 0x02:		/* instruction debug event (BKPT) */
+			if(user){
+				snprint(buf, sizeof buf, "sys: breakpoint");
+				postnote(up, 1, buf, NDebug);
+			}else{
+				iprint("kernel bkpt: pc %#lux inst %#ux\n",
+					ureg->pc, *(u32int*)ureg->pc);
+				panic("kernel bkpt");
+			}
+			break;
+		default:
+			faultarm(ureg, ureg->pc, user, 1);
+			break;
+		}
+		break;
+	case PsrMabt+1:			/* data fault */
+		va = farget();
+		inst = *(ulong*)(ureg->pc);
+		/* bits 12 and 10 have to be concatenated with status */
+		x = fsrget();
+		fsr = (x>>7) & 0x20 | (x>>6) & 0x10 | x & 0xf;
+		switch(fsr){
+		default:
+		case 0xa:		/* ? was under external abort */
+			panic("unknown data fault, 6b fsr %#lux", fsr);
+			break;
+		case 0x0:
+			panic("vector exception at %#lux", ureg->pc);
+			break;
+		case 0x1:		/* alignment fault */
+		case 0x3:		/* access flag fault (section) */
+			if(user){
+				snprint(buf, sizeof buf,
+					"sys: alignment: pc %#lux va %#p\n",
+					ureg->pc, va);
+				postnote(up, 1, buf, NDebug);
+			} else
+				panic("kernel alignment: pc %#lux va %#p", ureg->pc, va);
+			break;
+		case 0x2:
+			panic("terminal exception at %#lux", ureg->pc);
+			break;
+		case 0x4:		/* icache maint fault */
+		case 0x6:		/* access flag fault (page) */
+		case 0x8:		/* precise external abort, non-xlat'n */
+		case 0x28:
+		case 0xc:		/* l1 translation, precise ext. abort */
+		case 0x2c:
+		case 0xe:		/* l2 translation, precise ext. abort */
+		case 0x2e:
+		case 0x16:		/* imprecise ext. abort, non-xlt'n */
+		case 0x36:
+			panic("external abort %#lux pc %#lux addr %#p",
+				fsr, ureg->pc, va);
+			break;
+		case 0x1c:		/* l1 translation, precise parity err */
+		case 0x1e:		/* l2 translation, precise parity err */
+		case 0x18:		/* imprecise parity or ecc err */
+			panic("translation parity error %#lux pc %#lux addr %#p",
+				fsr, ureg->pc, va);
+			break;
+		case 0x5:		/* translation fault, no section entry */
+		case 0x7:		/* translation fault, no page entry */
+			faultarm(ureg, va, user, !writetomem(inst));
+			break;
+		case 0x9:
+		case 0xb:
+			/* domain fault, accessing something we shouldn't */
+			if(user){
+				snprint(buf, sizeof buf,
+					"sys: access violation: pc %#lux va %#p\n",
+					ureg->pc, va);
+				postnote(up, 1, buf, NDebug);
+			} else
+				panic("kernel access violation: pc %#lux va %#p",
+					ureg->pc, va);
+			break;
+		case 0xd:
+		case 0xf:
+			/* permission error, copy on write or real permission error */
+			faultarm(ureg, va, user, !writetomem(inst));
+			break;
+		}
+		break;
+	case PsrMund:			/* undefined instruction */
+		if(user){
+			if(seg(up, ureg->pc, 0) != nil &&
+			   *(u32int*)ureg->pc == 0xD1200070)
+				postnote(up, 1, "sys: breakpoint", NDebug);
+			else{
+				/* look for floating point instructions to interpret */
+				rv = fpuemu(ureg);
+				if(rv == 0){
+					snprint(buf, sizeof buf,
+						"undefined instruction: pc %#lux\n",
+						ureg->pc);
+					postnote(up, 1, buf, NDebug);
+				}
+			}
+		}else{
+			if (ureg->pc & 3) {
+				iprint("rounding fault pc %#lux down to word\n",
+					ureg->pc);
+				ureg->pc &= ~3;
+			}
+			iprint("undefined instruction: pc %#lux inst %#ux\n",
+				ureg->pc, *(u32int*)ureg->pc);
+			panic("undefined instruction");
+		}
+		break;
+	}
+	splhi();
+
+	/* delaysched set because we held a lock or because our quantum ended */
+	if(up && up->delaysched && clockintr){
+		sched();		/* can cause more traps */
+		splhi();
+	}
+
+	if(user){
+		if(up->procctl || up->nnote)
+			notify(ureg);
+		kexit(ureg);
+	}
+}
+
+int
+isvalidaddr(void *v)
+{
+	return (uintptr)v >= KZERO;
+}
+
+static void
+dumplongs(char *msg, ulong *v, int n)
+{
+	int i, l;
+
+	l = 0;
+	iprint("%s at %.8p: ", msg, v);
+	for(i=0; i<n; i++){
+		if(l >= 4){
+			iprint("\n    %.8p: ", v);
+			l = 0;
+		}
+		if(isvalidaddr(v)){
+			iprint(" %.8lux", *v++);
+			l++;
+		}else{
+			iprint(" invalid");
+			break;
+		}
+	}
+	iprint("\n");
+}
+
+static void
+dumpstackwithureg(Ureg *ureg)
+{
+	uintptr l, i, v, estack;
+	u32int *p;
+	char *s;
+
+	if((s = getconf("*nodumpstack")) != nil && strcmp(s, "0") != 0){
+		iprint("dumpstack disabled\n");
+		return;
+	}
+	iprint("ktrace /kernel/path %#.8lux %#.8lux %#.8lux # pc, sp, link\n",
+		ureg->pc, ureg->sp, ureg->r14);
+	delay(2000);
+	i = 0;
+	if(up != nil && (uintptr)&l <= (uintptr)up->kstack+KSTACK)
+		estack = (uintptr)up->kstack+KSTACK;
+	else if((uintptr)&l >= (uintptr)m->stack
+	     && (uintptr)&l <= (uintptr)m+MACHSIZE)
+		estack = (uintptr)m+MACHSIZE;
+	else{
+		if(up != nil)
+			iprint("&up->kstack %#p &l %#p\n", up->kstack, &l);
+		else
+			iprint("&m %#p &l %#p\n", m, &l);
+		return;
+	}
+	for(l = (uintptr)&l; l < estack; l += sizeof(uintptr)){
+		v = *(uintptr*)l;
+		if(KTZERO < v && v < (uintptr)etext && !(v & 3)){
+			v -= sizeof(u32int);		/* back up an instr */
+			p = (u32int*)v;
+			if((*p & 0x0f000000) == 0x0b000000){	/* BL instr? */
+				iprint("%#8.8lux=%#8.8lux ", l, v);
+				i++;
+			}
+		}
+		if(i == 4){
+			i = 0;
+			iprint("\n");
+		}
+	}
+	if(i)
+		iprint("\n");
+}
+
+/*
+ * Fill in enough of Ureg to get a stack trace, and call a function.
+ * Used by debugging interface rdb.
+ */
+void
+callwithureg(void (*fn)(Ureg*))
+{
+	Ureg ureg;
+
+	ureg.pc = getcallerpc(&fn);
+	ureg.sp = PTR2UINT(&fn);
+	fn(&ureg);
+}
+
+void
+dumpstack(void)
+{
+	callwithureg(dumpstackwithureg);
+}
+
+void
+dumpregs(Ureg* ureg)
+{
+	int s;
+
+	if (ureg == nil) {
+		iprint("trap: no user process\n");
+		return;
+	}
+	s = splhi();
+	iprint("trap: %s", trapname(ureg->type));
+	if(ureg != nil && (ureg->psr & PsrMask) != PsrMsvc)
+		iprint(" in %s", trapname(ureg->psr));
+	iprint("\n");
+	iprint("psr %8.8lux type %2.2lux pc %8.8lux link %8.8lux\n",
+		ureg->psr, ureg->type, ureg->pc, ureg->link);
+	iprint("R14 %8.8lux R13 %8.8lux R12 %8.8lux R11 %8.8lux R10 %8.8lux\n",
+		ureg->r14, ureg->r13, ureg->r12, ureg->r11, ureg->r10);
+	iprint("R9  %8.8lux R8  %8.8lux R7  %8.8lux R6  %8.8lux R5  %8.8lux\n",
+		ureg->r9, ureg->r8, ureg->r7, ureg->r6, ureg->r5);
+	iprint("R4  %8.8lux R3  %8.8lux R2  %8.8lux R1  %8.8lux R0  %8.8lux\n",
+		ureg->r4, ureg->r3, ureg->r2, ureg->r1, ureg->r0);
+	iprint("stack is at %#p\n", ureg);
+	iprint("pc %#lux link %#lux\n", ureg->pc, ureg->link);
+
+	if(up)
+		iprint("user stack: %#p-%#p\n", up->kstack, up->kstack+KSTACK-4);
+	else
+		iprint("kernel stack: %8.8lux-%8.8lux\n",
+			(ulong)(m+1), (ulong)m+BY2PG-4);
+	dumplongs("stack", (ulong *)(ureg + 1), 16);
+	delay(2000);
+	dumpstack();
+	splx(s);
+}

+ 423 - 0
sys/src/9/bcm/uartmini.c

@@ -0,0 +1,423 @@
+/*
+ * bcm2835 mini uart (UART1)
+ */
+
+#include "u.h"
+#include "../port/lib.h"
+#include "../port/error.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+#include "io.h"
+
+#define GPIOREGS	(VIRTIO+0x200000)
+#define AUXREGS		(VIRTIO+0x215000)
+#define	OkLed		16
+#define	TxPin		14
+#define	RxPin		15
+
+/* GPIO regs */
+enum {
+	Fsel0	= 0x00>>2,
+		FuncMask= 0x7,
+		Input	= 0x0,
+		Output	= 0x1,
+		Alt0	= 0x4,
+		Alt1	= 0x5,
+		Alt2	= 0x6,
+		Alt3	= 0x7,
+		Alt4	= 0x3,
+		Alt5	= 0x2,
+	Set0	= 0x1c>>2,
+	Clr0	= 0x28>>2,
+	Lev0	= 0x34>>2,
+	PUD	= 0x94>>2,
+		Off	= 0x0,
+		Pulldown= 0x1,
+		Pullup	= 0x2,
+	PUDclk0	= 0x98>>2,
+	PUDclk1	= 0x9c>>2,
+};
+
+/* AUX regs */
+enum {
+	Irq	= 0x00>>2,
+		UartIrq	= 1<<0,
+	Enables	= 0x04>>2,
+		UartEn	= 1<<0,
+	MuIo	= 0x40>>2,
+	MuIer	= 0x44>>2,
+		RxIen	= 1<<0,
+		TxIen	= 1<<1,
+	MuIir	= 0x48>>2,
+	MuLcr	= 0x4c>>2,
+		Bitsmask= 3<<0,
+		Bits7	= 2<<0,
+		Bits8	= 3<<0,
+	MuMcr	= 0x50>>2,
+		RtsN	= 1<<1,
+	MuLsr	= 0x54>>2,
+		TxDone	= 1<<6,
+		TxRdy	= 1<<5,
+		RxRdy	= 1<<0,
+	MuCntl	= 0x60>>2,
+		CtsFlow	= 1<<3,
+		TxEn	= 1<<1,
+		RxEn	= 1<<0,
+	MuBaud	= 0x68>>2,
+};
+
+extern PhysUart miniphysuart;
+
+static Uart miniuart = {
+	.regs	= (u32int*)AUXREGS,
+	.name	= "uart0",
+	.freq	= 250000000,
+	.phys	= &miniphysuart,
+};
+
+void
+gpiosel(uint pin, int func)
+{	
+	u32int *gp, *fsel;
+	int off;
+
+	gp = (u32int*)GPIOREGS;
+	fsel = &gp[Fsel0 + pin/10];
+	off = (pin % 10) * 3;
+	*fsel = (*fsel & ~(FuncMask << off)) | func << off;
+}
+
+void
+gpiopulloff(uint pin)
+{
+	u32int *gp, *reg;
+	u32int mask;
+
+	gp = (u32int*)GPIOREGS;
+	reg = &gp[PUDclk0 + pin/32];
+	mask = 1 << (pin % 32);
+	gp[PUD] = Off;
+	microdelay(1);
+	*reg = mask;
+	microdelay(1);
+	*reg = 0;
+}
+
+void
+gpioout(uint pin, int set)
+{
+	u32int *gp;
+	int v;
+
+	gp = (u32int*)GPIOREGS;
+	v = set? Set0: Clr0;
+	gp[v + pin/32] = 1 << (pin % 32);
+}
+
+int
+gpioin(uint pin)
+{
+	u32int *gp;
+
+	gp = (u32int*)GPIOREGS;
+	return (gp[Lev0 + pin/32] & (1 << (pin % 32))) != 0;
+}
+
+static void
+interrupt(Ureg*, void *arg)
+{
+	Uart *uart;
+	u32int *ap;
+
+	uart = arg;
+	ap = (u32int*)uart->regs;
+
+	coherence();
+	if(0 && (ap[Irq] & UartIrq) == 0)
+		return;
+	if(ap[MuLsr] & TxRdy)
+		uartkick(uart);
+	if(ap[MuLsr] & RxRdy){
+		if(uart->console)
+			if(uart->opens == 1)
+				uart->putc = kbdcr2nl;
+			else
+				uart->putc = nil;
+		do{
+			uartrecv(uart, ap[MuIo] & 0xFF);
+		}while(ap[MuLsr] & RxRdy);
+	}
+	coherence();
+}
+
+static Uart*
+pnp(void)
+{
+	Uart *uart;
+
+	uart = &miniuart;
+	if(uart->console == 0)
+		kbdq = qopen(8*1024, 0, nil, nil);
+	return uart;
+}
+
+static void
+enable(Uart *uart, int ie)
+{
+	u32int *ap;
+
+	ap = (u32int*)uart->regs;
+	delay(10);
+	gpiosel(TxPin, Alt5);
+	gpiosel(RxPin, Alt5);
+	gpiopulloff(TxPin);
+	gpiopulloff(RxPin);
+	ap[Enables] |= UartEn;
+	ap[MuIir] = 6;
+	ap[MuLcr] = Bits8;
+	ap[MuCntl] = TxEn|RxEn;
+	ap[MuBaud] = 250000000 / (115200 * 8) - 1;
+	if(ie){
+		intrenable(IRQaux, interrupt, uart, 0, "uart");
+		ap[MuIer] = RxIen|TxIen;
+	}else
+		ap[MuIer] = 0;
+}
+
+static void
+disable(Uart *uart)
+{
+	u32int *ap;
+
+	ap = (u32int*)uart->regs;
+	ap[MuCntl] = 0;
+	ap[MuIer] = 0;
+}
+
+static void
+kick(Uart *uart)
+{
+	u32int *ap;
+
+	ap = (u32int*)uart->regs;
+	if(uart->blocked)
+		return;
+	coherence();
+	while(ap[MuLsr] & TxRdy){
+		if(uart->op >= uart->oe && uartstageoutput(uart) == 0)
+			break;
+		ap[MuIo] = *(uart->op++);
+	}
+	if(ap[MuLsr] & TxDone)
+		ap[MuIer] &= ~TxIen;
+	else
+		ap[MuIer] |= TxIen;
+	coherence();
+}
+
+/* TODO */
+static void
+dobreak(Uart *uart, int ms)
+{
+	USED(uart, ms);
+}
+
+static int
+baud(Uart *uart, int n)
+{
+	u32int *ap;
+
+	ap = (u32int*)uart->regs;
+	if(uart->freq == 0 || n <= 0)
+		return -1;
+	ap[MuBaud] = (uart->freq + 4*n - 1) / (8 * n) - 1;
+	uart->baud = n;
+	return 0;
+}
+
+static int
+bits(Uart *uart, int n)
+{
+	u32int *ap;
+	int set;
+
+	ap = (u32int*)uart->regs;
+	switch(n){
+	case 7:
+		set = Bits7;
+		break;
+	case 8:
+		set = Bits8;
+		break;
+	default:
+		return -1;
+	}
+	ap[MuLcr] = (ap[MuLcr] & ~Bitsmask) | set;
+	uart->bits = n;
+	return 0;
+}
+
+static int
+stop(Uart *uart, int n)
+{
+	if(n != 1)
+		return -1;
+	uart->stop = n;
+	return 0;
+}
+
+static int
+parity(Uart *uart, int n)
+{
+	if(n != 'n')
+		return -1;
+	uart->parity = n;
+	return 0;
+}
+
+/*
+ * cts/rts flow control
+ *   need to bring signals to gpio pins before enabling this
+ */
+
+static void
+modemctl(Uart *uart, int on)
+{
+	u32int *ap;
+
+	ap = (u32int*)uart->regs;
+	if(on)
+		ap[MuCntl] |= CtsFlow;
+	else
+		ap[MuCntl] &= ~CtsFlow;
+	uart->modem = on;
+}
+
+static void
+rts(Uart *uart, int on)
+{
+	u32int *ap;
+
+	ap = (u32int*)uart->regs;
+	if(on)
+		ap[MuMcr] &= ~RtsN;
+	else
+		ap[MuMcr] |= RtsN;
+}
+
+static long
+status(Uart *uart, void *buf, long n, long offset)
+{
+	char *p;
+
+	p = malloc(READSTR);
+	if(p == nil)
+		error(Enomem);
+	snprint(p, READSTR,
+		"b%d\n"
+		"dev(%d) type(%d) framing(%d) overruns(%d) "
+		"berr(%d) serr(%d)\n",
+
+		uart->baud,
+		uart->dev,
+		uart->type,
+		uart->ferr,
+		uart->oerr,
+		uart->berr,
+		uart->serr
+	);
+	n = readstr(offset, buf, n, p);
+	free(p);
+
+	return n;
+}
+
+static void
+donothing(Uart*, int)
+{
+}
+
+void
+putc(Uart*, int c)
+{
+	u32int *ap;
+
+	ap = (u32int*)AUXREGS;
+	while((ap[MuLsr] & TxRdy) == 0)
+		;
+	ap[MuIo] = c;
+	while((ap[MuLsr] & TxRdy) == 0)
+		;
+}
+
+int
+getc(Uart*)
+{
+	u32int *ap;
+
+	ap = (u32int*)AUXREGS;
+	while((ap[MuLsr] & RxRdy) == 0)
+		;
+	return ap[MuIo] & 0xFF;
+}
+
+void
+uartconsinit(void)
+{
+	Uart *uart;
+	int n;
+	char *p, *cmd;
+
+	if((p = getconf("console")) == nil)
+		return;
+	n = strtoul(p, &cmd, 0);
+	if(p == cmd)
+		return;
+	switch(n){
+	default:
+		return;
+	case 0:
+		uart = &miniuart;
+		break;
+	}
+
+	if(!uart->enabled)
+		(*uart->phys->enable)(uart, 0);
+	uartctl(uart, "b9600 l8 pn s1");
+	if(*cmd != '\0')
+		uartctl(uart, cmd);
+
+	consuart = uart;
+	uart->console = 1;
+}
+
+PhysUart miniphysuart = {
+	.name		= "miniuart",
+	.pnp		= pnp,
+	.enable		= enable,
+	.disable	= disable,
+	.kick		= kick,
+	.dobreak	= dobreak,
+	.baud		= baud,
+	.bits		= bits,
+	.stop		= stop,
+	.parity		= parity,
+	.modemctl	= donothing,
+	.rts		= rts,
+	.dtr		= donothing,
+	.status		= status,
+	.fifo		= donothing,
+	.getc		= getc,
+	.putc		= putc,
+};
+
+void
+okay(int on)
+{
+	static int first;
+
+	if(!first++)
+		gpiosel(OkLed, Output);
+	gpioout(OkLed, !on);
+}

+ 962 - 0
sys/src/9/bcm/usbdwc.c

@@ -0,0 +1,962 @@
+/*
+ * USB host driver for BCM2835
+ *	Synopsis DesignWare Core USB 2.0 OTG controller
+ *
+ * Copyright © 2012 Richard Miller <r.miller@acm.org>
+ *
+ * This is work in progress:
+ * - no isochronous pipes
+ * - no bandwidth budgeting
+ * - frame scheduling is crude
+ * - error handling is overly optimistic
+ * It should be just about adequate for a Plan 9 terminal with
+ * keyboard, mouse, ethernet adapter, and an external flash drive.
+ */
+
+#include	"u.h"
+#include	"../port/lib.h"
+#include	"mem.h"
+#include	"dat.h"
+#include	"fns.h"
+#include	"io.h"
+#include	"../port/error.h"
+#include	"../port/usb.h"
+
+#include "dwcotg.h"
+
+enum
+{
+	USBREGS		= VIRTIO + 0x980000,
+	Enabledelay	= 50,
+	Resetdelay	= 10,
+	ResetdelayHS	= 50,
+
+	Read		= 0,
+	Write		= 1,
+};
+
+typedef struct Ctlr Ctlr;
+typedef struct Epio Epio;
+
+struct Ctlr {
+	Dwcregs	*regs;		/* controller registers */
+	int	nchan;		/* number of host channels */
+	ulong	chanbusy;	/* bitmap of in-use channels */
+	QLock	chanlock;	/* serialise access to chanbusy */
+	QLock	split;		/* serialise split transactions */
+	int	splitretry;	/* count retries of Nyet */
+	int	sofchan;	/* bitmap of channels waiting for sof */
+	int	wakechan;	/* bitmap of channels to wakeup after fiq */
+	int	debugchan;	/* bitmap of channels for interrupt debug */
+	Rendez	*chanintr;	/* sleep till interrupt on channel N */
+};
+
+struct Epio {
+	QLock;
+	Block	*cb;
+	ulong	lastpoll;
+};
+
+static Ctlr dwc;
+static int debug;
+
+static char Ebadlen[] = "bad usb request length";
+static char Enotconfig[] = "usb endpoint not configured";
+
+static void clog(Ep *ep, Hostchan *hc);
+static void logdump(Ep *ep);
+
+static Hostchan*
+chanalloc(Ep *ep)
+{
+	Ctlr *ctlr;
+	int bitmap, i;
+
+	ctlr = ep->hp->aux;
+	qlock(&ctlr->chanlock);
+	bitmap = ctlr->chanbusy;
+	for(i = 0; i < ctlr->nchan; i++)
+		if((bitmap & (1<<i)) == 0){
+			ctlr->chanbusy = bitmap | 1 << i;
+			qunlock(&ctlr->chanlock);
+			return &ctlr->regs->hchan[i];
+		}
+	qunlock(&ctlr->chanlock);
+	panic("miller is a lazy git");
+	return nil;
+}
+
+static void
+chanrelease(Ep *ep, Hostchan *chan)
+{
+	Ctlr *ctlr;
+	int i;
+
+	ctlr = ep->hp->aux;
+	i = chan - ctlr->regs->hchan;
+	qlock(&ctlr->chanlock);
+	ctlr->chanbusy &= ~(1 << i);
+	qunlock(&ctlr->chanlock);
+}
+
+static void
+chansetup(Hostchan *hc, Ep *ep)
+{
+	int hcc;
+	Ctlr *ctlr = ep->hp->aux;
+
+	if(ep->debug)
+		ctlr->debugchan |= 1 << (hc - ctlr->regs->hchan);
+	else
+		ctlr->debugchan &= ~(1 << (hc - ctlr->regs->hchan));
+	switch(ep->dev->state){
+	case Dconfig:
+	case Dreset:
+		hcc = 0;
+		break;
+	default:
+		hcc = ep->dev->nb<<ODevaddr;
+		break;
+	}
+	hcc |= ep->maxpkt | 1<<OMulticnt | ep->nb<<OEpnum;
+	switch(ep->ttype){
+	case Tctl:
+		hcc |= Epctl;
+		break;
+	case Tiso:
+		hcc |= Episo;
+		break;
+	case Tbulk:
+		hcc |= Epbulk;
+		break;
+	case Tintr:
+		hcc |= Epintr;
+		break;
+	}
+	switch(ep->dev->speed){
+	case Lowspeed:
+		hcc |= Lspddev;
+		/* fall through */
+	case Fullspeed:
+		hc->hcsplt = Spltena | POS_ALL | ep->dev->hub << OHubaddr |
+			ep->dev->port;
+		break;
+	default:
+		hc->hcsplt = 0;
+		break;
+	}
+	hc->hcchar = hcc;
+	hc->hcint = ~0;
+}
+
+static int
+sofdone(void *a)
+{
+	Dwcregs *r;
+
+	r = a;
+	return r->gintsts & Sofintr;
+}
+
+static void
+sofwait(Ctlr *ctlr, int n)
+{
+	Dwcregs *r;
+	int x;
+
+	r = ctlr->regs;
+	do{
+		r->gintsts = Sofintr;
+		x = splfhi();
+		ctlr->sofchan |= 1 << n;
+		r->gintmsk |= Sofintr;
+		sleep(&ctlr->chanintr[n], sofdone, r);
+		splx(x);
+	}while((r->hfnum & 7) == 6);
+}
+
+static int
+chandone(void *a)
+{
+	Hostchan *hc;
+
+	hc = a;
+	return (hc->hcint & hc->hcintmsk) != 0;
+}
+
+static int
+chanwait(Ep *ep, Ctlr *ctlr, Hostchan *hc, int mask)
+{
+	int intr, n, x, ointr;
+	ulong start, now;
+	Dwcregs *r;
+
+	r = ctlr->regs;
+	n = hc - r->hchan;
+	for(;;){
+restart:
+		x = splfhi();
+		r->haintmsk |= 1 << n;
+		hc->hcintmsk = mask;
+		sleep(&ctlr->chanintr[n], chandone, hc);
+		hc->hcintmsk = 0;
+		splx(x);
+		intr = hc->hcint;
+		if(intr & Chhltd)
+			return intr;
+		start = fastticks(0);
+		ointr = intr;
+		now = start;
+		do{
+			intr = hc->hcint;
+			if(intr & Chhltd){
+				if((ointr != Ack && ointr != (Ack|Xfercomp)) ||
+				    intr != (Ack|Chhltd|Xfercomp) ||
+				    (now - start) > 60)
+					dprint("await %x after %ld %x -> %x\n",
+						mask, now - start, ointr, intr);
+				return intr;
+			}
+			if((intr & mask) == 0){
+				dprint("ep%d.%d await %x intr %x -> %x\n",						ep->dev->nb, ep->nb, mask, ointr, intr);
+				goto restart;
+			}
+			now = fastticks(0);
+		}while(now - start < 100);
+		dprint("ep%d.%d halting channel %8.8ux hcchar %8.8ux "
+			"grxstsr %8.8ux gnptxsts %8.8ux hptxsts %8.8ux\n",
+			ep->dev->nb, ep->nb, intr, hc->hcchar, r->grxstsr,
+			r->gnptxsts, r->hptxsts);
+		mask = Chhltd;
+		hc->hcchar |= Chdis;
+		start = m->ticks;
+		while(hc->hcchar & Chen){
+			if(m->ticks - start >= 100){
+				print("ep%d.%d channel won't halt hcchar %8.8ux\n",
+					ep->dev->nb, ep->nb, hc->hcchar);
+				break;
+			}
+		}
+		logdump(ep);
+	}
+}
+
+static int
+chanintr(Ctlr *ctlr, int n)
+{
+	Hostchan *hc;
+	int i;
+
+	hc = &ctlr->regs->hchan[n];
+	if(ctlr->debugchan & (1 << n))
+		clog(nil, hc);
+	if((hc->hcsplt & Spltena) == 0)
+		return 0;
+	i = hc->hcint;
+	if(i == (Chhltd|Ack)){
+		hc->hcsplt |= Compsplt;
+		ctlr->splitretry = 0;
+	}else if(i == (Chhltd|Nyet)){
+		if(++ctlr->splitretry >= 3)
+			return 0;
+	}else
+		return 0;
+	if(hc->hcchar & Chen){
+		iprint("hcchar %8.8ux hcint %8.8ux", hc->hcchar, hc->hcint);
+		hc->hcchar |= Chen | Chdis;
+		while(hc->hcchar&Chen)
+			;
+		iprint(" %8.8ux\n", hc->hcint);
+	}
+	hc->hcint = i;
+	if(ctlr->regs->hfnum & 1)
+		hc->hcchar &= ~Oddfrm;
+	else
+		hc->hcchar |= Oddfrm;
+	hc->hcchar = (hc->hcchar &~ Chdis) | Chen;
+	return 1;
+}
+
+static Reg chanlog[32][5];
+static int nchanlog;
+
+static void
+logstart(Ep *ep)
+{
+	if(ep->debug)
+		nchanlog = 0;
+}
+
+static void
+clog(Ep *ep, Hostchan *hc)
+{
+	Reg *p;
+
+	if(ep != nil && !ep->debug)
+		return;
+	if(nchanlog == 32)
+		nchanlog--;
+	p = chanlog[nchanlog];
+	p[0] = dwc.regs->hfnum;
+	p[1] = hc->hcchar;
+	p[2] = hc->hcint;
+	p[3] = hc->hctsiz;
+	p[4] = hc->hcdma;
+	nchanlog++;
+}
+
+static void
+logdump(Ep *ep)
+{
+	Reg *p;
+	int i;
+
+	if(!ep->debug)
+		return;
+	p = chanlog[0];
+	for(i = 0; i < nchanlog; i++){
+		print("%5.5d.%5.5d %8.8ux %8.8ux %8.8ux %8.8ux\n",
+			p[0]&0xFFFF, p[0]>>16, p[1], p[2], p[3], p[4]);
+		p += 5;
+	}
+	nchanlog = 0;
+}
+
+static int
+chanio(Ep *ep, Hostchan *hc, int dir, int pid, void *a, int len)
+{
+	Ctlr *ctlr;
+	int nleft, n, nt, i, maxpkt, npkt;
+	uint hcdma, hctsiz;
+
+	ctlr = ep->hp->aux;
+	maxpkt = ep->maxpkt;
+	npkt = HOWMANY(len, ep->maxpkt);
+	if(npkt == 0)
+		npkt = 1;
+
+	hc->hcchar = (hc->hcchar & ~Epdir) | dir;
+	if(dir == Epin)
+		n = ROUND(len, ep->maxpkt);
+	else
+		n = len;
+	hc->hctsiz = n | npkt << OPktcnt | pid;
+	hc->hcdma  = PADDR(a);
+
+	nleft = len;
+	logstart(ep);
+	for(;;){
+		hcdma = hc->hcdma;
+		hctsiz = hc->hctsiz;
+		hc->hctsiz = hctsiz & ~Dopng;
+		if(hc->hcchar&Chen){
+			dprint("ep%d.%d before chanio hcchar=%8.8ux\n",
+				ep->dev->nb, ep->nb, hc->hcchar);
+			hc->hcchar |= Chen | Chdis;
+			while(hc->hcchar&Chen)
+				;
+			hc->hcint = Chhltd;
+		}
+		if((i = hc->hcint) != 0){
+			dprint("ep%d.%d before chanio hcint=%8.8ux\n",
+				ep->dev->nb, ep->nb, i);
+			hc->hcint = i;
+		}
+		if(hc->hcsplt & Spltena){
+			qlock(&ctlr->split);
+			sofwait(ctlr, hc - ctlr->regs->hchan);
+			if((dwc.regs->hfnum & 1) == 0)
+				hc->hcchar &= ~Oddfrm;
+			else
+				hc->hcchar |= Oddfrm;
+		}
+		hc->hcchar = (hc->hcchar &~ Chdis) | Chen;
+		clog(ep, hc);
+		if(ep->ttype == Tbulk && dir == Epin)
+			i = chanwait(ep, ctlr, hc, /* Ack| */ Chhltd);
+		else if(ep->ttype == Tintr && (hc->hcsplt & Spltena))
+			i = chanwait(ep, ctlr, hc, Chhltd);
+		else
+			i = chanwait(ep, ctlr, hc, Chhltd|Nak);
+		clog(ep, hc);
+		hc->hcint = i;
+
+		if(hc->hcsplt & Spltena){
+			hc->hcsplt &= ~Compsplt;
+			qunlock(&ctlr->split);
+		}
+
+		if((i & Xfercomp) == 0 && i != (Chhltd|Ack) && i != Chhltd){
+			if(i & Stall)
+				error(Estalled);
+			if(i & Nyet)
+				continue;
+			if(i & Nak){
+				if(ep->ttype == Tintr)
+					tsleep(&up->sleep, return0, 0, ep->pollival);
+				else
+					tsleep(&up->sleep, return0, 0, 1);
+				continue;
+			}
+			print("usbotg: ep%d.%d error intr %8.8ux\n",
+				ep->dev->nb, ep->nb, i);
+			if(i & ~(Chhltd|Ack))
+				error(Eio);
+			if(hc->hcdma != hcdma)
+				print("usbotg: weird hcdma %x->%x intr %x->%x\n",
+					hcdma, hc->hcdma, i, hc->hcint);
+		}
+		n = hc->hcdma - hcdma;
+		if(n == 0)
+			if((hc->hctsiz & Pktcnt) != (hctsiz & Pktcnt))
+				break;
+			else
+				continue;
+		if(dir == Epin && ep->ttype == Tbulk && n == nleft){
+			nt = (hctsiz & Xfersize) - (hc->hctsiz & Xfersize);
+			if(nt != n)
+				if(n == ((nt+3) & ~3))
+					n = nt;
+				else
+					print("usbotg: intr %8.8ux dma "
+						"%8.8ux-%8.8ux hctsiz "
+						"%8.8ux-%8.ux\n",
+						i, hcdma, hc->hcdma, hctsiz,
+						hc->hctsiz);
+		}
+		if(n > nleft){
+			if(n != ((nleft+3) & ~3))
+				dprint("too much: wanted %d got %d\n",
+					len, len - nleft + n);
+			n = nleft;
+		}
+		nleft -= n;
+		if(nleft == 0 || n % maxpkt != 0)
+			break;
+		if((i & Xfercomp) && ep->ttype != Tctl)
+			break;
+		if(dir == Epout)
+			dprint("too little: nleft %d hcdma %x->%x hctsiz %x->%x intr %x\n",
+				nleft, hcdma, hc->hcdma, hctsiz, hc->hctsiz, i);
+	}
+	logdump(ep);
+	return len - nleft;
+}
+
+static long
+eptrans(Ep *ep, int rw, void *a, long n)
+{
+	Hostchan *hc;
+
+	if(ep->clrhalt){
+		ep->clrhalt = 0;
+		if(ep->mode != OREAD)
+			ep->toggle[Write] = DATA0;
+		if(ep->mode != OWRITE)
+			ep->toggle[Read] = DATA0;
+	}
+	hc = chanalloc(ep);
+	if(waserror()){
+		ep->toggle[rw] = hc->hctsiz & Pid;
+		chanrelease(ep, hc);
+		if(strcmp(up->errstr, Estalled) == 0)
+			return 0;
+		nexterror();
+	}
+	chansetup(hc, ep);
+	if(rw == Read && ep->ttype == Tbulk){
+		long sofar, m;
+
+		sofar = 0;
+		do{
+			m = n - sofar;
+			if(m > ep->maxpkt)
+				m = ep->maxpkt;
+			m = chanio(ep, hc, Epin, ep->toggle[rw],
+				(char*)a + sofar, m);
+			ep->toggle[rw] = hc->hctsiz & Pid;
+			sofar += m;
+		}while(sofar < n && m == ep->maxpkt);
+		n = sofar;
+	}else{
+		n = chanio(ep, hc, rw == Read? Epin: Epout, ep->toggle[rw],
+			a, n);
+		ep->toggle[rw] = hc->hctsiz & Pid;
+	}
+	chanrelease(ep, hc);
+	poperror();
+	return n;
+}
+
+static long
+ctltrans(Ep *ep, uchar *req, long n)
+{
+	Hostchan *hc;
+	Epio *epio;
+	Block *b;
+	uchar *data;
+	int datalen;
+
+	epio = ep->aux;
+	if(epio->cb != nil){
+		freeb(epio->cb);
+		epio->cb = nil;
+	}
+	if(n < Rsetuplen)
+		error(Ebadlen);
+	if(req[Rtype] & Rd2h){
+		datalen = GET2(req+Rcount);
+		if(datalen <= 0 || datalen > Maxctllen)
+			error(Ebadlen);
+		/* XXX cache madness */
+		epio->cb = b = allocb(ROUND(datalen, ep->maxpkt) + CACHELINESZ);
+		b->wp = (uchar*)ROUND((uintptr)b->wp, CACHELINESZ);
+		memset(b->wp, 0x55, b->lim - b->wp);
+		cachedwbinvse(b->wp, b->lim - b->wp);
+		data = b->wp;
+	}else{
+		b = nil;
+		datalen = n - Rsetuplen;
+		data = req + Rsetuplen;
+	}
+	hc = chanalloc(ep);
+	if(waserror()){
+		chanrelease(ep, hc);
+		if(strcmp(up->errstr, Estalled) == 0)
+			return 0;
+		nexterror();
+	}
+	chansetup(hc, ep);
+	chanio(ep, hc, Epout, SETUP, req, Rsetuplen);
+	if(req[Rtype] & Rd2h){
+		b->wp += chanio(ep, hc, Epin, DATA1, data, datalen);
+		chanio(ep, hc, Epout, DATA1, nil, 0);
+		n = Rsetuplen;
+	}else{
+		if(datalen > 0)
+			chanio(ep, hc, Epout, DATA1, data, datalen);
+		chanio(ep, hc, Epin, DATA1, nil, 0);
+		n = Rsetuplen + datalen;
+	}
+	chanrelease(ep, hc);
+	poperror();
+	return n;
+}
+
+static long
+ctldata(Ep *ep, void *a, long n)
+{
+	Epio *epio;
+	Block *b;
+
+	epio = ep->aux;
+	b = epio->cb;
+	if(b == nil)
+		return 0;
+	if(n > BLEN(b))
+		n = BLEN(b);
+	memmove(a, b->rp, n);
+	b->rp += n;
+	if(BLEN(b) == 0){
+		freeb(b);
+		epio->cb = nil;
+	}
+	return n;
+}
+
+static void
+greset(Dwcregs *r, int bits)
+{
+	r->grstctl |= bits;
+	while(r->grstctl & bits)
+		;
+	microdelay(10);
+}
+
+static void
+init(Hci *hp)
+{
+	Ctlr *ctlr;
+	Dwcregs *r;
+	uint n, rx, tx, ptx;
+
+	ctlr = hp->aux;
+	r = ctlr->regs;
+
+	ctlr->nchan = 1 + ((r->ghwcfg2 & Num_host_chan) >> ONum_host_chan);
+	ctlr->chanintr = malloc(ctlr->nchan * sizeof(Rendez));
+
+	r->gahbcfg = 0;
+	setpower(PowerUsb, 1);
+
+	while((r->grstctl&Ahbidle) == 0)
+		;
+	greset(r, Csftrst);
+
+	r->gusbcfg |= Force_host_mode;
+	tsleep(&up->sleep, return0, 0, 25);
+	r->gahbcfg |= Dmaenable;
+
+	n = (r->ghwcfg3 & Dfifo_depth) >> ODfifo_depth;
+	rx = 0x306;
+	tx = 0x100;
+	ptx = 0x200;
+	r->grxfsiz = rx;
+	r->gnptxfsiz = rx | tx << ODepth;
+	tsleep(&up->sleep, return0, 0, 1);
+	r->hptxfsiz = (rx + tx) | ptx << ODepth;
+	greset(r, Rxfflsh);
+	r->grstctl = TXF_ALL;
+	greset(r, Txfflsh);
+	dprint("usbotg: FIFO depth %d sizes rx/nptx/ptx %8.8ux %8.8ux %8.8ux\n",
+		n, r->grxfsiz, r->gnptxfsiz, r->hptxfsiz);
+
+	r->hport0 = Prtpwr|Prtconndet|Prtenchng|Prtovrcurrchng;
+	r->gintsts = ~0;
+	r->gintmsk = Hcintr;
+	r->gahbcfg |= Glblintrmsk;
+}
+
+static void
+dump(Hci*)
+{
+}
+
+static void
+fiqintr(Ureg*, void *a)
+{
+	Hci *hp;
+	Ctlr *ctlr;
+	Dwcregs *r;
+	uint intr, haint, wakechan;
+	int i;
+
+	hp = a;
+	ctlr = hp->aux;
+	r = ctlr->regs;
+	wakechan = 0;
+	intr = r->gintsts;
+	if(intr & Hcintr){
+		haint = r->haint & r->haintmsk;
+		for(i = 0; haint; i++){
+			if(haint & 1 && chanintr(ctlr, i) == 0){
+				r->haintmsk &= ~(1 << i);
+				wakechan |= 1 << i;
+			}
+			haint >>= 1;
+		}
+	}
+	if(intr & Sofintr){
+		r->gintsts = Sofintr;
+		if((r->hfnum&7) != 6){
+			r->gintmsk &= ~Sofintr;
+			wakechan |= ctlr->sofchan;
+			ctlr->sofchan = 0;
+		}
+	}
+	if(wakechan){
+		ctlr->wakechan |= wakechan;
+		armtimerset(1);
+	}
+}
+
+static void
+irqintr(Ureg*, void *a)
+{
+	Ctlr *ctlr;
+	uint wakechan;
+	int i, x;
+
+	ctlr = a;
+	x = splfhi();
+	armtimerset(0);
+	wakechan = ctlr->wakechan;
+	ctlr->wakechan = 0;
+	splx(x);
+	for(i = 0; wakechan; i++){
+		if(wakechan & 1)
+			wakeup(&ctlr->chanintr[i]);
+		wakechan >>= 1;
+	}
+}
+
+static void
+epopen(Ep *ep)
+{
+	ddprint("usbotg: epopen ep%d.%d ttype %d\n",
+		ep->dev->nb, ep->nb, ep->ttype);
+	switch(ep->ttype){
+	case Tnone:
+		error(Enotconfig);
+	case Tintr:
+		assert(ep->pollival > 0);
+		/* fall through */
+	case Tbulk:
+		if(ep->toggle[Read] == 0)
+			ep->toggle[Read] = DATA0;
+		if(ep->toggle[Write] == 0)
+			ep->toggle[Write] = DATA0;
+		break;
+	}
+	ep->aux = malloc(sizeof(Epio));
+	if(ep->aux == nil)
+		error(Enomem);
+}
+
+static void
+epclose(Ep *ep)
+{
+	ddprint("usbotg: epclose ep%d.%d ttype %d\n",
+		ep->dev->nb, ep->nb, ep->ttype);
+	switch(ep->ttype){
+	case Tctl:
+		freeb(((Epio*)ep->aux)->cb);
+		/* fall through */
+	default:
+		free(ep->aux);
+		break;
+	}
+}
+
+static long
+epread(Ep *ep, void *a, long n)
+{
+	Epio *epio;
+	Block *b;
+	uchar *p;
+	ulong elapsed;
+	long nr;
+
+	ddprint("epread ep%d.%d %ld\n", ep->dev->nb, ep->nb, n);
+	epio = ep->aux;
+	b = nil;
+	qlock(epio);
+	if(waserror()){
+		qunlock(epio);
+		if(b)
+			freeb(b);
+		nexterror();
+	}
+	switch(ep->ttype){
+	default:
+		error(Egreg);
+	case Tctl:
+		nr = ctldata(ep, a, n);
+		qunlock(epio);
+		poperror();
+		return nr;
+	case Tintr:
+		elapsed = TK2MS(m->ticks) - epio->lastpoll;
+		if(elapsed < ep->pollival)
+			tsleep(&up->sleep, return0, 0, ep->pollival - elapsed);
+		/* fall through */
+	case Tbulk:
+		/* XXX cache madness */
+		b = allocb(ROUND(n, ep->maxpkt) + CACHELINESZ);
+		p = (uchar*)ROUND((uintptr)b->base, CACHELINESZ);
+		cachedwbinvse(p, n);
+		nr = eptrans(ep, Read, p, n);
+		epio->lastpoll = TK2MS(m->ticks);
+		memmove(a, p, nr);
+		qunlock(epio);
+		freeb(b);
+		poperror();
+		return nr;
+	}
+}
+
+static long
+epwrite(Ep *ep, void *a, long n)
+{
+	Epio *epio;
+	Block *b;
+	uchar *p;
+	ulong elapsed;
+
+	ddprint("epwrite ep%d.%d %ld\n", ep->dev->nb, ep->nb, n);
+	epio = ep->aux;
+	b = nil;
+	qlock(epio);
+	if(waserror()){
+		qunlock(epio);
+		if(b)
+			freeb(b);
+		nexterror();
+	}
+	switch(ep->ttype){
+	default:
+		error(Egreg);
+	case Tintr:
+		elapsed = TK2MS(m->ticks) - epio->lastpoll;
+		if(elapsed < ep->pollival)
+			tsleep(&up->sleep, return0, 0, ep->pollival - elapsed);
+		/* fall through */
+	case Tctl:
+	case Tbulk:
+		/* XXX cache madness */
+		b = allocb(n + CACHELINESZ);
+		p = (uchar*)ROUND((uintptr)b->base, CACHELINESZ);
+		memmove(p, a, n);
+		cachedwbse(p, n);
+		if(ep->ttype == Tctl)
+			n = ctltrans(ep, p, n);
+		else{
+			n = eptrans(ep, Write, p, n);
+			epio->lastpoll = TK2MS(m->ticks);
+		}
+		qunlock(epio);
+		freeb(b);
+		poperror();
+		return n;
+	}
+}
+
+static char*
+seprintep(char *s, char*, Ep*)
+{
+	return s;
+}
+	
+static int
+portenable(Hci *hp, int port, int on)
+{
+	Ctlr *ctlr;
+	Dwcregs *r;
+
+	assert(port == 1);
+	ctlr = hp->aux;
+	r = ctlr->regs;
+	dprint("usbotg enable=%d; sts %#x\n", on, r->hport0);
+	if(!on)
+		r->hport0 = Prtpwr | Prtena;
+	tsleep(&up->sleep, return0, 0, Enabledelay);
+	dprint("usbotg enable=%d; sts %#x\n", on, r->hport0);
+	return 0;
+}
+
+static int
+portreset(Hci *hp, int port, int on)
+{
+	Ctlr *ctlr;
+	Dwcregs *r;
+	int b, s;
+
+	assert(port == 1);
+	ctlr = hp->aux;
+	r = ctlr->regs;
+	dprint("usbotg reset=%d; sts %#x\n", on, r->hport0);
+	if(!on)
+		return 0;
+	r->hport0 = Prtpwr | Prtrst;
+	tsleep(&up->sleep, return0, 0, ResetdelayHS);
+	r->hport0 = Prtpwr;
+	tsleep(&up->sleep, return0, 0, Enabledelay);
+	s = r->hport0;
+	b = s & (Prtconndet|Prtenchng|Prtovrcurrchng);
+	if(b != 0)
+		r->hport0 = Prtpwr | b;
+	dprint("usbotg reset=%d; sts %#x\n", on, s);
+	if((s & Prtena) == 0)
+		print("usbotg: host port not enabled after reset");
+	return 0;
+}
+
+static int
+portstatus(Hci *hp, int port)
+{
+	Ctlr *ctlr;
+	Dwcregs *r;
+	int b, s;
+
+	assert(port == 1);
+	ctlr = hp->aux;
+	r = ctlr->regs;
+	s = r->hport0;
+	b = s & (Prtconndet|Prtenchng|Prtovrcurrchng);
+	if(b != 0)
+		r->hport0 = Prtpwr | b;
+	b = 0;
+	if(s & Prtconnsts)
+		b |= HPpresent;
+	if(s & Prtconndet)
+		b |= HPstatuschg;
+	if(s & Prtena)
+		b |= HPenable;
+	if(s & Prtenchng)
+		b |= HPchange;
+	if(s & Prtovrcurract)
+		 b |= HPovercurrent;
+	if(s & Prtsusp)
+		b |= HPsuspend;
+	if(s & Prtrst)
+		b |= HPreset;
+	if(s & Prtpwr)
+		b |= HPpower;
+	switch(s & Prtspd){
+	case HIGHSPEED:
+		b |= HPhigh;
+		break;
+	case LOWSPEED:
+		b |= HPslow;
+		break;
+	}
+	return b;
+}
+
+static void
+shutdown(Hci*)
+{
+}
+
+static void
+setdebug(Hci*, int d)
+{
+	debug = d;
+}
+
+static int
+reset(Hci *hp)
+{
+	Ctlr *ctlr;
+	uint id;
+
+	ctlr = &dwc;
+	if(ctlr->regs != nil)
+		return -1;
+	ctlr->regs = (Dwcregs*)USBREGS;
+	id = ctlr->regs->gsnpsid;
+	if((id>>16) != ('O'<<8 | 'T'))
+		return -1;
+	dprint("usbotg: rev %d.%3.3x\n", (id>>12)&0xF, id&0xFFF);
+
+	intrenable(IRQtimerArm, irqintr, ctlr, 0, "dwc");
+
+	hp->aux = ctlr;
+	hp->port = 0;
+	hp->irq = IRQusb;
+	hp->tbdf = 0;
+	hp->nports = 1;
+	hp->highspeed = 1;
+
+	hp->init = init;
+	hp->dump = dump;
+	hp->interrupt = fiqintr;
+	hp->epopen = epopen;
+	hp->epclose = epclose;
+	hp->epread = epread;
+	hp->epwrite = epwrite;
+	hp->seprintep = seprintep;
+	hp->portenable = portenable;
+	hp->portreset = portreset;
+	hp->portstatus = portstatus;
+	hp->shutdown = shutdown;
+	hp->debug = setdebug;
+	hp->type = "dwcotg";
+	return 0;
+}
+
+void
+usbdwclink(void)
+{
+	addhcitype("dwcotg", reset);
+}

+ 290 - 0
sys/src/9/bcm/vcore.c

@@ -0,0 +1,290 @@
+#include "u.h"
+#include "../port/lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+
+/*
+ * Mailbox interface with videocore gpu
+ */
+
+#define	MAILBOX		(VIRTIO+0xB880)
+
+typedef struct Prophdr Prophdr;
+typedef struct Fbinfo Fbinfo;
+
+enum {
+	Read		= 0x00>>2,
+	Write		= 0x00>>2,
+	Peek		= 0x10>>2,
+	Sender		= 0x14>>2,
+	Status		= 0x18>>2,
+		Full		= 1<<31,
+		Empty		= 1<<30,
+	Config		= 0x1C>>2,
+	NRegs		= 0x20>>2,
+
+	ChanMask	= 0xF,
+	ChanProps	= 8,
+	ChanFb		= 1,
+
+	Req			= 0x0,
+	RspOk		= 0x80000000,
+	TagResp		= 1<<31,
+
+	TagGetfwrev	= 0x00000001,
+	TagGetmac	= 0x00010003,
+	TagGetram	= 0x00010005,
+	TagGetpower	= 0x00020001,
+	TagSetpower	= 0x00028001,
+		Powerwait	= 1<<1,
+	TagGetclkspd= 0x00030002,
+	TagFballoc	= 0x00040001,
+	TagFbfree	= 0x00048001,
+	TagFbblank	= 0x00040002,
+	TagGetres	= 0x00040003,
+	TagSetres	= 0x00048003,
+	TagGetvres	= 0x00040004,
+	TagSetvres	= 0x00048004,
+	TagGetdepth	= 0x00040005,
+	TagSetdepth	= 0x00048005,
+	TagGetrgb	= 0x00044006,
+	TagSetrgb	= 0x00048006,
+};
+
+struct Fbinfo {
+	u32int	xres;
+	u32int	yres;
+	u32int	xresvirtual;
+	u32int	yresvirtual;
+	u32int	pitch;			/* returned by gpu */
+	u32int	bpp;
+	u32int	xoffset;
+	u32int	yoffset;
+	u32int	base;			/* returned by gpu */
+	u32int	screensize;		/* returned by gpu */
+};
+
+
+struct Prophdr {
+	u32int	len;
+	u32int	req;
+	u32int	tag;
+	u32int	tagbuflen;
+	u32int	taglen;
+	u32int	data[1];
+};
+
+static void
+vcwrite(uint chan, int val)
+{
+	u32int *r;
+
+	r = (u32int*)MAILBOX + NRegs;
+	val &= ~ChanMask;
+	while(r[Status]&Full)
+		;
+	coherence();
+	r[Write] = val | chan;
+}
+
+static int
+vcread(uint chan)
+{
+	u32int *r;
+	int x;
+
+	r = (u32int*)MAILBOX;
+	do{
+		while(r[Status]&Empty)
+			;
+		coherence();
+		x = r[Read];
+	}while((x&ChanMask) != chan);
+	return x & ~ChanMask;
+}
+
+/*
+ * Property interface
+ */
+
+static int
+vcreq(int tag, void *buf, int vallen, int rsplen)
+{
+	uintptr r;
+	int n;
+	Prophdr *prop;
+	static uintptr base = BUSDRAM;
+
+	if(rsplen < vallen)
+		rsplen = vallen;
+	rsplen = (rsplen+3) & ~3;
+	prop = (Prophdr*)(VCBUFFER);
+	n = sizeof(Prophdr) + rsplen + 8;
+	memset(prop, 0, n);
+	prop->len = n;
+	prop->req = Req;
+	prop->tag = tag;
+	prop->tagbuflen = rsplen;
+	prop->taglen = vallen;
+	if(vallen > 0)
+		memmove(prop->data, buf, vallen);
+	cachedwbinvse(prop, prop->len);
+	for(;;){
+		vcwrite(ChanProps, PADDR(prop) + base);
+		r = vcread(ChanProps);
+		if(r == PADDR(prop) + base)
+			break;
+		if(base == 0)
+			return -1;
+		base = 0;
+	}
+	if(prop->req == RspOk && prop->tag == tag && prop->taglen & TagResp) {
+		if((n = prop->taglen & ~TagResp) < rsplen)
+			rsplen = n;
+		memmove(buf, prop->data, rsplen);
+	}else
+		rsplen = -1;
+
+	return rsplen;
+}
+
+/*
+ * Framebuffer
+ */
+
+static int
+fbdefault(int *width, int *height, int *depth)
+{
+	u32int buf[3];
+
+	if(vcreq(TagGetres, &buf[0], 0, 2*4) != 2*4 ||
+	   vcreq(TagGetdepth, &buf[2], 0, 4) != 4)
+		return -1;
+	*width = buf[0];
+	*height = buf[1];
+	*depth = buf[2];
+	return 0;
+}
+
+void*
+fbinit(int set, int *width, int *height, int *depth)
+{
+	Fbinfo *fi;
+	uintptr va;
+
+	if(!set)
+		fbdefault(width, height, depth);
+	/* Screen width must be a multiple of 16 */
+	*width &= ~0xF;
+	fi = (Fbinfo*)(VCBUFFER);
+	memset(fi, 0, sizeof(*fi));
+	fi->xres = fi->xresvirtual = *width;
+	fi->yres = fi->yresvirtual = *height;
+	fi->bpp = *depth;
+	cachedwbinvse(fi, sizeof(*fi));
+	vcwrite(ChanFb, DMAADDR(fi));
+	if(vcread(ChanFb) != 0)
+		return 0;
+	va = mmukmap(FRAMEBUFFER, PADDR(fi->base), fi->screensize);
+	if(va)
+		memset((char*)va, 0x7F, fi->screensize);
+	return (void*)va;
+}
+
+int
+fbblank(int blank)
+{
+	u32int buf[1];
+
+	buf[0] = blank;
+	if(vcreq(TagFbblank, buf, sizeof buf, sizeof buf) != sizeof buf)
+		return -1;
+	return buf[0] & 1;
+}
+
+/*
+ * Power management
+ */
+void
+setpower(int dev, int on)
+{
+	u32int buf[2];
+
+	buf[0] = dev;
+	buf[1] = Powerwait | (on? 1: 0);
+	vcreq(TagSetpower, buf, sizeof buf, sizeof buf);
+}
+
+int
+getpower(int dev)
+{
+	u32int buf[2];
+
+	buf[0] = dev;
+	buf[1] = 0;
+	if(vcreq(TagGetpower, buf, sizeof buf[0], sizeof buf) != sizeof buf)
+		return -1;
+	return buf[0] & 1;
+}
+
+/*
+ * Get ethernet address (as hex string)
+ *	 [not reentrant]
+ */
+char *
+getethermac(void)
+{
+	uchar ea[8];
+	char *p;
+	int i;
+	static char buf[16];
+
+	memset(ea, 0, sizeof ea);
+	vcreq(TagGetmac, ea, 0, sizeof ea);
+	p = buf;
+	for(i = 0; i < 6; i++)
+		p += sprint(p, "%.2x", ea[i]);
+	return buf;
+}
+
+/*
+ * Get firmware revision
+ */
+uint
+getfirmware(void)
+{
+	u32int buf[1];
+
+	if(vcreq(TagGetfwrev, buf, 0, sizeof buf) != sizeof buf)
+		return 0;
+	return buf[0];
+}
+
+/*
+ * Get ARM ram
+ */
+void
+getramsize(Confmem *mem)
+{
+	u32int buf[2];
+
+	if(vcreq(TagGetram, buf, 0, sizeof buf) != sizeof buf)
+		return;
+	mem->base = buf[0];
+	mem->limit = buf[1];
+}
+
+/*
+ * Get clock rate
+ */
+ulong
+getclkrate(int clkid)
+{
+	u32int buf[2];
+
+	buf[0] = clkid;
+	if(vcreq(TagGetclkspd, buf, sizeof(buf[0]), sizeof(buf)) != sizeof buf)
+		return 0;
+	return buf[1];
+}

+ 518 - 0
sys/src/9/bcm/vfp3.c

@@ -0,0 +1,518 @@
+/*
+ * VFPv2 or VFPv3 floating point unit
+ */
+#include "u.h"
+#include "../port/lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+#include "ureg.h"
+#include "arm.h"
+
+/* subarchitecture code in m->havefp */
+enum {
+	VFPv2	= 2,
+	VFPv3	= 3,
+};
+
+/* fp control regs.  most are read-only */
+enum {
+	Fpsid =	0,
+	Fpscr =	1,			/* rw */
+	Mvfr1 =	6,
+	Mvfr0 =	7,
+	Fpexc =	8,			/* rw */
+	Fpinst= 9,			/* optional, for exceptions */
+	Fpinst2=10,
+};
+enum {
+	/* Fpexc bits */
+	Fpex =		1u << 31,
+	Fpenabled =	1 << 30,
+	Fpdex =		1 << 29,	/* defined synch exception */
+//	Fp2v =		1 << 28,	/* Fpinst2 reg is valid */
+//	Fpvv =		1 << 27,	/* if Fpdex, vecitr is valid */
+//	Fptfv = 	1 << 26,	/* trapped fault is valid */
+//	Fpvecitr =	MASK(3) << 8,
+	/* FSR bits appear here */
+	Fpmbc =		Fpdex,		/* bits exception handler must clear */
+
+	/* Fpscr bits; see u.h for more */
+	Stride =	MASK(2) << 20,
+	Len =		MASK(3) << 16,
+	Dn=		1 << 25,
+	Fz=		1 << 24,
+	/* trap exception enables (not allowed in vfp3) */
+	FPIDNRM =	1 << 15,	/* input denormal */
+	Alltraps = FPIDNRM | FPINEX | FPUNFL | FPOVFL | FPZDIV | FPINVAL,
+	/* pending exceptions */
+	FPAIDNRM =	1 << 7,		/* input denormal */
+	Allexc = FPAIDNRM | FPAINEX | FPAUNFL | FPAOVFL | FPAZDIV | FPAINVAL,
+	/* condition codes */
+	Allcc =		MASK(4) << 28,
+};
+enum {
+	/* CpCPaccess bits */
+	Cpaccnosimd =	1u << 31,
+	Cpaccd16 =	1 << 30,
+};
+
+static char *
+subarch(int impl, uint sa)
+{
+	static char *armarchs[] = {
+		"VFPv1 (unsupported)",
+		"VFPv2",
+		"VFPv3+ with common VFP subarch v2",
+		"VFPv3+ with null subarch",
+		"VFPv3+ with common VFP subarch v3",
+	};
+
+	if (impl != 'A' || sa >= nelem(armarchs))
+		return "GOK";
+	else
+		return armarchs[sa];
+}
+
+static char *
+implement(uchar impl)
+{
+	if (impl == 'A')
+		return "arm";
+	else
+		return "unknown";
+}
+
+static int
+havefp(void)
+{
+	int gotfp;
+	ulong acc, sid;
+
+	if (m->havefpvalid)
+		return m->havefp;
+
+	m->havefp = 0;
+	gotfp = 1 << CpFP | 1 << CpDFP;
+	cpwrsc(0, CpCONTROL, 0, CpCPaccess, MASK(28));
+	acc = cprdsc(0, CpCONTROL, 0, CpCPaccess);
+	if ((acc & (MASK(2) << (2*CpFP))) == 0) {
+		gotfp &= ~(1 << CpFP);
+		print("fpon: no single FP coprocessor\n");
+	}
+	if ((acc & (MASK(2) << (2*CpDFP))) == 0) {
+		gotfp &= ~(1 << CpDFP);
+		print("fpon: no double FP coprocessor\n");
+	}
+	if (!gotfp) {
+		print("fpon: no FP coprocessors\n");
+		m->havefpvalid = 1;
+		return 0;
+	}
+	m->fpon = 1;			/* don't panic */
+	sid = fprd(Fpsid);
+	m->fpon = 0;
+	switch((sid >> 16) & MASK(7)){
+	case 0:				/* VFPv1 */
+		break;
+	case 1:				/* VFPv2 */
+		m->havefp = VFPv2;
+		m->fpnregs = 16;
+		break;
+	default:			/* VFPv3 or later */
+		m->havefp = VFPv3;
+		m->fpnregs = (acc & Cpaccd16) ? 16 : 32;
+		break;
+	}
+	if (m->machno == 0)
+		print("fp: %d registers, %s simd\n", m->fpnregs,
+			(acc & Cpaccnosimd? " no": ""));
+	m->havefpvalid = 1;
+	return 1;
+}
+
+/*
+ * these can be called to turn the fpu on or off for user procs,
+ * not just at system start up or shutdown.
+ */
+
+void
+fpoff(void)
+{
+	if (m->fpon) {
+		fpwr(Fpexc, 0);
+		m->fpon = 0;
+	}
+}
+
+void
+fpononly(void)
+{
+	if (!m->fpon && havefp()) {
+		/* enable fp.  must be first operation on the FPUs. */
+		fpwr(Fpexc, Fpenabled);
+		m->fpon = 1;
+	}
+}
+
+static void
+fpcfg(void)
+{
+	int impl;
+	ulong sid;
+	static int printed;
+
+	/* clear pending exceptions; no traps in vfp3; all v7 ops are scalar */
+	m->fpscr = Dn | Fz | FPRNR | (FPINVAL | FPZDIV | FPOVFL) & ~Alltraps;
+	fpwr(Fpscr, m->fpscr);
+	m->fpconfiged = 1;
+
+	if (printed)
+		return;
+	sid = fprd(Fpsid);
+	impl = sid >> 24;
+	print("fp: %s arch %s; rev %ld\n", implement(impl),
+		subarch(impl, (sid >> 16) & MASK(7)), sid & MASK(4));
+	printed = 1;
+}
+
+void
+fpinit(void)
+{
+	if (havefp()) {
+		fpononly();
+		fpcfg();
+	}
+}
+
+void
+fpon(void)
+{
+	if (havefp()) {
+	 	fpononly();
+		if (m->fpconfiged)
+			fpwr(Fpscr, (fprd(Fpscr) & Allcc) | m->fpscr);
+		else
+			fpcfg();	/* 1st time on this fpu; configure it */
+	}
+}
+
+void
+fpclear(void)
+{
+//	ulong scr;
+
+	fpon();
+//	scr = fprd(Fpscr);
+//	m->fpscr = scr & ~Allexc;
+//	fpwr(Fpscr, m->fpscr);
+
+	fpwr(Fpexc, fprd(Fpexc) & ~Fpmbc);
+}
+
+
+/*
+ * Called when a note is about to be delivered to a
+ * user process, usually at the end of a system call.
+ * Note handlers are not allowed to use the FPU so
+ * the state is marked (after saving if necessary) and
+ * checked in the Device Not Available handler.
+ */
+void
+fpunotify(Ureg*)
+{
+	if(up->fpstate == FPactive){
+		fpsave(&up->fpsave);
+		up->fpstate = FPinactive;
+	}
+	up->fpstate |= FPillegal;
+}
+
+/*
+ * Called from sysnoted() via the machine-dependent
+ * noted() routine.
+ * Clear the flag set above in fpunotify().
+ */
+void
+fpunoted(void)
+{
+	up->fpstate &= ~FPillegal;
+}
+
+/*
+ * Called early in the non-interruptible path of
+ * sysrfork() via the machine-dependent syscall() routine.
+ * Save the state so that it can be easily copied
+ * to the child process later.
+ */
+void
+fpusysrfork(Ureg*)
+{
+	if(up->fpstate == FPactive){
+		fpsave(&up->fpsave);
+		up->fpstate = FPinactive;
+	}
+}
+
+/*
+ * Called later in sysrfork() via the machine-dependent
+ * sysrforkchild() routine.
+ * Copy the parent FPU state to the child.
+ */
+void
+fpusysrforkchild(Proc *p, Ureg *, Proc *up)
+{
+	/* don't penalize the child, it hasn't done FP in a note handler. */
+	p->fpstate = up->fpstate & ~FPillegal;
+}
+
+/* should only be called if p->fpstate == FPactive */
+void
+fpsave(FPsave *fps)
+{
+	int n;
+
+	fpon();
+	fps->control = fps->status = fprd(Fpscr);
+	assert(m->fpnregs);
+	for (n = 0; n < m->fpnregs; n++)
+		fpsavereg(n, (uvlong *)fps->regs[n]);
+	fpoff();
+}
+
+static void
+fprestore(Proc *p)
+{
+	int n;
+
+	fpon();
+	fpwr(Fpscr, p->fpsave.control);
+	m->fpscr = fprd(Fpscr) & ~Allcc;
+	assert(m->fpnregs);
+	for (n = 0; n < m->fpnregs; n++)
+		fprestreg(n, *(uvlong *)p->fpsave.regs[n]);
+}
+
+/*
+ * Called from sched() and sleep() via the machine-dependent
+ * procsave() routine.
+ * About to go in to the scheduler.
+ * If the process wasn't using the FPU
+ * there's nothing to do.
+ */
+void
+fpuprocsave(Proc *p)
+{
+	if(p->fpstate == FPactive){
+		if(p->state == Moribund)
+			fpclear();
+		else{
+			/*
+			 * Fpsave() stores without handling pending
+			 * unmasked exeptions. Postnote() can't be called
+			 * here as sleep() already has up->rlock, so
+			 * the handling of pending exceptions is delayed
+			 * until the process runs again and generates an
+			 * emulation fault to activate the FPU.
+			 */
+			fpsave(&p->fpsave);
+		}
+		p->fpstate = FPinactive;
+	}
+}
+
+/*
+ * The process has been rescheduled and is about to run.
+ * Nothing to do here right now. If the process tries to use
+ * the FPU again it will cause a Device Not Available
+ * exception and the state will then be restored.
+ */
+void
+fpuprocrestore(Proc *)
+{
+}
+
+/*
+ * Disable the FPU.
+ * Called from sysexec() via sysprocsetup() to
+ * set the FPU for the new process.
+ */
+void
+fpusysprocsetup(Proc *p)
+{
+	p->fpstate = FPinit;
+	fpoff();
+}
+
+static void
+mathnote(void)
+{
+	ulong status;
+	char *msg, note[ERRMAX];
+
+	status = up->fpsave.status;
+
+	/*
+	 * Some attention should probably be paid here to the
+	 * exception masks and error summary.
+	 */
+	if (status & FPAINEX)
+		msg = "inexact";
+	else if (status & FPAOVFL)
+		msg = "overflow";
+	else if (status & FPAUNFL)
+		msg = "underflow";
+	else if (status & FPAZDIV)
+		msg = "divide by zero";
+	else if (status & FPAINVAL)
+		msg = "bad operation";
+	else
+		msg = "spurious";
+	snprint(note, sizeof note, "sys: fp: %s fppc=%#p status=%#lux",
+		msg, up->fpsave.pc, status);
+	postnote(up, 1, note, NDebug);
+}
+
+static void
+mathemu(Ureg *)
+{
+	if(m->havefp == VFPv3 && !(fprd(Fpexc) & (Fpex|Fpdex)))
+		iprint("mathemu: not an FP exception but an unknown FP opcode\n");
+	switch(up->fpstate){
+	case FPemu:
+		error("illegal instruction: VFP opcode in emulated mode");
+	case FPinit:
+		fpinit();
+		up->fpstate = FPactive;
+		break;
+	case FPinactive:
+		/*
+		 * Before restoring the state, check for any pending
+		 * exceptions.  There's no way to restore the state without
+		 * generating an unmasked exception.
+		 * More attention should probably be paid here to the
+		 * exception masks and error summary.
+		 */
+		if(up->fpsave.status & (FPAINEX|FPAUNFL|FPAOVFL|FPAZDIV|FPAINVAL)){
+			mathnote();
+			break;
+		}
+		fprestore(up);
+		up->fpstate = FPactive;
+		break;
+	case FPactive:
+		error("illegal instruction: bad vfp fpu opcode");
+		break;
+	}
+	fpclear();
+}
+
+void
+fpstuck(uintptr pc)
+{
+	if (m->fppc == pc && m->fppid == up->pid) {
+		m->fpcnt++;
+		if (m->fpcnt > 4)
+			panic("fpuemu: cpu%d stuck at pid %ld %s pc %#p "
+				"instr %#8.8lux", m->machno, up->pid, up->text,
+				pc, *(ulong *)pc);
+	} else {
+		m->fppid = up->pid;
+		m->fppc = pc;
+		m->fpcnt = 0;
+	}
+}
+
+enum {
+	N = 1<<31,
+	Z = 1<<30,
+	C = 1<<29,
+	V = 1<<28,
+	REGPC = 15,
+};
+
+static int
+condok(int cc, int c)
+{
+	switch(c){
+	case 0:	/* Z set */
+		return cc&Z;
+	case 1:	/* Z clear */
+		return (cc&Z) == 0;
+	case 2:	/* C set */
+		return cc&C;
+	case 3:	/* C clear */
+		return (cc&C) == 0;
+	case 4:	/* N set */
+		return cc&N;
+	case 5:	/* N clear */
+		return (cc&N) == 0;
+	case 6:	/* V set */
+		return cc&V;
+	case 7:	/* V clear */
+		return (cc&V) == 0;
+	case 8:	/* C set and Z clear */
+		return cc&C && (cc&Z) == 0;
+	case 9:	/* C clear or Z set */
+		return (cc&C) == 0 || cc&Z;
+	case 10:	/* N set and V set, or N clear and V clear */
+		return (~cc&(N|V))==0 || (cc&(N|V)) == 0;
+	case 11:	/* N set and V clear, or N clear and V set */
+		return (cc&(N|V))==N || (cc&(N|V))==V;
+	case 12:	/* Z clear, and either N set and V set or N clear and V clear */
+		return (cc&Z) == 0 && ((~cc&(N|V))==0 || (cc&(N|V))==0);
+	case 13:	/* Z set, or N set and V clear or N clear and V set */
+		return (cc&Z) || (cc&(N|V))==N || (cc&(N|V))==V;
+	case 14:	/* always */
+		return 1;
+	case 15:	/* never (reserved) */
+		return 0;
+	}
+	return 0;	/* not reached */
+}
+
+/* only called to deal with user-mode instruction faults */
+int
+fpuemu(Ureg* ureg)
+{
+	int s, nfp, cop, op;
+	uintptr pc;
+
+	if(waserror()){
+		postnote(up, 1, up->errstr, NDebug);
+		return 1;
+	}
+
+	if(up->fpstate & FPillegal)
+		error("floating point in note handler");
+
+	nfp = 0;
+	pc = ureg->pc;
+	validaddr(pc, 4, 0);
+	if(!condok(ureg->psr, *(ulong*)pc >> 28))
+		iprint("fpuemu: conditional instr shouldn't have got here\n");
+	op  = (*(ulong *)pc >> 24) & MASK(4);
+	cop = (*(ulong *)pc >>  8) & MASK(4);
+	if(m->fpon)
+		fpstuck(pc);		/* debugging; could move down 1 line */
+	if (ISFPAOP(cop, op)) {		/* old arm 7500 fpa opcode? */
+//		iprint("fpuemu: fpa instr %#8.8lux at %#p\n", *(ulong *)pc, pc);
+//		error("illegal instruction: old arm 7500 fpa opcode");
+		s = spllo();
+		if(waserror()){
+			splx(s);
+			nexterror();
+		}
+		nfp = fpiarm(ureg);	/* advances pc past emulated instr(s) */
+		if (nfp > 1)		/* could adjust this threshold */
+			m->fppc = m->fpcnt = 0;
+		splx(s);
+		poperror();
+	} else if (ISVFPOP(cop, op)) {	/* if vfp, fpu must be off */
+		mathemu(ureg);		/* enable fpu & retry */
+		nfp = 1;
+	}
+
+	poperror();
+	return nfp;
+}

+ 149 - 0
sys/src/9/bcm/words

@@ -0,0 +1,149 @@
+raspberry pi
+
+broadcom 2835 SoC (based on 2708)
+arm1176jzf-s (v6 arch) 700MHz cpu, apparently dual-issue, with vfp2
+videocore 4 gpu
+
+l1 I & D VIPT caches
+	16K each: 4-way, 128 sets, 32-byte lines
+	l1 D is write-through, l1 I is write-back
+unified l2 PIPT cache 128K: 4-way?, 1024? sets, 32-byte lines, mostly for gpu
+(by default CPU doesn't see it)
+
+we arrange that device register accesses are uncached.
+
+256MB of dram at physical address 0, shared with gpu
+non-16550 uart for console
+	uart serial voltages are wrong (3.3v but rs232 is nominally 12v);
+	could use usb serial (ick).
+there's no real ethernet controller, so we have to use usb ether,
+and the usb controller is nastier than usual.
+
+There's a serial port (115200b/s) on P1 connector pins (GND,TXD,RXD) =
+(6,8,10).  These are 3v TTL signals: use a level-shifter to convert to
+RS232, or a USB-to-TTL-serial adapter.  Add the line "console=0
+b115200" to the /cfg/pxe file on the server, or the parameter
+"console='0 b115200'" to cmdline.txt on the SD card.
+
+9pi is a Plan 9 terminal, which can boot with local fossil root on the
+sd card (/dev/sdM0), or with root from a Plan 9 file server via tcp.
+
+9picpu is a Plan 9 cpu server, which could be used in a headless
+configuration without screen, keyboard or mouse.
+
+9pifat is a minimal configuration which boots a shell script boot.rc
+with root in /plan9 on the dos partition, maybe useful for embedded
+applications where a full Plan 9 system is not needed.
+
+Network booting with u-boot:
+start with a normal rpi u-boot sd (e.g. raspberry-pi-uboot-20120707).
+update the start.elf with a version from a newer rpi distro (see below).
+mk installall
+add new system to ndb
+see booting(8)
+
+Booting from sd card:
+- start with a normal rpi distro sd (e.g. 2012-08-16-wheezy-raspbian)
+  [NB: versions of start.elf earlier than this may not be compatible]
+- copy 9pi to sd's root directory
+- add or change "kernel=" line in config.txt to "kernel=9pi"
+- plan9.ini is built from the "kernel arguments" in cmdline.txt - each
+  var=value entry becomes one plan9.ini line, so entries with spaces will
+  need single quotes.
+
+
+	physical mem map
+
+hex addr size	what
+----
+0	 256MB	sdram, cached
+00000000 64	exception vectors
+00000100 7936	boot ATAGs (inc. cmdline.txt)
+00002000 4K	Mach
+00003000 1K	L2 page table for exception vectors
+00003400 1K	videocore mailbox buffer
+00003800 2K	FIQ stack
+00004000 16K	L1 page table for kernel
+00008000 -	default kernel load address
+01000000 16K	u-boot env
+20000000 16M	peripherals
+20003000	system timer(s)
+20007000	dma
+2000B000	arm control: intr, timers 0 & 1, semas, doorbells, mboxes
+20100000	power, reset, watchdog
+20200000	gpio
+20201000	uart0
+20202000	mmc
+20215040	uart1 (mini uart)
+20300000	eMMC
+20600000	smi
+20980000	otg usb
+
+40000000	l2 cache only
+7e00b000	arm control
+7e2000c0	jtag
+7e201000?	pl011 usrt
+7e215000	aux: uart1, spi[12]
+
+80000000
+
+c0000000	bypass caches
+
+	virtual mem map (from cpu address map & mmu mappings)
+
+hex addr size	what
+----
+0	 512MB	user process address space
+7e000000 16M	i/o registers
+80000000 <=224M	kernel ram (reserve some for GPU)
+c0000000 256MB	kzero, mapped to 0
+ffff0000 4K	exception vectors
+
+Linux params at *R2 (default 0x100) are a sequence of ATAGs
+  struct atag {
+	u32int size;		/* size of ATAG in words, including header */
+	u32int tag;		/* ATAG_CORE is first, ATAG_NONE is last */
+	u32int data[size-2];
+  };
+00000000	ATAG_NONE
+54410001	ATAG_CORE
+54410002	ATAG_MEM
+54410009	ATAG_CMDLINE
+
+uart dmas	15, 14
+
+intrs (96)
+irq1
+0	timer0
+1	timer1
+2	timer2
+3	timer3
+8	isp
+9	usb
+16	dma0
+17	dma1
+⋯
+28	dma12
+29	aux: uart1
+30	arm
+31	vpu dma
+
+irq2
+35	sdc
+36	dsio
+40	hdmi0
+41	hdmi1
+48	smi
+56	sdio
+57	uart1 aka "vc uart"
+
+irq0
+64	timer
+65	mbox
+66	doorbell0
+67	doorbell1
+75	usb
+77	dma2
+78	dma3
+82	sdio
+83	uart0

+ 1 - 0
sys/src/9/mkfile

@@ -1,4 +1,5 @@
 ARCH=\
+	bcm\
 	bitsy\
 	kw\
 	mtx\

+ 1 - 1
sys/src/9/ppc/blast

@@ -56,4 +56,4 @@ bootdir
 	/power/bin/auth/factotum
 	/power/bin/ls
 	/power/bin/auth/wrkey
-	/sys/lib/sysconfig/blast/boot
+	/sys/lib/sysconfig/ppc/boot

+ 2 - 0
sys/src/9/ppc/blast.h

@@ -27,6 +27,8 @@
  *
  *	CS2, CS3, CS4, (and CS5) are covered by DBAT 0,  CS0 and CS1 by DBAT 3, CS6 by DBAT 2
  */
+#define	IMMR		0xf0000000
+
 #define	FLASHMEM	0xfe000000
 #define	FLASHSIZE	0x01000000
 #define	DSP1BASE		0xfc000000

+ 19 - 1
sys/src/9/ppc/dat.h

@@ -1,4 +1,5 @@
 typedef struct Conf	Conf;
+typedef struct Confmem	Confmem;
 typedef struct FPsave	FPsave;
 typedef struct ISAConf	ISAConf;
 typedef struct Imap	Imap;
@@ -12,11 +13,13 @@ typedef struct Page	Page;
 typedef struct Pcidev	Pcidev;
 typedef struct Proc	Proc;
 typedef struct Sys	Sys;
+typedef vlong		Tval;
 typedef struct Ureg	Ureg;
 typedef struct Vctl	Vctl;
 
 #pragma incomplete Ureg
 #pragma incomplete Imap
+#pragma incomplete Mach
 
 #define MAXSYSARG	5	/* for mount(fd, mpt, flag, arg, srv) */
 
@@ -35,6 +38,7 @@ struct Lock
 	ulong	sr;
 	ulong	pc;
 	Proc	*p;
+	Mach	*m;
 	ulong	pid;
 	ushort	isilock;
 };
@@ -73,10 +77,19 @@ struct FPsave
 	};
 };
 
+struct Confmem
+{
+	ulong	base;
+	ulong	npage;
+	ulong	kbase;
+	ulong	klimit;
+};
+
 struct Conf
 {
 	ulong	nmach;		/* processors */
 	ulong	nproc;		/* processes */
+	Confmem	mem[2];
 	ulong	npage0;		/* total physical pages of memory */
 	ulong	npage1;		/* total physical pages of memory */
 	ulong	npage;		/* total physical pages of memory */
@@ -123,6 +136,9 @@ typedef	void		KMap;
 #define	kmap(p)		(KMap*)((p)->pa|KZERO)
 #define	kunmap(k)
 
+struct IMM;
+typedef struct IMM IMM;
+
 struct Mach
 {
 	/* OFFSETS OF THE FOLLOWING KNOWN BY l.s */
@@ -137,7 +153,9 @@ struct Mach
 	/* ordering from here on irrelevant */
 
 	Imap*	imap;
-
+#ifndef ucuconf
+	IMM*	immr;
+#endif
 	ulong	ticks;		/* of the clock since boot time */
 	Label	sched;		/* scheduler wakeup */
 	Lock	alarmlock;	/* access to alarm list */

+ 191 - 0
sys/src/9/ppc/devce.c

@@ -0,0 +1,191 @@
+//
+// channel element driver
+//
+#include "u.h"
+#include "../port/lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+#include "io.h"
+#include "../port/error.h"
+#include "msaturn.h"
+
+enum{
+	Ucuunknown = 0,
+	Ucu64,
+
+	Ndsp = 16,
+
+	Cedead = 0,
+	Cereset,
+	Celoaded,
+	Cerunning,
+	
+	Qdir = 0,
+	Qctl,
+	Qce,
+
+	Cpldbase = Saturn + 0x6000000,
+		Cplducuversion_ucu = 0x1c,
+	Cebase = Saturn + 0x3000000,
+	Cesize = 0x100000,
+};
+
+typedef struct Cpld Cpld;
+struct Cpld{
+	uchar	led;
+	uchar	fpga1;
+	uchar	slotid;
+	uchar	version;
+	uchar	watchdog;
+	uchar	spi;
+	uchar	asicreset;
+	uchar	dspreset;
+	uchar	generalreset;
+	uchar	ucuversion;
+	uchar	fpga2;
+};
+
+typedef struct Circbuf Circbuf;
+struct Circbuf{
+	uchar	*nextin;
+	uchar	*nextout;
+	uchar	*start;
+	uchar	*end;
+};
+
+typedef struct Dsp Dsp;
+struct Dsp{
+	Ref;
+	int		state;
+	Circbuf	*cb;
+};
+
+typedef struct Ce Ce;
+struct Ce{
+	int		ucutype;
+	Ce		*ces[Ndsp];
+};
+
+static Cpld*cpld = (Cpld*)Cpldbase;
+static Ce ce;
+
+static void
+ceinit(void)
+{
+	if(cpld->ucuversion & Cplducuversion_ucu)
+		ce.ucutype = Ucu64;
+	else{
+		print("ceinit: unsuppoerted UCU\n");
+		return;
+	}
+
+}
+
+static Chan*
+ceattach(char*spec)
+{
+	return devattach('C', spec);
+}
+
+#define DEV(q)			((int)(((q).path >> 8) & 0xff))
+#define TYPE(q)			((int)((q).path & 0xff))
+#define QID(d, t)		((((d) & 0xff) << 8) | (t))
+
+static int
+cegen(Chan*c, char*, Dirtab*, int, int i, Dir*dp)
+{
+	Qid qid;
+
+	switch(TYPE(c->qid)){
+	case Qdir:
+		if(i == DEVDOTDOT){
+			mkqid(&qid, QID(0, Qdir), 0, QTDIR);
+			devdir(c, qid, "#C", 0, eve, 0555, dp);
+			return 1;
+		}
+
+		if(i == 0){
+			mkqid(&qid, QID(-1, Qctl), 0, QTFILE);
+			devdir(c, qid, "cectl", 0, eve, 0644, dp);
+			return 1;
+		}
+
+		if (--i >= Ndsp)
+			return -1;
+	
+		mkqid(&qid, QID(Qce, i), 0, QTFILE);
+		snprint(up->genbuf, sizeof(up->genbuf), "ce%d", i);
+		devdir(c, qid, up->genbuf, 0, eve, 0644, dp);
+		return 1;
+
+	default:
+		return -1;
+	}
+}
+
+static Walkqid *
+cewalk(Chan*c, Chan*nc, char**name, int nname)
+{
+	return devwalk(c, nc, name, nname, 0, 0, cegen);
+}
+
+static int
+cestat(Chan*c, uchar*db, int n)
+{
+	return devstat(c, db, n, 0, 0, cegen);
+}
+
+static Chan*
+ceopen(Chan*c, int omode)
+{
+	c->mode = openmode(omode);
+	c->flag |= COPEN;
+	c->offset = 0;
+	return c;
+}
+
+static void
+ceclose(Chan*)
+{}
+
+static long
+ceread(Chan*c, void*a, long n, vlong)
+{
+	switch(TYPE(c->qid)){
+	case Qdir:
+		return devdirread(c, a, n, 0, 0, cegen);
+	
+	default:
+		error("unsupported operation");
+	}
+	return 0;
+}
+
+static long
+cewrite(Chan*, void*, long, vlong)
+{
+	return 0;
+}
+
+Dev cedevtab = {
+	'C',
+	"channel element",
+
+	devreset,
+	ceinit,
+	devshutdown,
+	ceattach,
+	cewalk,
+	cestat,
+	ceopen,
+	devcreate,
+	ceclose,
+	ceread,
+	devbread,
+	cewrite,
+	devbwrite,
+	devremove,
+	devwstat,
+	devpower,
+};

+ 18 - 1
sys/src/9/ppc/devirq.c

@@ -74,6 +74,23 @@ Lock irqlock;
 static void interrupt(Ureg*, void*);
 void dumpvno(void);
 
+#ifdef notdef
+ulong multiplier;
+
+ulong
+µs(void)
+{
+	uvlong x;
+
+	if(multiplier == 0){
+		multiplier = (uvlong)(1000000LL << 16) / m->cyclefreq;
+		print("µs: multiplier %ld, cyclefreq %lld, shifter %d\n", multiplier, m->cyclefreq, 16);
+	}
+	cycles(&x);
+	return (x*multiplier) >> 16;
+}
+#endif
+
 static void
 ticmstimer(Ureg*, Timer *t)
 {
@@ -302,7 +319,7 @@ irqwrite(Chan *c, void *a, long n, vlong)
 			iomem->simr_h, iomem->simr_l,
 			iomem->sipnr_h, iomem->sipnr_l,
 			iomem->siexr, iomem->siprr);
-		dumpvno();
+//		dumpvno();
 	}
 	poperror();
 	free(cb);

+ 2 - 0
sys/src/9/ppc/etherfcc.c

@@ -501,6 +501,8 @@ ifstat(Ether* ether, void* a, long n, ulong offset)
 	return n;
 }
 
+IMM* imm;
+
 /*
  * This follows the MPC8260 user guide: section28.9's initialisation sequence.
  */

+ 233 - 0
sys/src/9/ppc/ethermii.c

@@ -0,0 +1,233 @@
+#include "u.h"
+#include "../port/lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+#include "io.h"
+#include "../port/error.h"
+#include "../port/netif.h"
+
+#include "etherif.h"
+#include "../ppc/ethermii.h"
+
+int
+mii(Mii* mii, int mask)
+{
+	MiiPhy *miiphy;
+	int bit, oui, phyno, r, rmask;
+
+	/*
+	 * Probe through mii for PHYs in mask;
+	 * return the mask of those found in the current probe.
+	 * If the PHY has not already been probed, update
+	 * the Mii information.
+	 */
+	rmask = 0;
+	for(phyno = 0; phyno < NMiiPhy; phyno++){
+		bit = 1<<phyno;
+		if(!(mask & bit))
+			continue;
+		if(mii->mask & bit){
+			rmask |= bit;
+			continue;
+		}
+		if(mii->mir(mii, phyno, Bmsr) == -1)
+			continue;
+		r = mii->mir(mii, phyno, Phyidr1);
+		oui = (r & 0x3FFF)<<6;
+		r = mii->mir(mii, phyno, Phyidr2);
+		oui |= r>>10;
+		if(oui == 0xFFFFF || oui == 0)
+			continue;
+
+		if((miiphy = malloc(sizeof(MiiPhy))) == nil)
+			continue;
+
+		miiphy->mii = mii;
+		miiphy->oui = oui;
+		miiphy->phyno = phyno;
+
+		miiphy->anar = ~0;
+		miiphy->fc = ~0;
+		miiphy->mscr = ~0;
+
+		mii->phy[phyno] = miiphy;
+		if(mii->curphy == nil)
+			mii->curphy = miiphy;
+		mii->mask |= bit;
+		mii->nphy++;
+
+		rmask |= bit;
+	}
+	return rmask;
+}
+
+int
+miimir(Mii* mii, int r)
+{
+	if(mii == nil || mii->ctlr == nil || mii->curphy == nil)
+		return -1;
+	return mii->mir(mii, mii->curphy->phyno, r);
+}
+
+int
+miimiw(Mii* mii, int r, int data)
+{
+	if(mii == nil || mii->ctlr == nil || mii->curphy == nil)
+		return -1;
+	return mii->miw(mii, mii->curphy->phyno, r, data);
+}
+
+int
+miireset(Mii* mii)
+{
+	int bmcr;
+
+	if(mii == nil || mii->ctlr == nil || mii->curphy == nil)
+		return -1;
+	bmcr = mii->mir(mii, mii->curphy->phyno, Bmcr);
+	bmcr |= BmcrR;
+	mii->miw(mii, mii->curphy->phyno, Bmcr, bmcr);
+	microdelay(1);
+
+	return 0;
+}
+
+int
+miiane(Mii* mii, int a, int p, int e)
+{
+	int anar, bmsr, mscr, r, phyno;
+
+	if(mii == nil || mii->ctlr == nil || mii->curphy == nil)
+		return -1;
+	phyno = mii->curphy->phyno;
+
+	bmsr = mii->mir(mii, phyno, Bmsr);
+	if(!(bmsr & BmsrAna))
+		return -1;
+
+	anar = mii->mir(mii, phyno, Anar);
+	anar &= ~(AnaAP|AnaP|AnaT4|AnaTXFD|AnaTXHD|Ana10FD|Ana10HD);
+
+	if(a != ~0)
+		anar |= (AnaT4|AnaTXFD|AnaTXHD|Ana10FD|Ana10HD) & a;
+	else if(mii->curphy->anar != ~0)
+		anar = mii->curphy->anar;
+	else{
+		if(bmsr & Bmsr10THD)
+			anar |= Ana10HD;
+		if(bmsr & Bmsr10TFD)
+			anar |= Ana10FD;
+		if(bmsr & Bmsr100TXHD)
+			anar |= AnaTXHD;
+		if(bmsr & Bmsr100TXFD)
+			anar |= AnaTXFD;
+	}
+	mii->curphy->anar = anar;
+
+	if(p != ~0)
+		anar |= (AnaAP|AnaP) & p;
+	else if(mii->curphy->fc != ~0)
+		anar |= mii->curphy->fc;
+	mii->curphy->fc = (AnaAP|AnaP) & anar;
+
+	mii->miw(mii, phyno, Anar, anar);
+
+	mscr = 0;
+	if(bmsr & BmsrEs){
+		mscr = mii->mir(mii, phyno, Mscr);
+		mscr &= ~(Mscr1000TFD|Mscr1000THD);
+		if(e != ~0)
+			mscr |= (Mscr1000TFD|Mscr1000THD) & e;
+		else if(mii->curphy->mscr != ~0)
+			mscr = mii->curphy->mscr;
+		else{
+			r = mii->mir(mii, phyno, Esr);
+			if(r & Esr1000THD)
+				mscr |= Mscr1000THD;
+			if(r & Esr1000TFD)
+				mscr |= Mscr1000TFD;
+		}
+		mii->miw(mii, phyno, Mscr, mscr);
+	}
+	mii->curphy->mscr = mscr;
+
+	r = mii->mir(mii, phyno, Bmcr);
+	if(!(r & BmcrR)){
+		r |= BmcrAne|BmcrRan;
+		mii->miw(mii, phyno, Bmcr, r);
+	}
+
+	return 0;
+}
+
+int
+miistatus(Mii* mii)
+{
+	MiiPhy *phy;
+	int anlpar, bmsr, p, r, phyno;
+
+	if(mii == nil || mii->ctlr == nil || mii->curphy == nil)
+		return -1;
+	phy = mii->curphy;
+	phyno = phy->phyno;
+
+	/*
+	 * Check Auto-Negotiation is complete and link is up.
+	 * (Read status twice as the Ls bit is sticky).
+	 */
+	phy->bmsr = bmsr = mii->mir(mii, phyno, Bmsr);
+	if(!(bmsr & (BmsrAnc|BmsrAna)))
+		return -1;
+
+	phy->bmsr = bmsr = mii->mir(mii, phyno, Bmsr);
+	if(!(bmsr & BmsrLs)){
+		phy->link = 0;
+		return 0;
+	}
+
+	phy->speed = phy->fd = phy->rfc = phy->tfc = 0;
+	if(phy->mscr & (Mscr1000TFD|Mscr1000THD)){
+		r = mii->mir(mii, phyno, Mssr);
+		if((phy->mscr & Mscr1000TFD) && (r & Mssr1000TFD)){
+			phy->speed = 1000;
+			phy->fd = 1;
+		}
+		else if((phy->mscr & Mscr1000THD) && (r & Mssr1000THD))
+			phy->speed = 1000;
+	}
+
+	anlpar = mii->mir(mii, phyno, Anlpar);
+	if(phy->speed == 0){
+		r = phy->anar & anlpar;
+		if(r & AnaTXFD){
+			phy->speed = 100;
+			phy->fd = 1;
+		}
+		else if(r & AnaTXHD)
+			phy->speed = 100;
+		else if(r & Ana10FD){
+			phy->speed = 10;
+			phy->fd = 1;
+		}
+		else if(r & Ana10HD)
+			phy->speed = 10;
+	}
+	if(phy->speed == 0)
+		return -1;
+
+	if(phy->fd){
+		p = phy->fc;
+		r = anlpar & (AnaAP|AnaP);
+		if(p == AnaAP && r == (AnaAP|AnaP))
+			phy->tfc = 1;
+		else if(p == (AnaAP|AnaP) && r == AnaAP)
+			phy->rfc = 1;
+		else if((p & AnaP) && (r & AnaP))
+			phy->rfc = phy->tfc = 1;
+	}
+
+	phy->link = 1;
+
+	return 0;
+}

+ 117 - 0
sys/src/9/ppc/ethermii.h

@@ -0,0 +1,117 @@
+typedef struct Mii Mii;
+typedef struct MiiPhy MiiPhy;
+
+enum {					/* registers */
+	Bmcr		= 0x00,		/* Basic Mode Control */
+	Bmsr		= 0x01,		/* Basic Mode Status */
+	Phyidr1		= 0x02,		/* PHY Identifier #1 */
+	Phyidr2		= 0x03,		/* PHY Identifier #2 */
+	Anar		= 0x04,		/* Auto-Negotiation Advertisement */
+	Anlpar		= 0x05,		/* AN Link Partner Ability */
+	Aner		= 0x06,		/* AN Expansion */
+	Annptr		= 0x07,		/* AN Next Page TX */
+	Annprr		= 0x08,		/* AN Next Page RX */
+	Mscr		= 0x09,		/* MASTER-SLAVE Control */
+	Mssr		= 0x0A,		/* MASTER-SLAVE Status */
+	Esr		= 0x0F,		/* Extended Status */
+
+	NMiiPhyr	= 32,
+	NMiiPhy		= 32,
+};
+
+enum {					/* Bmcr */
+	BmcrSs1		= 0x0040,	/* Speed Select[1] */
+	BmcrCte		= 0x0080,	/* Collision Test Enable */
+	BmcrDm		= 0x0100,	/* Duplex Mode */
+	BmcrRan		= 0x0200,	/* Restart Auto-Negotiation */
+	BmcrI		= 0x0400,	/* Isolate */
+	BmcrPd		= 0x0800,	/* Power Down */
+	BmcrAne		= 0x1000,	/* Auto-Negotiation Enable */
+	BmcrSs0		= 0x2000,	/* Speed Select[0] */
+	BmcrLe		= 0x4000,	/* Loopback Enable */
+	BmcrR		= 0x8000,	/* Reset */
+};
+
+enum {					/* Bmsr */
+	BmsrEc		= 0x0001,	/* Extended Capability */
+	BmsrJd		= 0x0002,	/* Jabber Detect */
+	BmsrLs		= 0x0004,	/* Link Status */
+	BmsrAna		= 0x0008,	/* Auto-Negotiation Ability */
+	BmsrRf		= 0x0010,	/* Remote Fault */
+	BmsrAnc		= 0x0020,	/* Auto-Negotiation Complete */
+	BmsrPs		= 0x0040,	/* Preamble Suppression Capable */
+	BmsrEs		= 0x0100,	/* Extended Status */
+	Bmsr100T2HD	= 0x0200,	/* 100BASE-T2 HD Capable */
+	Bmsr100T2FD	= 0x0400,	/* 100BASE-T2 FD Capable */
+	Bmsr10THD	= 0x0800,	/* 10BASE-T HD Capable */
+	Bmsr10TFD	= 0x1000,	/* 10BASE-T FD Capable */
+	Bmsr100TXHD	= 0x2000,	/* 100BASE-TX HD Capable */
+	Bmsr100TXFD	= 0x4000,	/* 100BASE-TX FD Capable */
+	Bmsr100T4	= 0x8000,	/* 100BASE-T4 Capable */
+};
+
+enum {					/* Anar/Anlpar */
+	Ana10HD		= 0x0020,	/* Advertise 10BASE-T */
+	Ana10FD		= 0x0040,	/* Advertise 10BASE-T FD */
+	AnaTXHD		= 0x0080,	/* Advertise 100BASE-TX */
+	AnaTXFD		= 0x0100,	/* Advertise 100BASE-TX FD */
+	AnaT4		= 0x0200,	/* Advertise 100BASE-T4 */
+	AnaP		= 0x0400,	/* Pause */
+	AnaAP		= 0x0800,	/* Asymmetrical Pause */
+	AnaRf		= 0x2000,	/* Remote Fault */
+	AnaAck		= 0x4000,	/* Acknowledge */
+	AnaNp		= 0x8000,	/* Next Page Indication */
+};
+
+enum {					/* Mscr */
+	Mscr1000THD	= 0x0100,	/* Advertise 1000BASE-T HD */
+	Mscr1000TFD	= 0x0200,	/* Advertise 1000BASE-T FD */
+};
+
+enum {					/* Mssr */
+	Mssr1000THD	= 0x0400,	/* Link Partner 1000BASE-T HD able */
+	Mssr1000TFD	= 0x0800,	/* Link Partner 1000BASE-T FD able */
+};
+
+enum {					/* Esr */
+	Esr1000THD	= 0x1000,	/* 1000BASE-T HD Capable */
+	Esr1000TFD	= 0x2000,	/* 1000BASE-T FD Capable */
+	Esr1000XHD	= 0x4000,	/* 1000BASE-X HD Capable */
+	Esr1000XFD	= 0x8000,	/* 1000BASE-X FD Capable */
+};
+
+typedef struct Mii {
+	Lock;
+	int	nphy;
+	int	mask;
+	MiiPhy*	phy[NMiiPhy];
+	MiiPhy*	curphy;
+
+	void*	ctlr;
+	int	(*mir)(Mii*, int, int);
+	int	(*miw)(Mii*, int, int, int);
+} Mii;
+
+typedef struct MiiPhy {
+	Mii*	mii;
+	int	oui;
+	int	phyno;
+
+	int	bmsr;
+	int	anar;
+	int	fc;
+	int	mscr;
+
+	int	link;
+	int	speed;
+	int	fd;
+	int	rfc;
+	int	tfc;
+};
+
+extern int mii(Mii*, int);
+extern int miiane(Mii*, int, int, int);
+extern int miimir(Mii*, int);
+extern int miimiw(Mii*, int, int);
+extern int miireset(Mii*);
+extern int miistatus(Mii*);

+ 1 - 0
sys/src/9/ppc/ethersaturn.c

@@ -65,6 +65,7 @@ typedef struct {
 	ulong	overflows;
 } Ctlr;
 
+uchar etheraddr[6] = { 0x90, 0x85, 0x82, 0x32, 0x83, 0x00};
 static ushort*etcr=(ushort*)Etcr;
 static ushort*etsr=(ushort*)Etsr;
 static ushort*ercr=(ushort*)Ercr;

+ 2 - 0
sys/src/9/ppc/fns.h

@@ -1,9 +1,11 @@
 #include "../port/portfns.h"
 
+ulong	cankaddr(ulong);
 int	cistrcmp(char*, char*);
 int	cistrncmp(char*, char*, int);
 void	clockinit(void);
 void	clockintr(Ureg*);
+int	cmpswap(long*, long, long);
 void	cpuidprint(void);
 void	cycles(uvlong*);
 void	dbgputc(int c);

+ 730 - 0
sys/src/9/ppc/imm.h

@@ -0,0 +1,730 @@
+enum{
+	Nuart	= 1,		/* Number of SMC Uarts */
+};
+
+enum{
+	/* Vectors */
+	VecXXX0,
+	VecI2C,
+	VecSPI,
+	VecRisc,
+	VecSMC1,
+	VecSMC2,
+	VecIDMA1,
+	VecIDMA2,
+	VecIDMA3,
+	VecIDMA4,
+	VecSDMA,
+	VecXXX11,
+	VecTimer1,
+	VecTimer2,
+	VecTimer3,
+	VecTimer4,
+	VecTMCNT,
+	VecPIT,
+	VecXXX18,
+	VecIRQ1,
+	VecIRQ2,
+	VecIRQ3,
+	VecIRQ4,
+	VecIRQ5,
+	VecIRQ6,
+	VecIRQ7,
+	VecXXX26,
+	VecXXX27,
+	VecXXX28,
+	VecXXX29,
+	VecXXX30,
+	VecXXX31,
+	VecFCC1,
+	VecFCC2,
+	VecFCC3,
+	VecXXX35,
+	VecMCC1,
+	VecMCC2,
+	VecXXX38,
+	VecXXX39,
+	VecSCC1,
+	VecSCC2,
+	VecSCC3,
+	VecSCC4,
+	VecXXX44,
+	VecXXX45,
+	VecXXX46,
+	VecXXX47,
+	VecPC15,
+	VecPC14,
+	VecPC13,
+	VecPC12,
+	VecPC11,
+	VecPC10,
+	VecPC9,
+	VecPC8,
+	VecPC7,
+	VecPC6,
+	VecPC5,
+	VecPC4,
+	VecPC3,
+	VecPC2,
+	VecPC1,
+	VecPC0,
+};
+
+typedef struct BD BD;
+struct BD {
+	ushort	status;
+	ushort	length;
+	ulong	addr;
+};
+
+enum{
+	BDEmpty=	SBIT(0),
+	BDReady=	SBIT(0),
+	BDWrap=		SBIT(2),
+	BDInt=		SBIT(3),
+	BDLast=		SBIT(4),
+	BDFirst=	SBIT(5),
+};
+
+typedef struct Ring Ring;
+
+struct Ring {
+	BD*	rdr;		/* receive descriptor ring */
+	void*	rrb;		/* receive ring buffers */
+	int	rdrx;		/* index into rdr */
+	int	nrdre;		/* length of rdr */
+
+	BD*	tdr;		/* transmit descriptor ring */
+	void**	txb;		/* corresponding transmit ring buffers */
+	int	tdrh;		/* host index into tdr */
+	int	tdri;		/* interface index into tdr */
+	int	ntdre;		/* length of tdr */
+	int	ntq;		/* pending transmit requests */
+};
+
+int	ioringinit(Ring*, int, int, int);
+
+/*
+ * MCC parameters
+ */
+typedef struct MCCparam MCCparam;
+struct MCCparam {
+/*0x00*/	ulong	mccbase;
+/*0x04*/	ushort	mccstate;
+/*0x06*/	ushort	mrblr;	
+/*0x08*/	ushort	grfthr;	
+/*0x0a*/	ushort	grfcnt;	
+/*0x0c*/	ulong	rinttmp;
+/*0x10*/	ulong	data0;
+/*0x14*/	ulong	data1;
+/*0x18*/	ulong	tintbase;
+/*0x1c*/	ulong	tintptr;
+/*0x20*/	ulong	tinttmp;
+/*0x24*/	ushort	sctpbase;
+/*0x26*/	ushort	Rsvd26;
+/*0x28*/	ulong	cmask32;
+/*0x2c*/	ushort	xtrabase;
+/*0x2e*/	ushort	cmask16;
+/*0x30*/	ulong	rinttmp[4];
+/*0x40*/	struct {
+			ulong	base;
+			ulong	ptr;
+		}		rint[4];
+/*0x60*/	ulong	tstmp;
+/*0x64*/
+};
+/*
+ * IO controller parameters
+ */
+typedef struct IOCparam IOCparam;
+struct IOCparam {
+/*0x00*/	ushort	rbase;
+/*0x02*/	ushort	tbase;
+/*0x04*/	uchar	rfcr;
+/*0x05*/	uchar	tfcr;
+/*0x06*/	ushort	mrblr;
+/*0x08*/	ulong	rstate;
+/*0x0c*/	ulong	rxidp;
+/*0x10*/	ushort	rbptr;
+/*0x12*/	ushort	rxibc;
+/*0x14*/	ulong	rxtemp;
+/*0x18*/	ulong	tstate;
+/*0x1c*/	ulong	txidp;
+/*0x20*/	ushort	tbptr;
+/*0x22*/	ushort	txibc;
+/*0x24*/	ulong	txtemp;
+/*0x28*/
+};
+
+typedef struct SCCparam SCCparam;
+struct SCCparam {
+	IOCparam;
+	ulong	rcrc;
+	ulong	tcrc;
+};
+
+typedef struct FCCparam FCCparam;
+struct FCCparam {
+/*0x00*/	ushort	riptr;
+/*0x02*/	ushort	tiptr;
+/*0x04*/	ushort	Rsvd04;
+/*0x06*/	ushort	mrblr;
+/*0x08*/	ulong	rstate;
+/*0x0c*/	ulong	rbase;
+/*0x10*/	ushort	rbdstat;
+/*0x12*/	ushort	rbdlen;
+/*0x14*/	char*	rdptr;
+/*0x18*/	ulong	tstate;
+/*0x1c*/	ulong	tbase;
+/*0x20*/	ushort	tbdstat;
+/*0x22*/	ushort	tbdlen;
+/*0x24*/	ulong	tdptr;
+/*0x28*/	ulong	rbptr;
+/*0x2c*/	ulong	tbptr;
+/*0x30*/	ulong	rcrc;
+/*0x34*/	ulong	Rsvd34;
+/*0x38*/	ulong	tcrc;
+/*0x3c*/
+};
+
+typedef struct SCC SCC;
+struct SCC {
+	ulong	gsmrl;
+	ulong	gsmrh;
+	ushort	psmr;
+	uchar	rsvscc0[2];
+	ushort	todr;
+	ushort	dsr;
+	ushort	scce;
+	uchar	rsvscc1[2];
+	ushort	sccm;
+	uchar	rsvscc2;
+	uchar	sccs;
+	ushort	irmode;
+	ushort	irsip;
+	uchar	rsvscc3[4];	/* BUG */
+};
+
+typedef struct FCC FCC;
+struct FCC {
+/*0x00*/	ulong	gfmr;		/*  general mode register 28.2/28-3 */
+/*0x04*/	ulong	fpsmr;		/*  protocol-specific mode reg. 29.13.2(ATM) 30.18.1(Ether) */
+/*0x08*/	ushort	ftodr;		/*  transmit on demand register 28.5/28-7 */
+/*0x0A*/	ushort	Rsvd0A;
+/*0x0C*/	ushort	fdsr;		/*  data synchronization register 28.4/28-7 */
+/*0x0E*/	ushort	Rsvd0E;
+/*0x10*/	ushort	fcce;		/* event register 29.13.3 (ATM), 30.18.2 (Ethernet) */
+/*0x12*/	ushort	Rsvd12;
+/*0x14*/	ushort	fccm;		/* mask register */
+/*0x16*/	ushort	Rsvd16;
+/*0x18*/	uchar	fccs;		/* status register 8 bits 31.10 (HDLC) */
+/*0x19*/	uchar	Rsvd19[3];
+/*0x1C*/	uchar	ftirrphy[4];	/* transmit internal rate registers for PHY0DH3 29.13.4/29-88 (ATM) */
+/*0x20*/
+};
+
+typedef struct SMC SMC;
+struct SMC {
+/*0x0*/	ushort	pad1;
+/*0x2*/	ushort	smcmr;
+/*0x4*/	ushort	pad2;
+/*0x6*/	uchar	smce;
+/*0x7*/	uchar	pad3[3];
+/*0xa*/	uchar	smcm;
+/*0xb*/	uchar	pad4[5];
+/*0x10*/
+};
+
+typedef struct SPI SPI;
+struct SPI {
+	ushort	spmode;
+	uchar	res1[4];
+	uchar	spie;
+	uchar	res2[3];
+	uchar	spim;
+	uchar	res3[2];
+	uchar	spcom;
+	uchar	res4[2];
+};
+
+typedef struct Bankmap Bankmap;
+struct Bankmap {
+/*0*/	ulong	br;		/*  Base register bank 32 bits 10.3.1/10-14 */
+/*4*/	ulong	or;		/*  Option register bank 32 bits 10.3.2/10-16 */
+/*8*/
+};
+
+typedef struct Port Port;
+struct Port {
+/*0x00*/	ulong	pdir;		/*  Port A data direction register 32 bits 35.2.3/35-3 */
+/*0x04*/	ulong	ppar;	/*  Port Apin assignment register 32 bits 35.2.4/35-4 */
+/*0x08*/	ulong	psor;		/*  Port A special options register 32 bits 35.2.5/35-4 */
+/*0x0C*/	ulong	podr;	/*  Port Aopen drain register 32 bits 35.2.1/35-2 */
+/*0x10*/	ulong	pdat;		/*  Port A data register 32 bits 35.2.2/35-2 */
+/*0x14*/	uchar	Rsvd14[12];
+/*0x20*/
+};
+
+typedef struct IDMA IDMA;
+struct IDMA {
+/*0x0*/	uchar	idsr;		/*  IDMA event register 8 bits 18.8.4/18-22 */
+/*0x1*/	uchar	Rsvd1[3];
+/*0x4*/	uchar	idmr;	/*  IDMA mask register 8 bits 18.8.4/18-22 */
+/*0x5*/	uchar	Rsvd5[3];
+/*0x8*/
+};
+
+typedef struct PrmSCC PrmSCC;
+struct PrmSCC {
+	uchar	sccbytes[0x100];
+};
+
+typedef struct PrmFCC PrmFCC;
+struct PrmFCC {
+	uchar	fccbytes[0x100];
+};
+
+typedef struct Bases Bases;
+struct Bases {
+/*0x00*/	uchar	mcc[0x80];
+/*0x80*/	uchar	Rsvd80[0x60];
+/*0xe0*/	uchar	risctimers[0x10];
+/*0xf0*/	ushort	revnum;
+/*0xf2*/	uchar	Rsvdf2[6];
+/*0xf8*/	ulong	rand;
+/*0xfc*/	ushort	smcbase;
+#define	i2cbase	smcbase
+/*0xfe*/	ushort	idmabase;
+/*0x100*/
+};
+
+typedef struct Uartsmc Uartsmc;
+struct Uartsmc {
+/*0x00*/	IOCparam;
+/*0x28*/	ushort	maxidl;
+/*0x2a*/	ushort	idlc;
+/*0x2c*/	ushort	brkln;
+/*0x2e*/	ushort	brkec;
+/*0x30*/	ushort	brkcr;
+/*0x32*/	ushort	r_mask;
+/*0x34*/	ulong	sdminternal;
+/*0x38*/	uchar	Rsvd38[8];
+/*0x40*/
+};
+
+typedef struct SI SI;
+struct SI {
+/*0x11B20*/	ushort	siamr;		/*  SI TDMA1 mode register 16 bits 14.5.2/14-17 */
+/*0x11B22*/	ushort	sibmr;		/*  SI TDMB1 mode register 16 bits */
+/*0x11B24*/	ushort	sicmr;		/*  SI TDMC1 mode register 16 bits */
+/*0x11B26*/	ushort	sidmr;		/*  SI TDMD1 mode register 16 bits */
+/*0x11B28*/	uchar	sigmr;		/*  SI global mode register 8 bits 14.5.1/14-17 */
+/*0x11B29*/	uchar	Rsvd11B29;
+/*0x11B2A*/	ushort	sicmdr;		/*  SI command register 8 bits 14.5.4/14-24 */
+/*0x11B2C*/	ushort	sistr;			/*  SI status register 8 bits 14.5.5/14-25 */
+/*0x11B2E*/	ushort	sirsr;			/*  SI RAM shadow address register 16 bits 14.5.3/14-23 */
+};
+
+typedef struct RegMap RegMap;
+struct RegMap {
+/* General SIU */
+/*0x10000*/	ulong	siumcr;		/*  SIU module configuration register 32 bits 4.3.2.6/4-31 */
+/*0x10004*/	ulong	sypcr;		/*  System protection control register 32 bits 4.3.2.8/4-35 */
+/*0x10008*/	uchar	Rsvd10008[0xe-0x8];
+/*0x1000E*/	ushort	swsr;		/*  Softwareservice register 16 bits 4.3.2.9/4-36 */
+/*0x10010*/	uchar	Rsvd10010[0x14];
+/*0x10024*/	ulong	bcr;			/*  Bus configuration register 32 bits 4.3.2.1/4-25 */
+/*0x10028*/	ulong	PPC_ACR;		/*  60x bus arbiter configuration register 8 bits 4.3.2.2/4-28 */
+/*0x1002C*/	ulong	PPCALRH;		/*  60x bus arbitration-level register high (first 8 clients) 32 bits 4.3.2.3/4-28 */
+/*0x10030*/	ulong	PPC_ALRL;	/*  60x bus arbitration-level register low (next 8 clients) 32 bits 4.3.2.3/4-28 */
+/*0x10034*/	ulong	LCL_ACR;		/*  Local arbiter configuration register 8 bits 4.3.2.4/4-29 */
+/*0x10038*/	ulong	LCL_ALRH;	/*  Local arbitration-level register (first 8 clients) 32 bits 4.3.2.5/4-30 */
+
+/*0x1003C*/	ulong	LCL_ALRL;	/*  Local arbitration-level register (next 8 clients) 32 bits 4.3.2.3/4-28 */
+/*0x10040*/	ulong	TESCR1;		/*  60x bus transfer error status control register1 32 bits 4.3.2.10/4-36 */
+/*0x10044*/	ulong	TESCR2;		/*  60x bus transfer error status control register2 32 bits 4.3.2.11/4-37 */
+/*0x10048*/	ulong	L_TESCR1;	/*  Local bus transfer error status control register1 32 bits 4.3.2.12/4-38 */
+/*0x1004C*/	ulong	L_TESCR2;	/*  Local bus transfer error status control register2 32 bits 4.3.2.13/4-39 */
+/*0x10050*/	ulong	pdtea;		/*  60x bus DMAtransfer error address 32 bits 18.2.3/18-4 */
+/*0x10054*/	uchar	pdtem;		/*  60x bus DMAtransfer error MSNUM 8 bits 18.2.4/18-4 */
+/*0x10055*/	uchar	Rsvd10055[3];
+/*0x10058*/	void*	ldtea;		/*  Local bus DMA transfer error address 32 bits 18.2.3/18-4 */
+/*0x1005C*/	uchar	ldtem;		/*  Local bus DMA transfer error MSNUM 8 bits 18.2.4/18-4 */
+/*0x1005D*/	uchar	Rsvd1005D[163];
+
+/* Memory Controller */
+/*0x10100*/	Bankmap	bank[12];
+
+/*0x10160*/	uchar	Rsvd10160[8];
+/*0x10168*/	void*	MAR;		/*  Memory address register 32 bits 10.3.7/10-29 */
+/*0x1016C*/	ulong	Rsvd1016C;
+/*0x10170*/	ulong	MAMR;		/*  Machine A mode register 32 bits 10.3.5/10-26 */
+/*0x10174*/	ulong	MBMR;		/*  Machine B mode register 32 bits */
+/*0x10178*/	ulong	MCMR;		/*  Machine C mode register 32 bits */
+/*0x1017C*/	uchar	Rsvd1017C[6];
+/*0x10184*/	ulong	mptpr;		/*  Memory periodic timer prescaler 16 bits 10.3.12/10-32 */
+/*0x10188*/	ulong	mdr;			/*  Memorydata register 32 bits 10.3.6/10-28 */
+/*0x1018C*/	ulong	Rsvd1018C;
+/*0x10190*/	ulong	psdmr;		/*  60x bus SDRAM mode register 32 bits 10.3.3/10-21 */
+/*0x10194*/	ulong	lsdmr;		/*  Local bus SDRAM mode register 32 bits 10.3.4/10-24 */
+/*0x10198*/	ulong	PURT;		/*  60x bus-assigned UPM refresh timer 8 bits 10.3.8/10-30 */
+/*0x1019C*/	ulong	PSRT;		/*  60x bus-assigned SDRAM refresh timer 8 bits 10.3.10/10-31 */
+
+/*0x101A0*/	ulong	LURT;		/*  Local bus-assigned UPM refresh timer8 bits 10.3.9/10-30 */
+/*0x101A4*/	ulong	LSRT;		/*  Local bus-assigned SDRAM refresh timer 8 bits 10.3.11/10-32 */
+
+/*0x101A8*/	ulong	immr;		/*  Internal memory map register 32 bits 4.3.2.7/4-34 */
+/*0x101AC*/	uchar	Rsvd101AC[84];
+/* System Integration Timers */
+/*0x10200*/	uchar	Rsvd10200[32];
+/*0x10220*/	ulong	TMCNTSC;	/*  Time counter statusand control register 16 bits 4.3.2.14/4-40 */
+
+/*0x10224*/	ulong	TMCNT;		/*  Time counter register 32 bits 4.3.2.15/4-41 */
+/*0x10228*/	ulong	Rsvd10228;
+/*0x1022C*/	ulong	TMCNTAL;	/*  Time counter alarm register 32 bits 4.3.2.16/4-41 */
+/*0x10230*/	uchar	Rsvd10230[0x10];
+/*0x10240*/	ulong	PISCR;		/*  Periodic interrupt statusand control register 16 bits 4.3.3.1/4-42 */
+
+/*0x10244*/	ulong	PITC;		/*  Periodic interrupt count register 32 bits 4.3.3.2/4-43 */
+/*0x10248*/	ulong	PITR;			/*  Periodic interrupt timer register 32 bits 4.3.3.3/4-44 */
+/*0x1024C*/	uchar	Rsvd1024C[94];
+/*0x102AA*/	uchar	Rsvd102AA[2390];
+
+/* Interrupt Controller */
+/*0x10C00*/	ushort	sicr;		/*  SIU interrupt configuration register 16 bits 4.3.1.1/4-17 */
+/*0x10C02*/	ushort	Rsvd10C02;
+/*0x10C04*/	ulong	sivec;		/*  SIU interrupt vector register 32 bits 4.3.1.6/4-23 */
+/*0x10C08*/	ulong	sipnr_h;	/*  SIU interrupt pending register(high) 32 bits 4.3.1.4/4-21 */
+/*0x10C0C*/	ulong	sipnr_l;	/*  SIU interrupt pending register(low) 32 bits 4.3.1.4/4-21 */
+/*0x10C10*/	ulong	siprr;		/*  SIU interrupt priority register 32 bits 4.3.1.2/4-18 */
+/*0x10C14*/	ulong	scprr_h;	/*  CPM interrupt priority register(high) 32 bits 4.3.1.3/4-19 */
+/*0x10C18*/	ulong	scprr_l;	/*  CPM interrupt priority register(low) 32 bits 4.3.1.3/4-19 */
+/*0x10C1C*/	ulong	simr_h;		/*  SIU interrupt mask register(high) 32 bits 4.3.1.5/4-22 */
+/*0x10C20*/	ulong	simr_l;		/*  SIU interrupt mask register(low) 32 bits 4.3.1.5/4-22 */
+/*0x10C24*/	ulong	siexr;		/*  SIUexternal interrupt control register 32 bits 4.3.1.7/4-24 */
+/*0x10C28*/	uchar	Rsvd10C28[88];
+
+/* Clocks and Reset */
+/*0x10C80*/	ulong	sccr;		/*  System clock control register 32 bits 9.8/9-8 */
+/*0x10C84*/	uchar	Rsvd10C84[4];
+/*0x10C88*/	ulong	scmr;		/*  System clock mode register 32 bits 9.9/9-9 */
+/*0x10C8C*/	uchar	Rsvd10C8C[4];
+/*0x10C90*/	ulong	rsr;		/*  Reset status register 32 bits 5.2/5-4 */
+/*0x10C94*/	ulong	rmr;		/*  Reset mode register 32 bits 5.3/5-5 */
+/*0x10C98*/	uchar	Rsvd10C98[104];
+
+/* Part I.Overview Input/Output Port */
+/*0x10D00*/	Port		port[4];
+
+/* CPMTimers */
+/*0x10D80*/	uchar	tgcr1;		/*  Timer1 and timer2 global configuration register 8 bits 17.2.2/17-4 */
+
+/*0x10D81*/	uchar	Rsvd10D81[3];
+/*0x10D84*/	uchar	tgcr2;		/*  Timer3 and timer4 global configuration register 8 bits 17.2.2/17-4 */
+/*0x10D85*/	uchar	Rsvd10D85[3];
+
+/*0x10D88*/	uchar	Rsvd10D88[8];
+/*0x10D90*/	ushort	tmr1;		/*  Timer1 mode register 16 bits 17.2.3/17-6 */
+/*0x10D92*/	ushort	tmr2;		/*  Timer2 mode register 16 bits 17.2.3/17-6 */
+		union{
+			struct {
+/*0x10D94*/	ushort	trr1;			/*  Timer1 reference register 16 bits 17.2.4/17-7 */
+/*0x10D96*/	ushort	trr2;			/*  Timer2 reference register 16 bits 17.2.4/17-7 */
+			};
+/*0x10D94*/	ulong	trrl1;			/*  Combined Timer 1/2 trr register */
+		};
+		union{
+			struct {
+/*0x10D98*/	ushort	tcr1;			/*  Timer1 capture register 16 bits 17.2.5/17-8 */
+/*0x10D9A*/	ushort	tcr2;			/*  Timer2 capture register 16 bits 17.2.5/17-8 */
+			};
+/*0x10D98*/	ulong	tcrl1;		/*  Combined timer1/2 capture register */
+		};
+		union{
+			struct {
+/*0x10D9C*/	ushort	tcn1;			/*  Timer1 counter 16 bits 17.2.6/17-8 */
+/*0x10D9E*/	ushort	tcn2;			/*  Timer2 counter 16 bits 17.2.6/17-8 */
+			};
+/*0x10D9C*/	ulong	tcnl1;		/*  Combined timer1/2 counter */
+		};
+/*0x10DA0*/	ushort	tmr3;		/*  Timer3 mode register 16 bits 17.2.3/17-6 */
+/*0x10DA2*/	ushort	tmr4;		/*  Timer4 mode register 16 bits 17.2.3/17-6 */
+		union{
+			struct {
+/*0x10DA4*/	ushort	trr3;			/*  Timer3 reference register 16 bits 17.2.4/17-7 */
+/*0x10DA6*/	ushort	trr4;			/*  Timer4 reference register 16 bits 17.2.4/17-7 */
+			};
+/*0x10DA4*/	ulong	trrl3;
+		};			
+		union{
+			struct {
+/*0x10DA8*/	ushort	tcr3;			/*  Timer3 capture register 16 bits 17.2.5/17-8 */
+/*0x10DAA*/	ushort	tcr4;			/*  Timer4 capture register 16 bits 17.2.5/17-8 */
+			};
+/*0x10DA8*/	ulong	tcrl3;
+		};
+		union{
+			struct {
+/*0x10DAC*/	ushort	tcn3;			/*  Timer3 counter 16 bits 17.2.6/17-8 */
+/*0x10DAE*/	ushort	tcn4;			/*  Timer4 counter 16 bits 17.2.6/17-8 */
+			};
+/*0x10DAC*/	ulong	tcnl3;
+		};
+/*0x10DB0*/	ushort	ter1;			/*  Timer1 event register 16 bits 17.2.7/17-8 */
+/*0x10DB2*/	ushort	ter2;			/*  Timer2 event register 16 bits 17.2.7/17-8 */
+/*0x10DB4*/	ushort	ter3;			/*  Timer3 event register 16 bits 17.2.7/17-8 */
+/*0x10DB6*/	ushort	ter4;			/*  Timer4 event register 16 bits 17.2.7/17-8 */
+/*0x10DB8*/	uchar	Rsvd10DB8[608];
+
+/* SDMADHGeneral */
+/*0x11018*/	uchar	sdsr;			/*  SDMA status register 8 bits 18.2.1/18-3 */
+/*0x11019*/	uchar	Rsvd11019[3];
+/*0x1101C*/	uchar	sdmr;		/*  SDMA mask register 8 bits 18.2.2/18-4 */
+/*0x1101D*/	uchar	Rsvd1101D[3];
+
+/* IDMA */
+/*0x11020*/	IDMA	idma[4];
+
+/*0x11040*/	uchar	Rsvd11040[704];
+
+/*0x11300*/	FCC	fcc[3];
+
+/*0x11360*/	uchar	Rsvd11360[0x290];
+
+/* BRGs5DH8 */
+/*0x115F0*/	ulong	BRGC5;		/*  BRG5 configuration register 32 bits 16.1/16-2 */
+/*0x115F4*/	ulong	BRGC6;		/*  BRG6configuration register 32 bits */
+/*0x115F8*/	ulong	BRGC7;		/*  BRG7configuration register 32 bits */
+/*0x115FC*/	ulong	BRGC8;		/*  BRG8configuration register 32 bits */
+/*0x11600*/	uchar	Rsvd11600[0x260];
+/*0x11860*/	uchar	I2MOD;		/*  I2C mode register 8 bits 34.4.1/34-6 */
+/*0x11861*/	uchar	Rsvd11861[3];
+/*0x11864*/	uchar	I2ADD;		/*  I2C address register 8 bits 34.4.2/34-7 */
+/*0x11865*/	uchar	Rsvd11865[3];
+/*0x11868*/	uchar	I2BRG;		/*  I2C BRG register 8 bits 34.4.3/34-7 */
+/*0x11869*/	uchar	Rsvd11869[3];
+/*0x1186C*/	uchar	I2COM;		/*  I2C command register 8 bits 34.4.5/34-8 */
+/*0x1186D*/	uchar	Rsvd1186D[3];
+/*0x11870*/	uchar	I2CER;		/*  I2C event register 8 bits 34.4.4/34-8 */
+/*0x11871*/	uchar	Rsvd11871[3];
+/*0x11874*/	uchar	I2CMR;		/*  I2C mask register 8 bits 34.4.4/34-8 */
+/*0x11875*/	uchar	Rsvd11875[331];
+
+/* Communications Processor */
+/*0x119C0*/	ulong	cpcr;		/*  Communications processor command register 32 bits 13.4.1/13-11 */
+
+/*0x119C4*/	ulong	rccr;		/*  CP configuration register 32 bits 13.3.6/13-7 */
+/*0x119C8*/	uchar	Rsvd119C8[14];
+/*0x119D6*/	ushort	rter;		/*  CP timers event register 16 bits 13.6.4/13-21 */
+/*0x119D8*/	ushort	Rsvd119D8;
+/*0x119DA*/	ushort	rtmr;		/*  CP timers mask register 16 bits */
+/*0x119DC*/	ushort	rtscr;		/*  CPtime-stamp timer control register 16 bits 13.3.7/13-9 */
+/*0x119DE*/	ushort	Rsvd119DE;
+/*0x119E0*/	ulong	rtsr;		/*  CPtime-stamp register 32 bits 13.3.8/13-10 */
+/*0x119E4*/	uchar	Rsvd119E4[12];
+
+/*0x119F0*/	ulong	brgc[4];		/*  BRG configuration registers 32 bits 16.1/16-2 */
+
+/*0x11A00*/	SCC		scc[4];
+
+/*0x11A80*/	SMC		smc[2];
+
+		SPI;
+
+/*0x11AB0*/	uchar	Rsvd11AB0[80];
+
+/* CPMMux */
+/*0x11B00*/	uchar	cmxsi1cr;	/*  CPM mux SI1clock route register 8 bits 15.4.2/15-10 */
+/*0x11B01*/	uchar	Rsvd11B01;
+/*0x11B02*/	uchar	cmxsi2cr;	/*  CPM mux SI2clock route register 8 bits 15.4.3/15-11 */
+/*0x11B03*/	uchar	Rsvd11B03;
+/*0x11B04*/	ulong	cmxfcr;	/*  CPM mux FCC clock route register 32 bits 15.4.4/15-12 */
+/*0x11B08*/	ulong	cmxscr;	/*  CPM mux SCC clock route register 32 bits 15.4.5/15-14 */
+/*0x11B0C*/	uchar	cmxsmr;	/*  CPM mux SMC clock route register 8 bits 15.4.6/15-17 */
+/*0x11B0D*/	uchar	Rsvd11B0D;
+/*0x11B0E*/	ushort	cmxuar;	/*  CPM mux UTOPIA address register 16 bits 15.4.1/15-7 */
+/*0x11B10*/	uchar	Rsvd11B10[16];
+
+			SI		si1;			/* SI 1 Registers */
+
+/* MCC1Registers */
+/*0x11B30*/	ushort	MCCE1;		/*  MCC1 event register 16 bits 27.10.1/27-18 */
+/*0x11B32*/	ushort	Rsvd11B32;
+/*0x11B34*/	ushort	MCCM1;		/*  MCC1 mask register 16 bits */
+/*0x11B36*/	ushort	Rsvd11B36;
+/*0x11B38*/	uchar	MCCF1;		/*  MCC1 configuration register 8 bits 27.8/27-15 */
+/*0x11B39*/	uchar	Rsvd11B39[7];
+
+			SI		si2;			/* SI 2 Registers */
+
+/* MCC2Registers */
+/*0x11B50*/	ushort	MCCE2;		/*  MCC2 event register 16 bits 27.10.1/27-18 */
+/*0x11B52*/	ushort	Rsvd11B52;
+/*0x11B54*/	ushort	MCCM2;		/*  MCC2 mask register 16 bits */
+/*0x11B56*/	ushort	Rsvd11B56;
+/*0x11B58*/	uchar	MCCF2;		/*  MCC2 configuration register 8 bits 27.8/27-15 */
+/*0x11B59*/	uchar	Rsvd11B59[1191];
+
+/* SI1RAM */
+/*0x12000*/	uchar	SI1TxRAM[0x200];/*  SI1 transmit routing RAM	512 14.4.3/14-10 */
+/*0x12200*/	uchar	Rsvd12200[0x200];
+/*0x12400*/	uchar	SI1RxRAM[0x200];/*  SI1 receive routing RAM	512 14.4.3/14-10 */
+/*0x12600*/	uchar	Rsvd12600[0x200];
+
+/* SI2RAM */
+/*0x12800*/	uchar	SI2TxRAM[0x200];/*  SI2 transmit routing RAM	512 14.4.3/14-10 */
+/*0x12A00*/	uchar	Rsvd12A00[0x200];
+/*0x12C00*/	uchar	SI2RxRAM[0x200];/*  SI2 receive routing RAM	512 14.4.3/14-10 */
+/*0x12E00*/	uchar	Rsvd12E00[0x200];
+/*0x13000*/	uchar	Rsvd13000[0x800];
+/*0x13800*/	uchar	Rsvd13800[0x800];
+};
+
+typedef struct FCCextra FCCextra;
+struct FCCextra {
+/*0x00*/	uchar	ri[0x20];
+/*0x20*/	uchar	ti[0x20];
+/*0x40*/	uchar	pad[0x20];
+};
+
+typedef struct IMM IMM;
+struct IMM {
+/* CPMDual-Port RAM */
+/*0x00000*/	uchar	dpram1[0x3800];	/*  Dual-port RAM	16Kbytes 13.5/13-15 */
+/*0x03800*/	FCCextra	fccextra[4];
+/*0x03980*/	Uartsmc	uartsmc[2];
+/*0x03a00*/	uchar	dsp1p[0x40];
+/*0x03a40*/	uchar	dsp2p[0x40];
+/*0x03a80*/	BD	bd[(0x04000-0x03a80)/sizeof(BD)];	/* Buffer descriptors */
+/*0x04000*/	uchar	Rsvd4000[0x04000];
+
+/* Dual port RAM bank 2 -- Parameter Ram, Section 13.5 */
+/*0x08000*/	PrmSCC	prmscc[4];
+/*0x08400*/	PrmFCC	prmfcc[3];
+/*0x08700*/	Bases	param[4];
+/*0x08b00*/	uchar	dpram2[0x500];
+
+/*0x09000*/	uchar	Rsvd9000[0x2000];
+
+/* Dual port RAM bank 3 -- Section 13.5 */
+/*0x0B000*/	uchar	dpram3[0x1000];	/*  Dual-port RAM	4Kbytes 13.5/13-15 */
+/*0x0C000*/	uchar	Rsvdc000[0x4000];
+
+/*0x10000*/	RegMap;
+};
+
+enum {
+/* CPM Command register. */
+	cpm_rst		= 0x80000000,
+	cpm_page	= 0x7c000000,
+	cpm_sblock	= 0x03e00000,
+	cpm_flg		= 0x00010000,
+	cpm_mcn		= 0x00003fc0,
+	cpm_opcode	= 0x0000000f,
+
+/* Device sub-block and page codes. */
+	cpm_fcc1_sblock	= 0x10,
+	cpm_fcc2_sblock	= 0x11,
+	cpm_fcc3_sblock	= 0x12,
+	cpm_scc1_sblock	= 0x04,
+	cpm_scc2_sblock	= 0x05,
+	cpm_scc3_sblock	= 0x06,
+	cpm_scc4_sblock	= 0x07,
+	cpm_smc1_sblock	= 0x08,
+	cpm_smc2_sblock	= 0x09,
+	cpm_rand_sblock	= 0x0e,
+	cpm_spi_sblock	= 0x0a,
+	cpm_i2c_sblock	= 0x0b,
+	cpm_timer_sblock	= 0x0f,
+	cpm_mcc1_sblock	= 0x1c,
+	cpm_mcc2_sblock	= 0x1d,
+	cpm_idma1_sblock	= 0x14,
+	cpm_idma2_sblock	= 0x15,
+	cpm_idma3_sblock	= 0x16,
+	cpm_idma4_sblock	= 0x17,
+
+	cpm_scc1_page	= 0x00,
+	cpm_scc2_page	= 0x01,
+	cpm_scc3_page	= 0x02,
+	cpm_scc4_page	= 0x03,
+	cpm_smc1_page	= 0x07,
+	cpm_smc2_page	= 0x08,
+	cpm_spi_page		= 0x09,
+	cpm_i2c_page		= 0x0a,
+	cpm_timer_page	= 0x0a,
+	cpm_rand_page	= 0x0a,
+	cpm_fcc1_page	= 0x04,
+	cpm_fcc2_page	= 0x05,
+	cpm_fcc3_page	= 0x06,
+	cpm_idma1_page	= 0x07,
+	cpm_idma2_page	= 0x08,
+	cpm_idma3_page	= 0x09,
+	cpm_idma4_page	= 0x0a,
+	cpm_mcc1_page	= 0x07,
+	cpm_mcc2_page	= 0x08,
+
+};
+
+/*
+ * CPM
+ */
+enum {
+	/* commands */
+	InitRxTx =	0,
+	InitRx =		1,
+	InitTx =		2,
+	EnterHunt=	3,
+	StopTx=		4,
+	GracefulStopTx = 5,
+	InitIDMA =	5,
+	RestartTx =	6,
+	CloseRxBD =	7,
+	SetGroupAddr = 8,
+	SetTimer =	8,
+	GCITimeout =	9,
+	GCIAbort =	10,
+	StopIDMA =	11,
+	StartDSP = 	12,
+	ArmIDMA =	13,
+	InitDSP =		13,
+	USBCmd =	15,
+
+	/* channel IDs */
+	SCC1ID=	cpm_scc1_page << 5 | cpm_scc1_sblock,
+	SCC2ID=	cpm_scc2_page << 5 | cpm_scc2_sblock,
+	SCC3ID=	cpm_scc3_page << 5 | cpm_scc3_sblock,
+	SMC1ID=	cpm_smc1_page << 5 | cpm_smc1_sblock,
+	SMC2ID=	cpm_smc2_page << 5 | cpm_smc2_sblock,
+	FCC1ID=	cpm_fcc1_page << 5 | cpm_fcc1_sblock,
+	FCC2ID=	cpm_fcc2_page << 5 | cpm_fcc2_sblock,
+	FCC3ID=	cpm_fcc3_page << 5 | cpm_fcc3_sblock,
+//	USBID=	0,		These are wrong
+//	I2CID=	1,
+//	IDMA1ID= 1,
+//	SPIID=	5,
+//	IDMA2ID= 5,
+//	TIMERID=	5,
+//	DSP1ID=9,
+//	SCC4ID=	10,
+//	DSP2ID=	13,
+
+	/* sicr */
+	BRG1 = 0,
+	BRG2 = 1,
+	BRG3 = 2,
+	BRG4 = 4,
+	CLK1 = 4,
+	CLK2 = 5,
+	CLK3 = 6,
+	CLK4 = 7,
+
+};
+
+extern IMM* imm;
+extern int uartsmcoffset[];
+
+BD*	bdalloc(int);
+void	cpmop(int, int, int);
+void	ioplock(void);
+void	iopunlock(void);
+void	kreboot(ulong);

+ 16 - 0
sys/src/9/ppc/l.s

@@ -381,6 +381,22 @@ xdecloop:
 	BNE	xdecloop
 	RETURN
 
+TEXT cmpswap(SB),$0	/* int cmpswap(long*, long, long) */
+	MOVW	R3, R4	/* addr */
+	MOVW	old+4(FP), R5
+	MOVW	new+8(FP), R6
+	DCBF	(R4)		/* fix for 603x bug? */
+	LWAR	(R4), R3
+	CMP	R3, R5
+	BNE fail
+	STWCCC	R6, (R4)
+	BNE fail
+	MOVW $1, R3
+	RETURN
+fail:
+	MOVW $0, R3
+	RETURN
+
 TEXT tlbflushall(SB), $0
 	MOVW	$TLBENTRIES, R3
 	MOVW	R3, CTR

+ 19 - 2
sys/src/9/ppc/m8260.c

@@ -116,6 +116,7 @@ struct {
  */
 
 IMM* iomem = (IMM*)IOMEM;
+uchar etheraddr[6] = { 0x90, 0x85, 0x82, 0x32, 0x83, 0x00};
 
 static Lock cpmlock;
 
@@ -233,7 +234,8 @@ machinit(void)
 	 * if PLAN9INI == ~0, it's not stored in flash or there is no flash
 	 * if *cp == 0xff, flash memory is not initialized
 	 */
-	if (PLAN9INI == ~0 || *(plan9inistr = (char*)(FLASHMEM+PLAN9INI)) == 0xff){
+	if (PLAN9INI == ~0 ||
+	    (uchar)*(plan9inistr = (char*)(FLASHMEM+PLAN9INI)) == 0xff){
 		/* No plan9.ini in flash */
 		plan9inistr =
 			"console=0\n"
@@ -397,8 +399,23 @@ fastticks(uvlong *hz)
 	return ticks.val;
 }
 
+ulong multiplier;
+
+ulong
+µs(void)
+{
+	uvlong x;
+
+	if(multiplier == 0){
+		multiplier = (uvlong)(1000000LL << 16) / m->cyclefreq;
+		print("µs: multiplier %ld, cyclefreq %lld, shifter %d\n", multiplier, m->cyclefreq, 16);
+	}
+	cycles(&x);
+	return (x*multiplier) >> 16;
+}
+
 void
-timerset(uvlong next)
+timerset(Tval next)
 {
 	long offset;
 	uvlong now;

+ 3 - 0
sys/src/9/ppc/mem.h

@@ -8,6 +8,9 @@
 #include "blast.h"
 #endif
 
+#define MIN(a, b)	((a) < (b)? (a): (b))
+#define MAX(a, b)	((a) > (b)? (a): (b))
+
 /*
  * Sizes
  */

+ 4 - 2
sys/src/9/ppc/mkfile

@@ -64,8 +64,10 @@ LIB=\
 	/$objtype/lib/libmemlayer.a\
 	/$objtype/lib/libmemdraw.a\
 	/$objtype/lib/libdraw.a\
-	/$objtype/lib/libc.a\
 	/$objtype/lib/libsec.a\
+	/$objtype/lib/libmp.a\
+	/$objtype/lib/libip.a\
+	/$objtype/lib/libc.a\
 
 ETHER=`{echo devether.c ether*.c | sed 's/\.c/.'$O'/g'}
 VGA=`{echo devvga.c screen.c vga*.c | sed 's/\.c/.'$O'/g'}
@@ -103,6 +105,6 @@ init.h:	../port/initcode.c init9.s
 	$AS init9.s
 	$LD -l -s -R4 -o init.out init9.$O initcode.$O /power/lib/libc.a
 	{echo 'uchar initcode[]={'
-	 strip < init.out | xd -1x |
+		strip -o /fd/1 init.out | xd -1x |
 		sed -e 's/^[0-9a-f]+ //' -e 's/ ([0-9a-f][0-9a-f])/0x\1,/g'
 	 echo '};'} > init.h

+ 16 - 1
sys/src/9/ppc/saturntimer.c

@@ -26,6 +26,21 @@ static ulong ticks;
 static Lock tlock;
 static ushort timer_ctl;
 
+ulong multiplier;
+
+ulong
+µs(void)
+{
+	uvlong x;
+
+	if(multiplier == 0){
+		multiplier = (uvlong)(1000000LL << 16) / m->cyclefreq;
+		print("µs: multiplier %ld, cyclefreq %lld, shifter %d\n", multiplier, m->cyclefreq, 16);
+	}
+	cycles(&x);
+	return (x*multiplier) >> 16;
+}
+
 void
 saturntimerintr(Ureg *u, void*)
 {
@@ -72,7 +87,7 @@ fastticks(uvlong *hz)
 }
 
 void
-timerset(uvlong next)
+timerset(Tval next)
 {
 	ulong offset;
 	uvlong now;

+ 69 - 0
sys/src/9/ppc/trap.c

@@ -11,6 +11,18 @@
 static Lock vctllock;
 Vctl *vctl[256];
 
+int intrstack[5];
+uvlong intrtime[5];
+vlong lastoffset;
+int inintr;
+int nintr[10];
+int nintro;
+int dblintr[64];
+ulong thisto[32];
+ulong thistoo;
+vlong vnot[64];
+ulong vnon[64];
+
 void
 intrenable(int irq, void (*f)(Ureg*, void*), void* a, char *name)
 {
@@ -389,6 +401,63 @@ setmvec(int v, void (*r)(void), void (*t)(void))
 		vp[n] = (18<<26)|(pa&0x3FFFFFC)|3;	/* bla */
 }
 
+
+void
+intr(Ureg *ureg)
+{
+	int vno, pvno, i;
+	Vctl *ctl, *v;
+	void (*pt)(Proc*, int, vlong);
+	uvlong tt, x;
+
+	cycles(&tt);
+	pt = proctrace;
+	pvno = -1;
+	for(i = 0; i < 64; i++){
+		vno = intvec();
+		if(vno == 0)
+			break;
+		cycles(&x);
+		vnot[vno] -= x;
+		if(vno == pvno)
+			dblintr[vno]++;
+		pvno = vno;
+		if(pt && up && up->trace)
+			pt(up, (vno << 16) | SInts, 0);
+	
+		if(vno > nelem(vctl) || (ctl = vctl[vno]) == 0) {
+			iprint("spurious intr %d\n", vno);
+			return;
+		}
+
+		for(v = ctl; v != nil; v = v->next)
+			if(v->f)
+				v->f(ureg, v->a);
+
+		intend(vno);	/* reenable the interrupt */
+
+		if(pt && up && up->trace)
+			pt(up, (vno << 16) | SInte, 0);
+		cycles(&x);
+		vnot[vno] += x;
+		vnon[vno]++;
+	}
+	if(i < nelem(nintr))
+		nintr[i]++;
+	else
+		nintro++;
+	cycles(&x);
+	tt = x - tt;
+	i = tt / 3600;	 /* 100 microseconds units */
+	if(i < nelem(thisto))
+		thisto[i]++;
+	else
+		thistoo++;
+
+	if(up)
+		preempted();
+}
+
 char*
 fpexcname(Ureg *ur, ulong fpscr, char *buf)
 {

+ 51 - 0
sys/src/9/ppc/uartsmc.c

@@ -96,6 +96,57 @@ smcpnp(void)
 	return smcuart;
 }
 
+void
+smcsetup(Uart *uart)
+{
+	Uartsmc *p;
+	SMC *smc;
+	UartData *ud;
+
+	ud = uart->regs;
+
+	/* magic addresses */
+
+	p = &m->immr->uartsmc[ud->smcno];
+	smc = imm->smc + ud->smcno;	/* SMC1 */
+	ud->smc = smc;
+	ud->usmc = p;
+
+	/* step 0: disable rx/tx */
+	smc->smcmr &= ~3;
+
+	ioplock();
+
+	/* step 1, Using Port D */
+	if (ud->smcno != 0)
+		panic("Don't know how to set Port D bits");
+	imm->port[SMC1PORT].ppar |= SMRXD1|SMTXD1;
+	imm->port[SMC1PORT].pdir |= SMTXD1;
+	imm->port[SMC1PORT].pdir &= ~SMRXD1;
+	imm->port[SMC1PORT].psor &= ~(SMRXD1|SMTXD1);
+
+	/* step 2: set up brgc1 */
+	imm->brgc[ud->smcno]  = baudgen(uart->baud) | 0x10000;
+
+	/* step 3: route clock to SMC1 */
+	imm->cmxsmr &= (ud->smcno == 0) ? ~0xb0 : ~0xb;	/* clear smcx and smcxcs */
+
+	iopunlock();
+
+	/* step 4: assign a pointer to the SMCparameter RAM */
+	m->immr->param[ud->smcno].smcbase = (ulong)p - IMMR;
+
+	/* step 6: issue command to CP */
+	if (ud->smcno == 0)
+		cpmop(InitRxTx, SMC1ID, 0);
+	else
+		cpmop(InitRxTx, SMC2ID, 0);
+
+	/* step 7: protocol parameters */
+	p->rfcr = 0x30;
+	p->tfcr = 0x30;
+}
+
 void
 smcinit(Uart *uart)
 {

+ 17 - 0
sys/src/9/ppc/uartsmc.h

@@ -0,0 +1,17 @@
+
+typedef struct UartData UartData;
+struct UartData
+{
+	int	smcno;	/* smc number: 0 or 1 */
+	SMC	*smc;
+	Uartsmc	*usmc;
+	char	*rxbuf;
+	char	*txbuf;
+	BD*	rxb;
+	BD*	txb;
+	int	initialized;
+	int	enabled;
+} uartdata[Nuart];
+
+int baudgen(int);
+void smcsetup(Uart *uart);

+ 1 - 1
sys/src/9/ppc/ucu

@@ -56,4 +56,4 @@ bootdir
 	/power/bin/ls
 	/power/bin/ps
 	/power/bin/auth/wrkey
-	/sys/lib/sysconfig/blast/boot
+	/sys/lib/sysconfig/ppc/boot

+ 5 - 2
sys/src/cmd/acme/fsys.c

@@ -145,6 +145,8 @@ fsysproc(void *)
 	Fcall t;
 	uchar *buf;
 
+	threadsetname("fsysproc");
+
 	x = nil;
 	for(;;){
 		buf = emalloc(messagesize+UTFmax);	/* overflow for appending partial rune in xfidwrite */
@@ -323,7 +325,8 @@ fsysversion(Xfid *x, Fid*)
 
 	if(x->msize < 256)
 		return respond(x, &t, "version: message size too small");
-	messagesize = x->msize;
+	if(x->msize < messagesize)
+		messagesize = x->msize;
 	t.msize = messagesize;
 	if(strncmp(x->version, "9P2000", 6) != 0)
 		return respond(x, &t, "unrecognized 9P version");
@@ -625,7 +628,7 @@ fsysread(Xfid *x, Fid *f)
 			for(j=0; j<row.ncol; j++){
 				c = row.col[j];
 				for(k=0; k<c->nw; k++){
-					ids = realloc(ids, (nids+1)*sizeof(int));
+					ids = erealloc(ids, (nids+1)*sizeof(int));
 					ids[nids++] = c->w[k]->id;
 				}
 			}

+ 2 - 2
sys/src/libdraw/init.c

@@ -200,7 +200,7 @@ getwindow(Display *d, int ref)
 Display*
 initdisplay(char *dev, char *win, void(*error)(Display*, char*))
 {
-	char buf[NINFO+1], info[NINFO+1], *t, isnew;
+	char buf[128], info[NINFO+1], *t, isnew;
 	int n, datafd, ctlfd, reffd;
 	Display *disp;
 	Dir *dir;
@@ -240,7 +240,7 @@ initdisplay(char *dev, char *win, void(*error)(Display*, char*))
 	}
 	if(n==NINFO+1)
 		n = NINFO;
-	buf[n] = '\0';
+	info[n] = '\0';
 	isnew = 0;
 	if(n < NINFO)	/* this will do for now, we need something better here */
 		isnew = 1;